嵌入式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 ...
随机推荐
- 技术面试问题汇总第002篇:猎豹移动反病毒工程师part2
这次拿三个问题来讨论,是关于调试器的.因为对于反病毒工程师而言,类似于OllyDbg和IDA的使用方法是必须掌握的,但是在面试中又不太方便考察,所以只能对其快捷键或者调试器实现原理之类的问题进行提问. ...
- 从苏宁电器到卡巴斯基第14篇:我在苏宁电器当营业员 VI
我也过了一把讲师的瘾 由于iPhone已经成为了我们的主推产品,因此苏宁要求手机专区的每一个人,不论是自营还是厂促,都要对iPhone非常了解才可以.于是,督导也没有事先通知我,就直接让我给手机专区的 ...
- 在kubernetes上运行WASM负载
在kubernetes上运行WASM负载 WASM一般用在前端业务中,但目前有扩展到后端服务的趋势.本文使用Krustlet 将WASM服务部署到kubernetes. 简介 Krustlet 是一个 ...
- Access+Mssql+Oracle数据库注入
Access数据库注入 前言:在常见的注入测试中,由于每个数据库内置架构和应用不同,导致数据库类型将决定注入的手法,Access数据库是小型数据库类型,普遍用于小型WEB应用,其中Access注入手法 ...
- 深入浅出带你玩转sqlilabs(三)--GET,POST,COOKIE,万能密码注入
常见提交方式下的注入漏洞 WEB应用在数据传递接受中,针对SQL注入安全漏洞,由于数据大小,格式等原因,脚本在接受传递时会有多种传递方式,传递方式的不同将影响到安全测试的不同 第一点:数据常见提交方式 ...
- 【python】Leetcode每日一题-删除排序链表中的重复元素2
[python]Leetcode每日一题-删除排序链表中的重复元素2 [题目描述] 存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表 ...
- Action: Consider the following: If you want an embedded database (H2, HSQL or Derby), please put it on the classpath. If you have database settings to be loaded from a particular profile you may ne
更多精彩关注微信公众号 错误原因 在pom中引入了mybatis-spring-boot-starter ,Spring boot默认会加载org.springframework.boot.autoc ...
- 使用MindSpore的线性神经网络拟合非线性函数
技术背景 在前面的几篇博客中,我们分别介绍了MindSpore的CPU版本在Docker下的安装与配置方案.MindSpore的线性函数拟合以及MindSpore后来新推出的GPU版本的Docker编 ...
- 【Azure Developer】使用Microsoft Graph API 如何批量创建用户,用户属性中需要包含自定义字段(如:Store_code,Store_name等)
Microsoft Graph 是 Microsoft 365 中通往数据和智能的网关. 它提供统一的可编程模型,可用于访问 Microsoft 365.Windows 10 和企业移动性 + 安全性 ...
- "mysql第一次查询很慢,以后就很快"的解决方案
背景 有个项目使用的mysql数据库,第一次查询很慢,大约15s左右出结果,再次查询就很快了. 分析 后面变快的原因是mysql有缓存机制,但是过上一段时间不使用缓存会过期,我个人测了一下2~3分钟一 ...