Changeset View
Changeset View
Standalone View
Standalone View
sys/compat/linuxkpi/common/src/linux_firmware.c
- This file was added.
/*- | |||||
* SPDX-License-Identifier: BSD-2-Clause | |||||
* | |||||
* Copyright (c) 2020 The FreeBSD Foundation | |||||
* | |||||
* This software was developed by Björn Zeeb under sponsorship from | |||||
* the FreeBSD Foundation. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* | |||||
* $FreeBSD$ | |||||
*/ | |||||
#include <sys/types.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/firmware.h> | |||||
#include <linux/types.h> | |||||
#include <linux/device.h> | |||||
#include <linux/firmware.h> | |||||
#undef firmware | |||||
MALLOC_DEFINE(M_LKPI_FW, "lkpifw", "LinuxKPI Firmware"); | |||||
static int | |||||
_linuxkpi_request_firmware(const char *fw_name, const struct linuxkpi_firmware **fw, | |||||
gfp_t gfp __unused, bool enoentok) | |||||
{ | |||||
const struct firmware *fbdfw; | |||||
struct linuxkpi_firmware *lfw; | |||||
const char *fwimg; | |||||
char *p; | |||||
if (fw == NULL) | |||||
return (-EINVAL); | |||||
KASSERT(gfp == GFP_KERNEL, ("%s: gfp %#x", __func__, gfp)); | |||||
lfw = malloc(sizeof(*lfw), M_LKPI_FW, M_WAITOK); | |||||
kib: Extra () around condition. | |||||
/* | |||||
* Linux can have a path in the firmware which is hard to replicate | |||||
* for auto-firmware-module-loading. | |||||
* On FreeBSD, depending on what people do, the firmware will either | |||||
* be called "fw", or "dir_fw", or "modname_dir_fw". The later the | |||||
* driver author has to deal with herself. | |||||
* We also optionally flatten '.'s as some firmware modules do, | |||||
* while others do not. | |||||
*/ | |||||
/* (1) Try any name removed of path. */ | |||||
fwimg = strrchr(fw_name, '/'); | |||||
if (fwimg != NULL) | |||||
fwimg++; | |||||
if (fwimg == NULL || *fwimg == '\0') | |||||
fwimg = fw_name; | |||||
fbdfw = firmware_get(fwimg); | |||||
/* (2) Try the original name if we have not yet. */ | |||||
if (fbdfw == NULL && fwimg != fw_name) { | |||||
fwimg = fw_name; | |||||
fbdfw = firmware_get(fwimg); | |||||
} | |||||
/* (3) Flatten '/' and then '.' to '_' and try with adjusted name. */ | |||||
if (fbdfw == NULL && | |||||
(strchr(fw_name, '/') != NULL || strchr(fw_name, '.') != NULL)) { | |||||
fwimg = strdup(fw_name, M_LKPI_FW); | |||||
if (fwimg != NULL) { | |||||
while ((p = strchr(fwimg, '/')) != NULL) | |||||
*p = '_'; | |||||
fbdfw = firmware_get(fwimg); | |||||
if (fbdfw == NULL) { | |||||
while ((p = strchr(fwimg, '.')) != NULL) | |||||
*p = '_'; | |||||
fbdfw = firmware_get(fwimg); | |||||
} | |||||
free(__DECONST(void *, fwimg), M_LKPI_FW); | |||||
} | |||||
} | |||||
if (fbdfw == NULL) { | |||||
if (enoentok) | |||||
*fw = lfw; | |||||
else | |||||
free(lfw, M_LKPI_FW); | |||||
return (-ENOENT); | |||||
} | |||||
lfw->fbdfw = fbdfw; | |||||
lfw->data = (const uint8_t *)fbdfw->data; | |||||
lfw->size = fbdfw->datasize; | |||||
*fw = lfw; | |||||
return (0); | |||||
} | |||||
int | |||||
linuxkpi_request_firmware_nowait(struct module *mod __unused, bool _t __unused, | |||||
const char *fw_name, struct device *dev __unused, gfp_t gfp, void *drv, | |||||
void(*cont)(const struct linuxkpi_firmware *, void *)) | |||||
{ | |||||
const struct linuxkpi_firmware *lfw; | |||||
int error; | |||||
/* Linux seems to run the callback if it cannot find the firmware. */ | |||||
error = _linuxkpi_request_firmware(fw_name, &lfw, gfp, true); | |||||
if (error == -ENOENT) | |||||
error = 0; | |||||
/* XXX-BZ we do not run this deferred. It's a compat layer. */ | |||||
if (error == 0) | |||||
cont(lfw, drv); | |||||
return (error); | |||||
} | |||||
int | |||||
linuxkpi_request_firmware(const struct linuxkpi_firmware **fw, | |||||
const char *fw_name, struct device *dev __unused) | |||||
{ | |||||
return (_linuxkpi_request_firmware(fw_name, fw, GFP_KERNEL, false)); | |||||
} | |||||
void | |||||
linuxkpi_release_firmware(const struct linuxkpi_firmware *fw) | |||||
{ | |||||
if (fw == NULL) | |||||
return; | |||||
if (fw->fbdfw) | |||||
firmware_put(fw->fbdfw, FIRMWARE_UNLOAD); | |||||
free(__DECONST(void *, fw), M_LKPI_FW); | |||||
} | |||||
Done Inline Actions^^^ kib: ^^^ |
Extra () around condition.