162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/highmem.h>
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci#include <linux/security.h>
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/types.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "sysfs.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * sysfs support for firmware loader
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_civoid __fw_load_abort(struct fw_priv *fw_priv)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	/*
1862306a36Sopenharmony_ci	 * There is a small window in which user can write to 'loading'
1962306a36Sopenharmony_ci	 * between loading done/aborted and disappearance of 'loading'
2062306a36Sopenharmony_ci	 */
2162306a36Sopenharmony_ci	if (fw_state_is_aborted(fw_priv) || fw_state_is_done(fw_priv))
2262306a36Sopenharmony_ci		return;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	fw_state_aborted(fw_priv);
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER_USER_HELPER
2862306a36Sopenharmony_cistatic ssize_t timeout_show(const struct class *class, const struct class_attribute *attr,
2962306a36Sopenharmony_ci			    char *buf)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", __firmware_loading_timeout());
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/**
3562306a36Sopenharmony_ci * timeout_store() - set number of seconds to wait for firmware
3662306a36Sopenharmony_ci * @class: device class pointer
3762306a36Sopenharmony_ci * @attr: device attribute pointer
3862306a36Sopenharmony_ci * @buf: buffer to scan for timeout value
3962306a36Sopenharmony_ci * @count: number of bytes in @buf
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci *	Sets the number of seconds to wait for the firmware.  Once
4262306a36Sopenharmony_ci *	this expires an error will be returned to the driver and no
4362306a36Sopenharmony_ci *	firmware will be provided.
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci *	Note: zero means 'wait forever'.
4662306a36Sopenharmony_ci **/
4762306a36Sopenharmony_cistatic ssize_t timeout_store(const struct class *class, const struct class_attribute *attr,
4862306a36Sopenharmony_ci			     const char *buf, size_t count)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	int tmp_loading_timeout = simple_strtol(buf, NULL, 10);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (tmp_loading_timeout < 0)
5362306a36Sopenharmony_ci		tmp_loading_timeout = 0;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	__fw_fallback_set_timeout(tmp_loading_timeout);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	return count;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_cistatic CLASS_ATTR_RW(timeout);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic struct attribute *firmware_class_attrs[] = {
6262306a36Sopenharmony_ci	&class_attr_timeout.attr,
6362306a36Sopenharmony_ci	NULL,
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ciATTRIBUTE_GROUPS(firmware_class);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int do_firmware_uevent(const struct fw_sysfs *fw_sysfs, struct kobj_uevent_env *env)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	if (add_uevent_var(env, "FIRMWARE=%s", fw_sysfs->fw_priv->fw_name))
7062306a36Sopenharmony_ci		return -ENOMEM;
7162306a36Sopenharmony_ci	if (add_uevent_var(env, "TIMEOUT=%i", __firmware_loading_timeout()))
7262306a36Sopenharmony_ci		return -ENOMEM;
7362306a36Sopenharmony_ci	if (add_uevent_var(env, "ASYNC=%d", fw_sysfs->nowait))
7462306a36Sopenharmony_ci		return -ENOMEM;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	return 0;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic int firmware_uevent(const struct device *dev, struct kobj_uevent_env *env)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	const struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
8262306a36Sopenharmony_ci	int err = 0;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	mutex_lock(&fw_lock);
8562306a36Sopenharmony_ci	if (fw_sysfs->fw_priv)
8662306a36Sopenharmony_ci		err = do_firmware_uevent(fw_sysfs, env);
8762306a36Sopenharmony_ci	mutex_unlock(&fw_lock);
8862306a36Sopenharmony_ci	return err;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci#endif /* CONFIG_FW_LOADER_USER_HELPER */
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void fw_dev_release(struct device *dev)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (fw_sysfs->fw_upload_priv)
9762306a36Sopenharmony_ci		fw_upload_free(fw_sysfs);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	kfree(fw_sysfs);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic struct class firmware_class = {
10362306a36Sopenharmony_ci	.name		= "firmware",
10462306a36Sopenharmony_ci#ifdef CONFIG_FW_LOADER_USER_HELPER
10562306a36Sopenharmony_ci	.class_groups	= firmware_class_groups,
10662306a36Sopenharmony_ci	.dev_uevent	= firmware_uevent,
10762306a36Sopenharmony_ci#endif
10862306a36Sopenharmony_ci	.dev_release	= fw_dev_release,
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ciint register_sysfs_loader(void)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	int ret = class_register(&firmware_class);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (ret != 0)
11662306a36Sopenharmony_ci		return ret;
11762306a36Sopenharmony_ci	return register_firmware_config_sysctl();
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_civoid unregister_sysfs_loader(void)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	unregister_firmware_config_sysctl();
12362306a36Sopenharmony_ci	class_unregister(&firmware_class);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic ssize_t firmware_loading_show(struct device *dev,
12762306a36Sopenharmony_ci				     struct device_attribute *attr, char *buf)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
13062306a36Sopenharmony_ci	int loading = 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	mutex_lock(&fw_lock);
13362306a36Sopenharmony_ci	if (fw_sysfs->fw_priv)
13462306a36Sopenharmony_ci		loading = fw_state_is_loading(fw_sysfs->fw_priv);
13562306a36Sopenharmony_ci	mutex_unlock(&fw_lock);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", loading);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/**
14162306a36Sopenharmony_ci * firmware_loading_store() - set value in the 'loading' control file
14262306a36Sopenharmony_ci * @dev: device pointer
14362306a36Sopenharmony_ci * @attr: device attribute pointer
14462306a36Sopenharmony_ci * @buf: buffer to scan for loading control value
14562306a36Sopenharmony_ci * @count: number of bytes in @buf
14662306a36Sopenharmony_ci *
14762306a36Sopenharmony_ci *	The relevant values are:
14862306a36Sopenharmony_ci *
14962306a36Sopenharmony_ci *	 1: Start a load, discarding any previous partial load.
15062306a36Sopenharmony_ci *	 0: Conclude the load and hand the data to the driver code.
15162306a36Sopenharmony_ci *	-1: Conclude the load with an error and discard any written data.
15262306a36Sopenharmony_ci **/
15362306a36Sopenharmony_cistatic ssize_t firmware_loading_store(struct device *dev,
15462306a36Sopenharmony_ci				      struct device_attribute *attr,
15562306a36Sopenharmony_ci				      const char *buf, size_t count)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
15862306a36Sopenharmony_ci	struct fw_priv *fw_priv;
15962306a36Sopenharmony_ci	ssize_t written = count;
16062306a36Sopenharmony_ci	int loading = simple_strtol(buf, NULL, 10);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	mutex_lock(&fw_lock);
16362306a36Sopenharmony_ci	fw_priv = fw_sysfs->fw_priv;
16462306a36Sopenharmony_ci	if (fw_state_is_aborted(fw_priv) || fw_state_is_done(fw_priv))
16562306a36Sopenharmony_ci		goto out;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	switch (loading) {
16862306a36Sopenharmony_ci	case 1:
16962306a36Sopenharmony_ci		/* discarding any previous partial load */
17062306a36Sopenharmony_ci		fw_free_paged_buf(fw_priv);
17162306a36Sopenharmony_ci		fw_state_start(fw_priv);
17262306a36Sopenharmony_ci		break;
17362306a36Sopenharmony_ci	case 0:
17462306a36Sopenharmony_ci		if (fw_state_is_loading(fw_priv)) {
17562306a36Sopenharmony_ci			int rc;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci			/*
17862306a36Sopenharmony_ci			 * Several loading requests may be pending on
17962306a36Sopenharmony_ci			 * one same firmware buf, so let all requests
18062306a36Sopenharmony_ci			 * see the mapped 'buf->data' once the loading
18162306a36Sopenharmony_ci			 * is completed.
18262306a36Sopenharmony_ci			 */
18362306a36Sopenharmony_ci			rc = fw_map_paged_buf(fw_priv);
18462306a36Sopenharmony_ci			if (rc)
18562306a36Sopenharmony_ci				dev_err(dev, "%s: map pages failed\n",
18662306a36Sopenharmony_ci					__func__);
18762306a36Sopenharmony_ci			else
18862306a36Sopenharmony_ci				rc = security_kernel_post_load_data(fw_priv->data,
18962306a36Sopenharmony_ci								    fw_priv->size,
19062306a36Sopenharmony_ci								    LOADING_FIRMWARE,
19162306a36Sopenharmony_ci								    "blob");
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci			/*
19462306a36Sopenharmony_ci			 * Same logic as fw_load_abort, only the DONE bit
19562306a36Sopenharmony_ci			 * is ignored and we set ABORT only on failure.
19662306a36Sopenharmony_ci			 */
19762306a36Sopenharmony_ci			if (rc) {
19862306a36Sopenharmony_ci				fw_state_aborted(fw_priv);
19962306a36Sopenharmony_ci				written = rc;
20062306a36Sopenharmony_ci			} else {
20162306a36Sopenharmony_ci				fw_state_done(fw_priv);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci				/*
20462306a36Sopenharmony_ci				 * If this is a user-initiated firmware upload
20562306a36Sopenharmony_ci				 * then start the upload in a worker thread now.
20662306a36Sopenharmony_ci				 */
20762306a36Sopenharmony_ci				rc = fw_upload_start(fw_sysfs);
20862306a36Sopenharmony_ci				if (rc)
20962306a36Sopenharmony_ci					written = rc;
21062306a36Sopenharmony_ci			}
21162306a36Sopenharmony_ci			break;
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci		fallthrough;
21462306a36Sopenharmony_ci	default:
21562306a36Sopenharmony_ci		dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
21662306a36Sopenharmony_ci		fallthrough;
21762306a36Sopenharmony_ci	case -1:
21862306a36Sopenharmony_ci		fw_load_abort(fw_sysfs);
21962306a36Sopenharmony_ci		if (fw_sysfs->fw_upload_priv)
22062306a36Sopenharmony_ci			fw_state_init(fw_sysfs->fw_priv);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		break;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ciout:
22562306a36Sopenharmony_ci	mutex_unlock(&fw_lock);
22662306a36Sopenharmony_ci	return written;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ciDEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void firmware_rw_data(struct fw_priv *fw_priv, char *buffer,
23262306a36Sopenharmony_ci			     loff_t offset, size_t count, bool read)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	if (read)
23562306a36Sopenharmony_ci		memcpy(buffer, fw_priv->data + offset, count);
23662306a36Sopenharmony_ci	else
23762306a36Sopenharmony_ci		memcpy(fw_priv->data + offset, buffer, count);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic void firmware_rw(struct fw_priv *fw_priv, char *buffer,
24162306a36Sopenharmony_ci			loff_t offset, size_t count, bool read)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	while (count) {
24462306a36Sopenharmony_ci		int page_nr = offset >> PAGE_SHIFT;
24562306a36Sopenharmony_ci		int page_ofs = offset & (PAGE_SIZE - 1);
24662306a36Sopenharmony_ci		int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		if (read)
24962306a36Sopenharmony_ci			memcpy_from_page(buffer, fw_priv->pages[page_nr],
25062306a36Sopenharmony_ci					 page_ofs, page_cnt);
25162306a36Sopenharmony_ci		else
25262306a36Sopenharmony_ci			memcpy_to_page(fw_priv->pages[page_nr], page_ofs,
25362306a36Sopenharmony_ci				       buffer, page_cnt);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		buffer += page_cnt;
25662306a36Sopenharmony_ci		offset += page_cnt;
25762306a36Sopenharmony_ci		count -= page_cnt;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
26262306a36Sopenharmony_ci				  struct bin_attribute *bin_attr,
26362306a36Sopenharmony_ci				  char *buffer, loff_t offset, size_t count)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
26662306a36Sopenharmony_ci	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
26762306a36Sopenharmony_ci	struct fw_priv *fw_priv;
26862306a36Sopenharmony_ci	ssize_t ret_count;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	mutex_lock(&fw_lock);
27162306a36Sopenharmony_ci	fw_priv = fw_sysfs->fw_priv;
27262306a36Sopenharmony_ci	if (!fw_priv || fw_state_is_done(fw_priv)) {
27362306a36Sopenharmony_ci		ret_count = -ENODEV;
27462306a36Sopenharmony_ci		goto out;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci	if (offset > fw_priv->size) {
27762306a36Sopenharmony_ci		ret_count = 0;
27862306a36Sopenharmony_ci		goto out;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci	if (count > fw_priv->size - offset)
28162306a36Sopenharmony_ci		count = fw_priv->size - offset;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	ret_count = count;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (fw_priv->data)
28662306a36Sopenharmony_ci		firmware_rw_data(fw_priv, buffer, offset, count, true);
28762306a36Sopenharmony_ci	else
28862306a36Sopenharmony_ci		firmware_rw(fw_priv, buffer, offset, count, true);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ciout:
29162306a36Sopenharmony_ci	mutex_unlock(&fw_lock);
29262306a36Sopenharmony_ci	return ret_count;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	int err;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	err = fw_grow_paged_buf(fw_sysfs->fw_priv,
30062306a36Sopenharmony_ci				PAGE_ALIGN(min_size) >> PAGE_SHIFT);
30162306a36Sopenharmony_ci	if (err)
30262306a36Sopenharmony_ci		fw_load_abort(fw_sysfs);
30362306a36Sopenharmony_ci	return err;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci/**
30762306a36Sopenharmony_ci * firmware_data_write() - write method for firmware
30862306a36Sopenharmony_ci * @filp: open sysfs file
30962306a36Sopenharmony_ci * @kobj: kobject for the device
31062306a36Sopenharmony_ci * @bin_attr: bin_attr structure
31162306a36Sopenharmony_ci * @buffer: buffer being written
31262306a36Sopenharmony_ci * @offset: buffer offset for write in total data store area
31362306a36Sopenharmony_ci * @count: buffer size
31462306a36Sopenharmony_ci *
31562306a36Sopenharmony_ci *	Data written to the 'data' attribute will be later handed to
31662306a36Sopenharmony_ci *	the driver as a firmware image.
31762306a36Sopenharmony_ci **/
31862306a36Sopenharmony_cistatic ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
31962306a36Sopenharmony_ci				   struct bin_attribute *bin_attr,
32062306a36Sopenharmony_ci				   char *buffer, loff_t offset, size_t count)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
32362306a36Sopenharmony_ci	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
32462306a36Sopenharmony_ci	struct fw_priv *fw_priv;
32562306a36Sopenharmony_ci	ssize_t retval;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (!capable(CAP_SYS_RAWIO))
32862306a36Sopenharmony_ci		return -EPERM;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	mutex_lock(&fw_lock);
33162306a36Sopenharmony_ci	fw_priv = fw_sysfs->fw_priv;
33262306a36Sopenharmony_ci	if (!fw_priv || fw_state_is_done(fw_priv)) {
33362306a36Sopenharmony_ci		retval = -ENODEV;
33462306a36Sopenharmony_ci		goto out;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (fw_priv->data) {
33862306a36Sopenharmony_ci		if (offset + count > fw_priv->allocated_size) {
33962306a36Sopenharmony_ci			retval = -ENOMEM;
34062306a36Sopenharmony_ci			goto out;
34162306a36Sopenharmony_ci		}
34262306a36Sopenharmony_ci		firmware_rw_data(fw_priv, buffer, offset, count, false);
34362306a36Sopenharmony_ci		retval = count;
34462306a36Sopenharmony_ci	} else {
34562306a36Sopenharmony_ci		retval = fw_realloc_pages(fw_sysfs, offset + count);
34662306a36Sopenharmony_ci		if (retval)
34762306a36Sopenharmony_ci			goto out;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		retval = count;
35062306a36Sopenharmony_ci		firmware_rw(fw_priv, buffer, offset, count, false);
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	fw_priv->size = max_t(size_t, offset + count, fw_priv->size);
35462306a36Sopenharmony_ciout:
35562306a36Sopenharmony_ci	mutex_unlock(&fw_lock);
35662306a36Sopenharmony_ci	return retval;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic struct bin_attribute firmware_attr_data = {
36062306a36Sopenharmony_ci	.attr = { .name = "data", .mode = 0644 },
36162306a36Sopenharmony_ci	.size = 0,
36262306a36Sopenharmony_ci	.read = firmware_data_read,
36362306a36Sopenharmony_ci	.write = firmware_data_write,
36462306a36Sopenharmony_ci};
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic struct attribute *fw_dev_attrs[] = {
36762306a36Sopenharmony_ci	&dev_attr_loading.attr,
36862306a36Sopenharmony_ci#ifdef CONFIG_FW_UPLOAD
36962306a36Sopenharmony_ci	&dev_attr_cancel.attr,
37062306a36Sopenharmony_ci	&dev_attr_status.attr,
37162306a36Sopenharmony_ci	&dev_attr_error.attr,
37262306a36Sopenharmony_ci	&dev_attr_remaining_size.attr,
37362306a36Sopenharmony_ci#endif
37462306a36Sopenharmony_ci	NULL
37562306a36Sopenharmony_ci};
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic struct bin_attribute *fw_dev_bin_attrs[] = {
37862306a36Sopenharmony_ci	&firmware_attr_data,
37962306a36Sopenharmony_ci	NULL
38062306a36Sopenharmony_ci};
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic const struct attribute_group fw_dev_attr_group = {
38362306a36Sopenharmony_ci	.attrs = fw_dev_attrs,
38462306a36Sopenharmony_ci	.bin_attrs = fw_dev_bin_attrs,
38562306a36Sopenharmony_ci#ifdef CONFIG_FW_UPLOAD
38662306a36Sopenharmony_ci	.is_visible = fw_upload_is_visible,
38762306a36Sopenharmony_ci#endif
38862306a36Sopenharmony_ci};
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic const struct attribute_group *fw_dev_attr_groups[] = {
39162306a36Sopenharmony_ci	&fw_dev_attr_group,
39262306a36Sopenharmony_ci	NULL
39362306a36Sopenharmony_ci};
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistruct fw_sysfs *
39662306a36Sopenharmony_cifw_create_instance(struct firmware *firmware, const char *fw_name,
39762306a36Sopenharmony_ci		   struct device *device, u32 opt_flags)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct fw_sysfs *fw_sysfs;
40062306a36Sopenharmony_ci	struct device *f_dev;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	fw_sysfs = kzalloc(sizeof(*fw_sysfs), GFP_KERNEL);
40362306a36Sopenharmony_ci	if (!fw_sysfs) {
40462306a36Sopenharmony_ci		fw_sysfs = ERR_PTR(-ENOMEM);
40562306a36Sopenharmony_ci		goto exit;
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	fw_sysfs->nowait = !!(opt_flags & FW_OPT_NOWAIT);
40962306a36Sopenharmony_ci	fw_sysfs->fw = firmware;
41062306a36Sopenharmony_ci	f_dev = &fw_sysfs->dev;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	device_initialize(f_dev);
41362306a36Sopenharmony_ci	dev_set_name(f_dev, "%s", fw_name);
41462306a36Sopenharmony_ci	f_dev->parent = device;
41562306a36Sopenharmony_ci	f_dev->class = &firmware_class;
41662306a36Sopenharmony_ci	f_dev->groups = fw_dev_attr_groups;
41762306a36Sopenharmony_ciexit:
41862306a36Sopenharmony_ci	return fw_sysfs;
41962306a36Sopenharmony_ci}
420