热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linuxfirmware加载(2012040115:46)

标签:目录嵌入式firmwareexportLinux分类:Linux内核1、request_firmware在内核使用,需要文件系统支持,



标签:  目录  嵌入式  firmware  export  Linux  分类: Linux内核





1、request_firmware在内核使用,需要文件系统支持,就是说,启动的时候如果在驱动里面的probe函数调用 request_firmware ,那么系统将等待30s左右,因为文件系统还没有挂载,当然找不到固件了,所以最好在中断里面启动tasklet,然后request_firmware 。如果不想等待,就用request_firmware_nowait,好像是这样写的。

2、那么用户层怎么用?

实际上这个分x86和嵌入式,比如arm,平台。x86的用到了udev,

比如你要请求固件 fw.hex,那么必须在文件系统中导出环境变量,比如 export FIRMWARE=/lib/firmware,而且目录/lib/firmware不能少,因为busybox要用到。然后把固件fw.hex放到 /lib/firmware目录下即可。内核request_firmware的时候,busybox就知道去FIRMWARE找了。

 

3、对linux来讲,所谓的固件什么也不是,他只是按fopen()返回二进制文件给你,看看busybox的处理就知道了。所以你的文件随便定 义,比如一个mp3文件,你也可以称为固件:request_firmware(“xxx.mp3″),那么你文件系统里面也要有这个xxx.mp3文 件,只不过系统给你返回二进制数据,具体的处理要在内核进行。

request_firmware 返回二进制firmware文件的地址和大小

//firmware->data和firmware->size来读取有uevent处理程序init加载进来的firmware数据了

request_firmware( & priv- > firmware, fw_name, priv- > hotplug_device) ; 给priv- > hotplug_device设备申请名字为fw_name的firmware  数据, 然后将结果放到& priv- > firmware中,
struct firmware {
size_t size;
u8 * data;
} ;
可以看到, 如果应用层的程序成功load了firmware固件文件, 那么firmware. data将指向固件数据, firmware. size为固件大小.

前段时间移植 wifi 驱动到 android 的内核上,发现 firmware 的加载始终出错,问了几个人,都不是很了解,没办法,只好自己研究一下。

原理分析

从本质上来说, firmware 需要做的事情包括两件:

1,  通知用户态程序,我需要下载 firmware 了;

2,  用户态程序把用户态的数据 copy 到内核层;

3,  内核把内核态的数据写到设备上,比如 wifi 模块里;

其中第三步应该不难,关键是看看, linux 里面是如何实现第一、二步的;

实现机制

简单的说,它的机制分成以下几部分:

1,  通过一定的方式,通知用户态程序,比如 init 程序,如图所示:

显然是通过 kobject_uevent 的方式通知的 应用层,它的机制我有空再详细解释,简单的说,就是往一个 socket 广播一个消息,只需要在应用层打开 socket 监听 NETLINK_KOBJECT_UEVENT 组的消息,就可以收到了。

用户态的 init 是如何做的?

可以看到 init 程序打开了一个 socket ,然后绑定它, 最后通过 select 来监听 socket 上来的数据,最后调用 handle_device_fd 来处理收到的消息;当内核发送一个 KOBJ_ADD 的消息上来的时候,经过过 滤,判断是否是 firmware 要被加载的消息,然后调用

handle_firmware_event 来处理;

2,  用户态的数据如何下载到内核;

本质上它是内核创建了两个文件,一个文件 A 用来标志下载的开始和结 束,另外一个文件 B 用来接收用户层传下来的数据,当用户态的程序往 A 文件写入 1 的时候,标志用户态程序已经往里面写程序来,而往里面写入 0 的时候,就标志下载成功结束,如果写入 -1 就表示下载失败了;下面 看看这两个文件是如何被创建的 , 以及数据是如何写到内核的,请看图:

这个图基本上就是两个文件被创立的过程,以及当这两个文 件被用户态程序访问的时候将要被调用的函数,比如对于标志文件,如果往里面写入数据,将会触发函数 firmware_loading_store 函数,如果往 bin 文件里面写入数据将会触发 bin 文件类型的 write 函数;

用户态写数据的过程大约是这样的:当用户态收到 KOBJ_ADD 消息的时候 最终将会调用 handle_firmware_event 的函数;

