linux设备驱动之字符设备驱动模型(2)
在上一篇中我们已经了解了字符设备驱动的原理,也了解了应用层调用内核函数的机制,但是我们每次操作设备,都必须首先通过mknod命令创建一个设备文件名,比如说我们要打开u盘,硬盘等这些设备,难道我们还要自己创建,就如同刘老师常说的一句话,这也太山寨了吧,所以我们今天我们来点比较专业的,让函数帮我们自动创建;
在Linux 下,设备和驱动通常都需要挂接在一种总线上,总线有PCI、USB、I2C、SPI 等等,总线是处理器和设备之间的通道,在设备模型中,所有的设备都通过总线相连,一总线来管理设备和驱动函数;
因此我们先了解一下sys下的目录
block:用于管理块设备,系统中的每一个块设备会在该目录下对应一个子目录;
bus:用于管理总线,没注册一条总线,在该目录下有一个对应的子目录,其中,每个总线子目录下会有两个子目录:devices和drivers。
devices包含里系统中所有属于该总线的的设备。
drivers包含里系统中所有属于该总线的的驱动。
class:将系统中的设备按功能分类。
devices:该目录提供了系统中设备拓扑结构图。
dev:该目录已注册的设备节点的视图。
kernel:内核中的相关参数。
module:内核中的模块信息。
fireware:内核中的固件信息。
Fs:描述内核中的文件系统。
下面的代码是我们在sys/class中创建一个名为dog的类,然后在创建一个设备(wangcai);
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/cdev.h> 4 #include <linux/fs.h> 5 #include <linux/device.h> 6 #include <linux/err.h> 7 8 MODULE_LICENSE("GPL"); 9 MODULE_AUTHOR("bunfly"); 10 11 struct class *dog; 12 struct device *wangcai; 13 14 int bunfly_init() 15 { 16 dog = class_create(THIS_MODULE, "dog");//创建一个dog类 17 if(IS_ERR(dog)) { 18 PTR_ERR(dog); 19 return 1; 20 } 21 22 wangcai = device_create(dog, NULL, MKDEV(9, 0), NULL, "wangcai%d", 1);//创建一个名为旺财的设备 23 if(IS_ERR(wangcai)) { 24 PTR_ERR(wangcai); 25 return 1; 26 } 27 28 return 0; 29 } 30 31 int bunfly_exit() 32 { 33 printk("this is bunfly_exit\n"); 34 35 return 0; 36 } 37 38 module_init(bunfly_init); 39 module_exit(bunfly_exit); 40
在实际的工作中我们一般都不需要创建类,设备等,linux系统都为常见的设备分好了类,而设备厂商都已经提供了,我们做的就是来驱动这些设备;在sys/class类中我们经常用的就是misc(杂项类)
杂项设备也是在嵌入式系统中用得比较多的一种设备驱动,其定义如下:
1 struct device; 2 3 struct miscdevice { 4 int minor; //次设备号 5 const char *name; //设备名 6 const struct file_operations *fops;//文件操作 7 struct list_head list; //形成链表 8 struct device *parent; 9 struct device *this_device; 10 const char *nodename; 11 mode_t mode; 12 }; 13 14 extern int misc_register(struct miscdevice * misc); //混杂设备注册 15 extern int misc_deregister(struct miscdevice *misc); //混杂设备注销 16 17 #define MODULE_ALIAS_MISCDEV(minor) \ 18 MODULE_ALIAS("char-major-" __stringify(MISC_MAJOR) \ 19 "-" __stringify(minor)) 20 #endif
下面代码是在misc下注册一个名为bunfly_led的设备;插入模块后,在/dev下生成一个名为bunfly_led的设备名
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/cdev.h> 4 #include <linux/fs.h> 5 #include <linux/device.h> 6 #include <linux/err.h> 7 #include <linux/miscdevice.h> 8 9 MODULE_LICENSE("GPL"); 10 MODULE_AUTHOR("bunfly"); 11 12 struct file_operations fops;//方法 13 struct miscdevice led; 14 15 int bunfly_init() 16 { 17 led.name = "bunfly_led";//设备名 18 led.fops = &fops;//关联方法 19 misc_register(&led);//在杂项类中注册led 20 21 return 0; 22 } 23 24 int bunfly_exit() 25 { 26 printk("this is bunfly_exit\n"); 27 misc_deregister(&led);//注销 28 29 return 0; 30 } 31 32 module_init(bunfly_init); 33 module_exit(bunfly_exit); 34
下面代码的功能是用ioctl()函数控制led灯,格式:./ioctl /dev/bunfly_led 0 (灯亮) | 1(灯灭)
1 #include <stdio.h> 2 #include <string.h> 3 #include <fcntl.h> 4 5 //输入 ./ioctl /dev/bunly_led 1(灯灭) : 0(灯亮) 6 int main(int argc, char *argv[]) 7 { 8 if(argc != 3) { 9 printf("using %s <devname> 1:0\n", argv[0]); 10 return 1; 11 } 12 13 int fd = 0; 14 fd = open(argv[2], O_RDWR); 15 if(fd < 0) { 16 perror("open"); 17 return 1; 18 } 19 20 //argv【2】为字符,需要atoi转换为数字 21 ioctl(fd, atoi(argv[2])); 22 close(fd); 23 return 0; 24 }
内核中:
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/cdev.h> 4 #include <linux/fs.h> 5 #include <linux/device.h> 6 #include <linux/err.h> 7 #include <linux/miscdevice.h> 8 #include <linux/fs.h> 9 #include <linux/io.h> 10 #include <linux/gpio.h> 11 12 MODULE_LICENSE("GPL"); 13 MODULE_AUTHOR("bunfly"); 14 15 int bunfly_open(struct inode *n, struct file *fp); 16 long bunfly_ioctl(struct file *fp, unsigned int num, unsigned long vlaue); 17 void led_on(); 18 void led_off(); 19 20 struct file_operations fops;//方法 21 struct miscdevice led; 22 23 unsigned long gpio_virt; 24 unsigned long *gpm4con, *gpm4dat; 25 26 int bunfly_init() 27 { 28 fops.open = bunfly_open;//调用系统函数 29 fops.unlocked_ioctl = bunfly_ioctl; 30 31 gpio_virt = ioremap(0x11000000, SZ_4K);//led物理地址到虚拟地址的映射 32 gpm4con = gpio_virt + 0x02e0; 33 gpm4dat = gpio_virt + 0x02e4; 34 35 led.name = "bunfly_led"; 36 led.fops = &fops; 37 misc_register(&led);//注册杂项类设备led 38 39 return 0; 40 } 41 42 int bunfly_exit() 43 { 44 printk("this is bunfly_exit\n"); 45 misc_deregister(&led);//注销设备 46 47 return 0; 48 } 49 50 module_init(bunfly_init); 51 module_exit(bunfly_exit); 52 53 int bunfly_open(struct inode *n, struct file *fp) 54 { 55 printk("this is bunfly_open\n"); 56 return 0; 57 } 58 59 long bunfly_ioctl(struct file *fp, unsigned int num, unsigned long vlaue) 60 { 61 if(num == 0) { 62 led_on(); 63 } 64 else { 65 if(num == 1) { 66 led_off(); 67 } 68 else { 69 printk("unkonw command %d\n", num); 70 } 71 } 72 73 return 0; 74 } 75 76 void led_on() 77 { 78 *gpm4con &= ~0xffff; 79 *gpm4con |= 0x1111; 80 *gpm4dat = 0x0; 81 } 82 83 void led_off() 84 { 85 *gpm4con &= ~0xffff; 86 *gpm4con |= 0x1111; 87 *gpm4dat = 0xf; 88 89 }