Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firmware_loader: Add firmware-upload support

Extend the firmware subsystem to support a persistent sysfs interface that
userspace may use to initiate a firmware update. For example, FPGA based
PCIe cards load firmware and FPGA images from local FLASH when the card
boots. The images in FLASH may be updated with new images provided by the
user at his/her convenience.

A device driver may call firmware_upload_register() to expose persistent
"loading" and "data" sysfs files. These files are used in the same way as
the fallback sysfs "loading" and "data" files. When 0 is written to
"loading" to complete the write of firmware data, the data is transferred
to the lower-level driver using pre-registered call-back functions. The
data transfer is done in the context of a kernel worker thread.

Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
Reviewed-by: Tianfei zhang <tianfei.zhang@intel.com>
Tested-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
Signed-off-by: Russ Weight <russell.h.weight@intel.com>
Link: https://lore.kernel.org/r/20220421212204.36052-5-russell.h.weight@intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Russ Weight and committed by
Greg Kroah-Hartman
97730bbb e0c11a8b

+595 -12
+32
Documentation/ABI/testing/sysfs-class-firmware
··· 1 + What: /sys/class/firmware/.../data 2 + Date: July 2022 3 + KernelVersion: 5.19 4 + Contact: Russ Weight <russell.h.weight@intel.com> 5 + Description: The data sysfs file is used for firmware-fallback and for 6 + firmware uploads. Cat a firmware image to this sysfs file 7 + after you echo 1 to the loading sysfs file. When the firmware 8 + image write is complete, echo 0 to the loading sysfs file. This 9 + sequence will signal the completion of the firmware write and 10 + signal the lower-level driver that the firmware data is 11 + available. 12 + 13 + What: /sys/class/firmware/.../loading 14 + Date: July 2022 15 + KernelVersion: 5.19 16 + Contact: Russ Weight <russell.h.weight@intel.com> 17 + Description: The loading sysfs file is used for both firmware-fallback and 18 + for firmware uploads. Echo 1 onto the loading file to indicate 19 + you are writing a firmware file to the data sysfs node. Echo 20 + -1 onto this file to abort the data write or echo 0 onto this 21 + file to indicate that the write is complete. For firmware 22 + uploads, the zero value also triggers the transfer of the 23 + firmware data to the lower-level device driver. 24 + 25 + What: /sys/class/firmware/.../timeout 26 + Date: July 2022 27 + KernelVersion: 5.19 28 + Contact: Russ Weight <russell.h.weight@intel.com> 29 + Description: This file supports the timeout mechanism for firmware 30 + fallback. This file has no affect on firmware uploads. For 31 + more information on timeouts please see the documentation 32 + for firmware fallback.
+107
Documentation/driver-api/firmware/fw_upload.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + =================== 4 + Firmware Upload API 5 + =================== 6 + 7 + A device driver that registers with the firmware loader will expose 8 + persistent sysfs nodes to enable users to initiate firmware updates for 9 + that device. It is the responsibility of the device driver and/or the 10 + device itself to perform any validation on the data received. Firmware 11 + upload uses the same *loading* and *data* sysfs files described in the 12 + documentation for firmware fallback. 13 + 14 + Register for firmware upload 15 + ============================ 16 + 17 + A device driver registers for firmware upload by calling 18 + firmware_upload_register(). Among the parameter list is a name to 19 + identify the device under /sys/class/firmware. A user may initiate a 20 + firmware upload by echoing a 1 to the *loading* sysfs file for the target 21 + device. Next, the user writes the firmware image to the *data* sysfs 22 + file. After writing the firmware data, the user echos 0 to the *loading* 23 + sysfs file to signal completion. Echoing 0 to *loading* also triggers the 24 + transfer of the firmware to the lower-lever device driver in the context 25 + of a kernel worker thread. 26 + 27 + To use the firmware upload API, write a driver that implements a set of 28 + ops. The probe function calls firmware_upload_register() and the remove 29 + function calls firmware_upload_unregister() such as:: 30 + 31 + static const struct fw_upload_ops m10bmc_ops = { 32 + .prepare = m10bmc_sec_prepare, 33 + .write = m10bmc_sec_write, 34 + .poll_complete = m10bmc_sec_poll_complete, 35 + .cancel = m10bmc_sec_cancel, 36 + .cleanup = m10bmc_sec_cleanup, 37 + }; 38 + 39 + static int m10bmc_sec_probe(struct platform_device *pdev) 40 + { 41 + const char *fw_name, *truncate; 42 + struct m10bmc_sec *sec; 43 + struct fw_upload *fwl; 44 + unsigned int len; 45 + 46 + sec = devm_kzalloc(&pdev->dev, sizeof(*sec), GFP_KERNEL); 47 + if (!sec) 48 + return -ENOMEM; 49 + 50 + sec->dev = &pdev->dev; 51 + sec->m10bmc = dev_get_drvdata(pdev->dev.parent); 52 + dev_set_drvdata(&pdev->dev, sec); 53 + 54 + fw_name = dev_name(sec->dev); 55 + truncate = strstr(fw_name, ".auto"); 56 + len = (truncate) ? truncate - fw_name : strlen(fw_name); 57 + sec->fw_name = kmemdup_nul(fw_name, len, GFP_KERNEL); 58 + 59 + fwl = firmware_upload_register(sec->dev, sec->fw_name, &m10bmc_ops, sec); 60 + if (IS_ERR(fwl)) { 61 + dev_err(sec->dev, "Firmware Upload driver failed to start\n"); 62 + kfree(sec->fw_name); 63 + return PTR_ERR(fwl); 64 + } 65 + 66 + sec->fwl = fwl; 67 + return 0; 68 + } 69 + 70 + static int m10bmc_sec_remove(struct platform_device *pdev) 71 + { 72 + struct m10bmc_sec *sec = dev_get_drvdata(&pdev->dev); 73 + 74 + firmware_upload_unregister(sec->fwl); 75 + kfree(sec->fw_name); 76 + return 0; 77 + } 78 + 79 + firmware_upload_register 80 + ------------------------ 81 + .. kernel-doc:: drivers/base/firmware_loader/sysfs_upload.c 82 + :identifiers: firmware_upload_register 83 + 84 + firmware_upload_unregister 85 + -------------------------- 86 + .. kernel-doc:: drivers/base/firmware_loader/sysfs_upload.c 87 + :identifiers: firmware_upload_unregister 88 + 89 + Firmware Upload Ops 90 + ------------------- 91 + .. kernel-doc:: include/linux/firmware.h 92 + :identifiers: fw_upload_ops 93 + 94 + Firmware Upload Progress Codes 95 + ------------------------------ 96 + The following progress codes are used internally by the firmware loader: 97 + 98 + .. kernel-doc:: drivers/base/firmware_loader/sysfs_upload.h 99 + :identifiers: fw_upload_prog 100 + 101 + Firmware Upload Error Codes 102 + --------------------------- 103 + The following error codes may be returned by the driver ops in case of 104 + failure: 105 + 106 + .. kernel-doc:: include/linux/firmware.h 107 + :identifiers: fw_upload_err
+1
Documentation/driver-api/firmware/index.rst
··· 8 8 core 9 9 efi/index 10 10 request_firmware 11 + fw_upload 11 12 other_interfaces 12 13 13 14 .. only:: subproject and html
+14
drivers/base/firmware_loader/Kconfig
··· 202 202 203 203 If unsure, say Y. 204 204 205 + config FW_UPLOAD 206 + bool "Enable users to initiate firmware updates using sysfs" 207 + select FW_LOADER_SYSFS 208 + select FW_LOADER_PAGED_BUF 209 + help 210 + Enabling this option will allow device drivers to expose a persistent 211 + sysfs interface that allows firmware updates to be initiated from 212 + userspace. For example, FPGA based PCIe cards load firmware and FPGA 213 + images from local FLASH when the card boots. The images in FLASH may 214 + be updated with new images provided by the user. Enable this device 215 + to support cards that rely on user-initiated updates for firmware files. 216 + 217 + If unsure, say N. 218 + 205 219 endif # FW_LOADER 206 220 endmenu
+1
drivers/base/firmware_loader/Makefile
··· 7 7 firmware_class-$(CONFIG_FW_LOADER_USER_HELPER) += fallback.o 8 8 firmware_class-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += fallback_platform.o 9 9 firmware_class-$(CONFIG_FW_LOADER_SYSFS) += sysfs.o 10 + firmware_class-$(CONFIG_FW_UPLOAD) += sysfs_upload.o 10 11 11 12 obj-y += builtin/
+6
drivers/base/firmware_loader/firmware.h
··· 87 87 }; 88 88 89 89 extern struct mutex fw_lock; 90 + extern struct firmware_cache fw_cache; 90 91 91 92 static inline bool __fw_state_check(struct fw_priv *fw_priv, 92 93 enum fw_status status) ··· 160 159 return __fw_state_check(fw_priv, FW_STATUS_LOADING); 161 160 } 162 161 162 + int alloc_lookup_fw_priv(const char *fw_name, struct firmware_cache *fwc, 163 + struct fw_priv **fw_priv, void *dbuf, size_t size, 164 + size_t offset, u32 opt_flags); 163 165 int assign_fw(struct firmware *fw, struct device *device); 166 + void free_fw_priv(struct fw_priv *fw_priv); 167 + void fw_state_init(struct fw_priv *fw_priv); 164 168 165 169 #ifdef CONFIG_FW_LOADER 166 170 bool firmware_is_builtin(const struct firmware *fw);
+6 -10
drivers/base/firmware_loader/main.c
··· 92 92 * guarding for corner cases a global lock should be OK */ 93 93 DEFINE_MUTEX(fw_lock); 94 94 95 - static struct firmware_cache fw_cache; 95 + struct firmware_cache fw_cache; 96 96 97 - static void fw_state_init(struct fw_priv *fw_priv) 97 + void fw_state_init(struct fw_priv *fw_priv) 98 98 { 99 99 struct fw_state *fw_st = &fw_priv->fw_st; 100 100 ··· 164 164 } 165 165 166 166 /* Returns 1 for batching firmware requests with the same name */ 167 - static int alloc_lookup_fw_priv(const char *fw_name, 168 - struct firmware_cache *fwc, 169 - struct fw_priv **fw_priv, 170 - void *dbuf, 171 - size_t size, 172 - size_t offset, 173 - u32 opt_flags) 167 + int alloc_lookup_fw_priv(const char *fw_name, struct firmware_cache *fwc, 168 + struct fw_priv **fw_priv, void *dbuf, size_t size, 169 + size_t offset, u32 opt_flags) 174 170 { 175 171 struct fw_priv *tmp; 176 172 ··· 221 225 kfree(fw_priv); 222 226 } 223 227 224 - static void free_fw_priv(struct fw_priv *fw_priv) 228 + void free_fw_priv(struct fw_priv *fw_priv) 225 229 { 226 230 struct firmware_cache *fwc = fw_priv->fwc; 227 231 spin_lock(&fwc->lock);
+17 -2
drivers/base/firmware_loader/sysfs.c
··· 6 6 #include <linux/slab.h> 7 7 #include <linux/types.h> 8 8 9 - #include "firmware.h" 10 9 #include "sysfs.h" 10 + #include "sysfs_upload.h" 11 11 12 12 /* 13 13 * sysfs support for firmware loader ··· 94 94 { 95 95 struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); 96 96 97 + if (fw_sysfs->fw_upload_priv) { 98 + free_fw_priv(fw_sysfs->fw_priv); 99 + kfree(fw_sysfs->fw_upload_priv); 100 + } 97 101 kfree(fw_sysfs); 98 102 } 99 103 ··· 203 199 written = rc; 204 200 } else { 205 201 fw_state_done(fw_priv); 202 + 203 + /* 204 + * If this is a user-initiated firmware upload 205 + * then start the upload in a worker thread now. 206 + */ 207 + rc = fw_upload_start(fw_sysfs); 208 + if (rc) 209 + written = rc; 206 210 } 207 211 break; 208 212 } ··· 220 208 fallthrough; 221 209 case -1: 222 210 fw_load_abort(fw_sysfs); 211 + if (fw_sysfs->fw_upload_priv) 212 + fw_state_init(fw_sysfs->fw_priv); 213 + 223 214 break; 224 215 } 225 216 out: ··· 230 215 return written; 231 216 } 232 217 233 - static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store); 218 + DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store); 234 219 235 220 static void firmware_rw_data(struct fw_priv *fw_priv, char *buffer, 236 221 loff_t offset, size_t count, bool read)
+4
drivers/base/firmware_loader/sysfs.h
··· 4 4 5 5 #include <linux/device.h> 6 6 7 + #include "firmware.h" 8 + 7 9 MODULE_IMPORT_NS(FIRMWARE_LOADER_PRIVATE); 8 10 9 11 extern struct firmware_fallback_config fw_fallback_config; 12 + extern struct device_attribute dev_attr_loading; 10 13 11 14 #ifdef CONFIG_FW_LOADER_USER_HELPER 12 15 /** ··· 76 73 struct device dev; 77 74 struct fw_priv *fw_priv; 78 75 struct firmware *fw; 76 + void *fw_upload_priv; 79 77 }; 80 78 81 79 static inline struct fw_sysfs *to_fw_sysfs(struct device *dev)
+276
drivers/base/firmware_loader/sysfs_upload.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <linux/firmware.h> 4 + #include <linux/module.h> 5 + #include <linux/slab.h> 6 + 7 + #include "sysfs.h" 8 + #include "sysfs_upload.h" 9 + 10 + /* 11 + * Support for user-space to initiate a firmware upload to a device. 12 + */ 13 + 14 + static void fw_upload_update_progress(struct fw_upload_priv *fwlp, 15 + enum fw_upload_prog new_progress) 16 + { 17 + mutex_lock(&fwlp->lock); 18 + fwlp->progress = new_progress; 19 + mutex_unlock(&fwlp->lock); 20 + } 21 + 22 + static void fw_upload_set_error(struct fw_upload_priv *fwlp, 23 + enum fw_upload_err err_code) 24 + { 25 + mutex_lock(&fwlp->lock); 26 + fwlp->err_progress = fwlp->progress; 27 + fwlp->err_code = err_code; 28 + mutex_unlock(&fwlp->lock); 29 + } 30 + 31 + static void fw_upload_prog_complete(struct fw_upload_priv *fwlp) 32 + { 33 + mutex_lock(&fwlp->lock); 34 + fwlp->progress = FW_UPLOAD_PROG_IDLE; 35 + mutex_unlock(&fwlp->lock); 36 + } 37 + 38 + static void fw_upload_main(struct work_struct *work) 39 + { 40 + struct fw_upload_priv *fwlp; 41 + struct fw_sysfs *fw_sysfs; 42 + u32 written = 0, offset = 0; 43 + enum fw_upload_err ret; 44 + struct device *fw_dev; 45 + struct fw_upload *fwl; 46 + 47 + fwlp = container_of(work, struct fw_upload_priv, work); 48 + fwl = fwlp->fw_upload; 49 + fw_sysfs = (struct fw_sysfs *)fwl->priv; 50 + fw_dev = &fw_sysfs->dev; 51 + 52 + fw_upload_update_progress(fwlp, FW_UPLOAD_PROG_PREPARING); 53 + ret = fwlp->ops->prepare(fwl, fwlp->data, fwlp->remaining_size); 54 + if (ret != FW_UPLOAD_ERR_NONE) { 55 + fw_upload_set_error(fwlp, ret); 56 + goto putdev_exit; 57 + } 58 + 59 + fw_upload_update_progress(fwlp, FW_UPLOAD_PROG_TRANSFERRING); 60 + while (fwlp->remaining_size) { 61 + ret = fwlp->ops->write(fwl, fwlp->data, offset, 62 + fwlp->remaining_size, &written); 63 + if (ret != FW_UPLOAD_ERR_NONE || !written) { 64 + if (ret == FW_UPLOAD_ERR_NONE) { 65 + dev_warn(fw_dev, "write-op wrote zero data\n"); 66 + ret = FW_UPLOAD_ERR_RW_ERROR; 67 + } 68 + fw_upload_set_error(fwlp, ret); 69 + goto done; 70 + } 71 + 72 + fwlp->remaining_size -= written; 73 + offset += written; 74 + } 75 + 76 + fw_upload_update_progress(fwlp, FW_UPLOAD_PROG_PROGRAMMING); 77 + ret = fwlp->ops->poll_complete(fwl); 78 + if (ret != FW_UPLOAD_ERR_NONE) 79 + fw_upload_set_error(fwlp, ret); 80 + 81 + done: 82 + if (fwlp->ops->cleanup) 83 + fwlp->ops->cleanup(fwl); 84 + 85 + putdev_exit: 86 + put_device(fw_dev->parent); 87 + 88 + /* 89 + * Note: fwlp->remaining_size is left unmodified here to provide 90 + * additional information on errors. It will be reinitialized when 91 + * the next firmeware upload begins. 92 + */ 93 + mutex_lock(&fw_lock); 94 + fw_free_paged_buf(fw_sysfs->fw_priv); 95 + fw_state_init(fw_sysfs->fw_priv); 96 + mutex_unlock(&fw_lock); 97 + fwlp->data = NULL; 98 + fw_upload_prog_complete(fwlp); 99 + } 100 + 101 + /* 102 + * Start a worker thread to upload data to the parent driver. 103 + * Must be called with fw_lock held. 104 + */ 105 + int fw_upload_start(struct fw_sysfs *fw_sysfs) 106 + { 107 + struct fw_priv *fw_priv = fw_sysfs->fw_priv; 108 + struct device *fw_dev = &fw_sysfs->dev; 109 + struct fw_upload_priv *fwlp; 110 + 111 + if (!fw_sysfs->fw_upload_priv) 112 + return 0; 113 + 114 + if (!fw_priv->size) { 115 + fw_free_paged_buf(fw_priv); 116 + fw_state_init(fw_sysfs->fw_priv); 117 + return 0; 118 + } 119 + 120 + fwlp = fw_sysfs->fw_upload_priv; 121 + mutex_lock(&fwlp->lock); 122 + 123 + /* Do not interfere with an on-going fw_upload */ 124 + if (fwlp->progress != FW_UPLOAD_PROG_IDLE) { 125 + mutex_unlock(&fwlp->lock); 126 + return -EBUSY; 127 + } 128 + 129 + get_device(fw_dev->parent); /* released in fw_upload_main */ 130 + 131 + fwlp->progress = FW_UPLOAD_PROG_RECEIVING; 132 + fwlp->err_code = 0; 133 + fwlp->remaining_size = fw_priv->size; 134 + fwlp->data = fw_priv->data; 135 + 136 + pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n", 137 + __func__, fw_priv->fw_name, 138 + fw_priv, fw_priv->data, 139 + (unsigned int)fw_priv->size); 140 + 141 + queue_work(system_long_wq, &fwlp->work); 142 + mutex_unlock(&fwlp->lock); 143 + 144 + return 0; 145 + } 146 + 147 + /** 148 + * firmware_upload_register() - register for the firmware upload sysfs API 149 + * @parent: parent device instantiating firmware upload 150 + * @name: firmware name to be associated with this device 151 + * @ops: pointer to structure of firmware upload ops 152 + * @dd_handle: pointer to parent driver private data 153 + * 154 + * @name must be unique among all users of firmware upload. The firmware 155 + * sysfs files for this device will be found at /sys/class/firmware/@name. 156 + * 157 + * Return: struct fw_upload pointer or ERR_PTR() 158 + * 159 + **/ 160 + struct fw_upload * 161 + firmware_upload_register(struct module *module, struct device *parent, 162 + const char *name, const struct fw_upload_ops *ops, 163 + void *dd_handle) 164 + { 165 + u32 opt_flags = FW_OPT_NOCACHE; 166 + struct fw_upload *fw_upload; 167 + struct fw_upload_priv *fw_upload_priv; 168 + struct fw_sysfs *fw_sysfs; 169 + struct fw_priv *fw_priv; 170 + struct device *fw_dev; 171 + int ret; 172 + 173 + if (!name || name[0] == '\0') 174 + return ERR_PTR(-EINVAL); 175 + 176 + if (!ops || !ops->cancel || !ops->prepare || 177 + !ops->write || !ops->poll_complete) { 178 + dev_err(parent, "Attempt to register without all required ops\n"); 179 + return ERR_PTR(-EINVAL); 180 + } 181 + 182 + if (!try_module_get(module)) 183 + return ERR_PTR(-EFAULT); 184 + 185 + fw_upload = kzalloc(sizeof(*fw_upload), GFP_KERNEL); 186 + if (!fw_upload) { 187 + ret = -ENOMEM; 188 + goto exit_module_put; 189 + } 190 + 191 + fw_upload_priv = kzalloc(sizeof(*fw_upload_priv), GFP_KERNEL); 192 + if (!fw_upload_priv) { 193 + ret = -ENOMEM; 194 + goto free_fw_upload; 195 + } 196 + 197 + fw_upload_priv->fw_upload = fw_upload; 198 + fw_upload_priv->ops = ops; 199 + mutex_init(&fw_upload_priv->lock); 200 + fw_upload_priv->module = module; 201 + fw_upload_priv->name = name; 202 + fw_upload_priv->err_code = 0; 203 + fw_upload_priv->progress = FW_UPLOAD_PROG_IDLE; 204 + INIT_WORK(&fw_upload_priv->work, fw_upload_main); 205 + fw_upload->dd_handle = dd_handle; 206 + 207 + fw_sysfs = fw_create_instance(NULL, name, parent, opt_flags); 208 + if (IS_ERR(fw_sysfs)) { 209 + ret = PTR_ERR(fw_sysfs); 210 + goto free_fw_upload_priv; 211 + } 212 + fw_upload->priv = fw_sysfs; 213 + fw_sysfs->fw_upload_priv = fw_upload_priv; 214 + fw_dev = &fw_sysfs->dev; 215 + 216 + ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, NULL, 0, 0, 217 + FW_OPT_NOCACHE); 218 + if (ret != 0) { 219 + if (ret > 0) 220 + ret = -EINVAL; 221 + goto free_fw_sysfs; 222 + } 223 + fw_priv->is_paged_buf = true; 224 + fw_sysfs->fw_priv = fw_priv; 225 + 226 + ret = device_add(fw_dev); 227 + if (ret) { 228 + dev_err(fw_dev, "%s: device_register failed\n", __func__); 229 + put_device(fw_dev); 230 + goto exit_module_put; 231 + } 232 + 233 + return fw_upload; 234 + 235 + free_fw_sysfs: 236 + kfree(fw_sysfs); 237 + 238 + free_fw_upload_priv: 239 + kfree(fw_upload_priv); 240 + 241 + free_fw_upload: 242 + kfree(fw_upload); 243 + 244 + exit_module_put: 245 + module_put(module); 246 + 247 + return ERR_PTR(ret); 248 + } 249 + EXPORT_SYMBOL_GPL(firmware_upload_register); 250 + 251 + /** 252 + * firmware_upload_unregister() - Unregister firmware upload interface 253 + * @fw_upload: pointer to struct fw_upload 254 + **/ 255 + void firmware_upload_unregister(struct fw_upload *fw_upload) 256 + { 257 + struct fw_sysfs *fw_sysfs = fw_upload->priv; 258 + struct fw_upload_priv *fw_upload_priv = fw_sysfs->fw_upload_priv; 259 + 260 + mutex_lock(&fw_upload_priv->lock); 261 + if (fw_upload_priv->progress == FW_UPLOAD_PROG_IDLE) { 262 + mutex_unlock(&fw_upload_priv->lock); 263 + goto unregister; 264 + } 265 + 266 + fw_upload_priv->ops->cancel(fw_upload); 267 + mutex_unlock(&fw_upload_priv->lock); 268 + 269 + /* Ensure lower-level device-driver is finished */ 270 + flush_work(&fw_upload_priv->work); 271 + 272 + unregister: 273 + device_unregister(&fw_sysfs->dev); 274 + module_put(fw_upload_priv->module); 275 + } 276 + EXPORT_SYMBOL_GPL(firmware_upload_unregister);
+49
drivers/base/firmware_loader/sysfs_upload.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef __FIRMWARE_UPLOAD_H 3 + #define __FIRMWARE_UPLOAD_H 4 + 5 + #include <linux/device.h> 6 + 7 + /** 8 + * enum fw_upload_prog - firmware upload progress codes 9 + * @FW_UPLOAD_PROG_IDLE: there is no firmware upload in progress 10 + * @FW_UPLOAD_PROG_RECEIVING: worker thread is receiving firmware data 11 + * @FW_UPLOAD_PROG_PREPARING: target device is preparing for firmware upload 12 + * @FW_UPLOAD_PROG_TRANSFERRING: data is being copied to the device 13 + * @FW_UPLOAD_PROG_PROGRAMMING: device is performing the firmware update 14 + * @FW_UPLOAD_PROG_MAX: Maximum progress code marker 15 + */ 16 + enum fw_upload_prog { 17 + FW_UPLOAD_PROG_IDLE, 18 + FW_UPLOAD_PROG_RECEIVING, 19 + FW_UPLOAD_PROG_PREPARING, 20 + FW_UPLOAD_PROG_TRANSFERRING, 21 + FW_UPLOAD_PROG_PROGRAMMING, 22 + FW_UPLOAD_PROG_MAX 23 + }; 24 + 25 + struct fw_upload_priv { 26 + struct fw_upload *fw_upload; 27 + struct module *module; 28 + const char *name; 29 + const struct fw_upload_ops *ops; 30 + struct mutex lock; /* protect data structure contents */ 31 + struct work_struct work; 32 + const u8 *data; /* pointer to update data */ 33 + u32 remaining_size; /* size remaining to transfer */ 34 + enum fw_upload_prog progress; 35 + enum fw_upload_prog err_progress; /* progress at time of failure */ 36 + enum fw_upload_err err_code; /* security manager error code */ 37 + }; 38 + 39 + #ifdef CONFIG_FW_UPLOAD 40 + int fw_upload_start(struct fw_sysfs *fw_sysfs); 41 + umode_t fw_upload_is_visible(struct kobject *kobj, struct attribute *attr, int n); 42 + #else 43 + static inline int fw_upload_start(struct fw_sysfs *fw_sysfs) 44 + { 45 + return 0; 46 + } 47 + #endif 48 + 49 + #endif /* __FIRMWARE_UPLOAD_H */
+82
include/linux/firmware.h
··· 17 17 void *priv; 18 18 }; 19 19 20 + /** 21 + * enum fw_upload_err - firmware upload error codes 22 + * @FW_UPLOAD_ERR_NONE: returned to indicate success 23 + * @FW_UPLOAD_ERR_HW_ERROR: error signalled by hardware, see kernel log 24 + * @FW_UPLOAD_ERR_TIMEOUT: SW timed out on handshake with HW/firmware 25 + * @FW_UPLOAD_ERR_CANCELED: upload was cancelled by the user 26 + * @FW_UPLOAD_ERR_BUSY: there is an upload operation already in progress 27 + * @FW_UPLOAD_ERR_INVALID_SIZE: invalid firmware image size 28 + * @FW_UPLOAD_ERR_RW_ERROR: read or write to HW failed, see kernel log 29 + * @FW_UPLOAD_ERR_WEAROUT: FLASH device is approaching wear-out, wait & retry 30 + * @FW_UPLOAD_ERR_MAX: Maximum error code marker 31 + */ 32 + enum fw_upload_err { 33 + FW_UPLOAD_ERR_NONE, 34 + FW_UPLOAD_ERR_HW_ERROR, 35 + FW_UPLOAD_ERR_TIMEOUT, 36 + FW_UPLOAD_ERR_CANCELED, 37 + FW_UPLOAD_ERR_BUSY, 38 + FW_UPLOAD_ERR_INVALID_SIZE, 39 + FW_UPLOAD_ERR_RW_ERROR, 40 + FW_UPLOAD_ERR_WEAROUT, 41 + FW_UPLOAD_ERR_MAX 42 + }; 43 + 44 + struct fw_upload { 45 + void *dd_handle; /* reference to parent driver */ 46 + void *priv; /* firmware loader private fields */ 47 + }; 48 + 49 + /** 50 + * struct fw_upload_ops - device specific operations to support firmware upload 51 + * @prepare: Required: Prepare secure update 52 + * @write: Required: The write() op receives the remaining 53 + * size to be written and must return the actual 54 + * size written or a negative error code. The write() 55 + * op will be called repeatedly until all data is 56 + * written. 57 + * @poll_complete: Required: Check for the completion of the 58 + * HW authentication/programming process. 59 + * @cancel: Required: Request cancellation of update. This op 60 + * is called from the context of a different kernel 61 + * thread, so race conditions need to be considered. 62 + * @cleanup: Optional: Complements the prepare() 63 + * function and is called at the completion 64 + * of the update, on success or failure, if the 65 + * prepare function succeeded. 66 + */ 67 + struct fw_upload_ops { 68 + enum fw_upload_err (*prepare)(struct fw_upload *fw_upload, 69 + const u8 *data, u32 size); 70 + enum fw_upload_err (*write)(struct fw_upload *fw_upload, 71 + const u8 *data, u32 offset, 72 + u32 size, u32 *written); 73 + enum fw_upload_err (*poll_complete)(struct fw_upload *fw_upload); 74 + void (*cancel)(struct fw_upload *fw_upload); 75 + void (*cleanup)(struct fw_upload *fw_upload); 76 + }; 77 + 20 78 struct module; 21 79 struct device; 22 80 ··· 166 108 void *buf, size_t size, size_t offset) 167 109 { 168 110 return -EINVAL; 111 + } 112 + 113 + #endif 114 + 115 + #ifdef CONFIG_FW_UPLOAD 116 + 117 + struct fw_upload * 118 + firmware_upload_register(struct module *module, struct device *parent, 119 + const char *name, const struct fw_upload_ops *ops, 120 + void *dd_handle); 121 + void firmware_upload_unregister(struct fw_upload *fw_upload); 122 + 123 + #else 124 + 125 + static inline struct fw_upload * 126 + firmware_upload_register(struct module *module, struct device *parent, 127 + const char *name, const struct fw_upload_ops *ops, 128 + void *dd_handle) 129 + { 130 + return ERR_PTR(-EINVAL); 131 + } 132 + 133 + static inline void firmware_upload_unregister(struct fw_upload *fw_upload) 134 + { 169 135 } 170 136 171 137 #endif