搜索
bottom↓
回复: 1

【正点原子Linux连载】第五十七章Linux MISC驱动实验--摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南

[复制链接]

出0入234汤圆

发表于 2020-7-7 10:14:50 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2020-10-26 11:55 编辑

1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html  
4)对正点原子Linux感兴趣的同学可以加群讨论:
876919289 QQ群头像.png
5)关注正点原子公众号,获取最新资料


100846rel79a9p4uelap24.jpg

100846f1ce1fg14zbg0va4.png

第五十七章Linux MISC驱动实验


        misc的意思是混合、杂项的,因此MISC驱动也叫做杂项驱动,也就是当我们板子上的某些外设无法进行分类的时候就可以使用MISC驱动。MISC驱动其实就是最简单的字符设备驱动,通常嵌套在platform总线驱动中,实现复杂的驱动,本章我们就来学习一下MISC驱动的编写。



57.1 MISC设备驱动简介
        所有的MISC设备驱动的主设备号都为10,不同的设备使用不同的从设备号。随着Linux字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主主设备号,MISC设备驱动就用于解决此问题。MISC设备会自动创建cdev,不需要像我们以前那样手动创建,因此采用MISC设备驱动可以简化字符设备驱动的编写。我们需要向Linux注册一个miscdevice设备,miscdevice是一个结构体,定义在文件
示例代码57.1.1 miscdevice结构体代码
  1. 57struct miscdevice  {
  2. 58        int minor;                /* 子设备号                */
  3. 59        constchar*name;                /* 设备名字                */
  4. 60        conststruct file_operations *fops;        /* 设备操作集        */
  5. 61        struct list_head list;
  6. 62        struct device *parent;
  7. 63        struct device *this_device;
  8. 64        conststruct attribute_group **groups;
  9. 65        constchar*nodename;
  10. 66        umode_t mode;
  11. 67};
复制代码

        定义一个MISC设备(miscdevice类型)以后我们需要设置minor、name和fops这三个成员变量。minor表示子设备号,MISC设备的主设备号为10,这个是固定的,需要用户指定子设备号,Linux系统已经预定义了一些MISC设备的子设备号,这些预定义的子设备号定义在include/linux/miscdevice.h文件中,如下所示:
示例代码57.1.2 预定义的MISC设备子设备号
  1. 13 #define PSMOUSE_MINOR                        1
  2. 14 #define MS_BUSMOUSE_MINOR                    2/* unused */
  3. 15 #define ATIXL_BUSMOUSE_MINOR         3/* unused */
  4. 16/*#define AMIGAMOUSE_MINOR           4   FIXME OBSOLETE */
  5. 17 #define ATARIMOUSE_MINOR                         5        /* unused */
  6. 18 #define SUN_MOUSE_MINOR                      6/* unused */
  7. ......
  8. 52 #define MISC_DYNAMIC_MINOR           255
复制代码

        我们在使用的时候可以从这些预定义的子设备号中挑选一个,当然也可以自己定义,只要这个子设备号没有被其他设备使用接口。
name就是此MISC设备名字,当此设备注册成功以后就会在/dev目录下生成一个名为name的设备文件。fops就是字符设备的操作集合,MISC设备驱动最终是需要使用用户提供的fops操作集合。
        当设置好miscdevice以后就需要使用misc_register函数向系统中注册一个MISC设备,此函数原型如下:
  1. int misc_register(struct miscdevice * misc)
复制代码

函数参数和返回值含义如下:
        misc:要注册的MISC设备。
        返回值:负数,失败;0,成功。
        以前我们需要自己调用一堆的函数去创建设备,比如在以前的字符设备驱动中我们会使用如下几个函数完成设备创建过程:
示例代码57.1.3 传统的创建设备过程
  1. 1 alloc_chrdev_region();        /* 申请设备号        */
  2. 2 cdev_init();                /* 初始化cdev  */
  3. 3 cdev_add();        /* 添加cdev          */
  4. 4 class_create();        /* 创建类                */
  5. 5 device_create();                /* 创建设备                */
复制代码

        现在我们可以直接使用misc_register一个函数来完成示例代码57.1.2中的这些步骤。当我们卸载设备驱动模块的时候需要调用misc_deregister函数来注销掉MISC设备,函数原型如下:
  1. int misc_deregister(struct miscdevice *misc)
复制代码

