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