From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1425072AbdDUT1r (ORCPT ); Fri, 21 Apr 2017 15:27:47 -0400 Received: from mga01.intel.com ([192.55.52.88]:14850 "EHLO mga01.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1425031AbdDUT1n (ORCPT ); Fri, 21 Apr 2017 15:27:43 -0400 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.37,230,1488873600"; d="scan'208";a="959722758" From: yi1.li@linux.intel.com To: gregkh@linuxfoundation.org, wagi@monom.org, dwmw2@infradead.org, rafal@milecki.pl, arend.vanspriel@broadcom.com, rjw@rjwysocki.net, moritz.fischer@ettus.com, pmladek@suse.com, johannes.berg@intel.com, emmanuel.grumbach@intel.com, luciano.coelho@intel.com, kvalo@codeaurora.org, luto@kernel.org, takahiro.akashi@linaro.org, dhowells@redhat.com, pjones@redhat.com, mcgrof@kernel.org, atull@kernel.org Cc: linux-kernel@vger.kernel.org, linux-fpga@vger.kernel.org, Yi Li Subject: [PATCHv1 1/2] firmware: Add streaming support Date: Fri, 21 Apr 2017 14:22:21 -0500 Message-Id: <1492802542-1408-2-git-send-email-yi1.li@linux.intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1492802542-1408-1-git-send-email-yi1.li@linux.intel.com> References: <1492802542-1408-1-git-send-email-yi1.li@linux.intel.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Yi Li Load firmware in multiple chucks(4KB) instead of the whole big firmware file at once. The streaming support is added in the driver_data_request_sync API based on Luis R. Rodriguez's 20170329-driver-data-v2-try3 branch. Signed-off-by: Yi Li --- drivers/base/firmware_class.c | 119 +++++++++++++++++++++++++++++++++++++++++- include/linux/driver_data.h | 13 +++++ 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index cc3c224..beecbf5 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -569,6 +569,73 @@ fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf) return rc; } +static int +fw_stream_filesystem_firmware(struct device *device, struct firmware_buf *buf, + size_t offset, size_t length, char **path) +{ + int i, len; + int rc = 0; + struct file *file; + + buf->size = 0; + if (!buf->data) { + buf->data = vmalloc(length); + if (!buf->data) { + rc = -ENOMEM; + return rc; + } + } + + /* skip the repeating folder searching */ + if (*path) { + file = filp_open(*path, O_RDONLY, 0); + if (IS_ERR(file)) { + rc = -ENOENT; + return rc; + } + + buf->size = kernel_read(file, offset, (char *)buf->data, + length); + fput(file); + return rc; + } + + dev_info(device, "search %s in the folder\n", buf->fw_id); + *path = __getname(); + if (!*path) + 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", + fw_path[i], buf->fw_id); + if (len >= PATH_MAX) { + rc = -ENAMETOOLONG; + break; + } + + file = filp_open(*path, O_RDONLY, 0); + if (IS_ERR(file)) { + rc = -ENOENT; + continue; + } + + buf->size = kernel_read(file, offset, (char *)buf->data, + length); + rc = 0; + fput(file); + break; + } + + if (rc) + dev_err(device, "loading %s failed with error %d\n", + *path, rc); + return rc; +} + /* firmware holds the ownership of pages */ static void firmware_free_data(const struct firmware *fw) { @@ -1310,6 +1377,46 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name, return 1; /* need to load */ } +static int +_stream_firmware(const struct firmware **firmware_p, const char *name, + struct driver_data_params *data_params, + struct device *device) +{ + int ret; + struct firmware *fw = NULL; + size_t offset = data_params->req_params.streaming_reqs.offset; + size_t buf_size = data_params->req_params.streaming_reqs.buf_size; + + if ((!firmware_p) || (!name || name[0] == '\0')) { + dev_err(device, "invalid firmware pointer or file name\n"); + return -EINVAL; + } + + if (!*firmware_p) { + ret = _request_firmware_prepare(&fw, name, device, data_params); + if (ret <= 0) { + dev_err(device, "%s: failed %d\n", + __func__, ret); + } + } else { + fw = (struct firmware *)*firmware_p; + } + + ret = fw_stream_filesystem_firmware(device, fw->priv, offset, buf_size, + data_params->req_params.streaming_reqs.path); + if (ret) { + if (!driver_data_param_optional(&data_params->req_params)) + dev_warn(device, + "streaming %s failed with error %d\n", + name, ret); + } else { + ret = assign_firmware_buf(fw, device, data_params); + } + *firmware_p = fw; + + return ret; +} + /* called from request_firmware() and request_firmware_work_func() */ static int _request_firmware(const struct firmware **firmware_p, const char *name, @@ -1577,7 +1684,17 @@ int driver_data_request_sync(const char *name, __module_get(sync_reqs->module); get_device(device); - ret = _request_firmware(&driver_data, name, ¶ms, device); + if (req_params->streaming_reqs.streaming) { + /* For streaming, driver_data is recycled and kept */ + if (!req_params->keep) + return -EINVAL; + + ret = _stream_firmware(req_params->streaming_reqs.driver_data, + name, ¶ms, device); + driver_data = *req_params->streaming_reqs.driver_data; + } else { + ret = _request_firmware(&driver_data, name, ¶ms, device); + } if (ret && driver_data_param_optional(req_params)) ret = driver_data_sync_opt_call_cb(req_params); else diff --git a/include/linux/driver_data.h b/include/linux/driver_data.h index fda1e71..ee485a5 100644 --- a/include/linux/driver_data.h +++ b/include/linux/driver_data.h @@ -62,6 +62,16 @@ struct driver_data_reqs { gfp_t gfp; }; +struct driver_data_streaming_reqs { + bool streaming; + char **path; + size_t offset; + size_t buf_size; + const struct firmware **driver_data; + void *opt_ctx1; + void *opt_ctx2; +}; + /** * struct driver_data_req_params - driver data request parameters * @optional: if true it is not a hard requirement by the caller that this @@ -93,6 +103,8 @@ struct driver_data_reqs { * digit will be placed in the middle, followed by the @api_name_postfix. * @sync_reqs: synchronization requirements * + * @streaming_reqs: streaming requirements + * * 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 * caller. @@ -107,6 +119,7 @@ struct driver_data_req_params { const char *api_name_postfix; struct driver_data_reqs sync_reqs; const union driver_data_cbs cbs; + struct driver_data_streaming_reqs streaming_reqs; }; /* -- 2.7.4