函数参数和返回值含义如下:
        misc:要注销的MISC设备。
        返回值:负数,失败;0,成功。
        以前注销设备驱动的时候,我们需要调用一堆的函数去删除此前创建的cdev、设备等等内容,如下所示:
示例代码57.1.3 传统的删除设备的过程
  1. 1 cdev_del();                                                /*  删除cdev         */
  2. 2 unregister_chrdev_region();        /* 注销设备号        */
  3. 3 device_destroy();                        /* 删除设备                */
  4. 4 class_destroy();                        /* 删除类                */
复制代码

        现在我们只需要一个misc_deregister函数即可完成示例代码57.1.3中的这些工作。关于MISC设备驱动就讲解到这里,接下来我们就使用platform加MISC驱动框架来编写beep蜂鸣器驱动。
57.2 硬件原理图分析
本章实验我们只使用到IMX6U-ALPHA开发板上的BEEP蜂鸣器,因此实验硬件原理图参考14.3小节即可。
57.3 实验程序编写
本实验对应的例程路径为:开发板光盘->2、Linux驱动例程->17_misc。
本章实验我们采用platform加misc的方式编写beep驱动,这也是实际的Linux驱动中很常用的方法。采用platform来实现总线、设备和驱动,misc主要负责完成字符设备的创建。
57.3.1 修改设备树
        本章实验我们需要用到蜂鸣器,因此需要在imx6ull-alientek-emmc.dts文件中创建蜂鸣器设备节点,这里我们直接使用46.3.1小节创建的beep这个设备节点即可。
57.3.2 beep驱动程序编写
        新建名为“19_miscbeep”的文件夹,然后在19_miscbeep文件夹里面创建vscode工程,工作区命名为“miscbeep。新建名为miscbeep.c的驱动文件,在miscbeep.c中输入如下所示内容:
