Chinaunix首页 | 论坛 | 博客
  • 博客访问: 723815
  • 博文数量: 104
  • 博客积分: 4320
  • 博客等级: 上校
  • 技术积分: 1948
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-30 14:42
文章分类

全部博文(104)

文章存档

2012年(4)

2011年(65)

2010年(35)

分类: LINUX

2011-10-27 17:26:55

    我们在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在 模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。如果是嵌入式linux,那就是mdev。
    内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建 好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应 device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
    注意,在2.6较早的内核版本中,device_create(…)函数名称不同,是class_device_create(…),所以在新的内核中编译以前的模块程序有时会报错,就是因为函数名称不同,而且里面的参数设置也有一些变化。
    struct class和class_create(…) 以及device_create(…)都定义在/include/linux/device.h中,使用的时候一定要包含这个头文件,否则编译器会报错。
    在2.6.32内核版本中,struct class定义在头文件include/linux/device.h中:
  1. /*
  2.  * device classes
  3.  */
  4. struct class {
  5.     const char *name;
  6.     struct module *owner;

  7.     struct class_attribute *class_attrs;
  8.     struct device_attribute *dev_attrs;
  9.     struct kobject *dev_kobj;

  10.     int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
  11.     char *(*devnode)(struct device *dev, mode_t *mode);

  12.     void (*class_release)(struct class *class);
  13.     void (*dev_release)(struct device *dev);

  14.     int (*suspend)(struct device *dev, pm_message_t state);
  15.     int (*resume)(struct device *dev);

  16.     const struct dev_pm_ops *pm;

  17.     struct class_private *p;
  18. };
class_create(…)在/drivers/base/class.c中实现:
  1. /**
  2.  * class_create - create a struct class structure
  3.  * @owner: pointer to the module that is to "own" this struct class
  4.  * @name: pointer to a string for the name of this class.
  5.  * @key: the lock_class_key for this class; used by mutex lock debugging
  6.  *
  7.  * This is used to create a struct class pointer that can then be used
  8.  * in calls to device_create().
  9.  *
  10.  * Note, the pointer created here is to be destroyed when finished by
  11.  * making a call to class_destroy().
  12.  */
  13. struct class *__class_create(struct module *owner, const char *name,
  14.                  struct lock_class_key *key)
  15. {
  16.     struct class *cls;
  17.     int retval;

  18.     cls = kzalloc(sizeof(*cls), GFP_KERNEL);
  19.     if (!cls) {
  20.         retval = -ENOMEM;
  21.         goto error;
  22.     }

  23.     cls->name = name;
  24.     cls->owner = owner;
  25.     cls->class_release = class_create_release;

  26.     retval = __class_register(cls, key);
  27.     if (retval)
  28.         goto error;

  29.     return cls;

  30. error:
  31.     kfree(cls);
  32.     return ERR_PTR(retval);
  33. }
  34. EXPORT_SYMBOL_GPL(__class_create);
    第一个参数指定类的所有者是哪个模块,第二个参数指定类名。 第三个参数用作mutex lock debugging。
    在class.c中,还定义了class_destroy(…)函数,用于在模块卸载时删除类。


device_create(…)函数在/drivers/base/core.c中实现:
  1. /**
  2.  * device_create - creates a device and registers it with sysfs
  3.  * @class: pointer to the struct class that this device should be registered to
  4.  * @parent: pointer to the parent struct device of this new device, if any
  5.  * @devt: the dev_t for the char device to be added
  6.  * @drvdata: the data to be added to the device for callbacks
  7.  * @fmt: string for the device's name
  8.  *
  9.  * This function can be used by char device classes. A struct device
  10.  * will be created in sysfs, registered to the specified class.
  11.  *
  12.  * A "dev" file will be created, showing the dev_t for the device, if
  13.  * the dev_t is not 0,0.
  14.  * If a pointer to a parent struct device is passed in, the newly created
  15.  * struct device will be a child of that device in sysfs.
  16.  * The pointer to the struct device will be returned from the call.
  17.  * Any further sysfs files that might be required can be created using this
  18.  * pointer.
  19.  *
  20.  * Note: the struct class passed to this function must have previously
  21.  * been created with a call to class_create().
  22.  */
  23. struct device *device_create(struct class *class, struct device *parent,
  24.                  dev_t devt, void *drvdata, const char *fmt, ...)
  25. {
  26.     va_list vargs;
  27.     struct device *dev;

  28.     va_start(vargs, fmt);
  29.     dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
  30.     va_end(vargs);
  31.     return dev;
  32. }
  33. EXPORT_SYMBOL_GPL(device_create);
