Commit 2887b395 authored by Ming Lei's avatar Ming Lei Committed by Greg Kroah-Hartman

firmware loader: introduce cache_firmware and uncache_firmware

This patches introduce two kernel APIs of cache_firmware and
uncache_firmware, both of which take the firmware file name
as the only parameter.

So any drivers can call cache_firmware to cache the specified
firmware file into kernel memory, and can use the cached firmware
in situations which can't request firmware from user space.
Signed-off-by: default avatarMing Lei <ming.lei@canonical.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 1f2b7959
...@@ -142,6 +142,17 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name, ...@@ -142,6 +142,17 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
return buf; 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, static int fw_lookup_and_allocate_buf(const char *fw_name,
struct firmware_cache *fwc, struct firmware_cache *fwc,
struct firmware_buf **buf) struct firmware_buf **buf)
...@@ -149,14 +160,13 @@ static int fw_lookup_and_allocate_buf(const char *fw_name, ...@@ -149,14 +160,13 @@ static int fw_lookup_and_allocate_buf(const char *fw_name,
struct firmware_buf *tmp; struct firmware_buf *tmp;
spin_lock(&fwc->lock); spin_lock(&fwc->lock);
list_for_each_entry(tmp, &fwc->head, list) tmp = __fw_lookup_buf(fw_name);
if (!strcmp(tmp->fw_id, fw_name)) { if (tmp) {
kref_get(&tmp->ref); kref_get(&tmp->ref);
spin_unlock(&fwc->lock); spin_unlock(&fwc->lock);
*buf = tmp; *buf = tmp;
return 1; return 1;
} }
tmp = __allocate_fw_buf(fw_name, fwc); tmp = __allocate_fw_buf(fw_name, fwc);
if (tmp) if (tmp)
list_add(&tmp->list, &fwc->head); list_add(&tmp->list, &fwc->head);
...@@ -167,6 +177,18 @@ static int fw_lookup_and_allocate_buf(const char *fw_name, ...@@ -167,6 +177,18 @@ static int fw_lookup_and_allocate_buf(const char *fw_name,
return tmp ? 0 : -ENOMEM; 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) static void __fw_free_buf(struct kref *ref)
{ {
struct firmware_buf *buf = to_fwbuf(ref); struct firmware_buf *buf = to_fwbuf(ref);
...@@ -852,6 +874,66 @@ request_firmware_nowait( ...@@ -852,6 +874,66 @@ request_firmware_nowait(
return 0; 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) static int __init firmware_class_init(void)
{ {
fw_cache_init(); fw_cache_init();
...@@ -869,3 +951,5 @@ module_exit(firmware_class_exit); ...@@ -869,3 +951,5 @@ module_exit(firmware_class_exit);
EXPORT_SYMBOL(release_firmware); EXPORT_SYMBOL(release_firmware);
EXPORT_SYMBOL(request_firmware); EXPORT_SYMBOL(request_firmware);
EXPORT_SYMBOL(request_firmware_nowait); EXPORT_SYMBOL(request_firmware_nowait);
EXPORT_SYMBOL_GPL(cache_firmware);
EXPORT_SYMBOL_GPL(uncache_firmware);
...@@ -47,6 +47,8 @@ int request_firmware_nowait( ...@@ -47,6 +47,8 @@ int request_firmware_nowait(
void (*cont)(const struct firmware *fw, void *context)); void (*cont)(const struct firmware *fw, void *context));
void release_firmware(const struct firmware *fw); void release_firmware(const struct firmware *fw);
int cache_firmware(const char *name);
int uncache_firmware(const char *name);
#else #else
static inline int request_firmware(const struct firmware **fw, static inline int request_firmware(const struct firmware **fw,
const char *name, const char *name,
...@@ -65,6 +67,16 @@ static inline int request_firmware_nowait( ...@@ -65,6 +67,16 @@ static inline int request_firmware_nowait(
static inline void release_firmware(const struct firmware *fw) 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
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment