在加载驱动之前,先来看一下/proc/devices,里面是现在内核所支持的设备。
第一列是主设备号,对应的是内核数组chrdevs的下标,第二列是它的名字。
加载驱动的命令:insmod first_drv.ko
cat /proc/devices
写个测试驱动程序来测试它。(见代码二)
mknod /dev/xxx c 主设备号 次设备号
register_chrdev(111,"first_drv",&first_drv_fops);
怎么确定这设备号111?
1、先cat /proc/devices,有哪一个空缺项,然后选取那个空缺项。(1~255)。
2、也可以写为0,系统会自动给我们分配一个主设备号,它在数组chrdevs找一个空缺项,给你返回主设备号。
major=register_chrdev(0,...,...);
1、驱动的主设备号:自动分配主设备号;手工指定主设备号。
2、应用:open("/dev/xxx");
xxx文件怎么来的?
a.mknod /dev/xxx c 主设备号 次设备号(手工建立,需要先知道主设备号)
b.自动创建:对于应用程序,udev机制;对于busybox来说,mdev程序
cd /sys(系统目录下的信息)。注册驱动时,先会在sys目录下生成设备信息
mdev:会根据系统的信息,创建设备节点。(驱动程序里,如果能提供系统的信息,mdev就能自动帮我们创建设备节点)
★★★
为什么sys目录下的信息一更改,mdev就会自动运行、生成呢?
vi /etc/init.d/rcS:
在创建根文件系统时:
内核里一旦有设备加载进去或者卸载之后,就会根据调用/proc/sys/kernel/hotplug所指示的应用程序。
程序一:first_dev.c
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> static struct class *firstdrv_class; //一个类 static struct class_device *firstdrv_class_dev; //一个类里面再建立一个设备 static int first_drv_open(struct inode *inode, struct file *file) { printk("first_drv_open\n"); return 0; } static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { printk("first_drv_write\n"); return 0; } static struct file_operations first_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = first_drv_open, .write = first_drv_write, }; int major; int first_drv_init(void) { //0:让系统给我们自动分配一个主设备号,在数组中找个空缺,返回主设备号 major = register_chrdev(0, "first_drv", &first_drv_fops); //注册驱动程序,告诉内核,再内核数组里,把fops结构填充进去 /* 下面这两条代码就会在sys目录下,class目录下,建立firstdrv这个类,再firstdrv这个类下,会创建xyz这个设备,再xyz目录下有个dev文件,里面有主设备号:次设备号 */ /* 然后mdev根据上面的系统信息,就会创建/dev/xyz这个设备节点 */ /* ★★下面两条代码会根据自动分配的主设备号,生成设备文件,并且生成系统信息 */ //先建立一个类 firstdrv_class = class_create(THIS_MODULE, "firstdrv"); //在这个类下面建立一个设备 firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); return 0; } void first_drv_exit(void) { unregister_chrdev(major, "first_drv"); //卸载驱动 class_device_unregister(firstdrv_class_dev); class_destroy(firstdrv_class); } module_init(first_drv_init); module_exit(first_drv_exit); MODULE_LICENSE("GPL"); //license,可以解决class_device_unregister和class_destory不能识别的问题
程序二:测试程序firstdrvtest.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main(int argc, char **argv) { int fd; int val = 1; fd = open("/dev/xyz", O_RDWR); //无论主设备号怎么变,/dev/xyz都是系统为我们创建的 if (fd < 0) { printf("can't open!\n"); } write(fd, &val, 4); return 0; }
编译程序:arm-linux-gcc -o fristdrvtest fristdrvtest.c