summaryrefslogtreecommitdiffstats
path: root/common/firmware.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/firmware.c')
-rw-r--r--common/firmware.c152
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");