我正在Ubuntu下编译一个自定义内核,我遇到的问题是我的内核似乎不知道在哪里寻找固件。在Ubuntu 8.04下,固件与内核版本绑定的方式与驱动程序模块相同。例如,内核2.6.24-24-generic将其内核模块存储在:
中/lib/modules/2.6.24-24-generic
及其固件:
/lib/firmware/2.6.24-24-generic
当我根据“Alternate Build Method: The Old-Fashioned Debian Way”编译2.6.24-24通用Ubuntu内核时,我得到了相应的模块目录,我的所有设备都工作,除了需要固件的设备,例如我的英特尔无线网卡(ipw2200模块)
内核日志显示,例如,当ipw2200尝试加载固件时,控制固件加载的内核子系统无法找到它:
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中创建一个符号链接,其中我的内核名称指向2.6.24-24-generic目录,但这导致了同样的错误。此固件是非GPL,由Intel提供并由Ubuntu打包。我不相信它与特定内核版本有任何实际联系。 cmp
表明各个目录中的版本是相同的。
那么内核如何知道在哪里寻找固件?
更新
我发现了this solution我遇到的确切问题,但是由于Ubuntu已经消除了/etc/hotplug.d
并且不再将其固件存储在/usr/lib/hotplug/firmware
中,因此它不再有效。
UPDATE2
更多研究提出了更多答案。直到udev
的版本92,程序firmware_helper
才是固件加载的方式。从udev
93开始,这个程序被替换为一个名为firmware.sh的脚本,据我所知,它提供了相同的功能。这两者都将固件路径硬编码为/lib/firmware
。 Ubuntu似乎仍在使用/lib/udev/firmware_helper
二进制文件。
固件文件的名称将传递给环境变量firmware_helper
中的$FIRMWARE
,该变量连接到路径/lib/firmware
并用于加载固件。
加载固件的实际请求是由驱动程序(在我的情况下为ipw2200)通过系统调用实现的:
request_firmware(..., "ipw2200-bss.fw", ...);
现在,在调用request_firmware
和firmware_helper
的驱动程序之间的某处查看$FIRMWARE
环境变量,内核程序包名称将被添加到固件名称之前。
那么谁在做呢?
答案 0 :(得分:41)
从内核的角度来看,请参阅/usr/src/linux/Documentation/firmware_class/README:
kernel(driver): calls request_firmware(&fw_entry, $FIRMWARE, device) userspace: - /sys/class/firmware/xxx/{loading,data} appear. - hotplug gets called with a firmware identifier in $FIRMWARE and the usual hotplug environment. - hotplug: echo 1 > /sys/class/firmware/xxx/loading kernel: Discard any previous partial load. userspace: - hotplug: cat appropriate_firmware_image > \ /sys/class/firmware/xxx/data kernel: grows a buffer in PAGE_SIZE increments to hold the image as it comes in. userspace: - hotplug: echo 0 > /sys/class/firmware/xxx/loading kernel: request_firmware() returns and the driver has the firmware image in fw_entry->{data,size}. If something went wrong request_firmware() returns non-zero and fw_entry is set to NULL. kernel(driver): Driver code calls release_firmware(fw_entry) releasing the firmware image and any related resource.
内核实际上并没有加载任何固件。它只是通知用户空间,“我想要一个名为 xxx 的固件”,并等待用户空间将固件映像传送回内核。
现在,在Ubuntu 8.04上,
$ grep firmware /etc/udev/rules.d/80-program.rules # Load firmware on demand SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware_helper"
正如您所发现的那样,udev
被配置为在内核请求固件时运行firmware_helper
。
$ apt-get source udev Reading package lists... Done Building dependency tree Reading state information... Done Need to get 312kB of source archives. Get:1 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (dsc) [716B] Get:2 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (tar) [245kB] Get:3 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (diff) [65.7kB] Fetched 312kB in 1s (223kB/s) gpg: Signature made Tue 14 Apr 2009 05:31:34 PM EDT using DSA key ID 17063E6D gpg: Can't check signature: public key not found dpkg-source: extracting udev in udev-117 dpkg-source: unpacking udev_117.orig.tar.gz dpkg-source: applying ./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
软件包具有始终首先查看/lib/firmware/$(uname -r)
的自定义项。此策略正在用户空间中处理。
答案 1 :(得分:12)
哇这是非常有用的信息,它使我在为需要固件的设备制作自定义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 Device Drivers, 3rd Ed., Ch. 14: The Linux Device Model):
loading
data
loading
并暂停固件加载过程loading
(发信号通知内核)如果你看一下Lucid的udev源代码页,在udev-151/extras/firmware/firmware.c
中,那个固件/ lib / udev / firmware二进制文件的源代码就是这样的。
摘录:Lucid source,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(从.sEX文件中的kernel_source / lib / firmware中的Makefile调用)将这些HEX文件转换为任意设计的二进制格式,然后Linux内核会使用request_ihex_firmware
,因为他们认为读取文本文件在内核中是愚蠢的(它会减慢速度)。
答案 2 :(得分:1)
在当前的Linux系统上,这是通过udev
和firmware.agent
。
答案 3 :(得分:1)
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