xref: /netbsd-src/sys/external/bsd/drm2/linux/linux_firmware.c (revision f7058029e19ae715ad1ae05468b7086669fbbc8b)
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