背景

在bt、wifi、tp-touch、camera、vpu等驱动中会经常遇到firmware, firmware主要是其他外设控制器的运行程序或者配置;

一般有以下2种使用方式:

  • 将fw data转化为特定的数组,编码在驱动代码中。会造成kernel镜像size变大, 有可能造成镜像超限, 导致kernel启动失败; 调试升级都不方便, 每次修改fw都需要重新编译内核
  • 将fw打包到文件系统中,如vendor,system,lib/firmware等等,需要的时候从用户空间中load到kernel空间中,在驱动中应用比较广泛

以前在汇顶的TP-touch驱动中load配置2种方式都有使用,常用的最后一种,比较灵活;

使用

主要API:

  • Synchronous:
    request_firmware
  • Asynchronous:
    request_firmware_nowait
    该接口不会导致进程睡眠,cannot be called in atomic contexts

使用流程:
request_firmware -> memcpy -> release_firmware

request_firmware流程:

           request_firmware()
                   |
                   |                     Y
 firmware built in kernel image? ------------------- load from kernel image -------
                   |                                                               |
                   |  N                                                            |
                   |                     Y                                         |
        load directly from fs ? ------------------  load directly from "path" -    |
                   |                                                           |   |
                   |  N                                                        |   |
                   |                                                           |   |
load with file node "/sys/class/firmware/xx.bin/"                              |   |
                   |                                                           |   |
                   |                                                           |   |
                  exit                                                          exit

Note:

  request_firmware在内核使用,需要文件系统支持,就是说,启动的时候如果在驱动里面的probe函数调用 request_firmware ,那么系统将等待30s左右,因为文件系统还没有挂载,当然找不到固件了,所以最好在中断里面启动tasklet,然后request_firmware 。如果不想等待,就用request_firmware_nowait,说明如下:
  Asynchronous variant of request_firmware for user contexts: - sleep for as small periods as possible since it may increase kernel boot time of built-in device drivers requesting firmware in their ->probe methods, if gfp is GFP_KERNEL.

从kernel-image中Load

Built-in firmware
config配置:

CONFIG_FIRMWARE_IN_KERNEL=y
CONFIG_EXTRA_FIRMWARE_DIR="firmware"  // this means $(source_dir)/firmware
CONFIG_EXTRA_FIRMWARE="fw_xxx.bin"

为啥会考虑这种?

  • speed
  • boot device需要该固件

不能使用的情形:

  • 许可问题,有些firmware不是GPL许可
  • firmware需要方便升级
  • firmware的size比较大
  • firmware需要动态修改,对于每个设备都不一样, 例如一些WiFi或者TP等的修正参数等

从文件系统Load

Firmware搜索路径,依次搜索:

  • fw_path_para - module parameter(firmware_class.path=xxx 或者 /sys/module/firmware_class/parameters/path指定)
  • /lib/firmware/updates/UTS_RELEASE/ - (UTS_RELEASE为kernel release version number)
  • /lib/firmware/updates/
  • /lib/firmware/UTS_RELEASE/
  • /lib/firmware/

MODULE_FIRMWARE

从userspace中Load

路径:/sys/class/firmware/xxx
hotplug-script的脚本sample:

#!/bin/sh
  
   # Simple hotplug script sample:
   # 
   # Both $DEVPATH and $FIRMWARE are already provided in the environment. 
   # $DEVPATH常用:/sys/class/firmware/xxx

   HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/
  
   if [ "$SUBSYSTEM" == "firmware" -a "$ACTION" == "add" ]; then
       if [ -f $HOTPLUG_FW_DIR/$FIRMWARE ]; then
           echo 1 > /sys/$DEVPATH/loading
           cat $HOTPLUG_FW_DIR/$FIRMWARE > /sys/$DEVPATH/data
           echo 0 > /sys/$DEVPATH/loading
       else
           echo -1 > /sys/$DEVPATH/loading
       fi
   fi

触发

热插拔uevent事件
udev

linux-firmware包

在Linux的发行版中常用linux-firmware软件包来维护firmware

参考: