《驱动学习 - 驱动程序框架简单介绍》
1.驱动简单架构
#include <linux/kernel.h> #include <linux/module.h> #include <linux/miscdevice.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/moduleparam.h> #include <linux/slab.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/delay.h> #include <linux/gpio.h> #include <mach/gpio.h> #include <plat/gpio-cfg.h> #define major 111 //当major设置为0的时候,就是让系统自动分配 static struct class * static int first_drv_open(struct inode *inode, struct file *file) { return 0; } static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { return 0; } static struct file_operations first_drv_fops = { .owner = THIS_MODULE, .open = first_drv_open, .write = first_drv_write, }; static int __init first_drv_init(void) { //major是主设备号 ,"first_drv"就是注册的名字 register_chrdev(major, "first_drv", &first_drv_fops); return 0; } static void first_drv_exit(void) { unregister_chrdev(major, "first_drv"); } module_init(first_drv_init); module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
register_chrdev函数解释:
register_chrdev会在内核中找到一个数组chrdev,然后在这个数组里面,以major为索引,将file_operations填充进去。这样应用程序就可以直接open("/dev/led"),来找到该设备的设备号,然后根据设备号找到对应的file_operations去执行对应的open函数。
应用程序怎么通过open("/dev/xxx","")找到相对应的设备函数?
应用程序:open("/dev/xxx","")
获取/dev/xxx的属性,比如字符型c,获取主设备号
通过C库进入到内核中
在VFS中(待确认,以及什么是虚拟文件系统)通过c找到chrdev,再通过主设备号找到对应的file_operation
通过file_operation找到对应的xxx_open,xxx_write等等
应用程序:open("/dev/xxx","")
获取/dev/xxx的属性,比如字符型c,获取主设备号
通过C库进入到内核中
在VFS中(待确认,以及什么是虚拟文件系统)通过c找到chrdev,再通过主设备号找到对应的file_operation
通过file_operation找到对应的xxx_open,xxx_write等等
那么驱动程序这边应该怎么做才能让应用程序找到自己?
在驱动程序中:编写xxx_open,xxx_write等等
定义一个file_operation结构体变量
将编写好的xxx_open,xxx_write等等初始化到定义的结构体变量中
将file_operation结构体通过register_chrdev函数注册到内核中
通过以上两个就能很直观的理解,应用程序是怎么找到相对应的驱动程序,驱动程序是怎么编写让应用程序可以找到。
主设备号怎么设置?
1.主设备号可以通过我们register_chrdev(111, "first_drv", &first_drv_fops)这样固定去设置。可以先通过cat /proc/devices查看已经有哪些主设备号被注册,然后选择没有被注册的。
2.major=register_chrdev(0, "first_drv", &first_drv_fops),将register_chrdev函数的第一个参数设置为0。那么系统会自动帮你分配一个主设备号major。
那么就有一个问题/dev/xxx是怎么创建的?
1.mknod /dev/xxx c 主设备号 次设备号(在开发板中进行mknod,而且这个时候需要知道设置的主设备号,还需要在开发板上面进行查看)
2.自动创建mkdev(根据系统信息创建设备节点,因为驱动程序加载的时候,就会在/sys里面生成信息)
cd /sys/class
1.mknod /dev/xxx c 主设备号 次设备号(在开发板中进行mknod,而且这个时候需要知道设置的主设备号,还需要在开发板上面进行查看)
2.自动创建mkdev(根据系统信息创建设备节点,因为驱动程序加载的时候,就会在/sys里面生成信息)
cd /sys/class
进入我们创建的比如firstdrv,里面假如有个xyz,
cd xyz
cat dev
cat dev
就可以看到主设备号和次设备号,mdev根据这个信息创建设备节点
那么怎么生成系统信息?
static struct class *firstdrv_class; static struct class_device *firstdrv_class_dev; int major; static int first_drv_init(void) { major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核 firstdrv_class = class_create(THIS_MODULE, "firstdrv"); firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */ gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16); gpbdat = gpbcon + 1; return 0; } static void first_drv_exit(void) { unregister_chrdev(major, "first_drv"); // 卸载 class_device_unregister(firstdrv_class_dev); class_destroy(firstdrv_class); iounmap(gpbcon); }
class_create和class_device_create,就会在/sys/class中生成一个firstdrv文件,里面会有一个xyz文件。
MKDEV会根据这个系统信息去创建一个/dev/xyz的设备节点。
问题:为什么一旦卸载驱动程序然后再insmod,mdev可以里面又创建新的设备节点
因为在rcs脚本中我们定义了ech0 /sbin/mdev >/proc/sys/kernel/hotplug
注意:register_chrdev函数的第二个参数,在后面使用设备树的时候有用。因为设备树通过这个参数一致名字
在开发板中cat /proc/devices可以看到已经注册的设备