字符设备驱动(1)代码分析---之register_chrdev
/***************************************************************************** 简 述:简单字符型驱动程序,手动静态分配设备号,手动创建设备节点 ******************************************************************************/ #include <linux/module.h> #include <linux/fs.h> #include <mach/gpio.h> #include <linux/irq.h> #include <linux/kdev_t.h> #include <linux/interrupt.h> #include <linux/init.h> #define DEVICE_NAME "leds" struct button_desc { int gpio; int number; char *name; }; struct led_desc { int gpio; int number; char *name; }; static struct button_desc buttons[] = { { S5PV210_GPH2(0), 0, "KEY0" }, { S5PV210_GPH2(1), 1, "KEY1" }, { S5PV210_GPH2(2), 2, "KEY2" }, { S5PV210_GPH2(3), 3, "KEY3" }, { S5PV210_GPH3(0), 4, "KEY4" }, { S5PV210_GPH3(1), 5, "KEY5" }, { S5PV210_GPH3(2), 6, "KEY6" }, { S5PV210_GPH3(3), 7, "KEY7" }, }; static struct led_desc leds[] = { {S5PV210_GPJ2(0),1,"LED1"}, {S5PV210_GPJ2(1),2,"LED2"}, {S5PV210_GPJ2(2),3,"LED3"}, {S5PV210_GPJ2(3),4,"LED4"}, }; #define OK (0) #define ERROR (-1) dev_t devNum; unsigned int subDevNum = 1;//要申请的次设备号个数 int reg_major = 234; int reg_minor = 0; static irqreturn_t button_interrupt(int irq, void *dev_id) { struct button_desc *bdata = (struct button_desc *)dev_id; int down; unsigned tmp; tmp = gpio_get_value(bdata->gpio); /* active low */ down = !tmp; printk("KEY %d: %08x\n", bdata->number, down); if(bdata->number < 4) { gpio_set_value(leds[bdata->number].gpio,0); printk("LED %d: On \n",leds[bdata->number].number); } else { gpio_set_value(leds[(bdata->number) - 4].gpio,1); printk("LED %d: OFF \n",leds[(bdata->number)-4].number); } return IRQ_HANDLED; } int butsOpen(struct inode *p, struct file *f) { int irq; int i; int err = 0; printk(KERN_EMERG"butsOpen\r\n"); for (i = 0; i < ARRAY_SIZE(buttons); i++) { if (!buttons[i].gpio) continue; irq = gpio_to_irq(buttons[i].gpio); // irq = IRQ_EINT(16)+i =160+i "S5PV210_GPH2(i)" //irq = IRQ_EINT(24)+i =168+i "S5PV210_GPH3(i)" err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_FALLING, buttons[i].name, (void *)&buttons[i]); if (err) break; } for(i = 0; i<ARRAY_SIZE(leds);i++) { if(!leds[i].gpio) continue; gpio_direction_output(leds[i].gpio,1); } if (err) { i--; for (; i >= 0; i--) { if (!buttons[i].gpio) continue; irq = gpio_to_irq(buttons[i].gpio); disable_irq(irq); free_irq(irq, (void *)&buttons[i]); } return -EBUSY; } return 0; } static const struct file_operations gFile = { .owner = THIS_MODULE, .open = butsOpen, }; int charDrvInit(void) { devNum = MKDEV(reg_major, reg_minor); printk(KERN_EMERG"devNum is %d\r\n", devNum); if(OK == register_chrdev(reg_major, DEVICE_NAME, &gFile)) { printk(KERN_EMERG "register_chrdev_region ok\r\n"); } else { printk(KERN_EMERG"register_chrdev_region error\r\n"); return ERROR; } printk(KERN_EMERG "button driver initial done...\r\n"); return 0; } void __exit charDrvExit(void) { int i,irq; unregister_chrdev(reg_major, DEVICE_NAME); for (i = 0; i < ARRAY_SIZE(buttons); i++) { if (!buttons[i].gpio) continue; irq = gpio_to_irq(buttons[i].gpio); disable_irq(irq); free_irq(irq, (void *)&buttons[i]); } return; } module_init(charDrvInit);//执行insmod时会执行此行代码并调用charDrvInit,驱动开始 module_exit(charDrvExit);//执行rmmod时,结束 MODULE_LICENSE("GPL"); MODULE_AUTHOR("LiuB");
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops); static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) { return __register_chrdev(major, 0, 256, name, fops); } int __register_chrdev(unsigned int major, unsigned int baseminor, unsigned int count, const char *name,const struct file_operations *fops) { struct char_device_struct *cd; struct cdev *cdev; cd = __register_chrdev_region(major, baseminor, count, name); { struct char_device_struct *cd, **cp; cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL); cd->major = major; cd->baseminor = baseminor; cd->minorct = minorct; strlcpy(cd->name, name, sizeof(cd->name)); i = major_to_index(major); for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) 。。。。。 //找到可以添加新的设备的位置*cp //此处cd为链表中唯一节点,cd->next = NULL,所以这句代码相当于在cd链表后添加一个节点*cp cd->next = *cp; //继续让cd指向链表尾 *cp = cd; mutex_unlock(&chrdevs_lock); return cd; } cdev = cdev_alloc();//给局部结构体变量cdev分配空间 //将register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops)中的i2cdev_fops结构体的具体内容赋给新建的cdev结构体 cdev->owner = fops->owner; cdev->ops = fops; kobject_set_name(&cdev->kobj, "%s", name); //添加设备 err = cdev_add(cdev, MKDEV(cd->major, baseminor), count); { //dev= MKDEV(cd->major, baseminor) cdev->dev = dev;//添加设备号 cdev->count = count;//设备个数 //将指定的设备号加入到管理设备号及其对应设备的kobj_map结构体中的probe数组中 return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); } cd->cdev = cdev;//将赋值完成的cdev传到结构体cd中的cdev结构体成员 return major ? 0 : cd->major; }
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
退出for循环的四种情况:
1、已存在的主设备号大于即将新添加的主设备号
2、已存在的主设备号等于即将新添加的主设备号&&并且次设备最小号大于新注册的次设备最小号
3、已存在的主设备号等于即将新添加的主设备号&&次设备最小号等于新注册的次设备最小号
4、已存在的主设备号等于即将新添加的主设备号&&次设备最小号小于新注册的次设备最小号&&新注册的次设备最小号小于已存在的次设备号范围(最小号+个数)
注:
1、对于第一种情况跳过下面的if判断注册到链表中
2、对于第二、三、四种情况在跳出for循环后继续进行下面判断来分析是否冲突,如果不冲突则注册到链表中
/* Check for overlapping minor ranges. */ if (*cp && (*cp)->major == major) { int old_min = (*cp)->baseminor; int old_max = (*cp)->baseminor + (*cp)->minorct - 1; int new_min = baseminor; int new_max = baseminor + minorct - 1; /* New driver overlaps from the left. */ if (new_max >= old_min && new_max <= old_max) { //A ret = -EBUSY; goto out; } /* New driver overlaps from the right. */ if (new_min <= old_max && new_min >= old_min) {//B ret = -EBUSY; goto out; } }
//A --------->这个if判断针对第二种情况
//B ------>针对第三种情况(肯定冲突)
//B ------>针对第四种情况