九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 4852|回复: 1
打印 上一主题 下一主题

x4412&ibox项目实战23-使用register_chrdev注册字符设备

[复制链接]
跳转到指定楼层
楼主
发表于 2014-9-27 18:34:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
  为了加深对主次设备号以及设备文件系统的理解,我们编写一个最基础的字符设备文件系统,使用register_chrdev函数注册,手动指定驱动的主设备号,然后通过命令行创建设备节点,最后编写测试程序验证驱动的正确性。
       参考驱动源码如下:
  1. #include <linux/module.h>
  2. #include <asm/uaccess.h>
  3. #include <linux/cdev.h>
  4. #include <linux/fs.h>
  5. #define x4412_char_MAJOR 224 //主设备号
  6. static unsigned char simple_inc=0;
  7. static unsigned char demoBuffer[256];
  8. int x4412_char_open(struct inode *inode, struct file *filp)
  9. {
  10.     if(simple_inc>0)
  11.                   return -ERESTARTSYS;
  12.     simple_inc++;
  13.     return 0;
  14. }
  15. int x4412_char_release(struct inode *inode, struct file *filp)
  16. {
  17.     simple_inc--;
  18.     return 0;
  19. }
  20. ssize_t x4412_char_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
  21. {
  22.     /* 把数据复制到应用程序空间 */
  23.     if (copy_to_user(buf,demoBuffer,count))
  24.     {
  25.        count=-EFAULT;  
  26.     }
  27.     return count;
  28. }
  29. ssize_t x4412_char_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
  30. {
  31.     /* 把数据复制到内核空间 */
  32.     if (copy_from_user(demoBuffer+*f_pos, buf, count))
  33.     {
  34.         count = -EFAULT;
  35.     }
  36.     return count;
  37. }
  38. struct file_operations x4412_char_fops = {
  39.     .owner =    THIS_MODULE,
  40.     .read =     x4412_char_read,
  41.     .write =    x4412_char_write,
  42.     .open =     x4412_char_open,
  43.     .release =  x4412_char_release,
  44. };
  45. int x4412_char_init_module(void)
  46. {
  47.     int ret;
  48.     printk("x4412_char_init_module!\n");
  49.     //根据设备号与设备名注册字符设备
  50.     ret = register_chrdev(x4412_char_MAJOR, "x4412_char", &x4412_char_fops);  
  51.     if (ret < 0)
  52.     {
  53.         printk("Unable to register character device %d!\n",x4412_char_MAJOR);
  54.         return ret;
  55.     }
  56.     return 0;
  57. }
  58. void x4412_char_cleanup_module(void)
  59. {
  60.     unregister_chrdev(x4412_char_MAJOR,  "x4412_char");  
  61.     printk("x4412_char_cleanup_module!\n");
  62. }

  63. module_init(x4412_char_init_module);
  64. module_exit(x4412_char_cleanup_module);
复制代码
       本程序在模块初始化函数中调用register_chrdev函数,向内核注册一个名为x4412_char,主设备号为224的字符设备。在file_operations结构体中指定了x4412_char_readx4412_char_write两个回调函数,他们分别使用copy_to_usercopy_from_user函数和应用程序交互数据。
       将源码文件命名为x4412-char.c,并放到kernel/drivers/char/x4412目录,在kernel/drivers/char/Makefile文件中添加如下语句:
  1. obj-$(CONFIG_X4412_CHAR_DRIVER) += x4412-char.o
复制代码
       kernel/drivers/char/Kconfig文件中添加如下语句:
  1. config X4412_CHAR_DRIVER
  2.          tristate "x4412 char driver"
  3.          default y
  4.          help
  5.          compile for x4412 char driver,y for kernel,m for module.
复制代码
       为了一并测试注销设备的函数unregister_chrdev,将驱动编译成模块。加载驱动后,可以在/proc/modules下找到注册的设备名称和主设备号:
  1. [root@x4412 mnt]# more /proc/devices |grep x4412_char
  2. [root@x4412 mnt]# insmod x4412-char.ko
  3. [  168.241683] x4412_char_init_module!
  4. [root@x4412 mnt]# more /proc/devices |grep x4412_char
  5. 224 x4412_char
  6. [root@x4412 mnt]#
复制代码
       在驱动中并没有创建设备节点和次设备号。通过mknod指令手动创建设备结点以及次设备号:
  1. [root@x4412 mnt]# mknod /dev/x4412-char c 224 0
  2. [root@x4412 mnt]# ls /dev/x4412-char -la
  3. crw-r--r--    1 root     root      224,   0 Sep 27 08:48 /dev/x4412-char
复制代码
       编写测试应用程序,向驱动中传入字符串www.9tripod.com,再从驱动中读取该字符串,并打印到串口终端。参考源码如下:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <fcntl.h>
  4. void main(void)
  5. {
  6.     int fd;
  7.     int i;
  8.     char data[256];
  9.     int retval;
  10.     fd=open("/dev/x4412-char",O_RDWR);
  11.     if(fd==-1)
  12.     {
  13.         perror("error open\n");
  14.         exit(-1);
  15.     }
  16.     printf("open /dev/x4412-char successful\n");
  17.     //写数据
  18.     retval=write(fd,"www.9tripod.com",15);
  19.     if(retval==-1)
  20.     {
  21.         perror("write error\n");
  22.         exit(-1);
  23.     }
  24.     //读数据
  25.     retval=read(fd,data,15);
  26.     if(retval==-1)
  27.     {
  28.         perror("read error\n");
  29.         exit(-1);
  30.     }
  31.     data[retval]=0;
  32.     printf("read data:\n");
  33.     printf("%s\n",data);
  34.     //关闭设备
  35.     close(fd);
  36. }
