Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7fcfead5 authored by Vikram Mulukutla's avatar Vikram Mulukutla Committed by David Keitel
Browse files

firmware_class: Introduce a firmware descriptor structure



Introduce a firmware descriptor structure that makes it
easier to pass around various configuration options in the
internal implementation of firmware_class.

Change-Id: I5c1da222bccd568fabb26da5baccaa4035331efd
Signed-off-by: default avatarVikram Mulukutla <markivx@codeaurora.org>
[joshc: reworked to make use of upstream's opt_flags member]
Signed-off-by: default avatarJosh Cartwright <joshc@codeaurora.org>
[vmulukut: adjusted for upstream changes to opt_flags]
Signed-off-by: default avatarVikram Mulukutla <markivx@codeaurora.org>
[dkeitel: resolved merge conflicts]
Signed-off-by: default avatarDavid Keitel <dkeitel@codeaurora.org>
parent 0b556847
Loading
Loading
Loading
Loading
+114 −90
Original line number Original line Diff line number Diff line
@@ -163,6 +163,17 @@ struct fw_name_devm {
	const char *name;
	const char *name;
};
};


struct fw_desc {
	struct work_struct work;
	const struct firmware **firmware_p;
	const char *name;
	struct device *device;
	unsigned int opt_flags;
	struct module *module;
	void *context;
	void (*cont)(const struct firmware *fw, void *context);
};

#define to_fwbuf(d) container_of(d, struct firmware_buf, ref)
#define to_fwbuf(d) container_of(d, struct firmware_buf, ref)


#define	FW_LOADER_NO_CACHE	0
#define	FW_LOADER_NO_CACHE	0
@@ -268,6 +279,10 @@ static void __fw_free_buf(struct kref *ref)
static void fw_free_buf(struct firmware_buf *buf)
static void fw_free_buf(struct firmware_buf *buf)
{
{
	struct firmware_cache *fwc = buf->fwc;
	struct firmware_cache *fwc = buf->fwc;
	if (!fwc) {
		kfree(buf);
		return;
	}
	spin_lock(&fwc->lock);
	spin_lock(&fwc->lock);
	if (!kref_put(&buf->ref, __fw_free_buf))
	if (!kref_put(&buf->ref, __fw_free_buf))
		spin_unlock(&fwc->lock);
		spin_unlock(&fwc->lock);
@@ -885,25 +900,29 @@ static const struct attribute_group *fw_dev_attr_groups[] = {
};
};


static struct firmware_priv *
static struct firmware_priv *
fw_create_instance(struct firmware *firmware, const char *fw_name,
fw_create_instance(struct firmware *firmware, struct fw_desc *desc)
		   struct device *device, unsigned int opt_flags)
{
{
	struct firmware_priv *fw_priv;
	struct firmware_priv *fw_priv;
	struct device *f_dev;
	struct device *f_dev;


	fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL);
	fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL);
	if (!fw_priv) {
	if (!fw_priv) {
		dev_err(desc->device, "%s: kmalloc failed\n", __func__);
		fw_priv = ERR_PTR(-ENOMEM);
		fw_priv = ERR_PTR(-ENOMEM);
		goto exit;
		goto exit;
	}
	}


	fw_priv->nowait = !!(opt_flags & FW_OPT_NOWAIT);
	fw_priv->nowait = !!(desc->opt_flags & FW_OPT_NOWAIT);
	fw_priv->fw = firmware;
	fw_priv->fw = firmware;

	INIT_DELAYED_WORK(&fw_priv->timeout_work,
		firmware_class_timeout_work);

	f_dev = &fw_priv->dev;
	f_dev = &fw_priv->dev;


	device_initialize(f_dev);
	device_initialize(f_dev);
	dev_set_name(f_dev, "%s", fw_name);
	dev_set_name(f_dev, "%s", desc->name);
	f_dev->parent = device;
	f_dev->parent = desc->device;
	f_dev->class = &firmware_class;
	f_dev->class = &firmware_class;
	f_dev->groups = fw_dev_attr_groups;
	f_dev->groups = fw_dev_attr_groups;
exit:
exit:
@@ -964,17 +983,16 @@ err_put_dev:
}
}


