• platform驱动模型


    一、总线驱动模型

    1.概念

            linux中将一个挂载在总线上的驱动的驱动模型分为三部分: devicedriverbus

            device是用来保存设备信息的对象,存放在内核中一个klist_device链表中进行管理。

            driver当前设备的驱动信息对象,存放在内核中一个klist_driver链表中进行管理。

            bus是当前设备挂载的总线的总线驱动

    2.流程

            bus负责完成device和driver到的匹配,这一步通过总线驱动中的match函数来实现。当device和driver匹配成功后执行driver端的probe函数,在probe函数中完成驱动的注册、设备节点的创建、以及后续的硬件控制工作。

    二、platform驱动模型

    1.引入的原因

            为了让没有挂载在总线上的设备也能够按照总线驱动模型进行驱动的编写

    2.platform总线驱动模型的部分

            设备端、驱动端、总线端

    3.过程

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

    4.设备端API

    1)分配设备信息对象并且初始化

            

    2)注册设备信息对象

            int platform_device_register(struct platform_device *pdev)

    3)注销设备对象

            void platform_device_unregister(struct platform_device *pdev)

    5.驱动端API

    三、名字表

    1.概述

            为了能够让驱动更加适配,我们可以在驱动端构建一个名字表。只要设备的名字和名字表中的任何一个名字一样,都可以执行驱动端probe函数

    2.名字表的构建

    struct platform_device_id idtable[]= {

            {"aaaaa",0},

            {"bbbbb",1},

            {"ccccc",2},

             {"ddddd",3},

             { },                   //防止数组越界

    }

    四、设备树匹配

    1.概述

            内核3.10版本以后要求将所有的设备信息都保存在设备树中,所有以后驱动端获取设备信息都在设备树中获取,所以需要使用驱动端的设备树匹配方式

    2.设备树匹配匹配项的类型

            

    3.准备

    1)添加用于设备树匹配的设备树节点

    2)构建用于设备树匹配的表

            struct of_device_id odtable[] = {

                    { .compatible = "hqyj,myplatform", },

                    { /* end node */ },       //防止数组越界

                    };

    五、任务

    pdrv.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. else//阻塞
    40. {
    41. wait_event_interruptible(wq_head,condition); //先检查condition再将进程休眠
    42. }
    43. ret = copy_to_user(ubuf, kbuf, size);
    44. if (ret)
    45. {
    46. printk("copy_to_ user err\n");
    47. return -EIO;
    48. }
    49. condition=0;//下一次硬件数据没有就绪
    50. return 0;
    51. }
    52. ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
    53. {
    54. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    55. return 0;
    56. }
    57. int mycdev_close(struct inode *inode, struct file *file)
    58. {
    59. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    60. return 0;
    61. }
    62. struct file_operations fops = {
    63. .open = mycdev_open,
    64. .read = mycdev_read,
    65. .write = mycdev_write,
    66. .release = mycdev_close,
    67. };
    68. // 定义中断处理函数
    69. irqreturn_t key_handler(int irq, void *dev)
    70. {
    71. if (number == 0)
    72. {
    73. number++;
    74. kbuf[0] = '1';
    75. // 灯亮
    76. gpiod_set_value(gpiono,1);
    77. }
    78. else if (number == 1)
    79. {
    80. number = 0;
    81. kbuf[0] = '0';
    82. // 灯灭
    83. gpiod_set_value(gpiono,0);
    84. }
    85. condition=1;//表示硬件数据就绪
    86. wake_up_interruptible(&wq_head);
    87. return IRQ_HANDLED;
    88. }
    89. // 封装probe函数
    90. int pdri_probe(struct platform_device *pdev)
    91. {
    92. //初始化等待队列
    93. init_waitqueue_head(&wq_head);
    94. // 1字符设备驱动注册
    95. major = register_chrdev(0, "myled", &fops);
    96. if (major < 0)
    97. {
    98. printk("字符设备驱动注册失败\n");
    99. return major;
    100. }
    101. printk("字符设备驱动注册成功:major=%d\n", major);
    102. // 2向上提交目录
    103. cls = class_create(THIS_MODULE, "myled");
    104. if (IS_ERR(cls))
    105. {
    106. printk("向上提交目录失败\n");
    107. return -PTR_ERR(cls);
    108. }
    109. printk("向上提交目录成功\n");
    110. // 3向上提交设备节点信息
    111. dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "myled0");
    112. if (IS_ERR(dev))
    113. {
    114. printk("向上提交设备节点信息失败\n");
    115. return -PTR_ERR(dev);
    116. }
    117. printk("向上提交设备节点信息成功\n");
    118. // 基于设备数节点信息获取gpio_desc对象指针
    119. gpiono = gpiod_get_from_of_node(pdev->dev.of_node, "led3-gpio", 0, GPIOD_OUT_LOW, NULL);
    120. if (IS_ERR(gpiono))
    121. {
    122. printk("解析GPIO管脚信息失败\n");
    123. return -ENXIO;
    124. }
    125. printk("解析GPIO管脚信息成功\n");
    126. // 获取中断类型的资源,中断号
    127. irqno = platform_get_irq(pdev, 0);
    128. if (irqno < 0)
    129. {
    130. printk("获取中断类型资源失败\n");
    131. return -ENXIO;
    132. }
    133. printk("key1_irq资源:%d\n", irqno);
    134. // 注册按键1中断
    135. int ret = request_irq(irqno, key_handler, IRQF_TRIGGER_FALLING, "key1_int", NULL);
    136. if (ret < 0)
    137. {
    138. printk("注册按键1中断失败\n");
    139. return ret;
    140. }
    141. printk("注册按键1中断成功\n");
    142. printk("%s-%s-%d\n", __FILE__, __func__, __LINE__);
    143. return 0;
    144. }
    145. // 封装remove函数
    146. int pdri_remove(struct platform_device *pdev)
    147. {
    148. // 注销中断
    149. free_irq(irqno, NULL);
    150. // 1销毁设备节点信息
    151. device_destroy(cls, MKDEV(major, 0));
    152. // 2销毁目录信息
    153. class_destroy(cls);
    154. // 3字符设备驱动注销
    155. unregister_chrdev(major, "myled");
    156. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    157. return 0;
    158. }
    159. // 构建设备树匹配表
    160. struct of_device_id oftable[] = {
    161. {.compatible = "hqyj,myplatform"},
    162. {}, // 防止数组越界
    163. };
    164. // 定义驱动信息对象并初始化
    165. struct platform_driver pdri = {
    166. .probe = pdri_probe,
    167. .remove = pdri_remove,
    168. .driver = {
    169. .name = "ccc",
    170. .of_match_table = oftable,
    171. },
    172. };
    173. module_platform_driver(pdri); // 一键注册宏
    174. MODULE_LICENSE("GPL");
    test.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. }
    测试现象:

  • 相关阅读:
    java 高级面试题分布式与微服务(借鉴)
    202311.13 windows通过vscode ssh远程连接到Ubuntu 连接失败 waiting for server log
    Spring Cloud OpenFeign - - - > 超时时间配置
    NET中使用Flurl高效处理Http请求
    天梯赛:L1-005 考试座位号
    【ARM Coresight OpenOCD 系列 2 -- OpenOCD 脚本语法详细介绍】
    变分自编码器(VAE)公式推导
    onnx删除无用属性
    【ai】trition:tritonclient yolov4:ubuntu18.04部署python client成功
    ZooKeeper(一)——基础知识
  • 原文地址:https://blog.csdn.net/m0_72852022/article/details/133067848