From patchwork Fri Mar 10 00:18:09 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Li, Yi" X-Patchwork-Id: 9614381 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 9A282602B4 for ; Fri, 10 Mar 2017 00:22:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8B5D8286B4 for ; Fri, 10 Mar 2017 00:22:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 7F30A286E5; Fri, 10 Mar 2017 00:22:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 16AFC286B4 for ; Fri, 10 Mar 2017 00:22:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754444AbdCJAWk (ORCPT ); Thu, 9 Mar 2017 19:22:40 -0500 Received: from mga04.intel.com ([192.55.52.120]:56023 "EHLO mga04.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752157AbdCJAWj (ORCPT ); Thu, 9 Mar 2017 19:22:39 -0500 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga104.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Mar 2017 16:22:37 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.36,137,1486454400"; d="scan'208";a="1120701752" Received: from yi-patch.an.intel.com ([10.122.105.158]) by fmsmga001.fm.intel.com with ESMTP; 09 Mar 2017 16:22:37 -0800 From: yi1.li@linux.intel.com To: ming.lei@canonical.com, mcgrof@kernel.org, gregkh@linuxfoundation.org, atull@opensource.altera.com, moritz.fischer@ettus.com, linux-kernel@vger.kernel.org, linux-fpga@vger.kernel.org Cc: Yi Li Subject: [RFC 1/2] firmware class: Add stream_firmware API. Date: Thu, 9 Mar 2017 18:18:09 -0600 Message-Id: <1489105090-4996-2-git-send-email-yi1.li@linux.intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1489105090-4996-1-git-send-email-yi1.li@linux.intel.com> References: <1489105090-4996-1-git-send-email-yi1.li@linux.intel.com> Sender: linux-fpga-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fpga@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Yi Li Add function to load firmware in multiple chucks instead of loading the whole big firmware file at once. Signed-off-by: Yi Li --- drivers/base/firmware_class.c | 128 ++++++++++++++++++++++++++++++++++++++++++ include/linux/firmware.h | 2 + 2 files changed, 130 insertions(+) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index ac350c5..44fddff 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -436,6 +436,62 @@ 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) +{ + int i, len; + char *path; + int rc = 0; + struct file *file; + + buf->size = 0; + + 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; + } + + if (!path || !*path) + continue; + + if (!buf->data) { + buf->data = vmalloc(length); + if (!buf->data) { + rc = -ENOMEM; + break; + } + } + + file = filp_open(path, O_RDONLY, 0); + if (IS_ERR(file)) + continue; + + buf->size = kernel_read(file, offset, (char *) buf->data, + length); + fput(file); + break; + } + + __putname(path); + + 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) { @@ -1267,6 +1323,78 @@ request_firmware(const struct firmware **firmware_p, const char *name, } EXPORT_SYMBOL(request_firmware); +static int +_stream_firmware(const struct firmware **firmware_p, const char *name, + struct device *device, void *buf, size_t size, + unsigned int opt_flags, size_t offset, size_t length) +{ + int ret; + struct firmware *fw = NULL; + struct firmware_buf *fbuf; + + 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, buf, size); + if (ret <= 0) { + dev_err(device, "%s: _request_firmware_prepare failed %d\n", + __func__, ret); + } + } else { + fw = (struct firmware *) *firmware_p; + } + + fbuf = (struct firmware_buf *) fw->priv; + ret = fw_stream_filesystem_firmware(device, fbuf, offset, length); + fw->size = fbuf->size; + fw->data = fbuf->data; + *firmware_p = fw; + + if (ret) + dev_err(device, "streaming with error %d\n", ret); + return ret; +} + +/** + * stream_firmware: - send firmware request and wait for it + * @firmware_p: pointer to firmware image + * @name: name of firmware file + * @device: device for which firmware is being loaded + * @offset: offset of the file to read from + * @length: length in bytes to read + * + * @firmware_p will be used to return a firmware image by the name + * of @name for device @device. + * + * Should be called from user context where sleeping is allowed. + * + * @name will be used as $FIRMWARE in the uevent environment and + * should be distinctive enough not to be confused with any other + * firmware image for this or any other device. + * + * Caller must hold the reference count of @device. + * + * The function can be called safely inside device's suspend and + * resume callback. + **/ +int +stream_firmware(const struct firmware **firmware_p, const char *name, + struct device *device, size_t offset, size_t length) +{ + size_t ret; + + /* Need to pin this module until return */ + __module_get(THIS_MODULE); + ret = _stream_firmware(firmware_p, name, device, NULL, 0, + FW_OPT_UEVENT | FW_OPT_NO_WARN, offset, length); + module_put(THIS_MODULE); + return ret; +} +EXPORT_SYMBOL(stream_firmware); + /** * request_firmware_direct: - load firmware directly without usermode helper * @firmware_p: pointer to firmware image diff --git a/include/linux/firmware.h b/include/linux/firmware.h index b1f9f0c..accd7f6 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -41,6 +41,8 @@ struct builtin_fw { #if defined(CONFIG_FW_LOADER) || (defined(CONFIG_FW_LOADER_MODULE) && defined(MODULE)) int request_firmware(const struct firmware **fw, const char *name, struct device *device); +int stream_firmware(const struct firmware **fw, const char *name, + struct device *device, size_t offset, size_t length); int request_firmware_nowait( struct module *module, bool uevent, const char *name, struct device *device, gfp_t gfp, void *context,