第一个参数指定所要创建的设备所从属的类,第二个参数是这个设备的父设备,如果没有就指定为NULL,第三个参数是设备号,第四个参数是添加到设备的数据,用于回调, 第五个参数是设备名称。
下面以一个简单字符设备led驱动来展示如何使用这几个函数


  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 <linux/gpio.h>
  7. #include <asm/irq.h>
  8. #include <mach/regs-gpio.h>
  9. #include <linux/cdev.h>
  10. #include <linux/device.h>

  11. #define DEVICE_NAME "leds" /* 加载模块后,执行”cat /proc/devices”命令看到的设备名称 */
  12. #define LED_MAJOR 231 /* 主设备号 */
  13. #define LED_MINOR 0 /* 次设备号 */
  14. #define LED_NUMBER 1 /* LED设备数 */

  15. struct cdev led_cdev;
  16. dev_t dev = 0;
  17. struct class *led_class;

  18. /* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
  19. #define IOCTL_LED_ON 0
  20. #define IOCTL_LED_OFF 1

  21. /* 用来指定LED所用的GPIO引脚 */
  22. static unsigned long led_table [] = {
  23.     S3C2410_GPB(5),
  24.     S3C2410_GPB(6),
  25.     S3C2410_GPB(7),
  26.     S3C2410_GPB(8),
  27. };
  28. /* 用来指定GPIO引脚的功能:输出 */
  29. static unsigned int led_cfg_table [] = {
  30.     S3C2410_GPIO_OUTPUT,
  31.     S3C2410_GPIO_OUTPUT,
  32.     S3C2410_GPIO_OUTPUT,
  33.     S3C2410_GPIO_OUTPUT,
  34. };

  35. /* 应用程序对设备文件/dev/leds执行open(...)时,
  36.  * 就会调用s3c24xx_leds_open函数
  37.  */
  38. static int s3c24xx_leds_open(struct inode *inode, struct file *file)
  39. {
  40.     int i;

  41.     for (i = 0; i < 4; i++) {
  42.         // 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能
  43.         s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
  44.     }
  45.     return 0;
  46. }

  47. /* 应用程序对设备文件/dev/leds执行ioclt(...)时,
  48.  * 就会调用s3c24xx_leds_ioctl函数
  49.  */
  50. static int s3c24xx_leds_ioctl(
  51.     struct inode *inode,
  52.     struct file *file,
  53.     unsigned int cmd,
  54.     unsigned long arg)
  55. {
  56.     if (arg > 4) {
  57.         return -EINVAL;
  58.     }

  59.     switch(cmd) {
  60.     case IOCTL_LED_ON:
  61.         // 设置指定引脚的输出电平为0
  62.         s3c2410_gpio_setpin(led_table[arg], 0);
  63.         return 0;

  64.     case IOCTL_LED_OFF:
  65.         // 设置指定引脚的输出电平为1
  66.         s3c2410_gpio_setpin(led_table[arg], 1);
  67.         return 0;

  68.     default:
  69.         return -EINVAL;
  70.     }
  71. }

  72. /* 这个结构是字符设备驱动程序的核心
  73.  * 当应用程序操作设备文件时所调用的open、read、write等函数,
  74.  * 最终会调用这个结构中指定的对应函数
  75.  */
  76. static struct file_operations s3c24xx_leds_fops = {
  77.     .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
  78.     .open = s3c24xx_leds_open,
  79.     .ioctl = s3c24xx_leds_ioctl,
  80. };

  81. /*
  82.  * 执行“insmod s3c24xx_leds.ko”命令时就会调用这个函数
  83.  */
  84. static int __init s3c24xx_leds_init(void)
  85. {
  86.     int ret;

  87.     dev = MKDEV(LED_MAJOR, LED_MINOR);
  88.     ret = register_chrdev_region(dev, LED_NUMBER, DEVICE_NAME);
  89.     if (ret < 0) {
  90.       printk(KERN_WARNING "%s can't register major number %d\n", DEVICE_NAME, LED_MAJOR);
  91.       return ret;
  92.     }

  93.     cdev_init(&led_cdev, &s3c24xx_leds_fops);
  94.     led_cdev.owner = THIS_MODULE;
  95.     led_cdev.ops = &s3c24xx_leds_fops;
  96.     ret = cdev_add(&led_cdev, dev, LED_NUMBER);
  97.     if (ret)
  98.         printk(KERN_NOTICE "Error %d adding led_cdev", ret);

  99.     /* create your own class under /sysfs */
  100.     led_class = class_create(THIS_MODULE, "led_class");
  101.     if(IS_ERR(led_class))
  102.     {
  103.         printk("Error: failed in creating led_class\n");
  104.         return -1;
  105.     }

  106.     /* register your own device in sysfs, and this will cause udev to create corresponding device node */
  107.     device_create(led_class, NULL, dev, NULL, DEVICE_NAME);

  108.     printk(KERN_INFO " Registered led driver success!\n");
  109.     return 0;
  110. }

  111. /*
  112.  * 执行”rmmod s3c24xx_leds.ko”命令时就会调用这个函数
  113.  */
  114. static void __exit s3c24xx_leds_exit(void)
  115. {
  116.     /* 卸载驱动程序 */
  117.     dev_t devno = MKDEV(LED_MAJOR, LED_MINOR);
  118.     cdev_del(&led_cdev);
  119.     device_destroy(led_class, devno);
  120.     class_destroy(led_class);
  121.     unregister_chrdev_region(devno, LED_NUMBER);
  122.     printk(KERN_INFO "led driver cleaned up\n");
  123. }

  124. /* 这两行指定驱动程序的初始化函数和卸载函数 */
  125. module_init(s3c24xx_leds_init);
  126. module_exit(s3c24xx_leds_exit);

  127. /* 描述驱动程序的一些信息,不是必须的 */
  128. MODULE_AUTHOR("eric.hu"); // 驱动程序的作者
  129. MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver"); // 一些描述信息
  130. MODULE_LICENSE("GPL"); // 遵循的协议
















阅读(3222) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~