Linux内核如何知道在哪里寻找驱动程序固件?

我正在Ubuntu下编译一个自定义的内核,我遇到了内核似乎不知道在哪里寻找固件的问题。 在Ubuntu 8.04下,固件与驱动程序模块的内核版本相同。 例如,内核2.6.24-24-generic将其内核模块存储在:

/lib/modules/2.6.24-24-generic 

和它的固件在:

 /lib/firmware/2.6.24-24-generic 

当我根据“ 替代构build方法:老式的Debian方式 ”编译2.6.24-24-generic Ubuntu内核时,我得到相应的模块目录,除了需要固件的那些设备(除了我的英特尔无线网卡(ipw2200模块)。

内核日志显示例如当ipw2200尝试加载固件时,控制固件加载的内核子系统无法find它:

 ipw2200: Detected Intel PRO/Wireless 2200BG Network Connection ipw2200: ipw2200-bss.fw request_firmware failed: Reason -2 

errno-base.h将其定义为:

 #define ENOENT 2 /* No such file or directory */ 

(返回ENOENT的函数在它前面放一个减号。)

我试图在/ lib / firmware中创build一个符号链接,其中我的内核名称指向2.6.24-24的通用目录,但是这导致了相同的错误。 这个固件是非英特尔的,由英特尔提供,并由Ubuntu打包。 我不相信它有一个特定的内核版本。 cmp显示各个目录中的版本是相同的。

那么内核如何知道在哪里寻找固件?

更新

我发现这个解决scheme是针对我遇到的确切问题,但是由于Ubuntu已经取消/etc/hotplug.d而不再工作,并且不再将其固件存储在/usr/lib/hotplug/firmware

UPDATE2

还有一些研究发现了更多的答案。 直到udev 92版本,程序firmware_helper是固件加载的方式。 从udev 93开始,这个程序被replace为一个名为firmware.sh的脚本,据我所知可以提供相同的function。 这两个硬编码到/lib/firmware的固件path。 Ubuntu似乎仍在使用/lib/udev/firmware_helper二进制文件。

将固件文件的名称传递给环境variables$FIRMWARE中的firmware_helper ,该环境variables连接到path/lib/firmware并用于加载固件。

加载固件的实际请求是由驱动程序(在我的情况下是ipw2200)通过系统调用完成的:

 request_firmware(..., "ipw2200-bss.fw", ...); 

现在,在调用request_firmware的驱动程序和查看$FIRMWARE环境variables的firmware_helper之间的某个地方,内核软件包的名称已经预先固定在固件名称上。

那么谁在做呢?

从内核的angular度来看,请参阅/ usr / src / linux / Documentation / firmware_class / README :

 内核(驱动程序):调用request_firmware(&fw_entry,$ FIRMWARE,设备)

 用户空间:
         -  / sys / class / firmware / xxx / {loading,data}出现。
         - 在$ FIRMWARE中使用固件标识符调用hotplug
          和通常的热插拔环境。
                 -  hotplug:echo 1> / sys / class / firmware / xxx / loading

 内核:丢弃任何以前的部分负载。

 用户空间:
                 -  hotplug:cat appropriate_firmware_image> \
                                         / SYS /class/固件/ XXX /数据

 内核:以PAGE_SIZE增量生长一个缓冲区来保存图像
         进来。

 用户空间:
                 -  hotplug:echo 0> / sys / class / firmware / xxx / loading

 内核:request_firmware()返回并且驱动程序具有固件
          image in fw_entry  - > {data,size}。 如果出了点问题
          request_firmware()返回非零值,fw_entry被设置为
         空值。

 内核(驱动程序):驱动程序代码调用release_firmware(fw_entry)释放
                 固件映像和任何相关资源。

内核实际上并没有加载任何固件。 它只是简单地通知用户空间,“我想要一个名为xxx的固件”,并等待用户空间将固件映像传回给内核。

现在,在Ubuntu 8.04上,

 $ grep固件/etc/udev/rules.d/80-program.rules
 #根据需要加载固件
 SUBSYSTEM ==“firmware”,ACTION ==“add”,RUN + =“firmware_helper”

所以如你udevudev被configuration为在内核要求firmware_helper时运行firmware_helper。

 $ apt-get source udev
正在读取软件包列表...完成
build立依赖关系树
阅读状态信息...完成
需要获得312kB的源文件。
获取:1 http://us.archive.ubuntu.com hardy-security / main udev 117-8ubuntu0.2(dsc)[716B]
获取:2 http://us.archive.ubuntu.com hardy-security / main udev 117-8ubuntu0.2(tar)[245kB]
获取:3 http://us.archive.ubuntu.com hardy-security / main udev 117-8ubuntu0.2(差异)[65.7kB]
在1秒内获得312kB(223kB / s)
 gpg:签名使用DSA密钥ID 17063E6D星期二14四月2009 05:31:34 PM EDT
 gpg:无法检查签名:找不到公钥
 dpkg-source:提取udev-117中的udev
 dpkg-source:解压udev_117.orig.tar.gz
 dpkg-source:应用./udev_117-8ubuntu0.2.diff.gz
 $ cd udev-117 /
 $ cat debian / patches / 80-extras-firmware.patch

如果你阅读了源代码,你会发现Ubuntu写了一个firmware_helper ,它是硬编码的,首先查找/lib/modules/$(uname -r)/$FIRMWARE ,然后是/lib/modules/$FIRMWARE ,其他地点。 把它翻译成sh ,大概是这样的:

 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 

这正是内核所期望的格式。


长话短说:Ubuntu的udev软件包具有自定义function,首先查看/lib/firmware/$(uname -r) 。 这个政策正在用户空间中处理。

哇这是非常有用的信息,它使我为我的问题的解决scheme,为需要固件的设备定制USB内核模块。

基本上,每个Ubuntu都会带来一个新的hal,sysfs,devfs,udev等等,事情就会改变。 其实我读了他们停止使用hal。

所以让我们再次进行逆向工程,以便与最新的[Ubuntu]系统相关。

在Ubuntu Lucid上(最新的写作时间),使用/lib/udev/rules.d/50-firmware.rules 。 这个文件调用二进制/lib/udev/firmware ,在那里发生魔法。

清单:/lib/udev/rules.d/50-firmware.rules

 # firmware-class requests, copies files into the kernel SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware --firmware=$env{FIRMWARE} --devpath=$env{DEVPATH}" 

神奇的东西应该是这样(来源: Linux设备驱动程序,第3版,第14章:Linux设备模型 ):

  • 回声1 loading
  • 将固件复制到data
  • 失败时,echo-1 loading并暂停固件加载过程
  • 回声0 loading (信号内核)
  • 然后,一个特定的内核模块接收数据并将其推送到设备

如果你看看udev的Lucid的源代码页,在udev-151/extras/firmware/firmware.c ,这个firmware / lib / udev / firmware二进制文件的源代码就是这样。

摘录:Lucid源码,udev-151 / extras / firmware / firmware.c

  util_strscpyl(datapath, sizeof(datapath), udev_get_sys_path(udev), devpath, "/data", NULL); if (!copy_firmware(udev, fwpath, datapath, statbuf.st_size)) { err(udev, "error sending firmware '%s' to device\n", firmware); set_loading(udev, loadpath, "-1"); rc = 4; goto exit; }; set_loading(udev, loadpath, "0"); 

此外,许多设备使用英特尔HEX格式(文本文件包含校验和和其他东西)(维基它,我没有声望,没有能力链接)。 内核程序ihex2fw(在kernel_source / lib / firmware中的.HEX文件中的Makefile中调用)将这些HEX文件转换为任意devise的二进制格式,然后Linux内核使用request_ihex_firmware进行拾取,因为他们认为读取内核中的文本文件愚蠢(这会减慢速度)。

在当前的Linux系统上,这是通过udevfirmware.agent处理的。

Linux 3.5.7 Gentoo,我有同样的问题。 解决了:

 emerge ipw2200-firmware 

然后转到/ usr / src / linux

 make menucofig 

在设备驱动程序上,删除所有不需要的无线驱动程序,将Intell 2200设置为模块并重新编译。

 make make modules_install cp arch/x86/boot/bzImage /boot/kernel-yourdefault