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: Ming Lei <ming.lei@canonical.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
1f2b79599e
commit
2887b3959c
2 changed files with 104 additions and 8 deletions
|
|
@ -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,
|
||||||
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,
|
||||||
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(
|
||||||
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);
|
||||||
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(
|
||||||
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(
|
||||||
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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue