文章目录
Linux内核字符设备文件的自动创建
自动创建字符设备文件概述
字符设备文件的自动创建只需要“三个保证”+“四个函数”
三个保证
四个函数
修改之前LED驱动代码
运行结果
Linux内核字符设备文件的自动创建
前面已经描述过通过mknod命令可以手动创建字符设备文件,那么如何实现自动创建设备文件呢?
自动创建字符设备文件概述
字符设备文件的自动创建只需要“三个保证”+“四个函数”
三个保证
保证根文件系统rootfs中必须有mdev可执行程序,例如:执行命令
#确认根文件系统中有mdev,mdev可以帮助自动创建设备文件 which is mdev
保证根文件系统rootfs的启动脚本rcS中必须有以下内容:
#将来解析fstab文件 mount -a #驱动程序解析hotplug来确认谁来帮驱动创建设备文件(需要mdev可执行程序) echo /sbin/mdev > /proc/sys/kernel/hotplug
保证根文件系统rootfs的配置文件fstab中必须有以下三句话:
/proc作为procfs虚拟文件系统的入口
/sys作为sysfs虚拟文件系统的入口
/dev作为tmpfs虚拟文件系统的入口
这三种文件系统的内容都是由内核创建并且创建在内存中,掉电丢失。
proc /proc proc defaults 0 0 sysfs /sys syfs defaluts 0 0 tmpfs /dev tmpfs defulats 0 0
四个函数
驱动程序接下来只需要调用以下四个函数即可完成设备文件的创建:
(编写驱动时记得添加头文件 #include <linux/device.h>)
- 定义一个设备类指针:
struct class *cls;
- 创建一个设备类对象,cls指向这个对象,并且设定这个对象类名:
cls = class_create(THIS_MODULE, "myclass");
- 正式创建设备文件,(暂时还是利用led等示例来说明)设备文件为/dev/myled,其中dev为设备号,myled为设备文件名。
device_create(cls, NULL, dev, NULL, "myled");
- 删除设备文件(在卸载驱动的时候自动删除设备文件)
device_destroy(cls, dev);
- 删除设备类(卸载驱动时用到)。
class_destroy(cls);
修改之前LED驱动代码
- led_drv.c
#include <linux/init.h> #include <linux/module.h> #include <linux/gpio.h> #include <mach/platform.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> //copy_from_user声明 #include <linux/device.h> //设备文件的自动创建 //声明描述LED硬件信息的数据结构 struct led_resource { int gpio; //GPIO编号 char *name; //LED名称 }; //定义初始化LED的硬件信息对象 static struct led_resource led_info[] = { { .name = "LED1", .gpio = PAD_GPIO_C+12 }, { .name = "LED2", .gpio = PAD_GPIO_C+7 }, { .name = "LED3", .gpio = PAD_GPIO_C+11 }, { .name = "LED4", .gpio = PAD_GPIO_B+26 } }; //调用关系:应用ioctl->软中断->内核sys_ioctl->驱动led_ioctl //例子:int index=1;ioctl(fd, LED_ON, &index);//开第1个灯 //参数关系: // fd<---->file 亲戚关系 // cmd=LED_ON或者cmd=LED_OFF // arg=(unsigned long)&index #define LED_ON 0x100001 //开灯命令 #define LED_OFF 0x100002 //关灯命令 static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { //1.分配内核缓冲区,保存用法发送过来的灯的编号 int kindex; //2.拷贝用户缓冲区数据到内核缓冲区 //kindex = *(int *)arg; //相当危险 copy_from_user(&kindex, (int *)arg, sizeof(kindex)); //3.解析用户发送过来的命令 switch(cmd) { case LED_ON: gpio_set_value(led_info[kindex-1].gpio, 0); printk("%s:开第%d个灯", __func__, kindex); break; case LED_OFF: gpio_set_value(led_info[kindex-1].gpio, 1); printk("%s:关第%d个灯", __func__, kindex); break; default: printk("无效命令!\n"); return -1; } return 0; //执行成功返回0,执行失败返回负值 } //定义初始化LED的硬件操作接口对象 static struct file_operations led_fops = { .unlocked_ioctl = led_ioctl, //不仅仅向硬件设备发送控制命令,还能和设备进行读写操作 }; //定义设备号对象 static dev_t dev; //定义字符设备对象 static struct cdev led_cdev; //定义设备类指针 static struct class *cls; static int led_init(void) { int i; //1.申请设备号 alloc_chrdev_region(&dev, 0, 1, "myled"); //2.初始化字符设备对象,本质就是给字符设备添加操作接口 cdev_init(&led_cdev, &led_fops); //3.向内核注册字符设备对象并且提供硬件操作接口 cdev_add(&led_cdev, dev, 1); //4.申请GPIO资源配置为输出,输出1 for(i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_request(led_info[i].gpio, led_info[i].name); gpio_direction_output(led_info[i].gpio, 1); } //5.创建设备类对象,类似长树枝 //将来会创建:rootfs/sys/class/zhangsan cls = class_create(THIS_MODULE, "zhangsan"); //6.创建设备文件/dev/myled,类似长苹果 //dev:就是提供创建设备文件时所需的设备号 //myled:就是提供创建设备文件是所需的设备文件名 device_create(cls, NULL, dev, NULL, "myled"); return 0; } static void led_exit(void) { int i; //1.输出1,释放GPIO资源 for (i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_set_value(led_info[i].gpio, 1); gpio_free(led_info[i].gpio); } //2.释放设备号 unregister_chrdev_region(dev, 1); //3.卸载字符设备对象 cdev_del(&led_cdev); //4.删除设备文件(摘苹果)和设备类(砍树枝) device_destroy(cls, dev); class_destroy(cls); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL");
- led_test.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #define LED_ON 0x100001 //开灯命令 #define LED_OFF 0x100002 //关灯命令 int main(int argc, char *argv[]) { int fd; int index; //分配用户缓冲区,保存操作灯的编号 if(argc != 3) { printf("用法:%s <on|off> <1|2|3|4>\n", argv[0]); return -1; } //打开设备 fd = open("/dev/myled", O_RDWR); if (fd < 0) { printf("打开设备失败!\n"); return -1; } //"1"->1 index = strtoul(argv[2], NULL, 0); //应用ioctl->软中断->内核sys_ioctl->驱动led_ioctl if(!strcmp(argv[1], "on")) ioctl(fd, LED_ON, &index); else if(!strcmp(argv[1], "off")) ioctl(fd, LED_OFF, &index); //关闭设备 close(fd); return 0; }
- Makefile
obj-m += led_drv.o all: make -C /opt/kernel SUBDIRS=$(PWD) modules clean: make -C /opt/kernel SUBDIRS=$(PWD) clean
运行结果
加载驱动:
insmod led_drv.ko