它的过程就是:

a, 先往标志文件里面写 1 ;

b, 从用户空间读取数据;

c, 往内核创建的文件里面写数据;

d, 如果成功写入 0 ,否则写入 -1 ;

 

下面看看内核是如何接受这些文件的,前面提到内核创建了一个 bin 文件,用来接收用户态的数据,下面看 看这个过程:

对于 SYSFS_KOBJ_BIN_ATTR 属 性的文件,在 inode 初始化的时候,将会被赋予 bin_fops 的文件操作函数集,于是当上层调用 write 的时候,将会走到内核的 bin_fops.write 函数;这个函数干的事情很简单,就是把用户态的数据 copyright 到 bb->buffer ,而 bb->buffer 其 实是在 open 的 时候分配的空间,这样的话,就实现了用户态的数据到内核的 copy ;过程是不是完了?

还有一个步骤,这个 bb->buffer 本身是如何与 wifi 驱动交互的呢?这只是一个中间层,它的数据必须要写到 wifi 的驱动才应该算完整,而这一步其实 就是通过 flush_write 来完成的,下面看看这个过程:

这里可以清楚的看到, flush_write 做的事情就是把 bb->buffer 的内容 copy 到 wifi driver 分配的空间 fw->data 里面去了,至此,用户态的数据已经完整的写到了 wifi 的 driver 空间了;

 

3,  内核态的数据到 wifi 模块

这个就比较简单了,通过函数 sdio_writesb 利用 sdio 总线把数据写到模块 里面去了;

 

 

总结

Firmware 的加载主要是利用了 uevent 的通讯机制实现用户态和内核 态的交互,另外还涉及了 sys 文件系统里的文件创建 , 我加载 wifi firmware 始终出错的原因是 android 的文件系统要求把 wifi 的 firmware helper 放到 /etc/firmware 里面,而把真正 的 firmware sd8686.bin 放到 /etc/firmware/mrvl 里面,估计是 marvel 修改后的结果,结论就是,这个设计真丑;

获取固件的正确方法是当需要时从用户空间获取它。一定不要试图从内核空间直接打开包含固件的文件,那是一个易出错的操作, 因为它把策略(以文件名的形式)包含进了内核。正确的方法是使用固件接口:

#include
int request_firmware(const struct firmware **fw,                      const char *name, /* name 为固件文件名*/

                     struct device *device);
/*要求用户空间定位并提供一个固件映象给内核;若成功加载, 返回值是 0(否则返回错误码)*/

/*因为 request_firmware 需要用户空间的操作, 所以返回前将保持休眠。若驱动必须使用固件而不能进入休眠时,可使用以下异步函数:*/
int request_firmware_nowait(
struct module *module, /* = THIS_MODULE*/
int uevent,
const char *name,
struct device *device,
void *context,/*不由固件子系统使用的私有数据指针*/
void (*cont)(const struct firmware *fw, void *context));
/*如果一切正常,request_firmware_nowait 开始固件加载过程并返回 0. 过了一段时间后(默认10秒),将用加载的结果(若加载失败, fw 为 NULL)作为参数调用 cont。*/

/* fw 参数指向以下结构体:*/
struct firmware {
size_t size;
u8 *data;
};
/*那个结构包含实际的固件, 它现在可被下载到设备中.但是请注意:在发送它到硬件之前,必须检查这个文件以确保它是正确的固件映象(设备固件常常包含标识字符串、 校验和等等)*/

/*当固件已经发送到设备后,应当释放 firmware 结构体, 使用:*/
void release_firmware(struct firmware *fw);

注意:要使用firmware,必须要在配置内核时选上:

   Device Drivers  --->

          Generic Driver Options  --->

              <*> Userspace firmware loading support

否则会出现: Unknown symbol release_firmware 和: Unknown symbol request_firmware 的错误。

当调用 request_firmware时, 函数将在 /sys/class/firmware 下创建一个以设备名为目录名的新目录&#xff0c;其中包含 3 个属性:

loading &#xff1a;这个属性应当被加载固件的用户空间进程设置为 1。当加载完毕, 它将被设为 0。被设为 -1 时&#xff0c;将中止固件加载。
data &#xff1a;一个用来接收固件数据的二进制属性。在设置 loading 为1后, 用户空间进程将固件写入这个属性。
device &#xff1a;一个链接到 /sys/devices 下相关入口项的符号链接。