static int fw_load_from_user_helper(struct firmware *firmware,
static int fw_load_from_user_helper(struct firmware *firmware,
				    const char *name, struct device *device,
				    struct fw_desc *desc, long timeout)
				    unsigned int opt_flags, long timeout)
{
{
	struct firmware_priv *fw_priv;
	struct firmware_priv *fw_priv;


	fw_priv = fw_create_instance(firmware, name, device, opt_flags);
	fw_priv = fw_create_instance(firmware, desc);
	if (IS_ERR(fw_priv))
	if (IS_ERR(fw_priv))
		return PTR_ERR(fw_priv);
		return PTR_ERR(fw_priv);


	fw_priv->buf = firmware->priv;
	fw_priv->buf = firmware->priv;
	return _request_firmware_load(fw_priv, opt_flags, timeout);
	return _request_firmware_load(fw_priv, desc->opt_flags, timeout);
}
}


#ifdef CONFIG_PM_SLEEP
#ifdef CONFIG_PM_SLEEP
@@ -995,9 +1013,8 @@ static void kill_requests_without_uevent(void)


#else /* CONFIG_FW_LOADER_USER_HELPER */
#else /* CONFIG_FW_LOADER_USER_HELPER */
static inline int
static inline int
fw_load_from_user_helper(struct firmware *firmware, const char *name,
fw_load_from_user_helper(struct firmware *firmware,
			 struct device *device, unsigned int opt_flags,
			 struct fw_desc *desc, long timeout)
			 long timeout)
{
{
	return -ENOENT;
	return -ENOENT;
}
}
@@ -1036,8 +1053,7 @@ static int sync_cached_firmware_buf(struct firmware_buf *buf)
 * or a negative error code
 * or a negative error code
 */
 */
static int
static int
_request_firmware_prepare(struct firmware **firmware_p, const char *name,
_request_firmware_prepare(struct firmware **firmware_p, struct fw_desc *desc)
			  struct device *device)
{
{
	struct firmware *firmware;
	struct firmware *firmware;
	struct firmware_buf *buf;
	struct firmware_buf *buf;
@@ -1045,17 +1061,18 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,


	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
	if (!firmware) {
	if (!firmware) {
		dev_err(device, "%s: kmalloc(struct firmware) failed\n",
		dev_err(desc->device, "%s: kmalloc(struct firmware) failed\n",
			__func__);
			__func__);
		return -ENOMEM;
		return -ENOMEM;
	}
	}


	if (fw_get_builtin_firmware(firmware, name)) {
	if (fw_get_builtin_firmware(firmware, desc->name)) {
		dev_dbg(device, "firmware: using built-in firmware %s\n", name);
		dev_dbg(desc->device, "firmware: using built-in firmware %s\n",
			desc->name);
		return 0; /* assigned */
		return 0; /* assigned */
	}
	}


	ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf);
	ret = fw_lookup_and_allocate_buf(desc->name, &fw_cache, &buf);


