通过ioctl函数实现6盏灯的亮灭,蜂鸣器、风扇、马达的开关
应用层代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "device.h"
int main(int argc,const char * argv[])
{
int fd=open("/dev/mydevice0",O_RDONLY);
if(fd<0)
{
perror("open");
return -1;
}
int device,status;
while (1)
{
printf("1-(top_led1),2-(top_led2),3-(top_led3)\n");
printf("4-(bot_led1),5-(bot_led2),6-(bot_led3)\n");
printf("7-(beep),8-(fan),9-(motor)\n");
printf("请选择要操作的设备>>");
scanf("%d",&device);
printf("请选择该设备的状态:1-(开),0-(关)>>");
scanf("%d",&status);
switch (device)
{
case 1:
if(status == 1){
ioctl(fd,DEVICE_ON,&device);
}else{
ioctl(fd,DEVICE_OFF,&device);
}
break;
case 2:
if(status == 1){
ioctl(fd,DEVICE_ON,&device);
}else{
ioctl(fd,DEVICE_OFF,&device);
}
break;
case 3:
if(status == 1){
ioctl(fd,DEVICE_ON,&device);
}else{
ioctl(fd,DEVICE_OFF,&device);
}
break;
case 4:
if(status == 1){
ioctl(fd,DEVICE_ON,&device);
}else{
ioctl(fd,DEVICE_OFF,&device);
}
break;
case 5:
if(status == 1){
ioctl(fd,DEVICE_ON,&device);
}else{
ioctl(fd,DEVICE_OFF,&device);
}
break;
case 6:
if(status == 1){
ioctl(fd,DEVICE_ON,&device);
}else{
ioctl(fd,DEVICE_OFF,&device);
}
break;
case 7:
if(status == 1){
ioctl(fd,DEVICE_ON,&device);
}else{
ioctl(fd,DEVICE_OFF,&device);
}
break;
case 8:
if(status == 1){
ioctl(fd,DEVICE_ON,&device);
}else{
ioctl(fd,DEVICE_OFF,&device);
}
break;
case 9:
if(status == 1){
ioctl(fd,DEVICE_ON,&device);
}else{
ioctl(fd,DEVICE_OFF,&device);
}
break;
default:
break;
}
}
return 0;
}
内核层代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include "device.h"
struct cdev *cdev;
int res,i;
int major=0;
int minor=0;
dev_t devnum;
struct class *cls;
struct device *dev;
gpio_t *top_led1;
gpio_t *top_led2;
gpio_t *top_led3;
gpio_t *bot_led1;
gpio_t *bot_led2;
gpio_t *bot_led3;
gpio_t *beep;
gpio_t *fan;
gpio_t *motor;
unsigned int *rcc_ahb4;
unsigned int *rcc_ahb5;
unsigned int *rcc_apb2;
//定义自己的open,read,write,close,ioctl函数
int my_open(struct inode *inode,struct file *file)
{
return 0;
}
ssize_t my_read(struct file *file,char *user,size_t size,loff_t *loff)
{
return 0;
}
ssize_t my_write(struct file *file,const char *user,size_t size,loff_t *loff)
{
return 0;
}
long my_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
int device;
//接收来自于用户空间的数据
res=copy_from_user(&device,(void *)arg,sizeof(int));
switch (device)
{
case 1:
if(cmd==DEVICE_ON){
top_led1->ODR |= (1<<10);
}else{
top_led1->ODR &= (~(1<<10));
}
break;
case 2:
if(cmd==DEVICE_ON){
top_led2->ODR |= (1<<10);
}else{
top_led2->ODR &= (~(1<<10));
}
break;
case 3:
if(cmd==DEVICE_ON){
top_led3->ODR |= (1<<8);
}else{
top_led3->ODR &= (~(1<<8));
}
break;
case 4:
if(cmd==DEVICE_ON){
bot_led1->ODR |= (1<<5);
}else{
bot_led1->ODR &= (~(1<<5));
}
break;
case 5:
if(cmd==DEVICE_ON){
bot_led2->ODR |= (1<<6);
}else{
bot_led2->ODR &= (~(1<<6));
}
break;
case 6:
if(cmd==DEVICE_ON){
bot_led3->ODR |= (1<<7);
}else{
bot_led3->ODR &= (~(1<<7));
}
break;
case 7:
if(cmd==DEVICE_ON){
beep->ODR |= (1<<6);
}else{
beep->ODR &= (~(1<<6));
}
break;
case 8:
if(cmd==DEVICE_ON){
fan->ODR |= (1<<9);
}else{
fan->ODR &= (~(1<<9));
}
break;
case 9:
if(cmd==DEVICE_ON){
motor->ODR |= (1<<6);
}else{
motor->ODR &= (~(1<<6));
}
break;
default:
break;
}
return 0;
}
int my_close(struct inode *inode,struct file *file)
{
return 0;
}
//初始化硬件设备
void device_init(void)
{
//映射扩展板led物理地址
rcc_ahb4=ioremap(RCC_AHB4,4);
if(rcc_ahb4==NULL)
{
printk("rcc寄存器地址映射失败\n");
}
top_led1=ioremap(GPIOE,sizeof(gpio_t));
if(top_led1==NULL)
{
printk("led1相关寄存器地址映射失败\n");
}
top_led2=ioremap(GPIOF,sizeof(gpio_t));
if(top_led2==NULL)
{
printk("led2相关寄存器地址映射失败\n");
}
top_led3=ioremap(GPIOE,sizeof(gpio_t));
if(top_led3==NULL)
{
printk("led3相关寄存器地址映射失败\n");
}
//初始化扩展板led,pe10,pf10,pe8
*rcc_ahb4 |= (3<<4);
top_led1->MODER &= (~(3<<20));
top_led1->MODER |= (1<<20);
top_led2->MODER &= (~(3<<20));
top_led2->MODER |= (1<<20);
top_led3->MODER &= (~(3<<16));
top_led3->MODER |= (1<<16);
//映射主板led物理地址
rcc_ahb5=ioremap(RCC_AHB5,4);
if(rcc_ahb5==NULL)
{
printk("rcc寄存器地址映射失败\n");
}
bot_led1=ioremap(GPIOZ,sizeof(gpio_t));
if(bot_led1==NULL)
{
printk("led1相关寄存器地址映射失败\n");
}
bot_led2=ioremap(GPIOZ,sizeof(gpio_t));
if(bot_led2==NULL)
{
printk("led2相关寄存器地址映射失败\n");
}
bot_led3=ioremap(GPIOZ,sizeof(gpio_t));
if(bot_led3==NULL)
{
printk("led3相关寄存器地址映射失败\n");
}
//初始化主板led,pz5,pz6,pz7
*rcc_ahb5 |= 1;
bot_led1->MODER &= (~(3<<10));
bot_led1->MODER |= (1<<10);
bot_led2->MODER &= (~(3<<12));
bot_led2->MODER |= (1<<12);
bot_led3->MODER &= (~(3<<14));
bot_led3->MODER |= (1<<14);
//映射蜂鸣器,风扇,马达物理地址
beep=ioremap(GPIOB,sizeof(gpio_t));
if(beep==NULL)
{
printk("fan相关寄存器地址映射失败\n");
}
fan=ioremap(GPIOE,sizeof(gpio_t));
if(fan==NULL)
{
printk("fan相关寄存器地址映射失败\n");
}
motor=ioremap(GPIOF,sizeof(gpio_t));
if(motor==NULL)
{
printk("fan相关寄存器地址映射失败\n");
}
//初始化beep-pb6,fan-pe9,motor-pf6,
beep->MODER &= (~(3<<12));
beep->MODER |= (1<<12);
fan->MODER &= (~(3<<18));
fan->MODER |= (1<<18);
motor->MODER &= (~(3<<12));
motor->MODER |= (1<<12);
}
//定义操作方法结构体并初始化
struct file_operations fops=
{
.open=my_open,
.read=my_read,
.write=my_write,
.unlocked_ioctl=my_ioctl,
.release=my_close,
};
//入口函数
static int __init mycdev_init(void)
{
//给字符设备驱动变量申请空间
cdev=cdev_alloc();
if(cdev==NULL)
{
printk("申请空间失败\n");
res=-ENOMEM;
goto ERR1;
}
//初始化字符设备驱动
cdev_init(cdev,&fops);
//动态申请字符设备号
res=alloc_chrdev_region(&devnum,minor,9,"mydevice");
{
if(res)
{
printk("动态申请设备号失败\n");
res=-ENOMEM;
goto ERR2;
}
major=MAJOR(devnum);
minor=MINOR(devnum);
}
//将字符设备驱动注册到内核中
res=cdev_add(cdev,devnum,9);
if(res)
{
printk("添加字符设备到内核失败\n");
res=-ENOMEM;
goto ERR3;
}
printk("字符设备驱动注册成功\n");
//向上提交目录
cls=class_create(THIS_MODULE,"mydevice");
if(IS_ERR(cls))
{
printk("向上提交目录失败\n");
res=PTR_ERR(cls);
goto ERR4;
}
printk("向上提交目录成功\n");
//向上提交设备节点
for(i=0;i<9;i++)
{
dev=device_create(cls,NULL,MKDEV(major,i),NULL,"mydevice%d",i);
if(IS_ERR(dev))
{
printk("向上提交节点信息失败\n");
res=PTR_ERR(dev);
goto ERR5;
}
}
printk("向上提交节点信息成功\n");
device_init();
return 0;
ERR5:
for(--i;i>=0;i--)
{
device_destroy(cls,MKDEV(major,i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(devnum,9);
ERR2:
kfree(cdev);
ERR1:
return res;
}
//出口函数
static void __exit mycdev_exit(void)
{
//取消映射
iounmap(rcc_ahb4);
iounmap(top_led1);
iounmap(top_led2);
iounmap(top_led3);
iounmap(rcc_ahb5);
iounmap(bot_led1);
iounmap(bot_led2);
iounmap(bot_led3);
iounmap(beep);
iounmap(fan);
iounmap(motor);
//销毁设备节点
for (i = 0; i < 9; i++)
{
device_destroy(cls,MKDEV(major,i));
}
//销毁目录
class_destroy(cls);
//注销字符设备驱动
cdev_del(cdev);
//释放设备号
unregister_chrdev_region(MKDEV(major,minor),9);
//释放内存空间
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
头文件
#ifndef __DEVICE_H__
#define __DEVICE_H__
typedef struct
{
volatile unsigned int MODER;
volatile unsigned int OTYPER;
volatile unsigned int OSPEEDR;
volatile unsigned int PUPDR;
volatile unsigned int IDR;
volatile unsigned int ODR;
}gpio_t;
#define RCC_AHB4 0x50000a28
#define RCC_AHB5 0x54000210
#define GPIOB 0x50003000
#define GPIOE 0x50006000
#define GPIOF 0x50007000
#define GPIOZ 0x54004000
#define DEVICE_ON _IOW('a',1,int)
#define DEVICE_OFF _IOW('a',0,int)
#endif
Makefile
file ?= demo
arch ?= arm
ifeq ($(arch),arm)
KERNELDIR := /home/ubuntu/fsmp1a/linux-stm32mp-5.10.61-stm32mp-r2-r0/linux-5.10.61
else
KERNELDIR := /lib/modules/$(shell uname -r)/build/
endif
PWD := $(shell pwd)
all:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
make -C $(KERNELDIR) M=$(PWD) clean
obj-m := $(file).o
该示例展示了如何通过用户空间的应用程序使用ioctl函数与内核模块交互,控制GPIO端口来开关6盏灯、蜂鸣器、风扇和马达。内核模块实现了设备驱动,包括初始化GPIO,以及处理来自用户空间的打开、关闭和状态切换请求。
4173

被折叠的 条评论
为什么被折叠?