一旦创建了 sysfs 入口项, 内核将为设备产生一个热插拔事件&#xff0c;并传递包括变量 FIRMWARE 的环境变量给处理热插拔的用户空间程序。FIRMWARE 被设置为提供给 request_firmware 的固件文件名。

用户空间程序定位固件文件, 并将其拷贝到内核提供的二进制属性&#xff1b;若无法定位文件, 用户空间程序设置 loading 属性为 -1。

若固件请求在 10 秒内没有被服务, 内核就放弃并返回一个失败状态给驱动。超时周期可通过 sysfs 属性 /sys/class/firmware/timeout 属性改变。

request_firmware 接口允许使用驱动发布设备固件。当正确地集成进热插拔机制后, 固件加载子系统允许设备不受干扰地工作。显然这是处理问题的最好方法&#xff0c;但固件受版权保护&#xff0c;小心违反版权法。

这里主要介绍硬件驱动使用 Linux kernel 提供Firmware load 功能的方法;
(1) kernel source code :
drivers/base/firmware_class.c // linux 2.6.11
(2) header file:

(3) document
Document/firmware/
(4) 使用例子
Documentation/firmware_class/firmware_sample_driver.c

 1 /*
  2  * firmware_sample_driver.c -
  3  *
  4  * Copyright (c) 2003 Manuel Estrada Sainz
  5  *
  6  * Sample code on how to use request_firmware() from drivers.
  7  *
  8  * Note that register_firmware() is currently useless.
  9  *
 10  */
 11
 12 #include
 13 #include
 14 #include
 15 #include
 16
 17 #include “linux/firmware.h”
 18
 19 #define WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
 20 #ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
 21 char __init inkernel_firmware[] &#61; “let’s say that this is firmware\n”;
 22 #endif
 23
 24 static struct device ghost_device &#61; {
 25         .name      &#61; “Ghost Device”,
 26         .bus_id    &#61; “ghost0″,
 27 };
 28
 29
 30 static void sample_firmware_load(char *firmware, int size)
 31 {
 32         u8 buf[size&#43;1];
 33         memcpy(buf, firmware, size);
 34         buf[size] &#61; ‘\0′;
 35         printk(“firmware_sample_driver: firmware: %s\n”, buf);
 36 }
 37
 38 static void sample_probe_default(void)
 39 {
 40         /* uses the default method to get the firmware */
 41         const struct firmware *fw_entry;
 42         printk(“firmware_sample_driver: a ghost device got inserted :) \n”);
 43
 44         if(request_firmware(&fw_entry, “sample_driver_fw”, &ghost_device)!&#61;0)
 45         {
 46                 printk(KERN_ERR
 47                        “firmware_sample_driver: Firmware not available\n”);
 48                 return;
 49         }
 50        
 51         sample_firmware_load(fw_entry->data, fw_entry->size);
 52
 53         release_firmware(fw_entry);
 54
 55         /* finish setting up the device */
 56 }
 57 static void sample_probe_specific(void)
 58 {
 59         /* Uses some specific hotplug support to get the firmware from
 60          * userspace  directly into the hardware, or via some sysfs file */
 61
 62         /* NOTE: This currently doesn’t work */
 63
 64         printk(“firmware_sample_driver: a ghost device got inserted :) \n”);
 65
 66         if(request_firmware(NULL, “sample_driver_fw”, &ghost_device)!&#61;0)
 67         {
 68                 printk(KERN_ERR
 69                        “firmware_sample_driver: Firmware load failed\n”);
 70                 return;
 71         }
 72        
 73         /* request_firmware blocks until userspace finished, so at
 74          * this point the firmware should be already in the device */
 75
 76         /* finish setting up the device */
 77 }
 78 static void sample_probe_async_cont(const struct firmware *fw, void *context)
 79 {
 80         if(!fw){
 81                 printk(KERN_ERR
 82                        “firmware_sample_driver: firmware load failed\n”);
 83                 return;
 84         }
 85
 86         printk(“firmware_sample_driver: device pointer \”%s\”\n”,
 87                (char *)context);
 88         sample_firmware_load(fw->data, fw->size);
 89 }
 90 static void sample_probe_async(void)
 91 {
 92         /* Let’s say that I can’t sleep */
 93         int error;
 94         error &#61; request_firmware_nowait (THIS_MODULE,
 95                                          “sample_driver_fw”, &ghost_device,
 96                                          “my device pointer”,
 97                                          sample_probe_async_cont);
 98         if(error){
 99                 printk(KERN_ERR
100                        “firmware_sample_driver:”
101                        ” request_firmware_nowait failed\n”);
102         }
103 }
104
105 static int sample_init(void)
106 {
107 #ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
108         register_firmware(“sample_driver_fw”, inkernel_firmware,
109                           sizeof(inkernel_firmware));
110 #endif
111         device_initialize(&ghost_device);
112         /* since there is no real hardware insertion I just call the
113          * sample probe functions here */
114         sample_probe_specific();
115         sample_probe_default();
116         sample_probe_async();
117         return 0;
118 }
119 static void __exit sample_exit(void)
120 {
121 }
122
123 module_init (sample_init);
124 module_exit (sample_exit);
125
126 MODULE_LICENSE(“GPL”);

