Linux驱动编程 step-by-step (二)

简单字符设备驱动
1、主次设备号
主设备号标识设备连接的的驱动,此设备好由内核使用,标识在相应驱动下得对应的设备
在linux中设备号是一个32位的dev_t类型

typedef__u32__kernel_dev_t;
typedef__kernel_dev_tdev_t;


crw-------1 rootroot10,1 Apr11 2011 psaux
crw-------1 rootroot4,1 Oct2803:04 tty1
crw-rw-rw-1 roottty4,64 Apr11 2011 ttys0
crw-rw----1 rootuucp4,65 Apr11 2011 ttyS
上图是再/dev目录下用$ls -l 命令显示的部分结果可以看到tty driver的主设备号都为4(各个系统版本有差别),次设备号不同

前12位标识主设备号 MAJOR(dev_t dev) 获得主设备号
后20位标识此设备号 MINOR(dev_t dev) 获得此设备号

由主次设备号生成设备号
可以使用宏MKDEV
dev_t dev_num = MKDEV(dev_t major, dev_t minor);

2、分配与释放设备号

在linux2.6的字符设备中(kernel3.0也是)首先做的事就是申请一个或者多个设备号

  1. /*静态分配设备号
  2. *parameter:
  3. *first:分配的第一个设备号
  4. *count:分配的设备个数
  5. *name:设备名
  6. *returnvalue:
  7. *0:success
  8. *负值:出现错误,错误码
  9. */
  10. intregister_chrdev_region(dev_tfirst,unsignedintcount,char*name);
  11. /*动态分配设备号
  12. *parameter:
  13. *dev:用来存储分配的设备号值
  14. *firstminor:次设备号(一般填0)
  15. *count:分配的设备个数
  16. *name:设备名
  17. *returnvalue:
  18. *0:success
  19. *负值:出现错误,错误码
  20. */
  21. intalloc_chrdev_region(dev_t*dev,unsignedintfirstminor,unsignedintcount,char*name);
  22. /*释放设备号
  23. *parameter:
  24. *first:设备号
  25. *count:分配的设备个数
  26. */
  27. voidunregister_chrdev_region(dev_tfirst,unsignedintcount);

静态分配设备号,是在已经知道一个可用设备号的时候使用,而程序员在编写程序之前大多并知道设备号是否可用,或者现在可用,不能确保在系统升级时候次设备还是可用的
所以linux社区极力推荐使用动态分配,它会去寻找可用的设备号,而不会产生冲突。在次设备卸载的时候需要释放次设备号。

3、一个没有作用的字符设备驱动

  1. #include<linux/init.h>
  2. #include<linux/module.h>
  3. #include<linux/types.h>
  4. #include<linux/fs.h>
  5. #defineSIMPLE_DEBUG1
  6. #defineDEV_COUNT2
  7. #defineSIMPLE_NAME"simple_char"
  8. staticintsimple_major=108;
  9. staticintsimple_minor=0;
  10. static__initintsimple_init(void)
  11. {
  12. dev_tdev;
  13. interr;
  14. #ifSIMPLE_DEBUG
  15. printk(KERN_INFO"In%s\n",__func__);
  16. #endif
  17. dev=MKDEV(simple_major,simple_minor);//求取设备号
  18. if(dev>0)//设备号有效
  19. {
  20. #ifSIMPLE_DEBUG
  21. printk(KERN_INFO"trytoregisterstaticchardev%d\n",dev);
  22. #endif
  23. err=register_chrdev_region(dev,DEV_COUNT,SIMPLE_NAME);//静态分配设备号
  24. if(err<0)//静态分配出错尝试使用动态分配
  25. {
  26. printk(KERN_WARNING"registerstaticchardeverror\n");
  27. err=alloc_chrdev_region(&dev,0,DEV_COUNT,SIMPLE_NAME);//动态分配设备号
  28. if(err<0)
  29. {
  30. printk(KERN_ERR"registerchardeverrorinline%d\n",__LINE__);
  31. gotoerror;
  32. }
  33. else
  34. {
  35. simple_major=MAJOR(dev);//重新计算主设备号
  36. simple_minor=MINOR(dev);//重新计算此设备号
  37. }
  38. }
  39. else{
  40. }
  41. }
  42. else//设备号无效使用动态分配
  43. {
  44. #ifSIMPLE_DEBUG
  45. printk(KERN_INFO"trytoregisterallocchardev\n");
  46. #endif
  47. err=alloc_chrdev_region(&dev,0,DEV_COUNT,SIMPLE_NAME);
  48. if(err<0)
  49. {
  50. printk(KERN_ERR"registerchardeverrorinline%d\n\n",__LINE__);
  51. gotoerror;
  52. }
  53. else
  54. {
  55. simple_major=MAJOR(dev);
  56. simple_minor=MINOR(dev);
  57. }
  58. }
  59. #ifSIMPLE_DEBUG
  60. printk(KERN_INFO"registerchardevsuccessmajor=%dminor=%d\n",simple_major,simple_minor);
  61. #endif
  62. error:
  63. returnerr;
  64. }
  65. static__exitvoidsimple_exit(void)
  66. {
  67. dev_tdev;
  68. #ifSIMPLE_DEBUG
  69. printk(KERN_INFO"In%s\n",__func__);
  70. #endif
  71. dev=MKDEV(simple_major,simple_minor);
  72. unregister_chrdev_region(dev,DEV_COUNT);//释放设备号
  73. }
  74. module_init(simple_init);
  75. module_exit(simple_exit);
  76. MODULE_LICENSE("GPL");
  77. MODULE_AUTHOR("kai_zhang([email protected])");
  78. MODULE_DESCRIPTION("simplechardriver!");

这里只在模块初始化的时候去分配设备号,在模块注销的时候去释放次驱动拥有的设备号
在函数里边我们看到用到了在应用编程里边声名狼藉的goto函数,在linux驱动编程时 goto 函数可以让我们的编程更加有条理性,在出现错误时候能更快的去处理。
如果在调用函数检查返回者都去做错误处理则模块函数就显得臃肿,庞大。所以还是建议合理使用goto函数的。
加载次模块后
运行 $cat /proc/devices可以看到simple_char 的设备以及主设备号。

Linux驱动编程 step-by-step (二)

这里我们看到原来假设的主设备号是不可用的,所以使用的动态分配设备号,由此我们申请到主设备号为249,我们可以在上边添加我们的设备,具体操作下一节会讲到。呵呵留点悬念先。