linux设备驱动(24)字符设备
1 字符设备的实例
以led驱动的简单字符设备驱动
led_driver.c
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <asm/uaccess.h> 7 #include <asm/irq.h> 8 #include <asm/io.h> 9 #include <asm/arch/regs-gpio.h> 10 #include <asm/hardware.h> 11 #include <linux/delay.h> 12 13 static struct class *led_drv_class; 14 static struct class_device *led_drv_class_dev; 15 16 volatile unsigned long *gpfcon = NULL; 17 volatile unsigned long *gpfdat = NULL; 18 19 //#define FIRST_DRV_MAJOR 111 20 //#define DEVICE_NAME first_drv 21 22 static int led_drv_open(struct inode *inode, struct file *file) 23 { 24 //printk("led_drv_open test!!!\n"); 25 *gpfcon &= ~((0x3<<(4*2))|(0x3<<(5*2))|(0x03<<(6*2)));//先清零 26 *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));//置1,设置GPIOE4/5/6为输出 27 28 return 0; 29 } 30 31 //static int led_drv_write(struct inode*inode,struct file*file){ 32 static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) 33 { 34 //printk("led_drv_write test!!!\n"); 35 int val = 0; 36 copy_from_user(&val,buf,count); 37 if(val == 1) 38 { 39 int i = 5; 40 while(i) 41 { 42 printk("test one 42"); 43 //*gpfdat &= ~((1<<4)|(1<<5)|(1<<6)); 44 45 *gpfdat &= ~(1<<4);//led4 on 46 *gpfdat |= ((1<<5)|(1<<6));//led5,led6 off 47 msleep(500); 48 49 *gpfdat &= ~(1<<5); 50 *gpfdat |= ((1<<4)|(1<<6)); 51 msleep(500); 52 53 *gpfdat &= ~(1<<6); 54 *gpfdat |= ((1<<4)|(1<<5)); 55 msleep(500); 56 57 i = i - 1; 58 } 59 } 60 else 61 { 62 printk("test two 62"); 63 *gpfdat |= ((1<<4)|(1<<5)|(1<<6)); 64 } 65 return 0; 66 } 67 68 static struct file_operations led_drv_fops = { 69 .owner = THIS_MODULE, 70 .open = led_drv_open, 71 .write = led_drv_write, 72 }; 73 74 int major; 75 /* 76 驱动的入口函数 77 */ 78 static int led_drv_init(void){ 79 80 //int major;第一个参数主设备号为0,表示系统会自动分配,还可以自己指定 81 major = register_chrdev(0,"led_drv",&led_drv_fops); 82 /* 83 if(ret<0){ 84 printk(DEVICE "can not register major number"); 85 return ret; 86 } 87 */ 88 /* 89 自动为主设备号为major的字符设备创建dev/xyz 90 */ 91 led_drv_class = class_create(THIS_MODULE, "led_drv"); 92 led_drv_class_dev = class_device_create(led_drv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */ 93 94 gpfcon = (volatile unsigned long *)ioremap(0x56000050,16); 95 //*gpfdat = (volatile unsigned long *)ioremap(0x56000044,16); 96 gpfdat = gpfcon + 1; 97 return 0; 98 } 99 100 static void led_drv_exit(void){ 101 102 unregister_chrdev(major,"led_drv"); 103 /* 104 自动卸载主设备号为major的字符设备dev/xyz 105 */ 106 class_device_unregister(led_drv_class_dev); 107 class_destroy(led_drv_class); 108 iounmap(gpfcon); 109 } 110 111 /* 112 封装驱动入口函数的宏,统一接口,方便内核调用 113 */ 114 module_init(led_drv_init); 115 module_exit(led_drv_exit); 116 117 MODULE_AUTHOR("action_er"); 118 MODULE_VERSION("v1.0"); 119 //MODULE_DISCRIPTION("leddrivertest"); 120 MODULE_LICENSE("GPL");
led_test.c
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <stdio.h> 5 6 int main(int argc,char**argv){ 7 8 int fd; 9 int val = 1; 10 fd = open("dev/xyz",O_RDWR); 11 if(fd<0){ 12 printf("hello lhs world!\n"); 13 printf("can not open !\n"); 14 } 15 16 if(argc != 2){ 17 printf("Usage:\n"); 18 printf("%s <on/off>\n",argv[0]); 19 20 return 0; 21 } 22 if(strcmp(argv[1],"on") == 0) 23 { 24 val = 1; 25 } 26 else 27 { 28 val = 0; 29 } 30 write(fd,&val,4); 31 return 0; 32 }
2 分析字符设备注册过程
函数register_chrdev,定义位于:include\linux\fs.h和fs\char_dev.c中
1 static inline int register_chrdev(unsigned int major, const char *name, 2 const struct file_operations *fops) 3 { 4 return __register_chrdev(major, 0, 256, name, fops); 5 } 6 7 int __register_chrdev(unsigned int major, unsigned int baseminor, 8 unsigned int count, const char *name, 9 const struct file_operations *fops) 10 { 11 struct char_device_struct *cd; 12 struct cdev *cdev; 13 int err = -ENOMEM; 14 15 cd = __register_chrdev_region(major, baseminor, count, name);//根据major注册字符设备 16 if (IS_ERR(cd)) 17 return PTR_ERR(cd); 18 19 cdev = cdev_alloc(); 20 if (!cdev) 21 goto out2; 22 23 cdev->owner = fops->owner; 24 cdev->ops = fops; 25 kobject_set_name(&cdev->kobj, "%s", name);//确定字符设备的名字 26 27 err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);//向系统中添加字符设备 28 if (err) 29 goto out; 30 31 cd->cdev = cdev; 32 33 return major ? 0 : cd->major; 34 out: 35 kobject_put(&cdev->kobj); 36 out2: 37 kfree(__unregister_chrdev_region(cd->major, baseminor, count)); 38 return err; 39 }
2.1 函数__register_chrdev_region
作用:
(1)确定字符设备的主设备号
(2)将此字符设备加入全局变量字符设备链表中
1 static struct char_device_struct * 2 __register_chrdev_region(unsigned int major, unsigned int baseminor, 3 int minorct, const char *name) 4 { 5 struct char_device_struct *cd, **cp; 6 int ret = 0; 7 int i; 8 9 cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);//分配字符设备结构,实际也是一个链表 10 if (cd == NULL) 11 return ERR_PTR(-ENOMEM); 12 13 mutex_lock(&chrdevs_lock); 14 15 /* temporary */ 16 if (major == 0) {//如果major为0,就会自动分配主设备;如果大于0,就以此值为主设备号 17 for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) { 18 if (chrdevs[i] == NULL) 19 break; 20 } 21 22 if (i == 0) { 23 ret = -EBUSY; 24 goto out; 25 } 26 major = i;//拿到自动分配的主设备号 27 ret = major; 28 } 29 /*给字符设备结构体赋值*/ 30 cd->major = major; 31 cd->baseminor = baseminor; 32 cd->minorct = minorct; 33 strlcpy(cd->name, name, sizeof(cd->name)); 34 35 i = major_to_index(major); 36 37 for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)//将新的字符设备cd加入字符设备结构体数组chrdevs链表中 38 if ((*cp)->major > major || 39 ((*cp)->major == major && 40 (((*cp)->baseminor >= baseminor) || 41 ((*cp)->baseminor + (*cp)->minorct > baseminor)))) 42 break; 43 44 /* Check for overlapping minor ranges. */ 45 if (*cp && (*cp)->major == major) { 46 int old_min = (*cp)->baseminor; 47 int old_max = (*cp)->baseminor + (*cp)->minorct - 1; 48 int new_min = baseminor; 49 int new_max = baseminor + minorct - 1; 50 51 /* New driver overlaps from the left. */ 52 if (new_max >= old_min && new_max <= old_max) { 53 ret = -EBUSY; 54 goto out; 55 } 56 57 /* New driver overlaps from the right. */ 58 if (new_min <= old_max && new_min >= old_min) { 59 ret = -EBUSY; 60 goto out; 61 } 62 } 63 64 cd->next = *cp; 65 *cp = cd; 66 mutex_unlock(&chrdevs_lock); 67 return cd; 68 out: 69 mutex_unlock(&chrdevs_lock); 70 kfree(cd); 71 return ERR_PTR(ret); 72 }
字符设备定义如下:
1 static struct char_device_struct { 2 struct char_device_struct *next; 3 unsigned int major; 4 unsigned int baseminor; 5 int minorct; 6 char name[64]; 7 struct cdev *cdev; /* will die */ 8 } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
2.2 函数cdev_add
1 int cdev_add(struct cdev *p, dev_t dev, unsigned count) 2 { 3 int error; 4 5 p->dev = dev; 6 p->count = count; 7 8 error = kobj_map(cdev_map, dev, count, NULL,//cdev_map是一个全局的kobj_map变量 9 exact_match, exact_lock, p); 10 if (error) 11 return error; 12 13 kobject_get(p->kobj.parent); 14 15 return 0; 16 }
定义全局的kobj_map变量的定义和初始化
drivers\base\map.c和fs\char_dev.c中
1 static struct kobj_map *cdev_map; 2 3 struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock) 4 { 5 /*分配一个struct kobj_map内存空间 */ 6 struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL); 7 /*分配一个struct probe指针内存空间 */ 8 struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL); 9 int i; 10 11 /*分配失败 */ 12 if ((p == NULL) || (base == NULL)) { 13 /*释放内存空间 */ 14 kfree(p); 15 kfree(base); 16 return NULL; 17 } 18 /*设置默认设备号为1,连续range个次设备,设置probe的实现 19 * 函数(回调) 20 */ 21 base->dev = 1; 22 base->range = ~0; 23 base->get = base_probe; 24 /*设置probes数组的初始值 */ 25 for (i = 0; i < 255; i++) 26 p->probes[i] = base; 27 p->lock = lock; 28 return p; 29 }
chrdev_init调用函数kobj_map_init初始化cdev_map
1 void __init chrdev_init(void) 2 { 3 cdev_map = kobj_map_init(base_probe, &chrdevs_lock); 4 bdi_init(&directly_mappable_cdev_bdi); 5 }
2.3 函数 kobj_map
定义位于:drivers\base\map.c
1 int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range, 2 struct module *module, kobj_probe_t *probe, 3 int (*lock)(dev_t, void *), void *data) 4 { 5 /*计算MAJOR(dev)到MAJOR(dev + range - 1)有几个 6 * 主设备,由于主设备号都一样,所以这里n = 1 7 */ 8 unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1; 9 /*以主设备号为索引 */ 10 unsigned index = MAJOR(dev); 11 unsigned i; 12 struct probe *p; 13 14 /*主设备超出255个 */ 15 if (n > 255) 16 n = 255; 17 /*分配n个struct probe内存空间*/ 18 p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL); 19 /*分配失败*/ 20 if (p == NULL) 21 return -ENOMEM; 22 /*填装n个struct probe,对应n个主设备号 23 * 24 */ 25 for (i = 0; i < n; i++, p++) { 26 p->owner = module; 27 p->get = probe; 28 p->lock = lock; 29 p->dev = dev; 30 p->range = range; 31 p->data = data; 32 } 33 /*进入临界区*/ 34 mutex_lock(domain->lock); 35 /*这里p -= n是因为,在以上for循环中,p++了n次 */ 36 for (i = 0, p -= n; i < n; i++, p++, index++) { 37 /*根据当前索引,从probes[]中 38 *取出一个probe 39 */ 40 struct probe **s = &domain->probes[index % 255]; 41 /*probe是一个链表,每个新加入的节点, 42 * 按照其range的大小,从小到大排列,即头结点的 43 * range是最小的 44 */ 45 while (*s && (*s)->range < range) 46 /*继续查找下一个probe,直到其range大于 47 * 或等于新加入probe的range为止 48 */ 49 s = &(*s)->next; 50 /* 找到了一个probe,其range大于或等于新加入 51 * probe的range,把这个新加入的probe下一节点指向 52 * 这个probe节点 53 */ 54 p->next = *s; 55 /*新加入的节点代替旧的位置 */ 56 *s = p; 57 } 58 /*退出临界区*/ 59 mutex_unlock(domain->lock); 60 return 0; 61 }
结构体的定义如下:
1 struct kobj_map { 2 struct probe { 3 struct probe *next; 4 dev_t dev; 5 unsigned long range; 6 struct module *owner; 7 kobj_probe_t *get; 8 int (*lock)(dev_t, void *); 9 void *data; 10 } *probes[255]; 11 struct mutex *lock; 12 };
行胜于言,自强不息。