前言 node
前段时间移植 wifi 驱动到 android 的内核上,发现 firmware 的加载始终出错,问了几我的,都不是很了解,没办法,只好本身研究一下。 linux
从本质上来讲, firmware 须要作的事情包括两件: android
1, 通知用户态程序,我须要下载 firmware 了; socket
2, 用户态程序把用户态的数据 copy 到内核层; 函数
3, 内核把内核态的数据写到设备上,好比 wifi 模块里; spa
其中第三步应该不难,关键是看看, linux 里面是如何实现第1、二步的; 设计
简单的说,它的机制分红如下几部分: it
1, 经过必定的方式,通知用户态程序,好比 init 程序,如图所示 io
用户态的 init 是如何作的? event
能够看到 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 修改后的结果,结论就是,这个设计真丑;