示例代码57.3.2.1 miscbeep.c文件代码段
  1. 1   #include <linux/types.h>
  2. 2   #include <linux/kernel.h>
  3. 3   #include <linux/delay.h>
  4. 4   #include <linux/ide.h>
  5. 5   #include <linux/init.h>
  6. 6   #include <linux/module.h>
  7. 7   #include <linux/errno.h>
  8. 8   #include <linux/gpio.h>
  9. 9   #include <linux/cdev.h>
  10. 10  #include <linux/device.h>
  11. 11  #include <linux/of.h>
  12. 12  #include <linux/of_address.h>
  13. 13  #include <linux/of_gpio.h>
  14. 14  #include <linux/platform_device.h>
  15. 15  #include <linux/miscdevice.h>
  16. 16  #include <asm/mach/map.h>
  17. 17  #include <asm/uaccess.h>
  18. 18  #include <asm/io.h>
  19. 19/***************************************************************
  20. 20  Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
  21. 21文件名                : miscbeep.c
  22. 22作者        : 左忠凯
  23. 23版本        : V1.0
  24. 24描述        : 采用MISC的蜂鸣器驱动程序。
  25. 25其他        : 无
  26. 26论坛        : <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  27. 27日志        : 初版V1.0 2019/8/20 左忠凯创建
  28. 28  ***************************************************************/
  29. 29  #define MISCBEEP_NAME           "miscbeep"        /* 名字        */
  30. 30  #define MISCBEEP_MINOR          144        /* 子设备号        */
  31. 31  #define BEEPOFF                  0                /* 关蜂鸣器        */
  32. 32  #define BEEPON                   1                /* 开蜂鸣器        */
  33. 33
  34. 34/* miscbeep设备结构体 */
  35. 35struct miscbeep_dev{
  36. 36      dev_t devid;                /* 设备号                                */
  37. 37struct cdev cdev;                /* cdev                                     */
  38. 38struct class *class;                /* 类                                        */
  39. 39struct device *device;                /* 设备                                        */
  40. 40struct device_node  *nd;        /* 设备节点                                        */
  41. 41int beep_gpio;                /* beep所使用的GPIO编号        */
  42. 42};
  43. 43
  44. 44struct miscbeep_dev miscbeep;        /* beep设备                                */
  45. 45
  46. 46/*
  47. 47   * @description          : 打开设备
  48. 48   * @param – inode        : 传递给驱动的inode
  49. 49   * @param - filp         : 设备文件,file结构体有个叫做private_data的成员变量
  50. 50   *                    一般在open的时候将private_data指向设备结构体。
  51. 51   * @return                : 0 成功;其他失败
  52. 52   */
  53. 53staticint miscbeep_open(struct inode *inode,struct file *filp)
  54. 54{
  55. 55      filp->private_data =&miscbeep;/* 设置私有数据 */
  56. 56return0;
  57. 57}
  58. 58
  59. 59/*
  60. 60   * @description          : 向设备写数据
  61. 61   * @param - filp         : 设备文件,表示打开的文件描述符
  62. 62   * @param - buf          : 要写给设备写入的数据
  63. 63   * @param - cnt         : 要写入的数据长度
  64. 64   * @param - offt         : 相对于文件首地址的偏移
  65. 65   * @return               : 写入的字节数,如果为负值,表示写入失败
  66. 66   */
  67. 67static ssize_t miscbeep_write(struct file *filp,
  68. constchar __user *buf,size_t cnt, loff_t *offt)
  69. 68{
  70. 69int retvalue;
  71. 70unsignedchar databuf[1];
  72. 71unsignedchar beepstat;
  73. 72struct miscbeep_dev *dev = filp->private_data;
  74. 73
  75. 74      retvalue = copy_from_user(databuf, buf, cnt);
  76. 75if(retvalue <0){
  77. 76          printk("kernel write failed!\r\n");
  78. 77return-EFAULT;
  79. 78}
  80. 79
  81. 80      beepstat = databuf[0];                                /* 获取状态值        */
  82. 81if(beepstat == BEEPON){
  83. 82          gpio_set_value(dev->beep_gpio,0);        /* 打开蜂鸣器        */
  84. 83}elseif(beepstat == BEEPOFF){
  85. 84          gpio_set_value(dev->beep_gpio,1);        /* 关闭蜂鸣器        */
  86. 85}
  87. 86return0;
  88. 87}
  89. 88
  90. 89/* 设备操作函数 */
  91. 90staticstruct file_operations miscbeep_fops ={
  92. 91.owner = THIS_MODULE,
  93. 92.open = miscbeep_open,
  94. 93.write = miscbeep_write,
  95. 94};
  96. 95
  97. 96/* MISC设备结构体 */
  98. 97staticstruct miscdevice beep_miscdev ={
  99. 98.minor = MISCBEEP_MINOR,
  100. 99.name = MISCBEEP_NAME,
  101. 100.fops =&miscbeep_fops,
  102. 101};
  103. 102
  104. 103/*
  105. 104   * @description        : flatform驱动的probe函数,当驱动与
  106. 105   *                    设备匹配以后此函数就会执行
  107. 106   * @param - dev         : platform设备
  108. 107   * @return              : 0,成功;其他负值,失败
  109. 108   */
  110. 109staticint miscbeep_probe(struct platform_device *dev)
  111. 110{
  112. 111int ret =0;
  113. 112
  114. 113     printk("beep driver and device was matched!\r\n");
  115. 114/* 设置BEEP所使用的GPIO */
  116. 115/* 1、获取设备节点:beep */
  117. 116     miscbeep.nd = of_find_node_by_path("/beep");
  118. 117if(miscbeep.nd ==NULL){
  119. 118         printk("beep node not find!\r\n");
  120. 119return-EINVAL;
  121. 120}
  122. 121
  123. 122/* 2、获取设备树中的gpio属性,得到BEEP所使用的BEEP编号 */
  124. 123     miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd,"beep-gpio",
  125. 0);
  126. 124if(miscbeep.beep_gpio <0){
  127. 125         printk("can't get beep-gpio");
  128. 126return-EINVAL;
  129. 127}
  130. 128
  131. 129/* 3、设置GPIO5_IO01为输出,并且输出高电平,默认关闭BEEP */
  132. 130     ret = gpio_direction_output(miscbeep.beep_gpio,1);
  133. 131if(ret <0){
  134. 132         printk("can't set gpio!\r\n");
  135. 133}
  136. 134
  137. 135/* 一般情况下会注册对应的字符设备,但是这里我们使用MISC设备
  138. 136      * 所以我们不需要自己注册字符设备驱动,只需要注册misc设备驱动即可
  139. 137      */
  140. 138     ret = misc_register(&beep_miscdev);
  141. 139if(ret <0){
  142. 140         printk("misc device register failed!\r\n");
  143. 141return-EFAULT;
  144. 142}
  145. 143
  146. 144return0;
  147. 145}
  148. 146
  149. 147/*
  150. 148  * @description          : remove函数,移除platform驱动的时候此函数会执行
  151. 149  * @param - dev          : platform设备
  152. 150  * @return                : 0,成功;其他负值,失败
  153. 151  */
  154. 152staticint miscbeep_remove(struct platform_device *dev)
  155. 153{
  156. 154/* 注销设备的时候关闭LED灯 */
  157. 155     gpio_set_value(miscbeep.beep_gpio,1);
  158. 156
  159. 157/* 注销misc设备驱动 */
  160. 158     misc_deregister(&beep_miscdev);
  161. 159return0;
  162. 160}
  163. 161
  164. 162/* 匹配列表 */
  165. 163staticconststruct of_device_id beep_of_match[]={
  166. 164{.compatible ="atkalpha-beep"},
  167. 165{/* Sentinel */}
  168. 166};
  169. 167
  170. 168/* platform驱动结构体 */
  171. 169staticstruct platform_driver beep_driver ={
  172. 170.driver     ={
  173. 171.name   ="imx6ul-beep",        /* 驱动名字                */
  174. 172.of_match_table = beep_of_match,        /* 设备树匹配表        */
  175. 173},
  176. 174.probe      = miscbeep_probe,
  177. 175.remove     = miscbeep_remove,
  178. 176};
  179. 177
  180. 178/*
  181. 179  * @description :        驱动入口函数
  182. 180  * @param               : 无
  183. 181  * @return              : 无
  184. 182  */
  185. 183staticint __init miscbeep_init(void)
  186. 184{
  187. 185return platform_driver_register(&beep_driver);
  188. 186}
  189. 187
  190. 188/*
  191. 189  * @description         : 驱动出口函数
  192. 190  * @param               : 无
  193. 191  * @return              : 无
  194. 192  */
  195. 193staticvoid __exit miscbeep_exit(void)
  196. 194{
  197. 195     platform_driver_unregister(&beep_driver);
  198. 196}
  199. 197
  200. 198 module_init(miscbeep_init);
  201. 199 module_exit(miscbeep_exit);
  202. 200 MODULE_LICENSE("GPL");
  203. 201 MODULE_AUTHOR("zuozhongkai");
