• 驱动开发练习,platform驱动模型的使用


    一.总线模型介绍

            linux中将一个挂载在总线上的驱动的驱动模型分为三部分:device、driver和bus;

            device部分:用来保存设备信息对象,在内核中一个klist_device链表中进行管理;

            driver部分:用来保存驱动信息对象,在内核中一个klist_driver链表中进行管理;

            bus部分:负责完成device和driver到的匹配,通过总线驱动中的match函数来实现;

            当device和driver匹配成功后执行driver端的probe函数,在probe函数中完成驱动的注册、设备节点的创建、以及后续的硬件控制工作。

    二.platform驱动模型的引入

    1.引入原因

            为了让没有挂载在总线上的设备也能够按照总线驱动模型进行驱动的编写,我们引入了paltform总线,引入platform统一我们的设备驱动模型。

    2.platform总线驱动模型介绍

            platform是一段内核抽象出来的总线驱动代码,但是现实中并没有和platform总线驱动对应的真实总线,它的作用就是管理没有挂载在总线上的设备,让这些设备有也可以按照总线驱动模型编写驱动。

            将一个platform总线驱动模型分为三部分:设备端、驱动端、总线端。由总线负责完成驱动和设备信息的匹配,当匹配成功之后会执行驱动端的probe函数。在probe函数中实现驱动的注册、设备节点的创建以及后续的硬件控制工作。

    三.练习(本例:按键1控制LED1)

    ---pdrive.c---驱动程序
    1. #include <linux/init.h>
    2. #include <linux/module.h>
    3. #include <linux/platform_device.h>
    4. #include <linux/mod_devicetable.h>
    5. #include <linux/of_gpio.h>
    6. #include <linux/gpio.h>
    7. #include <linux/io.h>
    8. #include <linux/device.h>
    9. #include <linux/uaccess.h>
    10. #include <linux/wait.h>
    11. #include <linux/of.h>
    12. #include <linux/of_irq.h>
    13. #include <linux/interrupt.h>
    14. #include <linux/slab.h>
    15. #include <linux/wait.h>
    16. // 定义一个等待队列头
    17. wait_queue_head_t wq_head;
    18. unsigned int condition = 0;
    19. unsigned int major;
    20. struct class *cls;
    21. struct device *dev;
    22. char kbuf[128] = {0};
    23. struct resource *res;
    24. unsigned int irqno;
    25. struct gpio_desc *gpiono;
    26. unsigned int number = 0;
    27. // 封装操作方法
    28. int mycdev_open(struct inode *inode, struct file *file)
    29. {
    30. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    31. return 0;
    32. }
    33. ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
    34. {
    35. int ret;
    36. // 判断IO方式
    37. if (file->f_flags & O_NONBLOCK) // 非阻塞
    38. {
    39. return -EINVAL;
    40. }
    41. else // 阻塞
    42. {
    43. wait_event_interruptible(wq_head, condition); // 先检查condition再将进程休眠
    44. }
    45. //将数据拷贝到用户空间
    46. ret = copy_to_user(ubuf, (void*)&number, size);
    47. if (ret)
    48. {
    49. printk("copy_to_ user err\n");
    50. return -EIO;
    51. }
    52. condition = 0; // 让下一次硬件数据没有就绪
    53. return 0;
    54. }
    55. ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
    56. {
    57. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    58. return 0;
    59. }
    60. int mycdev_close(struct inode *inode, struct file *file)
    61. {
    62. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    63. return 0;
    64. }
    65. struct file_operations fops = {
    66. .open = mycdev_open,
    67. .read = mycdev_read,
    68. .write = mycdev_write,
    69. .release = mycdev_close,
    70. };
    71. // 定义中断处理函数
    72. irqreturn_t key_handler(int irq, void *dev)
    73. {
    74. number = gpiod_get_value(gpiono); //获取led管脚值
    75. number = !number; //灯状态取反
    76. gpiod_set_value(gpiono, number); //重写管脚状态值
    77. condition = 1; // 表示硬件数据就绪
    78. wake_up_interruptible(&wq_head);
    79. return IRQ_HANDLED;
    80. }
    81. // 封装probe函数
    82. int pdri_probe(struct platform_device *pdev)
    83. {
    84. // 1初始化等待队列
    85. init_waitqueue_head(&wq_head);
    86. // 2字符设备驱动注册
    87. major = register_chrdev(0, "myled0", &fops);
    88. if (major < 0)
    89. {
    90. printk("字符设备驱动注册失败\n");
    91. return major;
    92. goto ERR1;
    93. }
    94. printk("字符设备驱动注册成功:major=%d\n", major);
    95. // 3向上提交目录
    96. cls = class_create(THIS_MODULE, "MYLED");
    97. if (IS_ERR(cls))
    98. {
    99. printk("向上提交目录失败\n");
    100. return -PTR_ERR(cls);
    101. goto ERR2;
    102. }
    103. printk("向上提交目录成功\n");
    104. // 4向上提交设备节点信息
    105. dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "myled0");
    106. if (IS_ERR(dev))
    107. {
    108. printk("向上提交设备节点信息失败\n");
    109. return -PTR_ERR(dev);
    110. goto ERR3;
    111. }
    112. printk("向上提交设备节点信息成功\n");
    113. // 5基于设备数节点信息获取gpio_desc对象指针
    114. gpiono = gpiod_get_from_of_node(pdev->dev.of_node, "led1-gpio", 0, GPIOD_OUT_LOW, NULL);
    115. if (IS_ERR(gpiono))
    116. {
    117. printk("解析GPIO管脚信息失败\n");
    118. return -ENXIO;
    119. goto ERR4;
    120. }
    121. printk("解析GPIO管脚信息成功\n");
    122. // 获取中断类型的资源,中断号
    123. irqno = platform_get_irq(pdev, 0);
    124. if (irqno < 0)
    125. {
    126. printk("获取中断类型资源失败\n");
    127. return -ENXIO;
    128. goto ERR5;
    129. }
    130. printk("key1_irq资源:%d\n", irqno);
    131. // 注册按键1中断
    132. int ret = request_irq(irqno, key_handler, IRQF_TRIGGER_FALLING, "key1_int", NULL);
    133. if (ret < 0)
    134. {
    135. printk("注册按键1中断失败\n");
    136. return ret;
    137. goto ERR5;
    138. }
    139. printk("注册按键1中断成功\n");
    140. printk("%s-%s-%d\n", __FILE__, __func__, __LINE__);
    141. ERR5:
    142. gpiod_put(gpiono);
    143. ERR4:
    144. device_destroy(cls, MKDEV(major, 0));
    145. ERR3:
    146. class_destroy(cls);
    147. ERR2:
    148. unregister_chrdev(major,"myled");
    149. ERR1:
    150. return ret;
    151. return 0;
    152. }
    153. // 封装remove函数
    154. int pdri_remove(struct platform_device *pdev)
    155. {
    156. // 注销中断
    157. free_irq(irqno, NULL);
    158. //释放GPIO管脚信息
    159. gpiod_put(gpiono);
    160. // 1销毁设备节点信息
    161. device_destroy(cls, MKDEV(major, 0));
    162. // 2销毁目录信息
    163. class_destroy(cls);
    164. // 3字符设备驱动注销
    165. unregister_chrdev(major, "myled");
    166. printk("%s-%s-%d\n", __FILE__, __func__, __LINE__);
    167. return 0;
    168. }
    169. // 构建设备树匹配表
    170. struct of_device_id oftable[] = {
    171. {.compatible = "myplatform"},
    172. {}, // 防止数组越界
    173. };
    174. // 定义驱动信息对象并初始化
    175. struct platform_driver pdri = {
    176. .probe = pdri_probe,
    177. .remove = pdri_remove,
    178. .driver = {
    179. .name = "ccc",
    180. .of_match_table = oftable,
    181. },
    182. };
    183. module_platform_driver(pdri); // 一键注册宏
    184. MODULE_LICENSE("GPL");
    ---read.c---应用层程序
    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/stat.h>
    4. #include <fcntl.h>
    5. #include <unistd.h>
    6. #include <stdlib.h>
    7. #include <string.h>
    8. int main(int argc,char const *argv[])
    9. {
    10. char buf[128]={0};
    11. int fd = open("/dev/myled0",O_RDWR);
    12. if(fd < 0)
    13. {
    14. printf("设备文件打开失败\n");
    15. exit(-1);
    16. }
    17. while(1)
    18. {
    19. //读取number的值
    20. read(fd,buf,sizeof(buf));
    21. printf("number = %s\n",buf);
    22. }
    23. close(fd);
    24. return 0;
    25. }
     现象:按下按键1,number = 1 灯亮;再按下按键1  number = 0 灯灭;

             

  • 相关阅读:
    47.各种类型的线程池
    【FreeSwitch开发实践】FreeSwitch常用知识点总结
    leetcode 241. 为运算表达式设计优先级
    通过@classmethod 实现多态
    vtk 动画入门 1 代码
    Spring MVC介绍
    MapInfo格式到ArcGIS格式的转换方法
    基于微信小程序的电影院票务系统设计与实现-计算机毕业设计源码+LW文档
    Hystrix 部署
    【工具使用】神经网络训练高效可视乎库visdom | 使用方式 概念全梳理
  • 原文地址:https://blog.csdn.net/weixin_46260677/article/details/133097621