diff options
| -rw-r--r-- | drivers/base/firmware_class.c | 100 | ||||
| -rw-r--r-- | include/linux/firmware.h | 12 | 
2 files changed, 104 insertions, 8 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 848ad97e8d7..fc119ce6fdb 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -142,6 +142,17 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name,  	return buf;  } +static struct firmware_buf *__fw_lookup_buf(const char *fw_name) +{ +	struct firmware_buf *tmp; +	struct firmware_cache *fwc = &fw_cache; + +	list_for_each_entry(tmp, &fwc->head, list) +		if (!strcmp(tmp->fw_id, fw_name)) +			return tmp; +	return NULL; +} +  static int fw_lookup_and_allocate_buf(const char *fw_name,  				      struct firmware_cache *fwc,  				      struct firmware_buf **buf) @@ -149,14 +160,13 @@ static int fw_lookup_and_allocate_buf(const char *fw_name,  	struct firmware_buf *tmp;  	spin_lock(&fwc->lock); -	list_for_each_entry(tmp, &fwc->head, list) -		if (!strcmp(tmp->fw_id, fw_name)) { -			kref_get(&tmp->ref); -			spin_unlock(&fwc->lock); -			*buf = tmp; -			return 1; -		} - +	tmp = __fw_lookup_buf(fw_name); +	if (tmp) { +		kref_get(&tmp->ref); +		spin_unlock(&fwc->lock); +		*buf = tmp; +		return 1; +	}  	tmp = __allocate_fw_buf(fw_name, fwc);  	if (tmp)  		list_add(&tmp->list, &fwc->head); @@ -167,6 +177,18 @@ static int fw_lookup_and_allocate_buf(const char *fw_name,  	return tmp ? 0 : -ENOMEM;  } +static struct firmware_buf *fw_lookup_buf(const char *fw_name) +{ +	struct firmware_buf *tmp; +	struct firmware_cache *fwc = &fw_cache; + +	spin_lock(&fwc->lock); +	tmp = __fw_lookup_buf(fw_name); +	spin_unlock(&fwc->lock); + +	return tmp; +} +  static void __fw_free_buf(struct kref *ref)  {  	struct firmware_buf *buf = to_fwbuf(ref); @@ -852,6 +874,66 @@ request_firmware_nowait(  	return 0;  } +/** + * cache_firmware - cache one firmware image in kernel memory space + * @fw_name: the firmware image name + * + * Cache firmware in kernel memory so that drivers can use it when + * system isn't ready for them to request firmware image from userspace. + * Once it returns successfully, driver can use request_firmware or its + * nowait version to get the cached firmware without any interacting + * with userspace + * + * Return 0 if the firmware image has been cached successfully + * Return !0 otherwise + * + */ +int cache_firmware(const char *fw_name) +{ +	int ret; +	const struct firmware *fw; + +	pr_debug("%s: %s\n", __func__, fw_name); + +	ret = request_firmware(&fw, fw_name, NULL); +	if (!ret) +		kfree(fw); + +	pr_debug("%s: %s ret=%d\n", __func__, fw_name, ret); + +	return ret; +} + +/** + * uncache_firmware - remove one cached firmware image + * @fw_name: the firmware image name + * + * Uncache one firmware image which has been cached successfully + * before. + * + * Return 0 if the firmware cache has been removed successfully + * Return !0 otherwise + * + */ +int uncache_firmware(const char *fw_name) +{ +	struct firmware_buf *buf; +	struct firmware fw; + +	pr_debug("%s: %s\n", __func__, fw_name); + +	if (fw_get_builtin_firmware(&fw, fw_name)) +		return 0; + +	buf = fw_lookup_buf(fw_name); +	if (buf) { +		fw_free_buf(buf); +		return 0; +	} + +	return -EINVAL; +} +  static int __init firmware_class_init(void)  {  	fw_cache_init(); @@ -869,3 +951,5 @@ module_exit(firmware_class_exit);  EXPORT_SYMBOL(release_firmware);  EXPORT_SYMBOL(request_firmware);  EXPORT_SYMBOL(request_firmware_nowait); +EXPORT_SYMBOL_GPL(cache_firmware); +EXPORT_SYMBOL_GPL(uncache_firmware); diff --git a/include/linux/firmware.h b/include/linux/firmware.h index e85b771f3d8..e4279fedb93 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -47,6 +47,8 @@ int request_firmware_nowait(  	void (*cont)(const struct firmware *fw, void *context));  void release_firmware(const struct firmware *fw); +int cache_firmware(const char *name); +int uncache_firmware(const char *name);  #else  static inline int request_firmware(const struct firmware **fw,  				   const char *name, @@ -65,6 +67,16 @@ static inline int request_firmware_nowait(  static inline void release_firmware(const struct firmware *fw)  {  } + +static inline int cache_firmware(const char *name) +{ +	return -ENOENT; +} + +static inline int uncache_firmware(const char *name) +{ +	return -EINVAL; +}  #endif  #endif  |