From: Yi Li <yi1...@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...@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

Reply via email to