	/*
	/*
	 * bind with 'buf' now to avoid warning in failure path
	 * bind with 'buf' now to avoid warning in failure path
@@ -1114,58 +1131,55 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device,
}
}


/* called from request_firmware() and request_firmware_work_func() */
/* called from request_firmware() and request_firmware_work_func() */
static int
static int _request_firmware(struct fw_desc *desc)
_request_firmware(const struct firmware **firmware_p, const char *name,
		  struct device *device, unsigned int opt_flags)
{
{
	struct firmware *fw;
	struct firmware *fw;
	long timeout;
	long timeout;
	int ret;
	int ret;


	if (!firmware_p)
	if (!desc->firmware_p)
		return -EINVAL;
		return -EINVAL;


	if (!name || name[0] == '\0')
	if (!desc->name || desc->name[0] == '\0')
		return -EINVAL;
		return -EINVAL;


	ret = _request_firmware_prepare(&fw, name, device);
	ret = _request_firmware_prepare(&fw, desc);
	if (ret <= 0) /* error or already assigned */
	if (ret <= 0) /* error or already assigned */
		goto out;
		goto out;


	ret = 0;
	ret = 0;
	timeout = firmware_loading_timeout();
	timeout = firmware_loading_timeout();
	if (opt_flags & FW_OPT_NOWAIT) {
	if (desc->opt_flags & FW_OPT_NOWAIT) {
		timeout = usermodehelper_read_lock_wait(timeout);
		timeout = usermodehelper_read_lock_wait(timeout);
		if (!timeout) {
		if (!timeout) {
			dev_dbg(device, "firmware: %s loading timed out\n",
			dev_dbg(desc->device, "firmware: %s loading timed out\n",
				name);
				desc->name);
			ret = -EBUSY;
			ret = -EBUSY;
			goto out;
			goto out;
		}
		}
	} else {
	} else {
		ret = usermodehelper_read_trylock();
		ret = usermodehelper_read_trylock();
		if (WARN_ON(ret)) {
		if (WARN_ON(ret)) {
			dev_err(device, "firmware: %s will not be loaded\n",
			dev_err(desc->device, "firmware: %s will not be loaded\n",
				name);
				desc->name);
			goto out;
			goto out;
		}
		}
	}
	}


	ret = fw_get_filesystem_firmware(device, fw->priv);
	ret = fw_get_filesystem_firmware(desc->device, fw->priv);
	if (ret) {
	if (ret) {
		if (!(opt_flags & FW_OPT_NO_WARN))
		if (!(desc->opt_flags & FW_OPT_NO_WARN))
			dev_warn(device,
			dev_warn(desc->device,
				 "Direct firmware load for %s failed with error %d\n",
				 "Direct firmware load for %s failed with error %d\n",
				 name, ret);
				 desc->name, ret);
		if (opt_flags & FW_OPT_USERHELPER) {
		if (desc->opt_flags & FW_OPT_USERHELPER) {
			dev_warn(device, "Falling back to user helper\n");
			dev_warn(desc->device, "Falling back to user helper\n");
			ret = fw_load_from_user_helper(fw, name, device,
			ret = fw_load_from_user_helper(fw, desc, timeout);
						       opt_flags, timeout);
		}
		}
	}
	}


	if (!ret)
	if (!ret)
		ret = assign_firmware_buf(fw, device, opt_flags);
		ret = assign_firmware_buf(fw, desc->device, desc->opt_flags);


	usermodehelper_read_unlock();
	usermodehelper_read_unlock();


@@ -1175,7 +1189,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
		fw = NULL;
		fw = NULL;
	}
	}


	*firmware_p = fw;
	*desc->firmware_p = fw;
	return ret;
	return ret;
}
}


@@ -1203,13 +1217,19 @@ int
request_firmware(const struct firmware **firmware_p, const char *name,
request_firmware(const struct firmware **firmware_p, const char *name,
		 struct device *device)
		 struct device *device)
{
{
	struct fw_desc desc;
	int ret;
	int ret;


	desc.firmware_p = firmware_p;
	desc.name = name;
	desc.device = device;
	desc.opt_flags = FW_OPT_UEVENT | FW_OPT_FALLBACK;

	/* Need to pin this module until return */
	/* Need to pin this module until return */
	__module_get(THIS_MODULE);
	__module_get(THIS_MODULE);
	ret = _request_firmware(firmware_p, name, device,
	ret = _request_firmware(&desc);
				FW_OPT_UEVENT | FW_OPT_FALLBACK);
	module_put(THIS_MODULE);
	module_put(THIS_MODULE);

	return ret;
	return ret;
}
}
EXPORT_SYMBOL(request_firmware);
EXPORT_SYMBOL(request_firmware);
@@ -1228,12 +1248,19 @@ EXPORT_SYMBOL(request_firmware);
int request_firmware_direct(const struct firmware **firmware_p,
int request_firmware_direct(const struct firmware **firmware_p,
			    const char *name, struct device *device)
			    const char *name, struct device *device)
{
{
	struct fw_desc desc;
	int ret;
	int ret;


	desc.firmware_p = firmware_p;
	desc.name = name;
	desc.device = device;
	desc.opt_flags = FW_OPT_UEVENT | FW_OPT_NO_WARN;

	/* Need to pin this module until return */
	__module_get(THIS_MODULE);
	__module_get(THIS_MODULE);
	ret = _request_firmware(firmware_p, name, device,
	ret = _request_firmware(&desc);
				FW_OPT_UEVENT | FW_OPT_NO_WARN);
	module_put(THIS_MODULE);
	module_put(THIS_MODULE);

	return ret;
	return ret;
}
}
EXPORT_SYMBOL_GPL(request_firmware_direct);
EXPORT_SYMBOL_GPL(request_firmware_direct);
@@ -1253,31 +1280,54 @@ void release_firmware(const struct firmware *fw)
EXPORT_SYMBOL(release_firmware);
EXPORT_SYMBOL(release_firmware);