The kernel doesn’t actually load any firmware at all. It simply informs userspace, “I want a firmware by the name of xxx“, and waits for userspace to pipe the firmware image back to the kernel.

udev is configured to run firmware_helper when the kernel asks for firmware
If you read the source, you’ll find that Ubuntu wrote a firmware_helper which is hard-coded to first look for /lib/modules/$(uname -r)/$FIRMWARE, then /lib/modules/$FIRMWARE, and no other locations. Translating it to sh, it does approximately this:

echo -n 1 > /sys/$DEVPATH/loading
cat /lib/firmware/$(uname -r)/$FIRMWARE > /sys/$DEVPATH/data \
    || cat /lib/firmware/$FIRMWARE      > /sys/$DEVPATH/data
if [ $? &#61; 0 ]; then
    echo -n  1 > /sys/$DEVPATH/loading
    echo -n -1 > /sys/$DEVPATH/loading
fi
which is exactly the format the kernel expects.



推荐阅读
  • Android ListView 自定义 CheckBox 实现列表项多选功能详解
    本文详细介绍了在Android开发中如何在ListView的每一行添加CheckBox,以实现列表项的多选功能。用户不仅可以通过点击复选框来选择项目,还可以通过点击列表的任意一行来完成选中操作,提升了用户体验和操作便捷性。同时,文章还探讨了相关的事件处理机制和布局优化技巧,帮助开发者更好地实现这一功能。 ... [详细]
  • C#中实现高效UDP数据传输技术
    C#中实现高效UDP数据传输技术 ... [详细]
  • 本文深入探讨了 iOS 开发中 `int`、`NSInteger`、`NSUInteger` 和 `NSNumber` 的应用与区别。首先,我们将详细介绍 `NSNumber` 类型,该类用于封装基本数据类型,如整数、浮点数等,使其能够在 Objective-C 的集合类中使用。通过分析这些类型的特性和应用场景,帮助开发者更好地理解和选择合适的数据类型,提高代码的健壮性和可维护性。苹果官方文档提供了更多详细信息,可供进一步参考。 ... [详细]
  • 本文详细探讨了Java集合框架的使用方法及其性能特点。首先,通过关系图展示了集合接口之间的层次结构,如`Collection`接口作为对象集合的基础,其下分为`List`、`Set`和`Queue`等子接口。其中,`List`接口支持按插入顺序保存元素且允许重复,而`Set`接口则确保元素唯一性。此外,文章还深入分析了不同集合类在实际应用中的性能表现,为开发者选择合适的集合类型提供了参考依据。 ... [详细]
  • 深入解析零拷贝技术(Zerocopy)及其应用优势
    零拷贝技术(Zero-copy)是Netty框架中的一个关键特性,其核心在于减少数据在操作系统内核与用户空间之间的传输次数。通过避免不必要的内存复制操作,零拷贝显著提高了数据传输的效率和性能。本文将深入探讨零拷贝的工作原理及其在实际应用中的优势,包括降低CPU负载、减少内存带宽消耗以及提高系统吞吐量等方面。 ... [详细]
  • 本文深入探讨了IO复用技术的原理与实现,重点分析了其在解决C10K问题中的关键作用。IO复用技术允许单个进程同时管理多个IO对象,如文件、套接字和管道等,通过系统调用如`select`、`poll`和`epoll`,高效地处理大量并发连接。文章详细介绍了这些技术的工作机制,并结合实际案例,展示了它们在高并发场景下的应用效果。 ... [详细]
  • 本文深入探讨了NDK与JNI技术在实际项目中的应用及其学习路径。通过分析工程目录结构和关键代码示例,详细介绍了如何在Android开发中高效利用NDK和JNI,实现高性能计算和跨平台功能。同时,文章还提供了从基础概念到高级实践的系统学习指南,帮助开发者快速掌握这些关键技术。 ... [详细]
  • 本文介绍了Android动画的基本概念及其主要类型。Android动画主要包括三种形式:视图动画(也称为补间动画或Tween动画),主要通过改变视图的属性来实现动态效果;帧动画,通过顺序播放一系列预定义的图像来模拟动画效果;以及属性动画,通过对对象的属性进行平滑过渡来创建更加复杂的动画效果。每种类型的动画都有其独特的应用场景和实现方式,开发者可以根据具体需求选择合适的动画类型。 ... [详细]
  • 利用C#技术实现Word文档的动态生成与编辑
    本文通过一个简单的示例,介绍了如何使用C#语言实现Word文档的动态生成与编辑功能。文章详细阐述了在项目中引用Word动态库的方法,并通过具体代码示例展示了如何创建和操作Word表格。此内容旨在为初学者提供参考和学习资料,欢迎读者提出宝贵意见和建议。 ... [详细]
  • 本文深入解析了 Apache 配置文件 `httpd.conf` 和 `.htaccess` 的优化方法,探讨了如何通过合理配置提升服务器性能和安全性。文章详细介绍了这两个文件的关键参数及其作用,并提供了实际应用中的最佳实践,帮助读者更好地理解和运用 Apache 配置。 ... [详细]
  • 本文深入探讨了 MXOTDLL.dll 在 C# 环境中的应用与优化策略。针对近期公司从某生物技术供应商采购的指纹识别设备,该设备提供的 DLL 文件是用 C 语言编写的。为了更好地集成到现有的 C# 系统中,我们对原生的 C 语言 DLL 进行了封装,并利用 C# 的互操作性功能实现了高效调用。此外,文章还详细分析了在实际应用中可能遇到的性能瓶颈,并提出了一系列优化措施,以确保系统的稳定性和高效运行。 ... [详细]
  • 进程(Process)是指计算机中程序对特定数据集的一次运行活动,是系统资源分配与调度的核心单元,构成了操作系统架构的基础。在早期以进程为中心的计算机体系结构中,进程被视为程序的执行实例,其状态和控制信息通过任务描述符(task_struct)进行管理和维护。本文将深入探讨进程的概念及其关键数据结构task_struct,解析其在操作系统中的作用和实现机制。 ... [详细]
  • MySQL性能优化与调参指南【数据库管理】
    本文详细探讨了MySQL数据库的性能优化与参数调整技巧,旨在帮助数据库管理员和开发人员提升系统的运行效率。内容涵盖索引优化、查询优化、配置参数调整等方面,结合实际案例进行深入分析,提供实用的操作建议。此外,还介绍了常见的性能监控工具和方法,助力读者全面掌握MySQL性能优化的核心技能。 ... [详细]
  • 尽管存在唯一列,仍显示“当前选择不包含唯一列。网格编辑、复选框、编辑、复制和删除功能不可用”的消息。 ... [详细]
  • Qt 34:深入探讨缓冲区管理、目录操作及文件系统监控技术——QBuffer、QDir与QFileSystemWatcher的应用分析
    本文深入探讨了Qt框架中QBuffer、QDir和QFileSystemWatcher三个核心类的应用。QBuffer作为缓冲区管理工具,提供了高效的数据读写功能;QDir则专注于目录操作,支持路径遍历和文件检索等任务;而QFileSystemWatcher则用于实时监控文件系统的变化,便于应用程序及时响应文件或目录的增删改操作。通过实例分析,详细解析了这三个类在实际开发中的应用场景和实现细节。 ... [详细]
author-avatar
纪志鹏大利集客_776
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有