Messages in this thread Patch in this message | | | From | yi1.li@linux ... | Subject | [PATCHv2 1/3] firmware: Add streaming support for driver_data_request_sync API | Date | Sat, 20 May 2017 01:49:06 -0500 |
| |
From: Yi Li <yi1.li@linux.intel.com>
By setting the driver_data_req_params req flag of DRIVER_DATA_REQ_STREAMING and DRIVER_DATA_REQ_NO_CACHE, caller can streaming firmware image to the pre-allocated buffer in small trunks. Caller also need to setup the img_offset pointer and firmware image **path to avoid searching the firmware folders repeatly. Details and examples please refer to the trigger_config_stream function of test_driver_data.c and fpga_mgr_firmware_stream function of fpga_mgr.c.
Signed-off-by: Yi Li <yi1.li@linux.intel.com> --- drivers/base/firmware_class.c | 94 +++++++++++++++++++++++++++++++++++++------ include/linux/driver_data.h | 10 +++++ 2 files changed, 91 insertions(+), 13 deletions(-)
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 461c7c2..9a63124 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -152,6 +152,20 @@ struct driver_data_params { DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT, \ } +#define __DATA_REQ_FIRMWARE_STREAM_BUF(buf, size, offset, path) \ + .req_params = { \ + .reqs = DRIVER_DATA_REQ_NO_CACHE | \ + DRIVER_DATA_REQ_STREAMING, \ + .alloc_buf = buf, \ + .alloc_buf_size = size, \ + .img_offset = offset, \ + .path = path, \ + }, \ + .priv_params = { \ + .priv_reqs = DRIVER_DATA_PRIV_REQ_FALLBACK | \ + DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT, \ + } + #define __DATA_REQ_FIRMWARE_NOWAIT(module, uevent, gfp, async_cb, async_ctx) \ .req_params = { \ .hold_module = module, \ @@ -180,6 +194,8 @@ struct driver_data_params { (!!((params)->priv_reqs & DRIVER_DATA_PRIV_REQ_FALLBACK_UEVENT)) #define driver_data_param_nocache(params) \ (!!((params)->reqs & DRIVER_DATA_REQ_NO_CACHE)) +#define driver_data_param_streaming(params) \ + (!!((params)->reqs & DRIVER_DATA_REQ_STREAMING)) #define driver_data_param_optional(params) \ (!!((params)->reqs & DRIVER_DATA_REQ_OPTIONAL)) @@ -625,14 +641,18 @@ module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644); MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path"); static int -fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf) +fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf, + struct driver_data_params *data_params) { loff_t size; int i, len; int rc = -ENOENT; - char *path; + char **path; + char *local_path = NULL; + loff_t *offset = data_params->req_params.img_offset; enum kernel_read_file_id id = READING_FIRMWARE; size_t msize = INT_MAX; + struct file *file; /* Already populated data member means we're loading into a buffer */ if (buf->data) { @@ -640,16 +660,58 @@ fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf) msize = buf->allocated_size; } - path = __getname(); - if (!path) + buf->size = 0; + + /* Save path for streaming case */ + if (driver_data_param_streaming(&data_params->req_params)) { + path = data_params->req_params.path; + if (!path) { + dev_err(device, "req_params.path not initialized\n"); + rc = -ENOENT; + return rc; + } + } else { + path = &local_path; + } + +streaming: + /* Skip the repeating folder searching, direct to load*/ + if (driver_data_param_streaming(&data_params->req_params) && *path) { + if (!offset) { + dev_err(device, "img_offset not initialized\n"); + rc = -ENOENT; + return rc; + } + + file = filp_open(*path, O_RDONLY, 0); + if (IS_ERR(file)) { + rc = -ENOENT; + return rc; + } + + buf->size = kernel_read(file, *offset, (char *)buf->data, + msize); + fput(file); + if (buf->size < msize) { + fw_state_done(&buf->fw_st); + __putname(*path); + } + return 0; + } + + /* First time to load the firmware */ + *path = __getname(); + if (!*path) { + dev_err(device, "cannot getname\n"); return -ENOMEM; + } for (i = 0; i < ARRAY_SIZE(fw_path); i++) { /* skip the unset customized path */ if (!fw_path[i][0]) continue; - len = snprintf(path, PATH_MAX, "%s/%s", + len = snprintf(*path, PATH_MAX, "%s/%s", fw_path[i], buf->fw_id); if (len >= PATH_MAX) { rc = -ENAMETOOLONG; @@ -657,23 +719,29 @@ fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf) } buf->size = 0; - rc = kernel_read_file_from_path(path, &buf->data, &size, msize, - id); + rc = kernel_read_file_from_path(*path, &buf->data, &size, + msize, id); if (rc) { - if (rc == -ENOENT) + if (driver_data_param_streaming(&data_params->req_params) + && (rc == -EFBIG)) { + rc = 0; + goto streaming; + } else if (rc == -ENOENT) dev_dbg(device, "loading %s failed with error %d\n", - path, rc); + *path, rc); else dev_warn(device, "loading %s failed with error %d\n", - path, rc); + *path, rc); continue; } dev_dbg(device, "direct-loading %s\n", buf->fw_id); buf->size = size; - fw_state_done(&buf->fw_st); + if (buf->size < msize) { + fw_state_done(&buf->fw_st); + __putname(*path); + } break; } - __putname(path); return rc; } @@ -1466,7 +1534,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name, goto out; } - ret = fw_get_filesystem_firmware(device, fw->priv); + ret = fw_get_filesystem_firmware(device, fw->priv, data_params); if (ret) { if (!driver_data_param_optional(&data_params->req_params)) dev_warn(device, diff --git a/include/linux/driver_data.h b/include/linux/driver_data.h index a3a5fbe..e9e828a 100644 --- a/include/linux/driver_data.h +++ b/include/linux/driver_data.h @@ -120,12 +120,16 @@ union driver_data_cbs { * @DRIVER_DATA_REQ_NO_CACHE: indicates that the driver data request * should not set up and use the internal caching mechanism to assist * drivers from fetching driver data at resume time after suspend. + * @DRIVER_DATA_REQ_STREAMING: indicates that the driver data request + * is in the streaming mode, the buffer is allocated outside the + * firmware driver. */ enum driver_data_reqs { DRIVER_DATA_REQ_OPTIONAL = 1 << 0, DRIVER_DATA_REQ_KEEP = 1 << 1, DRIVER_DATA_REQ_USE_API_VERSIONING = 1 << 2, DRIVER_DATA_REQ_NO_CACHE = 1 << 3, + DRIVER_DATA_REQ_STREAMING = 1 << 4, }; /** @@ -147,6 +151,10 @@ enum driver_data_reqs { * @alloc_buf: pointer of pointer to the buffer area allocated by the caller * so we can place the respective driver data * @alloc_buf_size: size of the @alloc_buf + * @img_offset: optional for streaming mode, define which offset address the + * firmware should be read from + * @path: optional for streaming mode, save the file path to avoid search over + * and over again * * This data structure is intended to carry all requirements and specifications * required to complete the task to get the requested driver date file to the @@ -162,6 +170,8 @@ struct driver_data_req_params { const union driver_data_cbs cbs; void **alloc_buf; size_t alloc_buf_size; + loff_t *img_offset; + char **path; }; /* -- 2.7.4
| |