/* Async support */
/* Async support */
struct firmware_work {
	struct work_struct work;
	struct module *module;
	const char *name;
	struct device *device;
	void *context;
	void (*cont)(const struct firmware *fw, void *context);
	unsigned int opt_flags;
};

static void request_firmware_work_func(struct work_struct *work)
static void request_firmware_work_func(struct work_struct *work)
{
{
	struct firmware_work *fw_work;
	const struct firmware *fw;
	const struct firmware *fw;
	struct fw_desc *desc;

	desc = container_of(work, struct fw_desc, work);
	desc->firmware_p = &fw;
	_request_firmware(desc);
	desc->cont(fw, desc->context);
	put_device(desc->device); /* taken in request_firmware_nowait() */


	fw_work = container_of(work, struct firmware_work, work);
	module_put(desc->module);
	kfree(desc);
}


	_request_firmware(&fw, fw_work->name, fw_work->device,
int
			  fw_work->opt_flags);
_request_firmware_nowait(
	fw_work->cont(fw, fw_work->context);
	struct module *module, bool uevent,
	put_device(fw_work->device); /* taken in request_firmware_nowait() */
	const char *name, struct device *device, gfp_t gfp, void *context,
	void (*cont)(const struct firmware *fw, void *context))
{
	struct fw_desc *desc;


	module_put(fw_work->module);
	desc = kzalloc(sizeof(struct fw_desc), gfp);
	kfree_const(fw_work->name);
	if (!desc)
	kfree(fw_work);
		return -ENOMEM;

	desc->module = module;
	desc->name = name;
	desc->device = device;
	desc->context = context;
	desc->cont = cont;
	desc->opt_flags = FW_OPT_FALLBACK | FW_OPT_NOWAIT;

	if (uevent)
		desc->opt_flags |= FW_OPT_UEVENT;
	else
		desc->opt_flags |= FW_OPT_USERHELPER;

	if (!try_module_get(module)) {
		kfree(desc);
		return -EFAULT;
	}

	get_device(desc->device);
	INIT_WORK(&desc->work, request_firmware_work_func);
	schedule_work(&desc->work);
	return 0;
}
}


/**
/**
@@ -1309,34 +1359,8 @@ request_firmware_nowait(
	const char *name, struct device *device, gfp_t gfp, void *context,
	const char *name, struct device *device, gfp_t gfp, void *context,
	void (*cont)(const struct firmware *fw, void *context))
	void (*cont)(const struct firmware *fw, void *context))
{
{
	struct firmware_work *fw_work;
	return _request_firmware_nowait(module, uevent, name, device, gfp,

					context, cont);
	fw_work = kzalloc(sizeof(struct firmware_work), gfp);
	if (!fw_work)
		return -ENOMEM;

	fw_work->module = module;
	fw_work->name = kstrdup_const(name, gfp);
	if (!fw_work->name) {
		kfree(fw_work);
		return -ENOMEM;
	}
	fw_work->device = device;
	fw_work->context = context;
	fw_work->cont = cont;
	fw_work->opt_flags = FW_OPT_NOWAIT | FW_OPT_FALLBACK |
		(uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER);

	if (!try_module_get(module)) {
		kfree_const(fw_work->name);
		kfree(fw_work);
		return -EFAULT;
	}

	get_device(fw_work->device);
	INIT_WORK(&fw_work->work, request_firmware_work_func);
	schedule_work(&fw_work->work);
	return 0;
}
}
EXPORT_SYMBOL(request_firmware_nowait);
EXPORT_SYMBOL(request_firmware_nowait);