分类: 嵌入式
2017-03-21 09:37:28
原文地址:linux下字符设备分配设备号,注册等详解 作者:andyhzw
PC操作系统:ubuntu 11.10
使用的开发板:am335x_evm
开发板使用的操作系统:linux 3.2
测试用例:
#include#include unregister_chrdev_region(MKDEV(major,0),1); printk("Goodbye,cruel world!\n"); } module_init(light_init); module_exit(light_cleanup);#include #include #include #include #include #include #include //register_chrdev_region #include #include #include MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("SM"); MODULE_VERSION("0.0.1"); //MODULE_DEVICE("global memory"); MODULE_DESCRIPTION("led ..."); MODULE_ALIAS("LED"); /*******************************************/ #define NAME "leds" #define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio)) #define LED_GPIO_1 1 #define LED_PIN_1 24 static int major =0;//定义主设备号 static struct class *led_class; /*******************************************/ void led_on(void) { gpio_set_value(GPIO_TO_PIN(LED_GPIO_1,LED_PIN_1), 1); } EXPORT_SYMBOL(led_on); void led_off(void) { gpio_set_value(GPIO_TO_PIN(LED_GPIO_1,LED_PIN_1), 0); } EXPORT_SYMBOL(led_off); void led_init(void) { int result; /* Allocating GPIOs and setting direction */ gpio_free(GPIO_TO_PIN(LED_GPIO_1,LED_PIN_1)); result = gpio_request(GPIO_TO_PIN(LED_GPIO_1,LED_PIN_1), "Leds");//usr1 if (result != 0) printk("gpio_request(%d_%d) failed!\n",LED_GPIO_1,LED_PIN_1); result = gpio_direction_output(GPIO_TO_PIN(LED_GPIO_1,LED_PIN_1), 1); if (result != 0) printk("gpio_direction(%d_%d) failed!\n",LED_GPIO_1,LED_PIN_1); } struct light_dev { struct cdev cdev; unsigned char value; }; struct light_dev *light_devp; // 打开和关闭函数 int light_open(struct inode *inode,struct file *filp) { struct light_dev *dev; // 获得设备结构体指针 dev = container_of(inode->i_cdev,struct light_dev,cdev); // 让设备结构体作为设备的私有信息 filp->private_data = dev; return 0; } int light_release(struct inode *inode,struct file *filp) { return 0; } // ioctl long light_ioctl(struct file *filp,unsigned int cmd, unsigned long arg) { struct light_dev *dev = filp->private_data; switch(cmd) { case 0: dev->value = 0; led_off(); break; case 1: dev->value = 1; led_on(); break; default: return -ENOTTY; // break; } return 0; } //set up the cdev structure for a device static void led_setup_cdev(struct cdev* dev,int minor,struct file_operations *fops) { int err, devno = MKDEV(major,minor); cdev_init(dev,fops); dev->owner = THIS_MODULE; dev->ops = fops; err = cdev_add(dev,devno,1); if(err) printk(KERN_NOTICE"Error %d adding led%d",err,minor); } struct file_operations light_fops = { .owner = THIS_MODULE, .unlocked_ioctl = light_ioctl, .open = light_open, .release = light_release, }; static struct cdev cdev_led; // 模块加载函数 int light_init(void) { int ret; dev_t dev = MKDEV(major,0); led_init(); printk(KERN_ALERT "led modules is install\n"); // ret=register_chrdev(major,NAME,&light_fops); if(major) ret = register_chrdev_region(dev,1,NAME); else { ret = alloc_chrdev_region(&dev,0,1,NAME); major = MAJOR(dev); } if(ret<0) { printk("unable to register myled driver!\n"); return ret; } printk(KERN_DEBUG"led device number : %x\n",dev); led_setup_cdev(&cdev_led,0,&light_fops); led_class = class_create(THIS_MODULE,"led_class"); if(IS_ERR(led_class)) { printk(KERN_INFO"create led class error\n"); return -1; } device_create(led_class,NULL,dev,NULL,"led" "%d",MINOR(dev)); return 0; } // 模块卸载函数 void light_cleanup(void) { // unregister_chrdev(major,NAME); device_destroy(led_class,MKDEV(major,0)); class_destroy(led_class); cdev_del(&cdev_led);
详解:
@a1@ 动态分配设备号
/** * alloc_chrdev_region() - register a range of char device numbers * @dev: output parameter for first assigned number * @baseminor: first of the requested range of minor numbers * @count: the number of minor numbers required * @name: the name of the associated device or driver * * Allocates a range of char device numbers. The major number will be * chosen dynamically, and returned (along with the first minor number) * in @dev. Returns zero or a negative error code. */ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, //alloc_chrdev_region(0,0,1,"leds") const char *name) { struct char_device_struct *cd; cd = __register_chrdev_region(0, baseminor, count, name); if (IS_ERR(cd)) return PTR_ERR(cd); *dev = MKDEV(cd->major, cd->baseminor); return 0; }
@b1@ 获得并注册设备号
/*hjhk* Register a single major with a specified minor range. * * If major == 0 this functions will dynamically allocate a major and return * its number. * * If major > 0 this function will attempt to reserve the passed range of * minors and will return zero on success. * * Returns a -ve errno on failure. */ static struct char_device_struct * __register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name) { struct char_device_struct *cd, **cp; int ret = 0; int i; //分配并清空内存空间用以保存这个设备的主设备号等信息 cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL); if (cd == NULL) return ERR_PTR(-ENOMEM); //锁住临界区 mutex_lock(&chrdevs_lock); //动态分配主设备号 /* temporary */ if (major == 0) { for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) { if (chrdevs[i] == NULL) break; } //当前没有可用的主设备号,异常退出 if (i == 0) { ret = -EBUSY; goto out; } //成功获得主设备号 major = i; ret = major; } //根据当前例子填充数据 cd->major = major; //动态分配的主设备号 cd->baseminor = baseminor;//当前为 0 (如果有多个设备,其表示第一个次设备号) cd->minorct = minorct; //设备个数 1 strlcpy(cd->name, name, sizeof(cd->name)); //设备名为 "leds" //主设备号是通过hash表来管理的 i = major_to_index(major); //当前动态获得的主设备号在所有设备中的索引值(即位于那条hash列中) /* #if 0 /* fs/char_dev.c */ #define CHRDEV_MAJOR_HASH_SIZE 255 /* index in the above */ static inline int major_to_index(unsigned major) { return major % CHRDEV_MAJOR_HASH_SIZE; } static struct char_device_struct { struct char_device_struct *next; unsigned int major; unsigned int baseminor; int minorct; char name[64]; struct cdev *cdev; /* will die */ } *chrdevs[CHRDEV_MAJOR_HASH_SIZE]; #endif */ /* chrdevs[i] @1@ 假设主设备号28并没有被使用,dev1的主设备号为28, @2@ @3@ */ for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if ((*cp)->major > major || ((*cp)->major == major && (((*cp)->baseminor >= baseminor) || ((*cp)->baseminor + (*cp)->minorct > baseminor)))) break; /* 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) { ret = -EBUSY; goto out; } /* New driver overlaps from the right. */ if (new_min <= old_max && new_min >= old_min) { ret = -EBUSY; goto out; } } cd->next = *cp; *cp = cd; mutex_unlock(&chrdevs_lock); return cd; out: mutex_unlock(&chrdevs_lock); kfree(cd); return ERR_PTR(ret); }
@b2@返回设备号
@b DONE@
@a2@ 初始化设备
@b1@
@b2@
@b3@
@b DONE@
@a3@ 添加设备
@b1@
@b2@
@b3@
@b DONE@
@a4@ 在 sysfs 中创建设备类型
@b1@
@b2@
@b3@
@b DONE@
@a5@ 在 sysfs 中对应的设备类型中添加设备
@b1@
@b2@
@b3@
@b DONE@
@a DONE@