diff --git a/sys/kern/subr_firmware.c b/sys/kern/subr_firmware.c --- a/sys/kern/subr_firmware.c +++ b/sys/kern/subr_firmware.c @@ -150,6 +150,19 @@ LIST_FOREACH(fp, &firmware_table, link) { if (fp->fw.name != NULL && strcasecmp(name, fp->fw.name) == 0) break; + + /* + * If the name looks like an absolute path, also try to match + * the last part of the string to the requested firmware if it + * matches the trailing components. This allows us to load + * /boot/firmware/abc/bca2233_fw.bin and match it against + * requests for bca2233_fw.bin or abc/bca2233_fw.bin. + */ + if (*fp->fw.name == '/' && strlen(fp->fw.name) > strlen(name)) { + const char *p = fp->fw.name + strlen(fp->fw.name) - strlen(name); + if (p[-1] == '/' && strcasecmp(name, p) == 0) + break; + } } return (fp); } @@ -412,9 +425,9 @@ mtx_lock(&firmware_mtx); restart: - LIST_FOREACH(fp, &firmware_table, link) { - if (fp->file == NULL || fp->refcnt != 0 || - (fp->flags & FW_UNLOAD) == 0) + LIST_FOREACH_SAFE(fp, &firmware_table, link, tmp) { + if (((fp->flags & FW_DIRECT) == 0 && fp->file == NULL) || + fp->refcnt != 0 || (fp->flags & FW_UNLOAD) == 0) continue; /* @@ -442,6 +455,42 @@ mtx_unlock(&firmware_mtx); } +/* + * Find all the firmware that was loaded in the boot loader vis load -t firmware + * foo. There is only one firmware per file, it's the whole file, and there's + * no meaningful version passed in, so pass 0 for that. If version is needed by + * the consumer (and not just arbitrarily defined), the .ko version must be used + * instead. + */ +static void +firmware_raw_files(void) +{ + caddr_t file; + char *name; + const char *type; + const void *addr; + size_t size; + unsigned int version = 0; + const struct firmware *fw; + struct priv_fw *fp; + + file = 0; + for (;;) { + file = preload_search_next_name(file); + if (file == 0) + break; + type = (const char *)preload_search_info(file, MODINFO_TYPE); + if (type == NULL || strcmp(type, "firmware") != 0) + continue; + name = preload_search_info(file, MODINFO_NAME); + addr = preload_fetch_addr(file); + size = preload_fetch_size(file); + fw = firmware_register(name, addr, size, version, NULL); + fp = PRIV_FW(fw); + fp->refcnt++; /* Hold an extra reference so we never unload */ + } +} + /* * Module glue. */ @@ -460,6 +509,7 @@ /* NB: use our own loop routine that sets up context */ (void) taskqueue_start_threads(&firmware_tq, 1, PWAIT, "firmware taskq"); + firmware_raw_files(); if (rootvnode != NULL) { /* * Root is already mounted so we won't get an event;