diff options
Diffstat (limited to 'common/firmware.c')
-rw-r--r-- | common/firmware.c | 152 |
1 files changed, 136 insertions, 16 deletions
diff --git a/common/firmware.c b/common/firmware.c index 609cf11822..3c7960581f 100644 --- a/common/firmware.c +++ b/common/firmware.c @@ -1,14 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013 Juergen Beisert <kernel@pengutronix.de>, Pengutronix - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <firmware.h> @@ -19,9 +11,13 @@ #include <libbb.h> #include <libfile.h> #include <fs.h> +#include <globalvar.h> +#include <magicvar.h> #include <linux/list.h> #include <linux/stat.h> #include <linux/err.h> +#include <uncompress.h> +#include <filetype.h> #define BUFSIZ 4096 @@ -69,13 +65,25 @@ struct firmware_mgr *firmwaremgr_find(const char *id) * handler. This allows to retrieve the firmware handler with a phandle from * the device tree. */ -struct firmware_mgr *firmwaremgr_find_by_node(const struct device_node *np) +struct firmware_mgr *firmwaremgr_find_by_node(struct device_node *np) { struct firmware_mgr *mgr; + char *na, *nb; - list_for_each_entry(mgr, &firmwaremgr_list, list) - if (mgr->handler->dev->parent->device_node == np) + na = of_get_reproducible_name(np); + + list_for_each_entry(mgr, &firmwaremgr_list, list) { + nb = of_get_reproducible_name(mgr->handler->device_node); + if (!strcmp(na, nb)) { + free(na); + free(nb); return mgr; + } + + free(nb); + } + + free(na); return NULL; } @@ -214,17 +222,129 @@ out: return ret; } +static char *firmware_path; + +char *firmware_get_searchpath(void) +{ + return strdup(firmware_path); +} + +void firmware_set_searchpath(const char *path) +{ + free(firmware_path); + firmware_path = strdup(path); +} + +static bool file_exists(const char *filename) +{ + struct stat s; + + return !stat(filename, &s); +} + /* * firmware_load_file - load a firmware to a device */ int firmwaremgr_load_file(struct firmware_mgr *mgr, const char *firmware) { - int ret; - char *name = basprintf("/dev/%s", mgr->handler->id); + char *dst; + enum filetype type; + int ret = 0; + char *fw = NULL; + int firmwarefd = 0; + int devicefd = 0; + + if (!firmware) + return -EINVAL; + + if (!mgr->handler->id) { + pr_err("id not defined for handler\n"); + return -ENODEV; + } - ret = copy_file(firmware, name, 0); + dst = basprintf("/dev/%s", mgr->handler->id); - free(name); + if (*firmware != '/') { + fw = find_path(firmware_path, firmware, file_exists); + if (fw) + firmware = fw; + } + + firmwarefd = open(firmware, O_RDONLY); + if (firmwarefd < 0) { + printf("could not open %s: %m\n", firmware); + ret = firmwarefd; + goto out; + } + + ret = file_name_detect_type(firmware, &type); + if (ret) + goto out; + + devicefd = open(dst, O_WRONLY); + if (devicefd < 0) { + printf("could not open %s: %m\n", dst); + ret = devicefd; + goto out; + } + + if (file_is_compressed_file(type)) + ret = uncompress_fd_to_fd(firmwarefd, devicefd, + uncompress_err_stdout); + else + ret = copy_fd(firmwarefd, devicefd); + +out: + free(dst); + free(fw); + + if (firmwarefd > 0) + close(firmwarefd); + if (devicefd > 0) + close(devicefd); return ret; } + +/* + * request_firmware - load a firmware to a device + */ +int request_firmware(const struct firmware **out, const char *fw_name, struct device *dev) +{ + char fw_path[PATH_MAX + 1]; + struct firmware *fw; + int ret; + + fw = kzalloc(sizeof(struct firmware), GFP_KERNEL); + if (!fw) + return -ENOMEM; + + snprintf(fw_path, sizeof(fw_path), "%s/%s", firmware_path, fw_name); + + ret = read_file_2(fw_path, &fw->size, (void *)&fw->data, FILESIZE_MAX); + if (ret) { + kfree(fw); + return ret; + } + + *out = fw; + + return 0; +} + +void release_firmware(const struct firmware *fw) +{ + kfree_const(fw->data); + kfree_const(fw); +} + +static int firmware_init(void) +{ + firmware_path = strdup("/env/firmware"); + globalvar_add_simple_string("firmware.path", &firmware_path); + + return 0; +} +device_initcall(firmware_init); + +BAREBOX_MAGICVAR(global.firmware.path, "Firmware search path"); |