1*f7058029Sriastradh /* $NetBSD: linux_firmware.c,v 1.2 2021/12/19 12:01:04 riastradh Exp $ */
210afc3d5Sriastradh
310afc3d5Sriastradh /*-
410afc3d5Sriastradh * Copyright (c) 2013 The NetBSD Foundation, Inc.
510afc3d5Sriastradh * All rights reserved.
610afc3d5Sriastradh *
710afc3d5Sriastradh * This code is derived from software contributed to The NetBSD Foundation
810afc3d5Sriastradh * by Taylor R. Campbell.
910afc3d5Sriastradh *
1010afc3d5Sriastradh * Redistribution and use in source and binary forms, with or without
1110afc3d5Sriastradh * modification, are permitted provided that the following conditions
1210afc3d5Sriastradh * are met:
1310afc3d5Sriastradh * 1. Redistributions of source code must retain the above copyright
1410afc3d5Sriastradh * notice, this list of conditions and the following disclaimer.
1510afc3d5Sriastradh * 2. Redistributions in binary form must reproduce the above copyright
1610afc3d5Sriastradh * notice, this list of conditions and the following disclaimer in the
1710afc3d5Sriastradh * documentation and/or other materials provided with the distribution.
1810afc3d5Sriastradh *
1910afc3d5Sriastradh * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2010afc3d5Sriastradh * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2110afc3d5Sriastradh * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2210afc3d5Sriastradh * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2310afc3d5Sriastradh * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2410afc3d5Sriastradh * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2510afc3d5Sriastradh * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2610afc3d5Sriastradh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2710afc3d5Sriastradh * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2810afc3d5Sriastradh * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2910afc3d5Sriastradh * POSSIBILITY OF SUCH DAMAGE.
3010afc3d5Sriastradh */
3110afc3d5Sriastradh
3210afc3d5Sriastradh #include <sys/cdefs.h>
33*f7058029Sriastradh __KERNEL_RCSID(0, "$NetBSD: linux_firmware.c,v 1.2 2021/12/19 12:01:04 riastradh Exp $");
3410afc3d5Sriastradh
3510afc3d5Sriastradh #include <sys/types.h>
3610afc3d5Sriastradh #include <sys/device.h>
3710afc3d5Sriastradh #include <sys/kmem.h>
3810afc3d5Sriastradh #include <sys/module.h>
3910afc3d5Sriastradh #include <sys/systm.h>
4010afc3d5Sriastradh
4110afc3d5Sriastradh #include <linux/firmware.h>
4210afc3d5Sriastradh #include <linux/slab.h>
4310afc3d5Sriastradh #include <linux/string.h>
4410afc3d5Sriastradh #include <linux/workqueue.h>
4510afc3d5Sriastradh
4610afc3d5Sriastradh struct firmload_work {
4710afc3d5Sriastradh char *flw_name;
4810afc3d5Sriastradh void (*flw_callback)(const struct firmware *, void *);
4910afc3d5Sriastradh void *flw_cookie;
5010afc3d5Sriastradh struct device *flw_device;
5110afc3d5Sriastradh struct module *flw_module;
5210afc3d5Sriastradh struct work_struct flw_work;
5310afc3d5Sriastradh };
5410afc3d5Sriastradh
5510afc3d5Sriastradh int
request_firmware(const struct firmware ** fwp,const char * image_name,struct device * dev)5610afc3d5Sriastradh request_firmware(const struct firmware **fwp, const char *image_name,
5710afc3d5Sriastradh struct device *dev)
5810afc3d5Sriastradh {
5910afc3d5Sriastradh const char *drvname;
6010afc3d5Sriastradh struct firmware *fw;
6110afc3d5Sriastradh firmware_handle_t handle;
6210afc3d5Sriastradh int ret;
6310afc3d5Sriastradh
6410afc3d5Sriastradh fw = kmem_alloc(sizeof(*fw), KM_SLEEP);
6510afc3d5Sriastradh
6610afc3d5Sriastradh /*
6710afc3d5Sriastradh * If driver xyz(4) asks for xyz/foo/bar.bin, turn that into
6810afc3d5Sriastradh * just foo/bar.bin. This leaves open the possibility of name
6910afc3d5Sriastradh * collisions. Let's hope upstream is sensible about this.
7010afc3d5Sriastradh */
7110afc3d5Sriastradh drvname = device_cfdriver(dev)->cd_name;
7210afc3d5Sriastradh if ((strncmp(drvname, image_name, strlen(drvname)) == 0) &&
7310afc3d5Sriastradh (image_name[strlen(drvname)] == '/'))
7410afc3d5Sriastradh image_name += (strlen(drvname) + 1);
7510afc3d5Sriastradh
7610afc3d5Sriastradh /* XXX errno NetBSD->Linux */
7710afc3d5Sriastradh ret = -firmware_open(drvname, image_name, &handle);
7810afc3d5Sriastradh if (ret)
7910afc3d5Sriastradh goto fail0;
8010afc3d5Sriastradh fw->size = firmware_get_size(handle);
8110afc3d5Sriastradh fw->data = firmware_malloc(fw->size);
8210afc3d5Sriastradh
8310afc3d5Sriastradh /* XXX errno NetBSD->Linux */
8410afc3d5Sriastradh ret = -firmware_read(handle, 0, fw->data, fw->size);
8510afc3d5Sriastradh (void)firmware_close(handle);
8610afc3d5Sriastradh if (ret)
8710afc3d5Sriastradh goto fail1;
8810afc3d5Sriastradh
8910afc3d5Sriastradh /* Success! */
9010afc3d5Sriastradh *fwp = fw;
9110afc3d5Sriastradh return 0;
9210afc3d5Sriastradh
9310afc3d5Sriastradh fail1: firmware_free(fw->data, fw->size);
9410afc3d5Sriastradh fail0: KASSERT(ret);
9510afc3d5Sriastradh kmem_free(fw, sizeof(*fw));
9610afc3d5Sriastradh *fwp = NULL;
9710afc3d5Sriastradh return ret;
9810afc3d5Sriastradh }
9910afc3d5Sriastradh
10010afc3d5Sriastradh int
request_firmware_direct(const struct firmware ** fwp,const char * image_name,struct device * dev)101*f7058029Sriastradh request_firmware_direct(const struct firmware **fwp, const char *image_name,
102*f7058029Sriastradh struct device *dev)
103*f7058029Sriastradh {
104*f7058029Sriastradh
105*f7058029Sriastradh return request_firmware(fwp, image_name, dev);
106*f7058029Sriastradh }
107*f7058029Sriastradh
108*f7058029Sriastradh int
firmware_request_nowarn(const struct firmware ** fwp,const char * image_name,struct device * dev)10910afc3d5Sriastradh firmware_request_nowarn(const struct firmware **fwp, const char *image_name,
11010afc3d5Sriastradh struct device *dev)
11110afc3d5Sriastradh {
11210afc3d5Sriastradh
11310afc3d5Sriastradh return request_firmware(fwp, image_name, dev);
11410afc3d5Sriastradh }
11510afc3d5Sriastradh
11610afc3d5Sriastradh static void
request_firmware_work(struct work_struct * wk)11710afc3d5Sriastradh request_firmware_work(struct work_struct *wk)
11810afc3d5Sriastradh {
11910afc3d5Sriastradh struct firmload_work *work = container_of(wk, struct firmload_work,
12010afc3d5Sriastradh flw_work);
12110afc3d5Sriastradh const struct firmware *fw;
12210afc3d5Sriastradh int ret;
12310afc3d5Sriastradh
12410afc3d5Sriastradh /* Reqeust the firmware. If it failed, set it to NULL. */
12510afc3d5Sriastradh ret = request_firmware(&fw, work->flw_name, work->flw_device);
12610afc3d5Sriastradh if (ret)
12710afc3d5Sriastradh fw = NULL;
12810afc3d5Sriastradh
12910afc3d5Sriastradh /* Call the callback. */
13010afc3d5Sriastradh (*work->flw_callback)(fw, work->flw_cookie);
13110afc3d5Sriastradh
13210afc3d5Sriastradh /*
13310afc3d5Sriastradh * Release the device and module references now that we're
13410afc3d5Sriastradh * done.
13510afc3d5Sriastradh *
13610afc3d5Sriastradh * XXX Heh. What if the module gets unloaded _during_
13710afc3d5Sriastradh * module_rele because it went to zero?
13810afc3d5Sriastradh */
13910afc3d5Sriastradh /* XXX device_release */
14010afc3d5Sriastradh if (work->flw_module)
14110afc3d5Sriastradh module_rele(work->flw_module);
14210afc3d5Sriastradh }
14310afc3d5Sriastradh
14410afc3d5Sriastradh int
request_firmware_nowait(struct module * module,bool uevent,const char * name,struct device * device,gfp_t gfp,void * cookie,void (* callback)(const struct firmware *,void *))14510afc3d5Sriastradh request_firmware_nowait(struct module *module, bool uevent, const char *name,
14610afc3d5Sriastradh struct device *device, gfp_t gfp, void *cookie,
14710afc3d5Sriastradh void (*callback)(const struct firmware *, void *))
14810afc3d5Sriastradh {
14910afc3d5Sriastradh char *namedup;
15010afc3d5Sriastradh struct firmload_work *work;
15110afc3d5Sriastradh
15210afc3d5Sriastradh /* Allocate memory for it, or fail if we can't. */
15310afc3d5Sriastradh work = kzalloc(sizeof(*work), gfp);
15410afc3d5Sriastradh if (work == NULL)
15510afc3d5Sriastradh goto fail0;
15610afc3d5Sriastradh
15710afc3d5Sriastradh /* Copy the name just in case. */
15810afc3d5Sriastradh namedup = kstrdup(name, gfp);
15910afc3d5Sriastradh if (namedup == NULL)
16010afc3d5Sriastradh goto fail1;
16110afc3d5Sriastradh
16210afc3d5Sriastradh /* Hold the module and device so they don't go away before callback. */
16310afc3d5Sriastradh if (module)
16410afc3d5Sriastradh module_hold(module);
16510afc3d5Sriastradh /* XXX device_acquire(device) */
16610afc3d5Sriastradh
16710afc3d5Sriastradh /* Initialize the work. */
16810afc3d5Sriastradh work->flw_name = namedup;
16910afc3d5Sriastradh work->flw_callback = callback;
17010afc3d5Sriastradh work->flw_cookie = cookie;
17110afc3d5Sriastradh work->flw_device = device;
17210afc3d5Sriastradh work->flw_module = module;
17310afc3d5Sriastradh INIT_WORK(&work->flw_work, request_firmware_work);
17410afc3d5Sriastradh
17510afc3d5Sriastradh /* Kick it off. */
17610afc3d5Sriastradh schedule_work(&work->flw_work);
17710afc3d5Sriastradh
17810afc3d5Sriastradh /* Success! */
17910afc3d5Sriastradh return 0;
18010afc3d5Sriastradh
18110afc3d5Sriastradh fail1: kfree(work);
18210afc3d5Sriastradh fail0: return -ENOMEM;
18310afc3d5Sriastradh }
18410afc3d5Sriastradh
18510afc3d5Sriastradh void
release_firmware(const struct firmware * fw)18610afc3d5Sriastradh release_firmware(const struct firmware *fw)
18710afc3d5Sriastradh {
18810afc3d5Sriastradh
18910afc3d5Sriastradh if (fw != NULL) {
19010afc3d5Sriastradh firmware_free(fw->data, fw->size);
19110afc3d5Sriastradh kmem_free(__UNCONST(fw), sizeof(*fw));
19210afc3d5Sriastradh }
19310afc3d5Sriastradh }
194