The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/compat/linuxkpi/common/src/linux_firmware.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause
    3  *
    4  * Copyright (c) 2020-2021 The FreeBSD Foundation
    5  * Copyright (c) 2022 Bjoern A. Zeeb
    6  *
    7  * This software was developed by Björn Zeeb under sponsorship from
    8  * the FreeBSD Foundation.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   29  * SUCH DAMAGE.
   30  *
   31  * $FreeBSD$
   32  */
   33 
   34 #include <sys/param.h>
   35 #include <sys/kernel.h>
   36 #include <sys/types.h>
   37 #include <sys/malloc.h>
   38 #include <sys/firmware.h>
   39 #include <sys/queue.h>
   40 #include <sys/taskqueue.h>
   41 
   42 #include <linux/types.h>
   43 #include <linux/device.h>
   44 
   45 #include <linux/firmware.h>
   46 #undef firmware
   47 
   48 MALLOC_DEFINE(M_LKPI_FW, "lkpifw", "LinuxKPI firmware");
   49 
   50 struct lkpi_fw_task {
   51         /* Task and arguments for the "nowait" callback. */
   52         struct task             fw_task;
   53         gfp_t                   gfp;
   54         const char              *fw_name;
   55         struct device           *dev;
   56         void                    *drv;
   57         void(*cont)(const struct linuxkpi_firmware *, void *);
   58 };
   59 
   60 static int
   61 _linuxkpi_request_firmware(const char *fw_name, const struct linuxkpi_firmware **fw,
   62     struct device *dev, gfp_t gfp __unused, bool enoentok, bool warn)
   63 {
   64         const struct firmware *fbdfw;
   65         struct linuxkpi_firmware *lfw;
   66         const char *fwimg;
   67         char *p;
   68         uint32_t flags;
   69 
   70         if (fw_name == NULL || fw == NULL || dev == NULL) {
   71                 *fw = NULL;
   72                 return (-EINVAL);
   73         }
   74 
   75         /* Set independent on "warn". To debug, bootverbose is avail. */
   76         flags = FIRMWARE_GET_NOWARN;
   77 
   78         KASSERT(gfp == GFP_KERNEL, ("%s: gfp %#x\n", __func__, gfp));
   79         lfw = malloc(sizeof(*lfw), M_LKPI_FW, M_WAITOK | M_ZERO);
   80 
   81         /*
   82          * Linux can have a path in the firmware which is hard to replicate
   83          * for auto-firmware-module-loading.
   84          * On FreeBSD, depending on what people do, the firmware will either
   85          * be called "fw", or "dir_fw", or "modname_dir_fw".  The latter the
   86          * driver author has to deal with herself (requesting the special name).
   87          * We also optionally flatten '/'s and '.'s as some firmware modules do.
   88          * We probe in the least-of-work order avoiding memory operations.
   89          * It will be preferred to build the firmware .ko in a well matching
   90          * way rather than adding more name-mangling-hacks here in the future
   91          * (though we could if needed).
   92          */
   93         /* (1) Try any name removed of path. */
   94         fwimg = strrchr(fw_name, '/');
   95         if (fwimg != NULL)
   96                 fwimg++;
   97         if (fwimg == NULL || *fwimg == '\0')
   98                 fwimg = fw_name;
   99         fbdfw = firmware_get_flags(fwimg, flags);
  100         /* (2) Try the original name if we have not yet. */
  101         if (fbdfw == NULL && fwimg != fw_name) {
  102                 fwimg = fw_name;
  103                 fbdfw = firmware_get_flags(fwimg, flags);
  104         }
  105         /* (3) Flatten '/', '.' and '-' to '_' and try with adjusted name. */
  106         if (fbdfw == NULL &&
  107             (strchr(fw_name, '/') != NULL || strchr(fw_name, '.') != NULL ||
  108             strchr(fw_name, '-'))) {
  109                 fwimg = strdup(fw_name, M_LKPI_FW);
  110                 if (fwimg != NULL) {
  111                         while ((p = strchr(fwimg, '/')) != NULL)
  112                                 *p = '_';
  113                         fbdfw = firmware_get_flags(fwimg, flags);
  114                         if (fbdfw == NULL) {
  115                                 while ((p = strchr(fwimg, '.')) != NULL)
  116                                         *p = '_';
  117                                 fbdfw = firmware_get_flags(fwimg, flags);
  118                         }
  119                         if (fbdfw == NULL) {
  120                                 while ((p = strchr(fwimg, '-')) != NULL)
  121                                         *p = '_';
  122                                 fbdfw = firmware_get_flags(fwimg, flags);
  123                         }
  124                         free(__DECONST(void *, fwimg), M_LKPI_FW);
  125                 }
  126         }
  127         if (fbdfw == NULL) {
  128                 if (enoentok)
  129                         *fw = lfw;
  130                 else {
  131                         free(lfw, M_LKPI_FW);
  132                         *fw = NULL;
  133                 }
  134                 if (warn)
  135                         device_printf(dev->bsddev, "could not load firmware "
  136                             "image '%s'\n", fw_name);
  137                 return (-ENOENT);
  138         }
  139 
  140         device_printf(dev->bsddev,"successfully loaded firmware image '%s'\n",
  141             fw_name);
  142         lfw->fbdfw = fbdfw;
  143         lfw->data = (const uint8_t *)fbdfw->data;
  144         lfw->size = fbdfw->datasize;
  145         *fw = lfw;
  146         return (0);
  147 }
  148 
  149 static void
  150 lkpi_fw_task(void *ctx, int pending)
  151 {
  152         struct lkpi_fw_task *lfwt;
  153         const struct linuxkpi_firmware *fw;
  154 
  155         KASSERT(ctx != NULL && pending == 1, ("%s: lfwt %p, pending %d\n",
  156             __func__, ctx, pending));
  157 
  158         lfwt = ctx;
  159         if (lfwt->cont == NULL)
  160                 goto out;
  161 
  162         _linuxkpi_request_firmware(lfwt->fw_name, &fw, lfwt->dev,
  163             lfwt->gfp, true, true);
  164 
  165         /*
  166          * Linux seems to run the callback if it cannot find the firmware.
  167          * We call it in all cases as it is the only feedback to the requester.
  168          */
  169         lfwt->cont(fw, lfwt->drv);
  170         /* Do not assume fw is still valid! */
  171 
  172 out:
  173         free(lfwt, M_LKPI_FW);
  174 }
  175 
  176 int
  177 linuxkpi_request_firmware_nowait(struct module *mod __unused, bool _t __unused,
  178     const char *fw_name, struct device *dev, gfp_t gfp, void *drv,
  179     void(*cont)(const struct linuxkpi_firmware *, void *))
  180 {
  181         struct lkpi_fw_task *lfwt;
  182         int error;
  183 
  184         lfwt = malloc(sizeof(*lfwt), M_LKPI_FW, M_WAITOK | M_ZERO);
  185         lfwt->gfp = gfp;
  186         lfwt->fw_name = fw_name;
  187         lfwt->dev = dev;
  188         lfwt->drv = drv;
  189         lfwt->cont = cont;
  190         TASK_INIT(&lfwt->fw_task, 0, lkpi_fw_task, lfwt);
  191         error = taskqueue_enqueue(taskqueue_thread, &lfwt->fw_task);
  192 
  193         if (error)
  194                 return (-error);
  195         return (0);
  196 }
  197 
  198 int
  199 linuxkpi_request_firmware(const struct linuxkpi_firmware **fw,
  200     const char *fw_name, struct device *dev)
  201 {
  202 
  203         return (_linuxkpi_request_firmware(fw_name, fw, dev, GFP_KERNEL, false,
  204             true));
  205 }
  206 
  207 int
  208 linuxkpi_firmware_request_nowarn(const struct linuxkpi_firmware **fw,
  209     const char *fw_name, struct device *dev)
  210 {
  211 
  212         return (_linuxkpi_request_firmware(fw_name, fw, dev, GFP_KERNEL, false,
  213             false));
  214 }
  215 
  216 void
  217 linuxkpi_release_firmware(const struct linuxkpi_firmware *fw)
  218 {
  219 
  220         if (fw == NULL)
  221                 return;
  222 
  223         if (fw->fbdfw)
  224                 firmware_put(fw->fbdfw, FIRMWARE_UNLOAD);
  225         free(__DECONST(void *, fw), M_LKPI_FW);
  226 }
  227 
  228 int
  229 linuxkpi_request_partial_firmware_into_buf(const struct linuxkpi_firmware **fw,
  230     const char *fw_name, struct device *dev, uint8_t *buf, size_t buflen,
  231     size_t offset)
  232 {
  233         const struct linuxkpi_firmware *lfw;
  234         int error;
  235 
  236         error = linuxkpi_request_firmware(fw, fw_name, dev);
  237         if (error != 0)
  238                 return (error);
  239 
  240         lfw = *fw;
  241         if ((offset + buflen) >= lfw->size) {
  242                 linuxkpi_release_firmware(lfw);
  243                 return (-ERANGE);
  244         }
  245 
  246         memcpy(buf, lfw->data + offset, buflen);
  247 
  248         return (0);
  249 }

Cache object: 18c2d91a355da27e2bbdd3c04a4f63ec


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.