复制代码

        第29~94行,标准的字符设备驱动。
        第97~101行,MISC设备beep_miscdev,第98行设置子设备号为144,第99行设置设备名字为“miscbeep”,这样当系统启动以后就会在/dev/目录下存在一个名为“miscbeep”的设备文件。第100行,设置MISC设备的操作函数集合,为file_operations类型。
        第109~145行,platform框架的probe函数,当驱动与设备匹配以后此函数就会执行,首先在此函数中初始化BEEP所使用的IO。最后在138行通过misc_register函数向Linux内核注册MISC设备,也就是前面定义的beep_miscdev。
        第152~160行,platform框架的remove函数,在此函数中调用misc_deregister函数来注销MISC设备。
        第163~196,标准的platform驱动。
57.3.3 编写测试APP
        新建miscbeepApp.c文件,然后在里面输入如下所示内容:
示例代码57.3.2.2 miscbeepApp.c文件代码段
  1. 1  #include "stdio.h"
  2. 2  #include "unistd.h"
  3. 3  #include "sys/types.h"
  4. 4  #include "sys/stat.h"
  5. 5  #include "fcntl.h"
  6. 6  #include "stdlib.h"
  7. 7  #include "string.h"
  8. 8/***************************************************************
  9. 9  Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
  10. 10文件名                : miscbeepApp.c
  11. 11作者        : 左忠凯
  12. 12版本        : V1.0
  13. 13描述        : MISC驱动框架下的beep测试APP。
  14. 14其他        : 无
  15. 15使用方法                :./miscbeepApp  /dev/miscbeep  0 关闭蜂鸣器
  16. 16          ./misdcbeepApp /dev/miscbeep  1 打开蜂鸣器
  17. 17论坛        : <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  18. 18日志        : 初版V1.0 2019/8/20 左忠凯创建
  19. 19 ***************************************************************/
  20. 20 #define BEEPOFF  0
  21. 21 #define BEEPON   1
  22. 22
  23. 23/*
  24. 24  * @description          : main主程序
  25. 25  * @param - argc         : argv数组元素个数
  26. 26  * @param - argv          : 具体参数
  27. 27  * @return                : 0 成功;其他失败
  28. 28  */
  29. 29int main(int argc,char*argv[])
  30. 30{
  31. 31        int fd, retvalue;
  32. 32                char*filename;
  33. 33        unsignedchar databuf[1];
  34. 34
  35. 35        if(argc !=3){
  36. 36        printf("Error Usage!\r\n");
  37. 37        return-1;
  38. 38        }
  39. 39
  40. 40        filename = argv[1];
  41. 41        fd = open(filename, O_RDWR);/* 打开beep驱动 */
  42. 42        if(fd <0){
  43. 43        printf("file %s open failed!\r\n", argv[1]);
  44. 44        return-1;
  45. 45        }
  46. 46
  47. 47        databuf[0]= atoi(argv[2]);                /* 要执行的操作:打开或关闭 */
  48. 48        retvalue = write(fd, databuf,sizeof(databuf));
  49. 49        if(retvalue <0){
  50. 50        printf("BEEP Control Failed!\r\n");
  51. 51        close(fd);
  52. 52        return-1;
  53. 53        }
  54. 54
  55. 55        retvalue = close(fd);                        /* 关闭文件 */
  56. 56        if(retvalue <0){
  57. 57        printf("file %s close failed!\r\n", argv[1]);
  58. 58        return-1;
  59. 59        }
  60. 60        return0;
  61. 61}
