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”
所以如你udev
, udev
被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系统上,这是通过udev
和firmware.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