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 下创建一个以设备名为目录名的新目录,其中包含 3 个属性: loading :这个属性应当被加载固件的用户空间进程设置为 1。当加载完毕, 它将被设为 0。被设为 -1 时,将中止固件加载。 data :一个用来接收固件数据的二进制属性。在设置 loading 为1后, 用户空间进程将固件写入这个属性。 device :一个链接到 /sys/devices 下相关入口项的符号链接。 一旦创建了 sysfs 入口项, 内核将为设备产生一个热插拔事件,并传递包括变量 FIRMWARE 的环境变量给处理热插拔的用户空间程序。FIRMWARE 被设置为提供给 request_firmware 的固件文件名。 用户空间程序定位固件文件, 并将其拷贝到内核提供的二进制属性;若无法定位文件, 用户空间程序设置 loading 属性为 -1。 若固件请求在 10 秒内没有被服务, 内核就放弃并返回一个失败状态给驱动。超时周期可通过 sysfs 属性 /sys/class/firmware/timeout 属性改变。 request_firmware 接口允许使用驱动发布设备固件。当正确地集成进热插拔机制后, 固件加载子系统允许设备不受干扰地工作。显然这是处理问题的最好方法,但固件受版权保护,小心违反版权法。 这里主要介绍硬件驱动使用 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_driv

Recommend:linux 光驱加载错误问题:can't find /mnt/cdrom in /etc/fstable or /etc/mtab

可以到dev下看是否有cdrom

输入:cd /dev ls -ahl 查看目录文件列表 看到有cdrom 通过 mount /dev/cdrom /mnt/cdrom 将cdrom挂在到/mnt/cdrom下

er.c -

3

*

4

* Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>

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[] = “let’s say that this is firmware\n”;

22 #endif

23

24 static struct device ghost_device = {

25

.name

= “Ghost Device”,

26

.bus_id

= “ghost0″,

27 };

28

29

30 static void sample_firmware_load(char *firmware, int size)

31 {

32

u8 buf[size+1];

33

memcpy(buf, firmware, size);

34

buf[size] = ‘\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)!=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)!=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 = 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 [ $? = 0 ]; then

echo -n

1 > /sys/$DEVPATH/loading

echo -n -1 > /sys/$DEVPATH/loading fi which is exactly the format the kernel expects.

Recommend:Linux USB驱动学习总结(三)---- USB鼠标的加载、初始化和通信过程

1、usbmouse的定义:usb鼠标既包含usb设备(usb_device)的属性也包含input输入设备(input_dev)的属性 struct usb_mouse {char name[128];///USB鼠标设备名称char phys[64];///路径struct usb_device *usbdev;///USB设备st

Recommend:Linux下c函数dlopen实现加载动态库so文件代码举例

dlopen()是一个强大的库函数。该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了。可以在自己的程序中使用 dlope

ori:http://blog.chinaunix.net/uid-30509496-id-5718268.html