复制代码
       将应用程序源码命名为x4412-char-app.c,保存到ubuntusamba目录,执行如下指令编译测试程序:
  1. arm-none-linux-gnueabi-gcc x4412-char-app.c -o x4412-char-app
复制代码
       将生成的测试程序映像x4412-char-app拷贝到开发板并执行,可以看到现象如下:
  1. [root@x4412 mnt]# ./x4412-char-app
  2. open /dev/x4412-char successful
  3. read data:
  4. www.9tripod.com
复制代码
       再从头梳理一遍整个实验原理。使用register_chrdev函数注册设备时,主设备号是手动指定的。如果内核已经存在这个主设备号,将不能正常注册。register_chrdev函数引入了自动注册主设备号的功能,当第一个参数为0时表示自动注册主设备号。不仿将驱动中的x4412_char_MAJOR定义为0,再来观察结果:
  1. [root@x4412 mnt]# insmod x4412-char.ko
  2. [   98.050237] x4412_char_init_module!
  3. [root@x4412 mnt]# more /proc/devices |grep x4412_char
  4. 249 x4412_char
  5. [root@x4412 mnt]#
复制代码
       可见,register_chrdev函数自动分配主设备号为249。使用自动分配的方式可以消除设备注册失败的风险。这时创立设备节点时,应该对应正确的设备号了:
  1. [root@x4412 mnt]# mknod /dev/x4412-char c 249 0
  2. [root@x4412 mnt]# ./x4412-char-app
  3. open /dev/x4412-char successful
  4. read data:
  5. www.9tripod.com
复制代码
       使用rmmod指令卸载驱动模块:
  1. [root@x4412 mnt]# rmmod x4412-char.ko
  2. [  382.105242] x4412_char_cleanup_module!
复制代码
       上面打印语句是在驱动的卸载函数中调用的。尽管这时模块被卸载,但是在/dev下的设备节点以及在/proc/devices下的设备名称仍然没有被删掉,机器重启之后,/dev一睥设备节点也不会删掉,这就是devfs文件系统最大的病源,udev文件系统应声而出,解决了devfs一系列令系统工程师头痛的问题。
       可以通过class_createdevice_create函数自动创建设备节点,在驱动源码中声明头文件:
  1. #include <linux/device.h>
复制代码
       在模块初始化函数中添加相应函数自动创建设备节点:
  1. int x4412_char_init_module(void)
  2. {
  3.     int ret;
  4.     printk("x4412_char_init_module!\n");
  5.     //根据设备号与设备名注册字符设备
  6.     ret = register_chrdev(x4412_char_MAJOR, "x4412_char", &x4412_char_fops);  
  7.     if (ret < 0)
  8.     {
  9.         printk("Unable to register character device %d!\n",x4412_char_MAJOR);
  10.         return ret;
  11.     }
  12.     //创建class
  13.     x4412_char_dev_class = class_create(THIS_MODULE, "x4412_char");
  14.     if (IS_ERR(x4412_char_dev_class))
  15.     {
  16.          printk("create x4412_char dev class error!\r\n");
  17.          unregister_chrdev(x4412_char_MAJOR, "x4412_char");
  18.          return PTR_ERR(x4412_char_dev_class);
  19.     }
  20.     //创建节点
  21.     device_create(x4412_char_dev_class, NULL, MKDEV(x4412_char_MAJOR, 0), NULL, "x4412-char");
  22.     return 0;
  23. }
复制代码
       在模块卸载函数中添加相应的卸载函数:
  1. void x4412_char_cleanup_module(void)
  2. {
  3.     unregister_chrdev(x4412_char_MAJOR,  "x4412_char");
  4.     device_destroy(x4412_char_dev_class, MKDEV(x4412_char_MAJOR, 0));
  5.     class_destroy(x4412_char_dev_class);
  6.     printk("x4412_char cleanup success!\n");
  7. }
复制代码
       指定主设备号:
  1. #define x4412_char_MAJOR 224
复制代码
       将模块编译进内核,不要编译成模块形式,更新内核,观察设备节点是否自动生成,并用测试程序测试驱动是否正常。
  1. [root@x4412 mnt]# ls /dev/x4412-char -la
  2. crw-rw----    1 root     root      224,   0 Sep 27 10:24 /dev/x4412-char
  3. [root@x4412 mnt]# more /proc/devices |grep x4412_char
  4. 224 x4412_char
  5. [root@x4412 mnt]# ./x4412-char-app
  6. open /dev/x4412-char successful
  7. read data:
  8. www.9tripod.com
复制代码
       使用这种自动生成节点的方法,如果将主设备号定义为0,即自动生成主设备号,将无法自动产生设备节点。即便是手动指定主设备号,将驱动编译成模块后,通过insmod手动加载时,也不会产生设备节点。可见,devfs文件系统确实存在相当多的毛病。
回复

使用道具 举报

沙发
发表于 2015-9-16 16:04:27 | 只看该作者
尽管这时模块被卸载,但是在/dev下的设备节点以及在/proc/devices下的设备名称仍然没有被删掉,机器重启之后,/dev一睥设备节点也不会删掉,这就是devfs文件系统最大的病源,udev文件系统应声而出,解决了devfs一系列令系统工程师头痛的问题。
devfs文件系统确实存在相当多的毛病
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-5-5 20:54 , Processed in 0.022229 second(s), 18 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表