54

我正在 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-generic 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表明各个目录中的版本是相同的。

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

更新

我找到了解决我遇到的确切问题的方法,但是它不再适用,因为 Ubuntu 已经消除/etc/hotplug.d并且不再将其固件存储在/usr/lib/hotplug/firmware.

更新2

更多的研究发现了更多的答案。在 92 版之前udev,该程序firmware_helper是加载固件的方式。从udev93 开始,这个程序被一个名为 firmware.sh 的脚本取代,据我所知,它提供了相同的功能。这两个都将固件路径硬编码为/lib/firmware. Ubuntu 似乎仍在使用/lib/udev/firmware_helper二进制文件。

固件文件的名称firmware_helper在环境变量$FIRMWARE中传递给连接到路径/lib/firmware并用于加载固件的环境变量。

加载固件的实际请求是由驱动程序(在我的例子中为 ipw2200)通过系统调用发出的:

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

现在在驱动程序调用request_firmwarefirmware_helper查看$FIRMWARE环境变量之间的某个地方,内核包名称被添加到固件名称之前。

那么谁在做呢?

4

4 回答 4

42

从内核的角度来看,请参见/usr/src/linux/Documentation/firmware_class/README

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

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

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

 用户空间:
                - 热插拔:cat proper_firmware_image > \
                                        /sys/类/固件/xxx/数据

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

 用户空间:
                - 热插拔:echo 0 > /sys/class/firmware/xxx/loading

 内核:request_firmware() 返回并且驱动程序有固件
         fw_entry->{data,size} 中的图像。如果出现问题
         request_firmware() 返回非零并且 fw_entry 设置为
         无效的。

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

内核实际上根本不加载任何固件。它只是通知用户空间,“我想要一个名为xxx的固件”,并等待用户空间将固件映像通过管道传回内核。

现在,在 Ubuntu 8.04 上,

$ grep 固件 /etc/udev/rules.d/80-program.rules
# 按需加载固件
SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware_helper"

如您所见,udev它被配置为firmware_helper在内核请求固件时运行。

$ apt-get 源代码 udev
阅读包裹清单...完成
构建依赖树
读取状态信息...完成
需要获取 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 (diff) [65.7kB]
在 1 秒内获取 312kB (223kB/s)
gpg:签名于 2009 年 4 月 14 日星期二 05:31:34 PM EDT 使用 DSA 密钥 ID 17063E6D
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软件包具有始终/lib/firmware/$(uname -r)先查看的自定义项。此策略正在用户空间中处理。

于 2009-06-04T22:15:23.893 回答
14

哇,这是非常有用的信息,它使我在为需要固件的设备制作自定义 USB 内核模块时找到了解决问题的方法。

基本上,每个 Ubuntu 都带来了 hal、sysfs、devfs、udev 等的新的重新散列……而事情只是发生了变化。事实上,我读到他们停止使用 hal。

因此,让我们再次对其进行逆向工程,使其与最新的 [Ubuntu] 系统相关。

在 Ubuntu Lucid(撰写本文时的最新版本)/lib/udev/rules.d/50-firmware.rules上使用。这个文件调用了 binary /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):

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

如果您查看 Lucid 的 udev 源页面,在udev-151/extras/firmware/firmware.c该固件 /lib/udev/firmware 二进制文件的源代码中,这正是发生的情况。

摘录:清晰的源代码,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(从 .HEX 文件上的 kernel_source/lib/firmware 中的 Makefile 调用)将这些 HEX 文件转换为 Linux 内核随后选择的任意设计的二进制格式request_ihex_firmware,因为他们认为在内核中读取文本文件是愚蠢的(它会减慢速度)。

于 2010-06-10T07:04:55.813 回答
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
于 2013-02-26T09:59:13.577 回答
1

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

于 2009-06-04T12:12:18.233 回答