嵌入式Linux设备驱动程序:发现硬件配置
嵌入式Linux设备驱动程序:发现硬件配置
Embedded Linux device drivers: Discovering the hardware configuration
Interfacing with Device Drivers
了解硬件配置
虚拟驱动程序演示了一个设备驱动程序的结构,但是由于它只操作内存结构,因此它缺乏与实际硬件的交互。设备驱动程序通常是用来与硬件交互的。部分原因是能够在第一时间发现硬件,记住它可能位于不同配置的不同地址。
在某些情况下,硬件本身提供信息。PCI或USB等可发现总线上的设备具有查询模式,该模式返回资源需求和唯一标识符。内核将标识符和可能的其他特征与设备驱动程序匹配,并将它们结合起来。
然而,嵌入式板上的大多数硬件块没有这样的标识符。您必须自己以设备树的形式或称为平台数据的C结构来提供信息。
在Linux的标准驱动程序模型中,设备驱动程序向相应的子系统注册:PCI、USB、开放固件(设备树)、平台设备等等。注册包括一个标识符和一个称为探测函数的回调函数,如果硬件ID和驱动程序的ID匹配,则调用该函数。对于PCI和USB,ID基于供应商和设备的产品ID;对于设备树和平台设备,它是一个名称(文本字符串)。
设备树
我在第三章中介绍了设备树,都是关于引导程序的。在这里,我想向您展示Linux设备驱动程序是如何与这些信息连接起来的。
作为一个例子,我将使用ARM多功能板,arch/ARM/boot/dts/Versatile-ab.dts公司,以太网适配器在此处定义:
net@10010000 { compatible = "smsc,lan91c111"; reg = <0x10010000 0x10000>; interrupts = <25>;};
平台数据
在没有设备树支持的情况下,有一种使用C结构描述硬件的后备方法,称为平台数据。
每个硬件由struct platform_device描述,它有一个名称和一个指向资源数组的指针。资源的类型由标志确定,这些标志包括:
IORESOURCE_MEM:这是内存区域的物理地址
IORESOURCE_IO:这是IO寄存器的物理地址或端口号
IORESOURCE_IRQ:这是中断号
下面是一个以太网控制器的平台数据示例,该数据取自arch/arm/mach versatile/core.c,为清晰起见,对其进行了编辑:
#define VERSATILE_ETH_BASE 0x10010000 #define IRQ_ETH 25 static struct resource smc91x_resources[] = { [0] = { .start = VERSATILE_ETH_BASE, .end = VERSATILE_ETH_BASE + SZ_64K - 1, .flags = IORESOURCE_MEM,},
[1] = { .start = IRQ_ETH, .end = IRQ_ETH, .flags = IORESOURCE_IRQ,},
}; static struct platform_device smc91x_device = { .name = "smc91x", .id = 0, .num_resources = ARRAY_SIZE(smc91x_resources), .resource = smc91x_resources,};
它有一个64KB的内存区和一个中断。平台数据必须在内核中注册,通常在板初始化时:
void __init versatile_init(void) { platform_device_register(&versatile_flash_device); platform_device_register(&versatile_i2c_device); platform_device_register(&smc91x_device); [ ...]
将硬件与设备驱动程序链接
在上一节中,您已经看到了如何使用设备树和平台数据来描述以太网适配器。相应的驱动程序代码在drivers/net/ethernet/smsc/smc91x.c中,它同时处理设备树和平台数据。以下是初始化代码,为清晰起见再次编辑:
static const struct of_device_id smc91x_match[] = { { .compatible = "smsc,lan91c94", }, { .compatible = "smsc,lan91c111", }, {}, }; MODULE_DEVICE_TABLE(of, smc91x_match); static struct platform_driver smc_driver = {.probe = smc_drv_probe,
.remove = smc_drv_remove,
.driver ={ .name = "smc91x", .of_match_table = of_match_ptr(smc91x_match), }, }; static int __init smc_driver_init(void) { return platform_driver_register(&smc_driver); } static void __exit smc_driver_exit(void) { platform_driver_unregister(&smc_driver); } module_init(smc_driver_init); module_exit(smc_driver_exit);
当驱动程序初始化时,它调用platform_driver_register(),指向struct platform_driver,其中有一个对探测函数的回调、一个驱动程序名smc91x和一个指向“设备”id的struct的指针。
如果这个驱动程序是由设备树配置的,内核将在设备树节点中的compatible属性和compatible structure元素所指向的字符串之间寻找匹配。对于每个匹配,它调用probe函数。
另一方面,如果它是通过平台数据配置的,则将为指向的字符串上的每个匹配调用probe函数驱动程序名.
probe函数提取有关接口的信息:
static int smc_drv_probe(struct platform_device *pdev) { struct smc91x_platdata *pd = dev_get_platdata(&pdev->dev); const struct of_device_id *match = NULL; struct resource *res, *ires; int irq;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); [...] addr = ioremap(res->start, SMC_IO_EXTENT); irq = ires->start;[...]
}
对platform_get_resource()的调用从设备树或平台数据中提取内存和irq信息。由驱动程序映射内存并安装中断处理程序。第三个参数在前面的两种情况下都为零,如果有一个以上的特定类型的资源,则会起作用。
设备树允许您配置的不仅仅是基本内存范围和中断。probe函数中有一段代码从设备树中提取可选参数。在此代码段中,它获取register io width属性:
match = of_match_device(of_match_ptr(smc91x_match), &pdev->dev); if (match) { struct device_node *np = pdev->dev.of_node; u32 val; [...] of_property_read_u32(np, "reg-io-width", &val); [...]}
对于大多数驱动程序,在Documentation/deviceree/bindings中记录了特定的绑定。对于这个特定的驱动程序,信息在Documentation/deviceree/bindings/net/smsc911x.txt中。
这里要记住的主要一点是,驱动程序应该注册一个探测函数和足够的信息,以便内核调用探测,因为它发现与它所知道的硬件匹配。设备树描述的硬件和设备驱动程序之间的链接是通过compatible属性实现的。平台数据和驱动程序之间的链接是通过名称实现的。
摘要
设备驱动程序的工作是处理设备,通常是物理硬件,但有时是虚拟接口,并以一致和有用的方式将它们呈现给用户空间。Linux设备驱动程序分为三大类:字符、块和网络。在这三种接口中,字符驱动接口是最灵活的,因此也是最常见的。Linux驱动程序适合于一个称为驱动程序模型的框架,该模型通过sysfs公开。在/sys中几乎可以看到设备和驱动程序的整个状态。
每个嵌入式系统都有自己独特的硬件接口和需求集。Linux为大多数标准接口提供了驱动程序,通过选择正确的内核配置,您可以很快得到一个工作的目标板。这就给您留下了非标准组件,您必须添加自己的设备支持。
在某些情况下,您可以通过使用GPIO、I2C等的通用驱动程序来回避这个问题,并编写用户空间代码来完成这项工作。我建议将此作为一个起点,因为它让您有机会在不编写内核代码的情况下熟悉硬件。编写内核驱动程序并不是特别困难,但是如果你真的这么做了,你需要小心编码,以免损害系统的稳定性。
我已经讨论过如何编写内核驱动程序代码:如果你沿着这条路走下去,你将不可避免地想知道如何检查它是否正常工作并检测出任何错误。
嵌入式Linux设备驱动程序:发现硬件配置的更多相关文章
- 嵌入式Linux设备驱动程序:在运行时读取驱动程序状态
嵌入式Linux设备驱动程序:在运行时读取驱动程序状态 Embedded Linux device drivers: Reading driver state at runtime 在运行时了解驱动程 ...
- 嵌入式Linux设备驱动程序:用户空间中的设备驱动程序
嵌入式Linux设备驱动程序:用户空间中的设备驱动程序 Embedded Linux device drivers: Device drivers in user space Interfacing ...
- 嵌入式Linux设备驱动程序:编写内核设备驱动程序
嵌入式Linux设备驱动程序:编写内核设备驱动程序 Embedded Linux device drivers: Writing a kernel device driver 编写内核设备驱动程序 最 ...
- linux设备驱动程序--hello-world
linux字符设备驱动程序--hello_world 基于4.14内核, beagleBone green平台 PC端的设备驱动程序 有过电脑使用经验的人都知道,当我们将外部硬件设备比如鼠标键盘插入到 ...
- linux设备驱动程序该添加哪些头文件以及驱动常用头文件介绍(转)
原文链接:http://blog.chinaunix.net/uid-22609852-id-3506475.html 驱动常用头文件介绍 #include <linux/***.h> 是 ...
- 【转】linux设备驱动程序中的阻塞机制
原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275272.html 阻塞与非阻塞是设备访问的两种方式.在写阻塞与非阻塞的驱动程序时,经 ...
- Linux设备驱动程序学习之分配内存
内核为设备驱动提供了一个统一的内存管理接口,所以模块无需涉及分段和分页等问题. 我已经在第一个scull模块中使用了 kmalloc 和 kfree 来分配和释放内存空间. kmalloc 函数内幕 ...
- linux设备驱动程序-设备树(1)-dtb转换成device_node
linux设备驱动程序-设备树(1)-dtb转换成device_node 本设备树解析基于arm平台 从start_kernel开始 linux最底层的初始化部分在HEAD.s中,这是汇编代码,我们暂 ...
- linux设备驱动程序-设备树(3)-设备树多级子节点的转换
linux设备驱动程序--设备树多级子节点的转换 在上一章:设备树处理之--device_node转换成platform_device中,有提到在设备树的device_node到platform_de ...
随机推荐
- hdu2962 二分 + spfa
题意: 给你一个无向图,每条路径上都有自己的长度和最大承受高度,给你起点终点还有车的最大承装高度,问你高度最大的前提下路径最短是多少,求高度和路径. 思路: 这种类型题目太多了, ...
- hdu4882 水贪心
题意: 给你n个任务,每个任务有两个权值,t[i],b[i],前面的是完成任务所需时间,后面的那个是个参数,每个任务完成的代价是完成当前任务总时间(之前的+现在的) sumt * b[i], ...
- hdu5108枚举因子求最小的m
题意: 给一个n(<=10Y),然后让找到一个最小的m使得n/m是一个素数. 思路: 先用sqrt(n)的时间把所有的因子都求出来,然后在排序,枚举,就行了,这个题目这么做 ...
- Django(2)python虚拟环境virtualenvwrapper
python虚拟环境 虚拟环境(virtual environment),它是一个虚拟化,从电脑独立开辟出来的环境.通俗的来讲,虚拟环境就是借助虚拟机来把一部分内容独立出来,我们把这部分独立出来的东西 ...
- InnoDB解决幻读的方案——LBCC&MVCC
最近要在公司内做一次技术分享,思来想去不知道该分享些什么,最后在朋友的提示下,准备分享一下MySQL的InnoDB引擎下的事务幻读问题与解决方案--LBCC&MVCC.经过好几天的熬夜通宵,终 ...
- 【Azure Redis 缓存】Azure Cache for Redis服务中,除开放端口6379,6380外,对13000,13001,15000,15001 为什么也是开放的呢?
问题描述 在使用安全检测工具对Azure Redis服务端口进行扫描时,发现Redis对外开放了13001, 13000,15000,15001端口.非常不理解的是,在门户上只开放了6379,6380 ...
- 【技术博客】忘记密码界面的Vue前端实现
一.基本流程 [登录界面] --> [点击忘记密码] --> [输入个人邮箱和验证码] --> [系统发送邮箱验证] --> [用户在限定时间内登录邮箱,查收验证码] --&g ...
- 从零搭建springboot服务03-redis消息订阅
愿历尽千帆,归来仍是少年 1.所需依赖 <!-- Redis依赖 --> <dependency> <groupId>org.springframework.boo ...
- [刷题] 200 Number of Islands
要求 给定一个二维数组,只有0和1两个字符,1代表陆地,0代表水域,纵向和横向的陆地连接成岛屿,被水域隔开,求出地图中有多少岛屿 思路 对要查找的区域进行双重循环遍历,寻找陆地 从陆地初始点开始进行深 ...
- Win10 禁用摄像头的方法及注意事项
Win10 禁用摄像头的方法及注意事项 windows教程 2020-03-04 223 最新的Windows10系统中应该如何禁用摄像头呢?下面MS酋长与大家分享一下.当然,如果你说用个便利贴把摄 ...