HDF驱动框架探路:Linux总线机制imx6ull驱动sr501红外传感器
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>前言
</li><li>
涉及到的概念介绍
<ul class="second_class_ul"><li>
1.总线的概念
</li><li>
驱动程序
</li><li>
2.修改设备树
</li><li>
3.应用侧测试程序
</li><li>
4.编译
</li></ul></li></ul></div><p>
<img title="HDF驱动框架探路:Linux总线机制imx6ull驱动sr501红外传感器" alt="HDF驱动框架探路:Linux总线机制imx6ull驱动sr501红外传感器" border="0" src="https://zhuji.jb51.net/uploads/img/202305/cd52575526132417b4f856d9faf3db9e.jpg"></p>
<center>
<img title="HDF驱动框架探路:Linux总线机制imx6ull驱动sr501红外传感器" alt="HDF驱动框架探路:Linux总线机制imx6ull驱动sr501红外传感器" src="https://zhuji.jb51.net/uploads/img/202305/7eae87c4254fadc06960b7c87c33cc58.jpg"><br>
</center>
<p>
HDF驱动框架探路6:
</p>
<p class="maodian"></p><h2>
前言
</h2>
<p>
上一篇文章中最后在操作led灯的硬件时候,我们是直接读的原理图,去操作的寄存器,这种情况是我们绝大多数情况下会这样子进行操作,而本章我们的核心重点是使用总线机制,也就是通过修改设备树的方法来操作硬件。
</p>
<p>
本章框架图
</p>
<center>
<img title="HDF驱动框架探路:Linux总线机制imx6ull驱动sr501红外传感器" alt="HDF驱动框架探路:Linux总线机制imx6ull驱动sr501红外传感器" src="https://zhuji.jb51.net/uploads/img/202305/ce555b549b3c8bd6e085059b10c27afd.jpg"><br>
</center>
<p>
总线框架图
</p>
<center>
<img title="HDF驱动框架探路:Linux总线机制imx6ull驱动sr501红外传感器" alt="HDF驱动框架探路:Linux总线机制imx6ull驱动sr501红外传感器" src="https://zhuji.jb51.net/uploads/img/202305/95add6c95831b8b47ea0b532e94c8284.jpg"><br>
</center>
<p class="maodian"></p><h2>
涉及到的概念介绍
</h2>
<p class="maodian"></p><h3>
1.总线的概念
</h3>
<p>
总线是处理器与一个或者多个设备之间的通道。在设备模型中,所有的设备都通过总线相连。甚至是那些内部的虚拟“平台”总线。总线可以相互插入,比如一个USB控制器通常是一个PCI设备。设备模型展示了总线和它们所控制的设备之间的连接。
</p>
<p class="maodian"></p><h3>
驱动程序
</h3>
<p>
1.1 总线的使用逻辑首先调用
</p>
<p>
为了正确地注册到内核,所有的platform驱动程序都必须创建的主要结构体是struct platform_driver结构体。该结构体由许多回调函数和变量组成,向platform描述了platform驱动程序。所以我们需要写一个struct platform_driver结构体。然后在其中有两个比较重要的回调函数:probe和remove
</p>
<ul>
<li>
int (*probe)(…)指向platform驱动程序中的探测函数的指针。当platform核心有一个它认为驱动程序需要控制的struct platform_device时,就会调用该函数。所以我们会在这个函数中写驱动程序的逻辑。
</li>
<li>
int (*remove)(…)指向一个移除函数的指针,当struct platform_device被从系统中移除,或者platform驱动程序正在从内核中卸载时,platform核心调用该函数。
</li>
</ul>
<p>
为了把struct platform_driver注册到platform核心中,需要调用以struct platform_driver为参数的platform_driver_register函数。通常在platform驱动程序的模块化代码中完成该工程。
</p>
<p>
当platform驱动程序将要被卸载的时候,需要把struct platform_driver从内核注销。这是通过调用platform_driver_unregister完成的。
</p>
<p>
1.2 完整实现代码
</p>
<ol class="dp-sql">
<li class="alt">
<span><span>#include <linux module.h></linux></span></span>
</li>
<li>
<span>#include <linux poll.h></linux></span>
</li>
<li class="alt">
<span>#include <linux fs.h></linux></span>
</li>
<li>
<span>#include <linux errno.h></linux></span>
</li>
<li class="alt">
<span>#include <linux miscdevice.h></linux></span>
</li>
<li>
<span>#include <linux kernel.h></linux></span>
</li>
<li class="alt">
<span>#include <linux major.h></linux></span>
</li>
<li>
<span>#include <linux mutex.h></linux></span>
</li>
<li class="alt">
<span>#include <linux proc_fs.h></linux></span>
</li>
<li>
<span>#include <linux seq_file.h></linux></span>
</li>
<li class="alt">
<span>#include <linux stat.h></linux></span>
</li>
<li>
<span>#include <linux init.h></linux></span>
</li>
<li class="alt">
<span>#include <linux device.h></linux></span>
</li>
<li>
<span>#include <linux tty.h></linux></span>
</li>
<li class="alt">
<span>#include <linux kmod.h></linux></span>
</li>
<li>
<span>#include <linux gfp.h></linux></span>
</li>
<li class="alt">
<span>#include <linux consumer.h gpio></linux></span>
</li>
<li>
<span>#include <linux platform_device.h></linux></span>
</li>
<li class="alt">
<span>#include <linux of_gpio.h></linux></span>
</li>
<li>
<span>#include <linux of_irq.h></linux></span>
</li>
<li class="alt">
<span>#include <linux interrupt.h></linux></span>
</li>
<li>
<span>#include <linux irq.h></linux></span>
</li>
<li class="alt">
<span>#include <linux slab.h></linux></span>
</li>
<li>
<span>#include <linux fcntl.h></linux></span>
</li>
<li class="alt">
<span>#include <linux timer.h></linux></span>
</li>
<li>
<span>#include <linux workqueue.h></linux></span>
</li>
<li class="alt">
<span>#include current</asm></span><span>.h> </span>
</li>
<li>
<span><span class="keyword">static</span><span> </span><span class="keyword">int</span><span> major; </span></span>
</li>
<li class="alt">
<span><span class="keyword">static</span><span> struct class *sr501_class; </span></span>
</li>
<li>
<span><span class="keyword">static</span><span> struct gpio_desc *sr501_gpio; </span></span>
</li>
<li class="alt">
<span><span class="keyword">static</span><span> </span><span class="keyword">int</span><span> irq; </span></span>
</li>
<li>
<span><span class="keyword">static</span><span> </span><span class="keyword">int</span><span> sr501_data = 0; </span></span>
</li>
<li class="alt">
<span><span class="keyword">static</span><span> wait_queue_head_t sr501_wq; </span></span>
</li>
<li>
<span>/* 实现对应的<span class="keyword">open</span><span>/</span><span class="keyword">read</span><span>/write等函数,填入file_operations结构体 */ </span></span>
</li>
<li class="alt">
<span><span class="keyword">static</span><span> ssize_t sr501_drv_read (struct file *file, </span><span class="keyword">char</span><span> __user *buf, size_t </span><span class="keyword">size</span><span>, loff_t *offset) </span></span>
</li>
<li>
<span>{ </span>
</li>
<li class="alt">
<span>#if 0 </span>
</li>
<li>
<span><span class="keyword">int</span><span> val; </span></span>
</li>
<li class="alt">
<span><span class="keyword">int</span><span> len = (</span><span class="keyword">size</span><span> < 4)? </span><span class="keyword">size</span><span> : 4; </span></span>
</li>
<li>
<span></span>
</li>
<li class="alt">
<span>val = gpiod_get_value(sr501_gpio); </span>
</li>
<li>
<span>copy_to_user(buf, &val, len); </span>
</li>
<li class="alt">
<span><span class="keyword">return</span><span> len; </span></span>
</li>
<li>
<span>#<span class="keyword">else</span><span> </span></span>
</li>
<li class="alt">
<span><span class="keyword">int</span><span> val; </span></span>
</li>
<li>
<span><span class="keyword">int</span><span> len = (</span><span class="keyword">size</span><span> < 4)? </span><span class="keyword">size</span><span> : 4; </span></span>
</li>
<li class="alt">
<span>/* 1. 有数据就copy_to_uesr */ </span>
</li>
<li>
<span>/* 2. 无数据就休眠: 放入某个链表 */ </span>
</li>
<li class="alt">
<span>wait_event_interruptible(sr501_wq, sr501_data); </span>
</li>
<li>
<span>copy_to_user(buf, &sr501_data, len); </span>
</li>
<li class="alt">
<span>sr501_data = 0; </span>
</li>
<li>
<span><span class="keyword">return</span><span> len; </span></span>
</li>
<li class="alt">
<span>#endif </span>
</li>
<li>
<span>} </span>
</li>
<li class="alt">
<span></span>
</li>
<li>
<span><span class="keyword">static</span><span> unsigned </span><span class="keyword">int</span><span> sr501_drv_poll(struct file *fp, poll_table * wait) </span></span>
</li>
<li class="alt">
<span>{ </span>
</li>
<li>
<span><span class="keyword">return</span><span> 0; </span></span>
</li>
<li class="alt">
<span>} </span>
</li>
<li>
<span>/* 定义自己的file_operations结构体 */ </span>
</li>
<li class="alt">
<span><span class="keyword">static</span><span> struct file_operations sr501_fops = { </span></span>
</li>
<li>
<span>.owner = THIS_MODULE, </span>
</li>
<li class="alt">
<span>.<span class="keyword">read</span><span> = sr501_drv_read, </span></span>
</li>
<li>
<span>.poll = sr501_drv_poll, </span>
</li>
<li class="alt">
<span>}; </span>
</li>
<li>
<span><span class="keyword">static</span><span> irqreturn_t sr501_isr(</span><span class="keyword">int</span><span> irq, void *dev_id) </span></span>
</li>
<li class="alt">
<span>{ </span>
</li>
<li>
<span>printk(<span class="string">"%s %s line %d\n"</span><span>, __FILE__, __FUNCTION__, __LINE__); </span></span>
</li>
<li class="alt">
<span>/* 1. 记录数据 */ </span>
</li>
<li>
<span>sr501_data = 1; </span>
</li>
<li class="alt">
<span>/* 2. 唤醒APP:去同一个链表把APP唤醒 */ </span>
</li>
<li>
<span>wake_up(&sr501_wq); </span>
</li>
<li class="alt">
<span><span class="keyword">return</span><span> IRQ_HANDLED; </span></span>
</li>
<li>
<span>} </span>
</li>
<li class="alt">
<span>/* 1. 从platform_device获得GPIO </span>
</li>
<li>
<span>* 2. gpio=>irq </span>
</li>
<li class="alt">
<span>* 3. request_irq </span>
</li>
<li>
<span>*/ </span>
</li>
<li class="alt">
<span><span class="keyword">static</span><span> </span><span class="keyword">int</span><span> sr501_probe(struct platform_device *pdev) </span></span>
</li>
<li>
<span>{ </span>
</li>
<li class="alt">
<span>/* 1. 获得硬件信息 */ </span>
</li>
<li>
<span>sr501_gpio = gpiod_get(&pdev->dev, <span class="op">NULL</span><span>, 0); </span></span>
</li>
<li class="alt">
<span>gpiod_direction_input(sr501_gpio); </span>
</li>
<li>
<span>irq = gpiod_to_irq(sr501_gpio); </span>
</li>
<li class="alt">
<span>request_irq(irq, sr501_isr, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, <span class="string">"sr501"</span><span>, </span><span class="op">NULL</span><span>); </span></span>
</li>
<li>
<span>/* 注册file_operations */ </span>
</li>
<li class="alt">
<span>major = register_chrdev(0, <span class="string">"sr501"</span><span>, &sr501_fops); </span></span>
</li>
<li>
<span></span>
</li>
<li class="alt">
<span>sr501_class = class_create(THIS_MODULE, <span class="string">"sr501_class"</span><span>); </span></span>
</li>
<li>
<span>if (IS_ERR(sr501_class)) { </span>
</li>
<li class="alt">
<span>printk(<span class="string">"%s %s line %d\n"</span><span>, __FILE__, __FUNCTION__, __LINE__); </span></span>
</li>
<li>
<span>unregister_chrdev(major, <span class="string">"sr501"</span><span>); </span></span>
</li>
<li class="alt">
<span><span class="keyword">return</span><span> PTR_ERR(sr501_class); </span></span>
</li>
<li>
<span>} </span>
</li>
<li class="alt">
<span>/* 2. device_create */ </span>
</li>
<li>
<span>device_create(sr501_class, <span class="op">NULL</span><span>, MKDEV(major, 0), </span><span class="op">NULL</span><span>, </span><span class="string">"sr501"</span><span>); </span></span>
</li>
<li class="alt">
<span></span>
</li>
<li>
<span><span class="keyword">return</span><span> 0; </span></span>
</li>
<li class="alt">
<span>} </span>
</li>
<li>
<span><span class="keyword">static</span><span> </span><span class="keyword">int</span><span> sr501_remove(struct platform_device *pdev) </span></span>
</li>
<li class="alt">
<span>{ </span>
</li>
<li>
<span>device_destroy(sr501_class, MKDEV(major, 0)); </span>
</li>
<li class="alt">
<span>class_destroy(sr501_class); </span>
</li>
<li>
<span>unregister_chrdev(major, <span class="string">"sr501"</span><span>); </span></span>
</li>
<li class="alt">
<span>} </span>
</li>
<li>
<span><span class="keyword">static</span><span> const struct of_device_id qzk_sr501[] = { </span></span>
</li>
<li class="alt">
<span>{ .compatible = <span class="string">"qzk,sr501"</span><span> }, </span></span>
</li>
<li>
<span>{ }, </span>
</li>
<li class="alt">
<span>}; </span>
</li>
<li>
<span>/* 1. 定义platform_driver */ </span>
</li>
<li class="alt">
<span><span class="keyword">static</span><span> struct platform_driver sr501s_driver = { </span></span>
</li>
<li>
<span>.probe = sr501_probe, </span>
</li>
<li class="alt">
<span>.remove = sr501_remove, </span>
</li>
<li>
<span>.driver = { </span>
</li>
<li class="alt">
<span>.<span class="keyword">name</span><span> = </span><span class="string">"qzk_sr501"</span><span>, </span></span>
</li>
<li>
<span>.of_match_table = qzk_sr501, </span>
</li>
<li class="alt">
<span>}, </span>
</li>
<li>
<span>}; </span>
</li>
<li class="alt">
<span>/* 2. 在入口函数注册platform_driver */ </span>
</li>
<li>
<span><span class="keyword">static</span><span> </span><span class="keyword">int</span><span> __init sr501_init(void) </span></span>
</li>
<li class="alt">
<span>{ </span>
</li>
<li>
<span><span class="keyword">int</span><span> err; </span></span>
</li>
<li class="alt">
<span>printk(<span class="string">"%s %s line %d\n"</span><span>, __FILE__, __FUNCTION__, __LINE__); </span></span>
</li>
<li>
<span>init_waitqueue_head(&sr501_wq); </span>
</li>
<li class="alt">
<span>err = platform_driver_register(&sr501s_driver); </span>
</li>
<li>
<span><span class="keyword">return</span><span> err; </span></span>
</li>
<li class="alt">
<span>} </span>
</li>
<li>
<span>/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 </span>
</li>
<li class="alt">
<span>* 卸载platform_driver </span>
</li>
<li>
<span>*/ </span>
</li>
<li class="alt">
<span><span class="keyword">static</span><span> void __exit sr501_exit(void) </span></span>
</li>
<li>
<span>{ </span>
</li>
<li class="alt">
<span>printk(<span class="string">"%s %s line %d\n"</span><span>, __FILE__, __FUNCTION__, __LINE__); </span></span>
</li>
<li>
<span></span>
</li>
<li class="alt">
<span>platform_driver_unregister(&sr501s_driver); </span>
</li>
<li>
<span>} </span>
</li>
<li class="alt">
<span>/* 7. 其他完善:提供设备信息,自动创建设备节点 */ </span>
</li>
<li>
<span></span>
</li>
<li class="alt">
<span>module_init(sr501_init); </span>
</li>
<li>
<span>module_exit(sr501_exit); </span>
</li>
<li class="alt">
<span>MODULE_LICENSE(<span class="string">"GPL"</span><span>); </span></span>
</li>
</ol>
<p class="maodian"></p><h3>
2.修改设备树
</h3>
<p>
2.1 修改思路
</p>
<p>
在上述驱动中of_device_id结构体中的.compatible = “qzk,sr501”,所以我们需要将设备树中添加一个这样的设备节点
</p>
<p>
2.2 修改代码如下:
</p>
<p>
Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14x14.dts
</p>
<center>
<img title="HDF驱动框架探路:Linux总线机制imx6ull驱动sr501红外传感器" alt="HDF驱动框架探路:Linux总线机制imx6ull驱动sr501红外传感器" src="https://zhuji.jb51.net/uploads/img/202305/c4d1146f98d2f1dfd058206007901c90.jpg"><br>
</center>
<p class="maodian"></p><h3>
3.应用侧测试程序
</h3>
<p>
3.1 测试程序逻辑
</p>
<p>
测试程序只需要不断的去read,等到中断收到返回时,就会获得返回。
</p>
<p>
3.2 测试程序完成实现代码
</p>
<ol class="dp-sql">
<li class="alt">
<span><span>#include <sys types.h></sys></span></span>
</li>
<li>
<span>#include <sys stat.h></sys></span>
</li>
<li class="alt">
<span>#include <fcntl.h></fcntl.h></span>
</li>
<li>
<span>#include <unistd.h></unistd.h></span>
</li>
<li class="alt">
<span>#include <stdio.h></stdio.h></span>
</li>
<li>
<span>#include <string.h></string.h></span>
</li>
<li class="alt">
<span></span>
</li>
<li>
<span><span class="keyword">int</span><span> main(</span><span class="keyword">int</span><span> argc, </span><span class="keyword">char</span><span> **argv) </span></span>
</li>
<li class="alt">
<span>{ </span>
</li>
<li>
<span><span class="keyword">int</span><span> fd; </span></span>
</li>
<li class="alt">
<span><span class="keyword">int</span><span> val; </span></span>
</li>
<li>
<span>fd = <span class="keyword">open</span><span>(</span><span class="string">"/dev/sr501"</span><span>, O_RDWR); </span></span>
</li>
<li class="alt">
<span>if (fd == -1) </span>
</li>
<li>
<span>{ </span>
</li>
<li class="alt">
<span>printf(<span class="string">"can not open file %s\n"</span><span>, argv); </span></span>
</li>
<li>
<span><span class="keyword">return</span><span> -1; </span></span>
</li>
<li class="alt">
<span>} </span>
</li>
<li>
<span></span>
</li>
<li class="alt">
<span>while (1) </span>
</li>
<li>
<span>{ </span>
</li>
<li class="alt">
<span><span class="keyword">read</span><span>(fd, &val, 4); </span></span>
</li>
<li>
<span>if (val == 0x1) { </span>
</li>
<li class="alt">
<span>printf(<span class="string">"detect people\n"</span><span>); </span></span>
</li>
<li>
<span>} </span>
</li>
<li class="alt">
<span>} </span>
</li>
<li>
<span></span>
</li>
<li class="alt">
<span><span class="keyword">close</span><span>(fd); </span></span>
</li>
<li>
<span></span>
</li>
<li class="alt">
<span><span class="keyword">return</span><span> 0; </span></span>
</li>
<li>
<span>} </span>
</li>
</ol>
<p class="maodian"></p><h3>
4.编译
</h3>
<p>
4.1 配置交叉编译工具链
</p>
<p>
回到源码根目录执行source env.sh配置编译工具链
</p>
<p>
4.2 重新生成内核设备树
</p>
<p>
进入源码目录下的Linux-4.9.88文件夹后执行make dtbs重新生成设备树
</p>
<p>
4.3 将重新生成的设备树放入开发板的boot目录
</p>
<p>
cp 100ask_imx6ull-14x14.dtb /boot
</p>
<p>
4.4 make编译驱动程序
</p>
<ol class="dp-sql">
<li class="alt">
<span><span>KERN_DIR = ~/imx6ullpro/Linux-4.9.88 # 板子所用内核源码的目录 </span></span>
</li>
<li>
<span><span class="op">all</span><span>: </span></span>
</li>
<li class="alt">
<span>make -C $(KERN_DIR) M=`pwd` modules </span>
</li>
<li>
<span>$(CROSS_COMPILE)gcc -o sr501_test sr501_test.c </span>
</li>
<li class="alt">
<span>clean: </span>
</li>
<li>
<span>make -C $(KERN_DIR) M=`pwd` modules clean </span>
</li>
<li class="alt">
<span>rm -rf modules.<span class="keyword">order</span><span> </span></span>
</li>
<li>
<span>obj-m += sr501_drv.o </span>
</li>
</ol>
<p>
直接执行make脚本执行上述Makefile生成测试程序和驱动ko文件
</p>
<p>
4.5 加载驱动程序
</p>
<ol class="dp-sql">
<li class="alt">
<span><span>insmod sr501_drv.ko </span></span>
</li>
</ol>
<p>
4.6 执行测试程序
</p>
<ol class="dp-sql">
<li class="alt">
<span><span>./sr501_test </span></span>
</li>
</ol>
<p>
原文链接: https://harmonyos.51cto.com
</p>
頁:
[1]