mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-28 20:02:17 +00:00
![Russ Weight](/assets/img/avatar_default.png)
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>
199 lines
5.7 KiB
C
199 lines
5.7 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _LINUX_FIRMWARE_H
|
|
#define _LINUX_FIRMWARE_H
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/gfp.h>
|
|
|
|
#define FW_ACTION_NOUEVENT 0
|
|
#define FW_ACTION_UEVENT 1
|
|
|
|
struct firmware {
|
|
size_t size;
|
|
const u8 *data;
|
|
|
|
/* firmware loader private fields */
|
|
void *priv;
|
|
};
|
|
|
|
/**
|
|
* enum fw_upload_err - firmware upload error codes
|
|
* @FW_UPLOAD_ERR_NONE: returned to indicate success
|
|
* @FW_UPLOAD_ERR_HW_ERROR: error signalled by hardware, see kernel log
|
|
* @FW_UPLOAD_ERR_TIMEOUT: SW timed out on handshake with HW/firmware
|
|
* @FW_UPLOAD_ERR_CANCELED: upload was cancelled by the user
|
|
* @FW_UPLOAD_ERR_BUSY: there is an upload operation already in progress
|
|
* @FW_UPLOAD_ERR_INVALID_SIZE: invalid firmware image size
|
|
* @FW_UPLOAD_ERR_RW_ERROR: read or write to HW failed, see kernel log
|
|
* @FW_UPLOAD_ERR_WEAROUT: FLASH device is approaching wear-out, wait & retry
|
|
* @FW_UPLOAD_ERR_MAX: Maximum error code marker
|
|
*/
|
|
enum fw_upload_err {
|
|
FW_UPLOAD_ERR_NONE,
|
|
FW_UPLOAD_ERR_HW_ERROR,
|
|
FW_UPLOAD_ERR_TIMEOUT,
|
|
FW_UPLOAD_ERR_CANCELED,
|
|
FW_UPLOAD_ERR_BUSY,
|
|
FW_UPLOAD_ERR_INVALID_SIZE,
|
|
FW_UPLOAD_ERR_RW_ERROR,
|
|
FW_UPLOAD_ERR_WEAROUT,
|
|
FW_UPLOAD_ERR_MAX
|
|
};
|
|
|
|
struct fw_upload {
|
|
void *dd_handle; /* reference to parent driver */
|
|
void *priv; /* firmware loader private fields */
|
|
};
|
|
|
|
/**
|
|
* struct fw_upload_ops - device specific operations to support firmware upload
|
|
* @prepare: Required: Prepare secure update
|
|
* @write: Required: The write() op receives the remaining
|
|
* size to be written and must return the actual
|
|
* size written or a negative error code. The write()
|
|
* op will be called repeatedly until all data is
|
|
* written.
|
|
* @poll_complete: Required: Check for the completion of the
|
|
* HW authentication/programming process.
|
|
* @cancel: Required: Request cancellation of update. This op
|
|
* is called from the context of a different kernel
|
|
* thread, so race conditions need to be considered.
|
|
* @cleanup: Optional: Complements the prepare()
|
|
* function and is called at the completion
|
|
* of the update, on success or failure, if the
|
|
* prepare function succeeded.
|
|
*/
|
|
struct fw_upload_ops {
|
|
enum fw_upload_err (*prepare)(struct fw_upload *fw_upload,
|
|
const u8 *data, u32 size);
|
|
enum fw_upload_err (*write)(struct fw_upload *fw_upload,
|
|
const u8 *data, u32 offset,
|
|
u32 size, u32 *written);
|
|
enum fw_upload_err (*poll_complete)(struct fw_upload *fw_upload);
|
|
void (*cancel)(struct fw_upload *fw_upload);
|
|
void (*cleanup)(struct fw_upload *fw_upload);
|
|
};
|
|
|
|
struct module;
|
|
struct device;
|
|
|
|
/*
|
|
* Built-in firmware functionality is only available if FW_LOADER=y, but not
|
|
* FW_LOADER=m
|
|
*/
|
|
#ifdef CONFIG_FW_LOADER
|
|
bool firmware_request_builtin(struct firmware *fw, const char *name);
|
|
#else
|
|
static inline bool firmware_request_builtin(struct firmware *fw,
|
|
const char *name)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#if IS_REACHABLE(CONFIG_FW_LOADER)
|
|
int request_firmware(const struct firmware **fw, const char *name,
|
|
struct device *device);
|
|
int firmware_request_nowarn(const struct firmware **fw, const char *name,
|
|
struct device *device);
|
|
int firmware_request_platform(const struct firmware **fw, const char *name,
|
|
struct device *device);
|
|
int request_firmware_nowait(
|
|
struct module *module, bool uevent,
|
|
const char *name, struct device *device, gfp_t gfp, void *context,
|
|
void (*cont)(const struct firmware *fw, void *context));
|
|
int request_firmware_direct(const struct firmware **fw, const char *name,
|
|
struct device *device);
|
|
int request_firmware_into_buf(const struct firmware **firmware_p,
|
|
const char *name, struct device *device, void *buf, size_t size);
|
|
int request_partial_firmware_into_buf(const struct firmware **firmware_p,
|
|
const char *name, struct device *device,
|
|
void *buf, size_t size, size_t offset);
|
|
|
|
void release_firmware(const struct firmware *fw);
|
|
#else
|
|
static inline int request_firmware(const struct firmware **fw,
|
|
const char *name,
|
|
struct device *device)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
static inline int firmware_request_nowarn(const struct firmware **fw,
|
|
const char *name,
|
|
struct device *device)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
static inline int firmware_request_platform(const struct firmware **fw,
|
|
const char *name,
|
|
struct device *device)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
static inline int request_firmware_nowait(
|
|
struct module *module, bool uevent,
|
|
const char *name, struct device *device, gfp_t gfp, void *context,
|
|
void (*cont)(const struct firmware *fw, void *context))
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
static inline void release_firmware(const struct firmware *fw)
|
|
{
|
|
}
|
|
|
|
static inline int request_firmware_direct(const struct firmware **fw,
|
|
const char *name,
|
|
struct device *device)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
static inline int request_firmware_into_buf(const struct firmware **firmware_p,
|
|
const char *name, struct device *device, void *buf, size_t size)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
static inline int request_partial_firmware_into_buf
|
|
(const struct firmware **firmware_p,
|
|
const char *name,
|
|
struct device *device,
|
|
void *buf, size_t size, size_t offset)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_FW_UPLOAD
|
|
|
|
struct fw_upload *
|
|
firmware_upload_register(struct module *module, struct device *parent,
|
|
const char *name, const struct fw_upload_ops *ops,
|
|
void *dd_handle);
|
|
void firmware_upload_unregister(struct fw_upload *fw_upload);
|
|
|
|
#else
|
|
|
|
static inline struct fw_upload *
|
|
firmware_upload_register(struct module *module, struct device *parent,
|
|
const char *name, const struct fw_upload_ops *ops,
|
|
void *dd_handle)
|
|
{
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
static inline void firmware_upload_unregister(struct fw_upload *fw_upload)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
int firmware_request_cache(struct device *device, const char *name);
|
|
|
|
#endif
|