复制代码

        miscbeepApp.c文件内容和其他例程的测试APP基本一致,很简单,这里就不讲解了。
57.4 运行测试
57.4.1 编译驱动程序和测试APP
1、编译驱动程序
        编写Makefile文件,本章实验的Makefile文件和第四十章实验基本一样,只是将obj-m变量的值改为“leddevice.o leddriver.o”,Makefile内容如下所示:
示例代码57.4.1.1 Makefile文件
  1. 1  KERNELDIR:= /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
  2. ......
  3. 4  obj-m := miscbeep.o
  4. ......
  5. 11 clean:
  6. 12$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
复制代码

        第4行,设置obj-m变量的值为“miscbeep.o”。
        输入如下命令编译出驱动模块文件:
  1. make-j32
复制代码

        编译成功以后就会生成一个名为“miscbeep.ko”的驱动模块文件。
        2、编译测试APP
        输入如下命令编译测试miscbeepApp.c这个测试程序:
  1. arm-linux-gnueabihf-gccmiscbeepApp.c -o miscbeepApp
复制代码

        编译成功以后就会生成miscbeepApp这个应用程序。
57.4.2 运行测试
        将上一小节编译出来miscbeep.ko和miscbeepApp这两个文件拷贝到rootfs/lib/modules/4.1.15目录中,重启开发板,进入到目录lib/modules/4.1.15中,输入如下命令加载miscbeep.ko这个驱动模块。
  1. depmod                                //第一次加载驱动的时候需要运行此命令
  2. modprobe miscbeep.ko        //加载设备模块
复制代码

        当驱动模块加载成功以后我们可以在/sys/class/misc这个目录下看到一个名为“miscbeep”的子目录,如图57.4.2.1所示:
image002.gif

图57.4.2.1 misbeep子目录

        所有的misc设备都属于同一个类,/sys/class/misc目录下就是misc这个类的所有设备,每个设备对应一个子目录。
        驱动与设备匹配成功以后就会生成/dev/miscbeep这个设备驱动文件,输入如下命令查看这个文件的主次设备号:
  1. ls /dev/miscbeep -l
复制代码

        结果如图57.4.2.2所示:
image004.jpg

图57.4.2.2 /dev/miscbeep设备文件

        从图57.4.2.2可以看出,/dev/miscbeep这个设备的主设备号为10,次设备号为144,和我们驱动程序里面设置的一致。
输入如下命令打开BEEP:
  1. ./miscbeepApp /dev/miscbeep 1        //打开BEEP
  2.         在输入如下命令关闭LED灯:
  3. ./miscbeepApp /dev/miscbeep 0//关闭BEEP
复制代码

        观察一下BEEP能否打开和关闭,如果可以的话就说明驱动工作正常,如果要卸载驱动的话输入如下命令即可:
  1. rmmod miscbeep.ko
复制代码

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出16170入6148汤圆

发表于 2020-7-7 10:16:05 来自手机 | 显示全部楼层
打赏!

庆祝论坛“打赏”功能实施, 现在开始发技术主题,可以获得打赏
https://www.amobbs.com/thread-5735948-1-1.html
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-27 11:57

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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