对于某些特定的外设, 可能需更新内部的 fw, 最常见的外设就是 tp, 内核提供了 4 种方式来更新 fw.
软件将固件放入到一个数组中, 在开机的时候内核将固件下发到外部 ic. 这种方式无法动态更新 fw ,早期的 tp 采用这种方式.
将固件提前编译进指定的数据段 builtin_fw, 开机的时候从该数据段获取固件, 下发至 ic
将固件放入指定的目录, 开机的时候内核从该目录获取固件, 下发至 ic
将固件放入指定目录, 开机的时候像用户空间发送 uevent 事件, 用户空间解析该事件, 将指定目录的固件通过属性节点下发到内核, 内核获取到固件之后, 再下发至ic
目前采用较多的是后面三种, 内核已经帮我们实现了
1 2 3 4 5 6 int request_firmware (const struct firmware **firmware_p, const char *name,struct device *device) int request_firmware_direct (const struct firmware **firmware_p, const char *name, struct device *device)
1、数据结构
1.1 firmware
用于保存获取到的固件,
1 2 3 4 5 6 7 8 struct firmware { size_t size; const u8 *data; struct page **pages ; void *priv; };
1.2 firmware_buf
该结构体包含固件的基本信息,已经下载的固件会保存到 fw_cache 中,以防重复下载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct firmware_buf { struct kref ref ; struct list_head list ; struct firmware_cache *fwc ; struct fw_state fw_st ; void *data; size_t size; size_t allocated_size; #ifdef CONFIG_FW_LOADER_USER_HELPER bool is_paged_buf; bool need_uevent; struct page **pages ; int nr_pages; int page_array_size; struct list_head pending_list ; #endif const char *fw_id; };
1.3 firmware_cache
用来记录固件的下载信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct firmware_cache { spinlock_t lock; struct list_head head ; int state; #ifdef CONFIG_PM_SLEEP spinlock_t name_lock; struct list_head fw_names ; struct delayed_work work ; struct notifier_block pm_notify ; #endif };
1.4 firmware_priv
当使用用户空间下载固件的方式时,使用该结构创建设备节点给用户空间使用。
1 2 3 4 5 6 struct firmware_priv { bool nowait; struct device dev ; struct firmware_buf *buf ; struct firmware *fw ; };
2、request_firmware
最常用的方式是直接调用 request_firmware 接口该接口已经被封装的十分简单,firmware_p 用来保存获取的固件,name 表示固件的名称,device 是固件所属设备在使用 FW_OPT_USERHELPER 方式获取时使用。request_firmware 接口直接调用了_request_firmware 来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 int request_firmware (const struct firmware **firmware_p, const char *name, struct device *device) { int ret; __module_get(THIS_MODULE); ret = _request_firmware(firmware_p, name, device, NULL , 0 , FW_OPT_UEVENT | FW_OPT_FALLBACK); module_put(THIS_MODULE); return ret; } static int _request_firmware(const struct firmware **firmware_p, const char *name, struct device *device, void *buf, size_t size, unsigned int opt_flags) { struct firmware *fw = NULL ; int ret; if (!firmware_p) return -EINVAL; if (!name || name[0 ] == '\0' ) { ret = -EINVAL; goto out; } ret = _request_firmware_prepare(&fw, name, device, buf, size); if (ret <= 0 ) goto out; ret = fw_get_filesystem_firmware(device, fw->priv); if (ret) { if (!(opt_flags & FW_OPT_NO_WARN)) dev_warn(device, "Direct firmware load for %s failed with error %d\n" , name, ret); if (opt_flags & FW_OPT_USERHELPER) { dev_warn(device, "Falling back to user helper\n" ); ret = fw_load_from_user_helper(fw, name, device, opt_flags); } } else ret = assign_firmware_buf(fw, device, opt_flags); out: if (ret < 0 ) { fw_abort_batch_reqs(fw); release_firmware(fw); fw = NULL ; } *firmware_p = fw; return ret; }
可以看出整个实现由三个函数分别实现 3 个功能,依次分析 3 个功能的实现
2.1 通过 build_in 方式
首先在 builtin_fw 数据段查找固件如果找不到,则在 fw_cache 中搜索固件是否已经下载,如果是已经下载的固件则直接返回对应的 firmware_buf。没有则动态创建一个空的 firmware_buf 返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 static int _request_firmware_prepare(struct firmware **firmware_p, const char *name, struct device *device, void *dbuf, size_t size) { struct firmware *firmware ; struct firmware_buf *buf ; int ret; *firmware_p = firmware = kzalloc(sizeof (*firmware), GFP_KERNEL); if (!firmware) { dev_err(device, "%s: kmalloc(struct firmware) failed\n" , __func__); return -ENOMEM; } if (fw_get_builtin_firmware(firmware, name, dbuf, size)) { dev_dbg(device, "using built-in %s\n" , name); return 0 ; } ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf, dbuf, size); firmware->priv = buf; if (ret > 0 ) { ret = fw_state_wait(&buf->fw_st); if (!ret) { fw_set_page_data(buf, firmware); return 0 ; } } if (ret < 0 ) return ret; return 1 ; }
2.1.1 fw_get_builtin_firmware
在 __start_builtin_fw 表示的数据段查找固件,也就是在 builtin_fw 数据段查找固件,找到则直接返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 .builtin_fw : AT(ADDR(.builtin_fw) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start_builtin_fw) = .; \ KEEP(*(.builtin_fw)) \ VMLINUX_SYMBOL(__end_builtin_fw) = .; \ } static bool fw_get_builtin_firmware (struct firmware *fw, const char *name, void *buf, size_t size) { struct builtin_fw *b_fw ; for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) { if (strcmp (name, b_fw->name) == 0 ) { fw->size = b_fw->size; fw->data = b_fw->data; if (buf && fw->size <= size) memcpy (buf, fw->data, fw->size); return true ; } } return false ; }
2.2 直接从内核读取文件
该接口很简单,就是直接遍历读取 fw_path 中支持的路径,并依次读取固件。需要注意的是使用这种方式获取固件要保证。fw_path 中的路径要在 request_firmware 调用之前创建,否则无法获取直接返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 static char fw_path_para[256 ]; static const char * const fw_path[] = { "/vendor/firmware" , fw_path_para, "/lib/firmware/updates/" UTS_RELEASE, "/lib/firmware/updates" , "/lib/firmware/" UTS_RELEASE, "/lib/firmware" }; static int fw_get_filesystem_firmware (struct device *device, struct firmware_buf *buf) { loff_t size; int i, len; int rc = -ENOENT; char *path; enum kernel_read_file_id id = READING_FIRMWARE; size_t msize = INT_MAX; for (i = 0 ; i < ARRAY_SIZE(fw_path); i++) { if (!fw_path[i][0 ]) continue ; len = snprintf (path, PATH_MAX, "%s/%s" , fw_path[i], buf->fw_id); buf->size = 0 ; rc = kernel_read_file_from_path(path, &buf->data, &size, msize, id); dev_dbg(device, "direct-loading %s\n" , buf->fw_id); buf->size = size; fw_state_done(&buf->fw_st); break ; } __putname(path); return rc; }
2.3 内核通知上层下发固件
该方式是实现比较复杂的,采用设备模型的 uevent 实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 static int fw_load_from_user_helper (struct firmware *firmware, const char *name, struct device *device, unsigned int opt_flags) { struct firmware_priv *fw_priv ; long timeout; int ret; timeout = firmware_loading_timeout(); if (opt_flags & FW_OPT_NOWAIT) { timeout = usermodehelper_read_lock_wait(timeout); if (!timeout) { dev_dbg(device, "firmware: %s loading timed out\n" , name); return -EBUSY; } } else { ret = usermodehelper_read_trylock(); if (WARN_ON(ret)) { dev_err(device, "firmware: %s will not be loaded\n" , name); return ret; } } fw_priv = fw_create_instance(firmware, name, device, opt_flags); if (IS_ERR(fw_priv)) { ret = PTR_ERR(fw_priv); goto out_unlock; } fw_priv->buf = firmware->priv; ret = _request_firmware_load(fw_priv, opt_flags, timeout); if (!ret) ret = assign_firmware_buf(firmware, device, opt_flags); out_unlock: usermodehelper_read_unlock(); return ret; }
2.3.1 fw_create_instance
创建一个中间设备 firmware_priv ,该设备包含两个属性节点, loading 和 data 为后面用户空间的下载做准备。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 static DEVICE_ATTR (loading, 0644 , firmware_loading_show, firmware_loading_store) ;static struct bin_attribute firmware_attr_data = { .attr = { .name = "data" , .mode = 0644 }, .size = 0 , .read = firmware_data_read, .write = firmware_data_write, }; static struct firmware_priv *fw_create_instance (struct firmware *firmware, const char *fw_name, struct device *device, unsigned int opt_flags) { struct firmware_priv *fw_priv ; struct device *f_dev ; fw_priv = kzalloc(sizeof (*fw_priv), GFP_KERNEL); if (!fw_priv) { fw_priv = ERR_PTR(-ENOMEM); goto exit ; } fw_priv->nowait = !!(opt_flags & FW_OPT_NOWAIT); fw_priv->fw = firmware; f_dev = &fw_priv->dev; device_initialize(f_dev); dev_set_name(f_dev, "%s" , fw_name); f_dev->parent = device; f_dev->class = &firmware_class; f_dev->groups = fw_dev_attr_groups; exit : return fw_priv; }
2.3.2 _request_firmware_load
该接口创建中间属性节点 date 和 loading,同时想上层发送 FIRMWARE 事件。上层获取到该事件后下载固件到内核。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 static int _request_firmware_load(struct firmware_priv *fw_priv, unsigned int opt_flags, long timeout) { int retval = 0 ; struct device *f_dev = &fw_priv->dev; struct firmware_buf *buf = fw_priv->buf; if (!buf->data) buf->is_paged_buf = true ; dev_set_uevent_suppress(f_dev, true ); retval = device_add(f_dev); if (retval) { dev_err(f_dev, "%s: device_register failed\n" , __func__); goto err_put_dev; } mutex_lock(&fw_lock); list_add(&buf->pending_list, &pending_fw_head); mutex_unlock(&fw_lock); if (opt_flags & FW_OPT_UEVENT) { buf->need_uevent = true ; dev_set_uevent_suppress(f_dev, false ); dev_dbg(f_dev, "firmware: requesting %s\n" , buf->fw_id); kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); } else { timeout = MAX_JIFFY_OFFSET; } retval = fw_state_wait_timeout(&buf->fw_st, timeout); if (retval < 0 ) { mutex_lock(&fw_lock); fw_load_abort(fw_priv); mutex_unlock(&fw_lock); } if (fw_state_is_aborted(&buf->fw_st)) { if (retval == -ERESTARTSYS) retval = -EINTR; else retval = -EAGAIN; } else if (buf->is_paged_buf && !buf->data) retval = -ENOMEM; device_del(f_dev); err_put_dev: put_device(f_dev); return retval; }
2.3.3 上层处理
上层在 ueventd_main 中监听 uevent事件,当检测到 FIRMWARE 事件后,由 ProcessFirmwareEvent 处理该事件。
1 2 3 4 5 --> ueventd_main --> uevent_listener.Poll --> ReadUevent (&uevent) --> ParseEvent (msg, uevent); --> ProcessFirmwareEvent
主要的下载由 ProcessFirmwareEvent 处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 static void ProcessFirmwareEvent (const Uevent& uevent) { int booting = IsBooting (); LOG (INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'" ; std::string root = "/sys" + uevent.path; std::string loading = root + "/loading" ; std::string data = root + "/data" ; unique_fd loading_fd (open(loading.c_str(), O_WRONLY | O_CLOEXEC)) ; unique_fd data_fd (open(data.c_str(), O_WRONLY | O_CLOEXEC)) ; static const char * firmware_dirs[] = {"/etc/firmware/" , "/odm/firmware/" , "/vendor/firmware/" , "/firmware/image/" }; try_loading_again: for (size_t i = 0 ; i < arraysize (firmware_dirs); i++) { std::string file = firmware_dirs[i] + uevent.firmware; unique_fd fw_fd (open(file.c_str(), O_RDONLY | O_CLOEXEC)) ; struct stat sb; if (fw_fd != -1 && fstat (fw_fd, &sb) != -1 ) { LoadFirmware (uevent, root, fw_fd, sb.st_size, loading_fd, data_fd); return ; } } if (booting) { std::this_thread::sleep_for (100 ms); booting = IsBooting (); goto try_loading_again; } LOG (ERROR) << "firmware: could not find firmware for " << uevent.firmware; write (loading_fd, "-1" , 2 ); }
2.3.3.1 LoadFirmware
将固件下载到内核。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static void LoadFirmware (const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size, int loading_fd, int data_fd) { WriteFully (loading_fd, "1" , 1 ); int rc = sendfile (data_fd, fw_fd, nullptr , fw_size); if (rc == -1 ) { PLOG (ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent.firmware << "' }" ; } const char * response = (rc != -1 ) ? "0" : "-1" ; WriteFully (loading_fd, response, strlen (response)); }
2.3.4 总结
首先创建一个中间设备包含两个属性节点 loading 和 data
向上层发送 FIRMWARE 固件下载 event 事件
上层监听到 FIRMWARE 启动固件下载,依次遍历 firmware_dirs 数组中的目录找到固件。
首先往 loading 节点下发 1 表示准备下载,然后将固件通过 data 目录下发到内核,发完之后向 loading 节点写 0 表示下载完成。