Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 372806 Details for
Bug 503886
sys-kernel/gentoo-sources-3.4.82 - backport direct firmware loading support
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
3.4.83-backport_direct_firmware_loading.patch
3.4.83-backport_direct_firmware_loading.patch (text/plain), 41.00 KB, created by
Andreas Sturmlechner
on 2014-03-16 14:47:40 UTC
(
hide
)
Description:
3.4.83-backport_direct_firmware_loading.patch
Filename:
MIME Type:
Creator:
Andreas Sturmlechner
Created:
2014-03-16 14:47:40 UTC
Size:
41.00 KB
patch
obsolete
>--- linux-3.4.83/include/linux/firmware.h 2012-05-21 00:29:13.000000000 +0200 >+++ linux-3.10.33/include/linux/firmware.h 2013-07-01 00:13:29.000000000 +0200 >@@ -12,6 +12,9 @@ > size_t size; > const u8 *data; > struct page **pages; >+ >+ /* firmware loader private fields */ >+ void *priv; > }; > > struct module; >@@ -44,6 +47,8 @@ > 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, >@@ -62,6 +67,16 @@ > 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 >--- linux-3.4.83/drivers/base/firmware_class.c 2012-05-21 00:29:13.000000000 +0200 >+++ linux-3.10.33/drivers/base/firmware_class.c 2013-07-01 00:13:29.000000000 +0200 >@@ -21,8 +21,17 @@ > #include <linux/firmware.h> > #include <linux/slab.h> > #include <linux/sched.h> >+#include <linux/file.h> >+#include <linux/list.h> >+#include <linux/async.h> >+#include <linux/pm.h> >+#include <linux/suspend.h> >+#include <linux/syscore_ops.h> > > #define to_dev(obj) container_of(obj, struct device, kobj) >+#include <generated/utsrelease.h> >+ >+#include "base.h" > > MODULE_AUTHOR("Manuel Estrada Sainz"); > MODULE_DESCRIPTION("Multi purpose firmware loading support"); >@@ -87,23 +96,352 @@ > return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT; > } > >-/* fw_lock could be moved to 'struct firmware_priv' but since it is just >- * guarding for corner cases a global lock should be OK */ >-static DEFINE_MUTEX(fw_lock); >+struct firmware_cache { >+ /* firmware_buf instance will be added into the below list */ >+ spinlock_t lock; >+ struct list_head head; >+ int state; >+ >+#ifdef CONFIG_PM_SLEEP >+ /* >+ * Names of firmware images which have been cached successfully >+ * will be added into the below list so that device uncache >+ * helper can trace which firmware images have been cached >+ * before. >+ */ >+ spinlock_t name_lock; >+ struct list_head fw_names; > >-struct firmware_priv { >+ struct delayed_work work; >+ >+ struct notifier_block pm_notify; >+#endif >+}; >+ >+struct firmware_buf { >+ struct kref ref; >+ struct list_head list; > struct completion completion; >- struct firmware *fw; >+ struct firmware_cache *fwc; > unsigned long status; >+ void *data; >+ size_t size; >+#ifdef CONFIG_FW_LOADER_USER_HELPER >+ bool is_paged_buf; > struct page **pages; > int nr_pages; > int page_array_size; >- struct timer_list timeout; >- struct device dev; >- bool nowait; >+#endif > char fw_id[]; > }; > >+struct fw_cache_entry { >+ struct list_head list; >+ char name[]; >+}; >+ >+struct fw_name_devm { >+ unsigned long magic; >+ char name[]; >+}; >+ >+#define to_fwbuf(d) container_of(d, struct firmware_buf, ref) >+ >+#define FW_LOADER_NO_CACHE 0 >+#define FW_LOADER_START_CACHE 1 >+ >+static int fw_cache_piggyback_on_request(const char *name); >+ >+/* fw_lock could be moved to 'struct firmware_priv' but since it is just >+ * guarding for corner cases a global lock should be OK */ >+static DEFINE_MUTEX(fw_lock); >+ >+static struct firmware_cache fw_cache; >+ >+static struct firmware_buf *__allocate_fw_buf(const char *fw_name, >+ struct firmware_cache *fwc) >+{ >+ struct firmware_buf *buf; >+ >+ buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1 , GFP_ATOMIC); >+ >+ if (!buf) >+ return buf; >+ >+ kref_init(&buf->ref); >+ strcpy(buf->fw_id, fw_name); >+ buf->fwc = fwc; >+ init_completion(&buf->completion); >+ >+ pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, 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, >+ struct firmware_cache *fwc, >+ struct firmware_buf **buf) >+{ >+ struct firmware_buf *tmp; >+ >+ spin_lock(&fwc->lock); >+ 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); >+ spin_unlock(&fwc->lock); >+ >+ *buf = tmp; >+ >+ 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); >+ struct firmware_cache *fwc = buf->fwc; >+ >+ pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", >+ __func__, buf->fw_id, buf, buf->data, >+ (unsigned int)buf->size); >+ >+ list_del(&buf->list); >+ spin_unlock(&fwc->lock); >+ >+#ifdef CONFIG_FW_LOADER_USER_HELPER >+ if (buf->is_paged_buf) { >+ int i; >+ vunmap(buf->data); >+ for (i = 0; i < buf->nr_pages; i++) >+ __free_page(buf->pages[i]); >+ kfree(buf->pages); >+ } else >+#endif >+ vfree(buf->data); >+ kfree(buf); >+} >+ >+static void fw_free_buf(struct firmware_buf *buf) >+{ >+ struct firmware_cache *fwc = buf->fwc; >+ spin_lock(&fwc->lock); >+ if (!kref_put(&buf->ref, __fw_free_buf)) >+ spin_unlock(&fwc->lock); >+} >+ >+/* direct firmware loading support */ >+static char fw_path_para[256]; >+static const char * const fw_path[] = { >+ fw_path_para, >+ "/lib/firmware/updates/" UTS_RELEASE, >+ "/lib/firmware/updates", >+ "/lib/firmware/" UTS_RELEASE, >+ "/lib/firmware" >+}; >+ >+/* >+ * Typical usage is that passing 'firmware_class.path=$CUSTOMIZED_PATH' >+ * from kernel command line because firmware_class is generally built in >+ * kernel instead of module. >+ */ >+module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644); >+MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path"); >+ >+/* Don't inline this: 'struct kstat' is biggish */ >+static noinline_for_stack long fw_file_size(struct file *file) >+{ >+ struct kstat st; >+ if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st)) >+ return -1; >+ if (!S_ISREG(st.mode)) >+ return -1; >+ if (st.size != (long)st.size) >+ return -1; >+ return st.size; >+} >+ >+static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf) >+{ >+ long size; >+ char *buf; >+ >+ size = fw_file_size(file); >+ if (size <= 0) >+ return false; >+ buf = vmalloc(size); >+ if (!buf) >+ return false; >+ if (kernel_read(file, 0, buf, size) != size) { >+ vfree(buf); >+ return false; >+ } >+ fw_buf->data = buf; >+ fw_buf->size = size; >+ return true; >+} >+ >+static bool fw_get_filesystem_firmware(struct device *device, >+ struct firmware_buf *buf) >+{ >+ int i; >+ bool success = false; >+ char *path = __getname(); >+ >+ for (i = 0; i < ARRAY_SIZE(fw_path); i++) { >+ struct file *file; >+ >+ /* skip the unset customized path */ >+ if (!fw_path[i][0]) >+ continue; >+ >+ snprintf(path, PATH_MAX, "%s/%s", fw_path[i], buf->fw_id); >+ >+ file = filp_open(path, O_RDONLY, 0); >+ if (IS_ERR(file)) >+ continue; >+ success = fw_read_file_contents(file, buf); >+ fput(file); >+ if (success) >+ break; >+ } >+ __putname(path); >+ >+ if (success) { >+ dev_dbg(device, "firmware: direct-loading firmware %s\n", >+ buf->fw_id); >+ mutex_lock(&fw_lock); >+ set_bit(FW_STATUS_DONE, &buf->status); >+ complete_all(&buf->completion); >+ mutex_unlock(&fw_lock); >+ } >+ >+ return success; >+} >+ >+/* firmware holds the ownership of pages */ >+static void firmware_free_data(const struct firmware *fw) >+{ >+ /* Loaded directly? */ >+ if (!fw->priv) { >+ vfree(fw->data); >+ return; >+ } >+ fw_free_buf(fw->priv); >+} >+ >+/* store the pages buffer info firmware from buf */ >+static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw) >+{ >+ fw->priv = buf; >+#ifdef CONFIG_FW_LOADER_USER_HELPER >+ fw->pages = buf->pages; >+#endif >+ fw->size = buf->size; >+ fw->data = buf->data; >+ >+ pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", >+ __func__, buf->fw_id, buf, buf->data, >+ (unsigned int)buf->size); >+} >+ >+#ifdef CONFIG_PM_SLEEP >+static void fw_name_devm_release(struct device *dev, void *res) >+{ >+ struct fw_name_devm *fwn = res; >+ >+ if (fwn->magic == (unsigned long)&fw_cache) >+ pr_debug("%s: fw_name-%s devm-%p released\n", >+ __func__, fwn->name, res); >+} >+ >+static int fw_devm_match(struct device *dev, void *res, >+ void *match_data) >+{ >+ struct fw_name_devm *fwn = res; >+ >+ return (fwn->magic == (unsigned long)&fw_cache) && >+ !strcmp(fwn->name, match_data); >+} >+ >+static struct fw_name_devm *fw_find_devm_name(struct device *dev, >+ const char *name) >+{ >+ struct fw_name_devm *fwn; >+ >+ fwn = devres_find(dev, fw_name_devm_release, >+ fw_devm_match, (void *)name); >+ return fwn; >+} >+ >+/* add firmware name into devres list */ >+static int fw_add_devm_name(struct device *dev, const char *name) >+{ >+ struct fw_name_devm *fwn; >+ >+ fwn = fw_find_devm_name(dev, name); >+ if (fwn) >+ return 1; >+ >+ fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm) + >+ strlen(name) + 1, GFP_KERNEL); >+ if (!fwn) >+ return -ENOMEM; >+ >+ fwn->magic = (unsigned long)&fw_cache; >+ strcpy(fwn->name, name); >+ devres_add(dev, fwn); >+ >+ return 0; >+} >+#else >+static int fw_add_devm_name(struct device *dev, const char *name) >+{ >+ return 0; >+} >+#endif >+ >+ >+/* >+ * user-mode helper code >+ */ >+#ifdef CONFIG_FW_LOADER_USER_HELPER >+struct firmware_priv { >+ struct delayed_work timeout_work; >+ bool nowait; >+ struct device dev; >+ struct firmware_buf *buf; >+ struct firmware *fw; >+}; >+ > static struct firmware_priv *to_firmware_priv(struct device *dev) > { > return container_of(dev, struct firmware_priv, dev); >@@ -111,11 +449,25 @@ > > static void fw_load_abort(struct firmware_priv *fw_priv) > { >- set_bit(FW_STATUS_ABORT, &fw_priv->status); >- wmb(); >- complete(&fw_priv->completion); >+ struct firmware_buf *buf = fw_priv->buf; >+ >+ /* >+ * There is a small window in which user can write to 'loading' >+ * between loading done and disappearance of 'loading' >+ */ >+ if (test_bit(FW_STATUS_DONE, &buf->status)) >+ return; >+ >+ set_bit(FW_STATUS_ABORT, &buf->status); >+ complete_all(&buf->completion); >+ >+ /* avoid user action after loading abort */ >+ fw_priv->buf = NULL; > } > >+#define is_fw_load_aborted(buf) \ >+ test_bit(FW_STATUS_ABORT, &(buf)->status) >+ > static ssize_t firmware_timeout_show(struct class *class, > struct class_attribute *attr, > char *buf) >@@ -156,11 +508,7 @@ > static void fw_dev_release(struct device *dev) > { > struct firmware_priv *fw_priv = to_firmware_priv(dev); >- int i; > >- for (i = 0; i < fw_priv->nr_pages; i++) >- __free_page(fw_priv->pages[i]); >- kfree(fw_priv->pages); > kfree(fw_priv); > > module_put(THIS_MODULE); >@@ -170,7 +518,7 @@ > { > struct firmware_priv *fw_priv = to_firmware_priv(dev); > >- if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id)) >+ if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->buf->fw_id)) > return -ENOMEM; > if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout)) > return -ENOMEM; >@@ -191,26 +539,35 @@ > struct device_attribute *attr, char *buf) > { > struct firmware_priv *fw_priv = to_firmware_priv(dev); >- int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status); >+ int loading = 0; > >- return sprintf(buf, "%d\n", loading); >-} >+ mutex_lock(&fw_lock); >+ if (fw_priv->buf) >+ loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf->status); >+ mutex_unlock(&fw_lock); > >-static void firmware_free_data(const struct firmware *fw) >-{ >- int i; >- vunmap(fw->data); >- if (fw->pages) { >- for (i = 0; i < PFN_UP(fw->size); i++) >- __free_page(fw->pages[i]); >- kfree(fw->pages); >- } >+ return sprintf(buf, "%d\n", loading); > } > > /* Some architectures don't have PAGE_KERNEL_RO */ > #ifndef PAGE_KERNEL_RO > #define PAGE_KERNEL_RO PAGE_KERNEL > #endif >+ >+/* one pages buffer should be mapped/unmapped only once */ >+static int fw_map_pages_buf(struct firmware_buf *buf) >+{ >+ if (!buf->is_paged_buf) >+ return 0; >+ >+ if (buf->data) >+ vunmap(buf->data); >+ buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO); >+ if (!buf->data) >+ return -ENOMEM; >+ return 0; >+} >+ > /** > * firmware_loading_store - set value in the 'loading' control file > * @dev: device pointer >@@ -229,45 +586,41 @@ > const char *buf, size_t count) > { > struct firmware_priv *fw_priv = to_firmware_priv(dev); >+ struct firmware_buf *fw_buf; > int loading = simple_strtol(buf, NULL, 10); > int i; > > mutex_lock(&fw_lock); >- >- if (!fw_priv->fw) >+ fw_buf = fw_priv->buf; >+ if (!fw_buf) > goto out; > > switch (loading) { > case 1: >- firmware_free_data(fw_priv->fw); >- memset(fw_priv->fw, 0, sizeof(struct firmware)); >- /* If the pages are not owned by 'struct firmware' */ >- for (i = 0; i < fw_priv->nr_pages; i++) >- __free_page(fw_priv->pages[i]); >- kfree(fw_priv->pages); >- fw_priv->pages = NULL; >- fw_priv->page_array_size = 0; >- fw_priv->nr_pages = 0; >- set_bit(FW_STATUS_LOADING, &fw_priv->status); >+ /* discarding any previous partial load */ >+ if (!test_bit(FW_STATUS_DONE, &fw_buf->status)) { >+ for (i = 0; i < fw_buf->nr_pages; i++) >+ __free_page(fw_buf->pages[i]); >+ kfree(fw_buf->pages); >+ fw_buf->pages = NULL; >+ fw_buf->page_array_size = 0; >+ fw_buf->nr_pages = 0; >+ set_bit(FW_STATUS_LOADING, &fw_buf->status); >+ } > break; > case 0: >- if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { >- vunmap(fw_priv->fw->data); >- fw_priv->fw->data = vmap(fw_priv->pages, >- fw_priv->nr_pages, >- 0, PAGE_KERNEL_RO); >- if (!fw_priv->fw->data) { >- dev_err(dev, "%s: vmap() failed\n", __func__); >- goto err; >- } >- /* Pages are now owned by 'struct firmware' */ >- fw_priv->fw->pages = fw_priv->pages; >- fw_priv->pages = NULL; >- >- fw_priv->page_array_size = 0; >- fw_priv->nr_pages = 0; >- complete(&fw_priv->completion); >- clear_bit(FW_STATUS_LOADING, &fw_priv->status); >+ if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) { >+ set_bit(FW_STATUS_DONE, &fw_buf->status); >+ clear_bit(FW_STATUS_LOADING, &fw_buf->status); >+ >+ /* >+ * Several loading requests may be pending on >+ * one same firmware buf, so let all requests >+ * see the mapped 'buf->data' once the loading >+ * is completed. >+ * */ >+ fw_map_pages_buf(fw_buf); >+ complete_all(&fw_buf->completion); > break; > } > /* fallthrough */ >@@ -275,7 +628,6 @@ > dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading); > /* fallthrough */ > case -1: >- err: > fw_load_abort(fw_priv); > break; > } >@@ -290,23 +642,23 @@ > struct bin_attribute *bin_attr, > char *buffer, loff_t offset, size_t count) > { > struct device *dev = to_dev(kobj); > struct firmware_priv *fw_priv = to_firmware_priv(dev); >- struct firmware *fw; >+ struct firmware_buf *buf; > ssize_t ret_count; > > mutex_lock(&fw_lock); >- fw = fw_priv->fw; >- if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) { >+ buf = fw_priv->buf; >+ if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) { > ret_count = -ENODEV; > goto out; > } >- if (offset > fw->size) { >+ if (offset > buf->size) { > ret_count = 0; > goto out; > } >- if (count > fw->size - offset) >- count = fw->size - offset; >+ if (count > buf->size - offset) >+ count = buf->size - offset; > > ret_count = count; > >@@ -316,11 +668,11 @@ > int page_ofs = offset & (PAGE_SIZE-1); > int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count); > >- page_data = kmap(fw_priv->pages[page_nr]); >+ page_data = kmap(buf->pages[page_nr]); > > memcpy(buffer, page_data + page_ofs, page_cnt); > >- kunmap(fw_priv->pages[page_nr]); >+ kunmap(buf->pages[page_nr]); > buffer += page_cnt; > offset += page_cnt; > count -= page_cnt; >@@ -332,12 +684,13 @@ > > static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) > { >+ struct firmware_buf *buf = fw_priv->buf; > int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT; > > /* If the array of pages is too small, grow it... */ >- if (fw_priv->page_array_size < pages_needed) { >+ if (buf->page_array_size < pages_needed) { > int new_array_size = max(pages_needed, >- fw_priv->page_array_size * 2); >+ buf->page_array_size * 2); > struct page **new_pages; > > new_pages = kmalloc(new_array_size * sizeof(void *), >@@ -346,24 +699,24 @@ > fw_load_abort(fw_priv); > return -ENOMEM; > } >- memcpy(new_pages, fw_priv->pages, >- fw_priv->page_array_size * sizeof(void *)); >- memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) * >- (new_array_size - fw_priv->page_array_size)); >- kfree(fw_priv->pages); >- fw_priv->pages = new_pages; >- fw_priv->page_array_size = new_array_size; >+ memcpy(new_pages, buf->pages, >+ buf->page_array_size * sizeof(void *)); >+ memset(&new_pages[buf->page_array_size], 0, sizeof(void *) * >+ (new_array_size - buf->page_array_size)); >+ kfree(buf->pages); >+ buf->pages = new_pages; >+ buf->page_array_size = new_array_size; > } > >- while (fw_priv->nr_pages < pages_needed) { >- fw_priv->pages[fw_priv->nr_pages] = >+ while (buf->nr_pages < pages_needed) { >+ buf->pages[buf->nr_pages] = > alloc_page(GFP_KERNEL | __GFP_HIGHMEM); > >- if (!fw_priv->pages[fw_priv->nr_pages]) { >+ if (!buf->pages[buf->nr_pages]) { > fw_load_abort(fw_priv); > return -ENOMEM; > } >- fw_priv->nr_pages++; >+ buf->nr_pages++; > } > return 0; > } >@@ -384,20 +737,21 @@ > struct bin_attribute *bin_attr, > char *buffer, loff_t offset, size_t count) > { > struct device *dev = to_dev(kobj); > struct firmware_priv *fw_priv = to_firmware_priv(dev); >- struct firmware *fw; >+ struct firmware_buf *buf; > ssize_t retval; > > if (!capable(CAP_SYS_RAWIO)) > return -EPERM; > > mutex_lock(&fw_lock); >- fw = fw_priv->fw; >- if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) { >+ buf = fw_priv->buf; >+ if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) { > retval = -ENODEV; > goto out; > } >+ > retval = fw_realloc_buffer(fw_priv, offset + count); > if (retval) > goto out; >@@ -410,17 +764,17 @@ > int page_ofs = offset & (PAGE_SIZE - 1); > int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count); > >- page_data = kmap(fw_priv->pages[page_nr]); >+ page_data = kmap(buf->pages[page_nr]); > > memcpy(page_data + page_ofs, buffer, page_cnt); > >- kunmap(fw_priv->pages[page_nr]); >+ kunmap(buf->pages[page_nr]); > buffer += page_cnt; > offset += page_cnt; > count -= page_cnt; > } > >- fw->size = max_t(size_t, offset, fw->size); >+ buf->size = max_t(size_t, offset, buf->size); > out: > mutex_unlock(&fw_lock); > return retval; >@@ -433,11 +787,14 @@ > .write = firmware_data_write, > }; > >-static void firmware_class_timeout(u_long data) >+static void firmware_class_timeout_work(struct work_struct *work) > { >- struct firmware_priv *fw_priv = (struct firmware_priv *) data; >+ struct firmware_priv *fw_priv = container_of(work, >+ struct firmware_priv, timeout_work.work); > >+ mutex_lock(&fw_lock); > fw_load_abort(fw_priv); >+ mutex_unlock(&fw_lock); > } > > static struct firmware_priv * >@@ -447,70 +804,38 @@ > struct firmware_priv *fw_priv; > struct device *f_dev; > >- fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL); >+ fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL); > if (!fw_priv) { > dev_err(device, "%s: kmalloc failed\n", __func__); >- return ERR_PTR(-ENOMEM); >+ fw_priv = ERR_PTR(-ENOMEM); >+ goto exit; > } > >- fw_priv->fw = firmware; > fw_priv->nowait = nowait; >- strcpy(fw_priv->fw_id, fw_name); >- init_completion(&fw_priv->completion); >- setup_timer(&fw_priv->timeout, >- firmware_class_timeout, (u_long) fw_priv); >+ fw_priv->fw = firmware; >+ INIT_DELAYED_WORK(&fw_priv->timeout_work, >+ firmware_class_timeout_work); > > f_dev = &fw_priv->dev; > > device_initialize(f_dev); >- dev_set_name(f_dev, "%s", dev_name(device)); >+ dev_set_name(f_dev, "%s", fw_name); > f_dev->parent = device; > f_dev->class = &firmware_class; >- >+exit: > return fw_priv; > } > >-static struct firmware_priv * >-_request_firmware_prepare(const struct firmware **firmware_p, const char *name, >- struct device *device, bool uevent, bool nowait) >-{ >- struct firmware *firmware; >- struct firmware_priv *fw_priv; >- >- if (!firmware_p) >- return ERR_PTR(-EINVAL); >- >- *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); >- if (!firmware) { >- dev_err(device, "%s: kmalloc(struct firmware) failed\n", >- __func__); >- return ERR_PTR(-ENOMEM); >- } >- >- if (fw_get_builtin_firmware(firmware, name)) { >- dev_dbg(device, "firmware: using built-in firmware %s\n", name); >- return NULL; >- } >- >- fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); >- if (IS_ERR(fw_priv)) { >- release_firmware(firmware); >- *firmware_p = NULL; >- } >- return fw_priv; >-} >- >-static void _request_firmware_cleanup(const struct firmware **firmware_p) >-{ >- release_firmware(*firmware_p); >- *firmware_p = NULL; >-} >- >+/* load a firmware via user helper */ > static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, > long timeout) > { > int retval = 0; > struct device *f_dev = &fw_priv->dev; >+ struct firmware_buf *buf = fw_priv->buf; >+ >+ /* fall back on userspace loading */ >+ buf->is_paged_buf = true; > > dev_set_uevent_suppress(f_dev, true); > >@@ -537,24 +862,16 @@ > > if (uevent) { > dev_set_uevent_suppress(f_dev, false); >- dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_id); >+ dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id); > if (timeout != MAX_SCHEDULE_TIMEOUT) >- mod_timer(&fw_priv->timeout, >- round_jiffies_up(jiffies + timeout)); >+ schedule_delayed_work(&fw_priv->timeout_work, timeout); > > kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); > } > >- wait_for_completion(&fw_priv->completion); >+ wait_for_completion(&buf->completion); > >- set_bit(FW_STATUS_DONE, &fw_priv->status); >- del_timer_sync(&fw_priv->timeout); >- >- mutex_lock(&fw_lock); >- if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) >- retval = -ENOENT; >- fw_priv->fw = NULL; >- mutex_unlock(&fw_lock); >+ cancel_delayed_work_sync(&fw_priv->timeout_work); > > device_remove_file(f_dev, &dev_attr_loading); > err_del_bin_attr: >@@ -566,6 +883,186 @@ > return retval; > } > >+static int fw_load_from_user_helper(struct firmware *firmware, >+ const char *name, struct device *device, >+ bool uevent, bool nowait, long timeout) >+{ >+ struct firmware_priv *fw_priv; >+ >+ fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); >+ if (IS_ERR(fw_priv)) >+ return PTR_ERR(fw_priv); >+ >+ fw_priv->buf = firmware->priv; >+ return _request_firmware_load(fw_priv, uevent, timeout); >+} >+#else /* CONFIG_FW_LOADER_USER_HELPER */ >+static inline int >+fw_load_from_user_helper(struct firmware *firmware, const char *name, >+ struct device *device, bool uevent, bool nowait, >+ long timeout) >+{ >+ return -ENOENT; >+} >+ >+/* No abort during direct loading */ >+#define is_fw_load_aborted(buf) false >+ >+#endif /* CONFIG_FW_LOADER_USER_HELPER */ >+ >+ >+/* wait until the shared firmware_buf becomes ready (or error) */ >+static int sync_cached_firmware_buf(struct firmware_buf *buf) >+{ >+ int ret = 0; >+ >+ mutex_lock(&fw_lock); >+ while (!test_bit(FW_STATUS_DONE, &buf->status)) { >+ if (is_fw_load_aborted(buf)) { >+ ret = -ENOENT; >+ break; >+ } >+ mutex_unlock(&fw_lock); >+ wait_for_completion(&buf->completion); >+ mutex_lock(&fw_lock); >+ } >+ mutex_unlock(&fw_lock); >+ return ret; >+} >+ >+/* prepare firmware and firmware_buf structs; >+ * return 0 if a firmware is already assigned, 1 if need to load one, >+ * or a negative error code >+ */ >+static int >+_request_firmware_prepare(struct firmware **firmware_p, const char *name, >+ struct device *device) >+{ >+ struct firmware *firmware; >+ struct firmware_buf *buf; >+ int ret; >+ >+ *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); >+ if (!firmware) { >+ dev_err(device, "%s: kmalloc(struct firmware) failed\n", >+ __func__); >+ return -ENOMEM; >+ } >+ >+ if (fw_get_builtin_firmware(firmware, name)) { >+ dev_dbg(device, "firmware: using built-in firmware %s\n", name); >+ return 0; /* assigned */ >+ } >+ >+ ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf); >+ >+ /* >+ * bind with 'buf' now to avoid warning in failure path >+ * of requesting firmware. >+ */ >+ firmware->priv = buf; >+ >+ if (ret > 0) { >+ ret = sync_cached_firmware_buf(buf); >+ if (!ret) { >+ fw_set_page_data(buf, firmware); >+ return 0; /* assigned */ >+ } >+ } >+ >+ if (ret < 0) >+ return ret; >+ return 1; /* need to load */ >+} >+ >+static int assign_firmware_buf(struct firmware *fw, struct device *device) >+{ >+ struct firmware_buf *buf = fw->priv; >+ >+ mutex_lock(&fw_lock); >+ if (!buf->size || is_fw_load_aborted(buf)) { >+ mutex_unlock(&fw_lock); >+ return -ENOENT; >+ } >+ >+ /* >+ * add firmware name into devres list so that we can auto cache >+ * and uncache firmware for device. >+ * >+ * device may has been deleted already, but the problem >+ * should be fixed in devres or driver core. >+ */ >+ if (device) >+ fw_add_devm_name(device, buf->fw_id); >+ >+ /* >+ * After caching firmware image is started, let it piggyback >+ * on request firmware. >+ */ >+ if (buf->fwc->state == FW_LOADER_START_CACHE) { >+ if (fw_cache_piggyback_on_request(buf->fw_id)) >+ kref_get(&buf->ref); >+ } >+ >+ /* pass the pages buffer to driver at the last minute */ >+ fw_set_page_data(buf, fw); >+ mutex_unlock(&fw_lock); >+ return 0; >+} >+ >+/* called from request_firmware() and request_firmware_work_func() */ >+static int >+_request_firmware(const struct firmware **firmware_p, const char *name, >+ struct device *device, bool uevent, bool nowait) >+{ >+ struct firmware *fw; >+ long timeout; >+ int ret; >+ >+ if (!firmware_p) >+ return -EINVAL; >+ >+ ret = _request_firmware_prepare(&fw, name, device); >+ if (ret <= 0) /* error or already assigned */ >+ goto out; >+ >+ ret = 0; >+ timeout = firmware_loading_timeout(); >+ if (nowait) { >+ timeout = usermodehelper_read_lock_wait(timeout); >+ if (!timeout) { >+ dev_dbg(device, "firmware: %s loading timed out\n", >+ name); >+ ret = -EBUSY; >+ goto out; >+ } >+ } else { >+ ret = usermodehelper_read_trylock(); >+ if (WARN_ON(ret)) { >+ dev_err(device, "firmware: %s will not be loaded\n", >+ name); >+ goto out; >+ } >+ } >+ >+ if (!fw_get_filesystem_firmware(device, fw->priv)) >+ ret = fw_load_from_user_helper(fw, name, device, >+ uevent, nowait, timeout); >+ if (!ret) >+ ret = assign_firmware_buf(fw, device); >+ >+ usermodehelper_read_unlock(); >+ >+ out: >+ if (ret < 0) { >+ release_firmware(fw); >+ fw = NULL; >+ } >+ >+ *firmware_p = fw; >+ return ret; >+} >+ > /** > * request_firmware: - send firmware request and wait for it > * @firmware_p: pointer to firmware image >@@ -580,31 +1077,17 @@ > * @name will be used as $FIRMWARE in the uevent environment and > * should be distinctive enough not to be confused with any other > * firmware image for this or any other device. >+ * >+ * Caller must hold the reference count of @device. >+ * >+ * The function can be called safely inside device's suspend and >+ * resume callback. > **/ > int > request_firmware(const struct firmware **firmware_p, const char *name, > struct device *device) > { >- struct firmware_priv *fw_priv; >- int ret; >- >- fw_priv = _request_firmware_prepare(firmware_p, name, device, true, >- false); >- if (IS_ERR_OR_NULL(fw_priv)) >- return PTR_RET(fw_priv); >- >- ret = usermodehelper_read_trylock(); >- if (WARN_ON(ret)) { >- dev_err(device, "firmware: %s will not be loaded\n", name); >- } else { >- ret = _request_firmware_load(fw_priv, true, >- firmware_loading_timeout()); >- usermodehelper_read_unlock(); >- } >- if (ret) >- _request_firmware_cleanup(firmware_p); >- >- return ret; >+ return _request_firmware(firmware_p, name, device, true, false); > } > > /** >@@ -635,32 +1118,13 @@ > { > struct firmware_work *fw_work; > const struct firmware *fw; >- struct firmware_priv *fw_priv; >- long timeout; >- int ret; > > fw_work = container_of(work, struct firmware_work, work); >- fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device, >- fw_work->uevent, true); >- if (IS_ERR_OR_NULL(fw_priv)) { >- ret = PTR_RET(fw_priv); >- goto out; >- } >- >- timeout = usermodehelper_read_lock_wait(firmware_loading_timeout()); >- if (timeout) { >- ret = _request_firmware_load(fw_priv, fw_work->uevent, timeout); >- usermodehelper_read_unlock(); >- } else { >- dev_dbg(fw_work->device, "firmware: %s loading timed out\n", >- fw_work->name); >- ret = -EAGAIN; >- } >- if (ret) >- _request_firmware_cleanup(&fw); > >- out: >+ _request_firmware(&fw, fw_work->name, fw_work->device, >+ fw_work->uevent, true); > fw_work->cont(fw, fw_work->context); >+ put_device(fw_work->device); /* taken in request_firmware_nowait() */ > > module_put(fw_work->module); > kfree(fw_work); >@@ -679,9 +1143,15 @@ > * @cont: function will be called asynchronously when the firmware > * request is over. > * >- * Asynchronous variant of request_firmware() for user contexts where >- * it is not possible to sleep for long time. It can't be called >- * in atomic contexts. >+ * Caller must hold the reference count of @device. >+ * >+ * Asynchronous variant of request_firmware() for user contexts: >+ * - sleep for as small periods as possible since it may >+ * increase kernel boot time of built-in device drivers >+ * requesting firmware in their ->probe() methods, if >+ * @gfp is GFP_KERNEL. >+ * >+ * - can't sleep at all if @gfp is GFP_ATOMIC. > **/ > int > request_firmware_nowait( >@@ -707,19 +1177,371 @@ > return -EFAULT; > } > >+ get_device(fw_work->device); > INIT_WORK(&fw_work->work, request_firmware_work_func); > schedule_work(&fw_work->work); > 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; >+} >+ >+#ifdef CONFIG_PM_SLEEP >+static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain); >+ >+static struct fw_cache_entry *alloc_fw_cache_entry(const char *name) >+{ >+ struct fw_cache_entry *fce; >+ >+ fce = kzalloc(sizeof(*fce) + strlen(name) + 1, GFP_ATOMIC); >+ if (!fce) >+ goto exit; >+ >+ strcpy(fce->name, name); >+exit: >+ return fce; >+} >+ >+static int __fw_entry_found(const char *name) >+{ >+ struct firmware_cache *fwc = &fw_cache; >+ struct fw_cache_entry *fce; >+ >+ list_for_each_entry(fce, &fwc->fw_names, list) { >+ if (!strcmp(fce->name, name)) >+ return 1; >+ } >+ return 0; >+} >+ >+static int fw_cache_piggyback_on_request(const char *name) >+{ >+ struct firmware_cache *fwc = &fw_cache; >+ struct fw_cache_entry *fce; >+ int ret = 0; >+ >+ spin_lock(&fwc->name_lock); >+ if (__fw_entry_found(name)) >+ goto found; >+ >+ fce = alloc_fw_cache_entry(name); >+ if (fce) { >+ ret = 1; >+ list_add(&fce->list, &fwc->fw_names); >+ pr_debug("%s: fw: %s\n", __func__, name); >+ } >+found: >+ spin_unlock(&fwc->name_lock); >+ return ret; >+} >+ >+static void free_fw_cache_entry(struct fw_cache_entry *fce) >+{ >+ kfree(fce); >+} >+ >+static void __async_dev_cache_fw_image(void *fw_entry, >+ async_cookie_t cookie) >+{ >+ struct fw_cache_entry *fce = fw_entry; >+ struct firmware_cache *fwc = &fw_cache; >+ int ret; >+ >+ ret = cache_firmware(fce->name); >+ if (ret) { >+ spin_lock(&fwc->name_lock); >+ list_del(&fce->list); >+ spin_unlock(&fwc->name_lock); >+ >+ free_fw_cache_entry(fce); >+ } >+} >+ >+/* called with dev->devres_lock held */ >+static void dev_create_fw_entry(struct device *dev, void *res, >+ void *data) >+{ >+ struct fw_name_devm *fwn = res; >+ const char *fw_name = fwn->name; >+ struct list_head *head = data; >+ struct fw_cache_entry *fce; >+ >+ fce = alloc_fw_cache_entry(fw_name); >+ if (fce) >+ list_add(&fce->list, head); >+} >+ >+static int devm_name_match(struct device *dev, void *res, >+ void *match_data) >+{ >+ struct fw_name_devm *fwn = res; >+ return (fwn->magic == (unsigned long)match_data); >+} >+ >+static void dev_cache_fw_image(struct device *dev, void *data) >+{ >+ LIST_HEAD(todo); >+ struct fw_cache_entry *fce; >+ struct fw_cache_entry *fce_next; >+ struct firmware_cache *fwc = &fw_cache; >+ >+ devres_for_each_res(dev, fw_name_devm_release, >+ devm_name_match, &fw_cache, >+ dev_create_fw_entry, &todo); >+ >+ list_for_each_entry_safe(fce, fce_next, &todo, list) { >+ list_del(&fce->list); >+ >+ spin_lock(&fwc->name_lock); >+ /* only one cache entry for one firmware */ >+ if (!__fw_entry_found(fce->name)) { >+ list_add(&fce->list, &fwc->fw_names); >+ } else { >+ free_fw_cache_entry(fce); >+ fce = NULL; >+ } >+ spin_unlock(&fwc->name_lock); >+ >+ if (fce) >+ async_schedule_domain(__async_dev_cache_fw_image, >+ (void *)fce, >+ &fw_cache_domain); >+ } >+} >+ >+static void __device_uncache_fw_images(void) >+{ >+ struct firmware_cache *fwc = &fw_cache; >+ struct fw_cache_entry *fce; >+ >+ spin_lock(&fwc->name_lock); >+ while (!list_empty(&fwc->fw_names)) { >+ fce = list_entry(fwc->fw_names.next, >+ struct fw_cache_entry, list); >+ list_del(&fce->list); >+ spin_unlock(&fwc->name_lock); >+ >+ uncache_firmware(fce->name); >+ free_fw_cache_entry(fce); >+ >+ spin_lock(&fwc->name_lock); >+ } >+ spin_unlock(&fwc->name_lock); >+} >+ >+/** >+ * device_cache_fw_images - cache devices' firmware >+ * >+ * If one device called request_firmware or its nowait version >+ * successfully before, the firmware names are recored into the >+ * device's devres link list, so device_cache_fw_images can call >+ * cache_firmware() to cache these firmwares for the device, >+ * then the device driver can load its firmwares easily at >+ * time when system is not ready to complete loading firmware. >+ */ >+static void device_cache_fw_images(void) >+{ >+ struct firmware_cache *fwc = &fw_cache; >+ int old_timeout; >+ DEFINE_WAIT(wait); >+ >+ pr_debug("%s\n", __func__); >+ >+ /* cancel uncache work */ >+ cancel_delayed_work_sync(&fwc->work); >+ >+ /* >+ * use small loading timeout for caching devices' firmware >+ * because all these firmware images have been loaded >+ * successfully at lease once, also system is ready for >+ * completing firmware loading now. The maximum size of >+ * firmware in current distributions is about 2M bytes, >+ * so 10 secs should be enough. >+ */ >+ old_timeout = loading_timeout; >+ loading_timeout = 10; >+ >+ mutex_lock(&fw_lock); >+ fwc->state = FW_LOADER_START_CACHE; >+ dpm_for_each_dev(NULL, dev_cache_fw_image); >+ mutex_unlock(&fw_lock); >+ >+ /* wait for completion of caching firmware for all devices */ >+ async_synchronize_full_domain(&fw_cache_domain); >+ >+ loading_timeout = old_timeout; >+} >+ >+/** >+ * device_uncache_fw_images - uncache devices' firmware >+ * >+ * uncache all firmwares which have been cached successfully >+ * by device_uncache_fw_images earlier >+ */ >+static void device_uncache_fw_images(void) >+{ >+ pr_debug("%s\n", __func__); >+ __device_uncache_fw_images(); >+} >+ >+static void device_uncache_fw_images_work(struct work_struct *work) >+{ >+ device_uncache_fw_images(); >+} >+ >+/** >+ * device_uncache_fw_images_delay - uncache devices firmwares >+ * @delay: number of milliseconds to delay uncache device firmwares >+ * >+ * uncache all devices's firmwares which has been cached successfully >+ * by device_cache_fw_images after @delay milliseconds. >+ */ >+static void device_uncache_fw_images_delay(unsigned long delay) >+{ >+ schedule_delayed_work(&fw_cache.work, >+ msecs_to_jiffies(delay)); >+} >+ >+static int fw_pm_notify(struct notifier_block *notify_block, >+ unsigned long mode, void *unused) >+{ >+ switch (mode) { >+ case PM_HIBERNATION_PREPARE: >+ case PM_SUSPEND_PREPARE: >+ device_cache_fw_images(); >+ break; >+ >+ case PM_POST_SUSPEND: >+ case PM_POST_HIBERNATION: >+ case PM_POST_RESTORE: >+ /* >+ * In case that system sleep failed and syscore_suspend is >+ * not called. >+ */ >+ mutex_lock(&fw_lock); >+ fw_cache.state = FW_LOADER_NO_CACHE; >+ mutex_unlock(&fw_lock); >+ >+ device_uncache_fw_images_delay(10 * MSEC_PER_SEC); >+ break; >+ } >+ >+ return 0; >+} >+ >+/* stop caching firmware once syscore_suspend is reached */ >+static int fw_suspend(void) >+{ >+ fw_cache.state = FW_LOADER_NO_CACHE; >+ return 0; >+} >+ >+static struct syscore_ops fw_syscore_ops = { >+ .suspend = fw_suspend, >+}; >+#else >+static int fw_cache_piggyback_on_request(const char *name) >+{ >+ return 0; >+} >+#endif >+ >+static void __init fw_cache_init(void) >+{ >+ spin_lock_init(&fw_cache.lock); >+ INIT_LIST_HEAD(&fw_cache.head); >+ fw_cache.state = FW_LOADER_NO_CACHE; >+ >+#ifdef CONFIG_PM_SLEEP >+ spin_lock_init(&fw_cache.name_lock); >+ INIT_LIST_HEAD(&fw_cache.fw_names); >+ >+ INIT_DELAYED_WORK(&fw_cache.work, >+ device_uncache_fw_images_work); >+ >+ fw_cache.pm_notify.notifier_call = fw_pm_notify; >+ register_pm_notifier(&fw_cache.pm_notify); >+ >+ register_syscore_ops(&fw_syscore_ops); >+#endif >+} >+ > static int __init firmware_class_init(void) > { >+ fw_cache_init(); >+#ifdef CONFIG_FW_LOADER_USER_HELPER > return class_register(&firmware_class); >+#else >+ return 0; >+#endif > } > > static void __exit firmware_class_exit(void) > { >+#ifdef CONFIG_PM_SLEEP >+ unregister_syscore_ops(&fw_syscore_ops); >+ unregister_pm_notifier(&fw_cache.pm_notify); >+#endif >+#ifdef CONFIG_FW_LOADER_USER_HELPER > class_unregister(&firmware_class); >+#endif > } > > fs_initcall(firmware_class_init); >@@ -728,3 +1550,5 @@ > 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/drivers/base/Kconfig b/drivers/base/Kconfig >index c8b4539..07abd9d 100644 >--- a/drivers/base/Kconfig >+++ b/drivers/base/Kconfig >@@ -145,6 +145,17 @@ config EXTRA_FIRMWARE_DIR > this option you can point it elsewhere, such as /lib/firmware/ or > some other directory containing the firmware files. > >+config FW_LOADER_USER_HELPER >+ bool "Fallback user-helper invocation for firmware loading" >+ depends on FW_LOADER >+ default y >+ help >+ This option enables / disables the invocation of user-helper >+ (e.g. udev) for loading firmware files as a fallback after the >+ direct file loading in kernel fails. The user-mode helper is >+ no longer required unless you have a special firmware file that >+ resides in a non-standard path. >+ > config DEBUG_DRIVER > bool "Driver Core verbose debug messages" > depends on DEBUG_KERNEL > >--- linux-3.4.83/firmware_class/README 2012-05-21 00:29:13.000000000 +0200 >+++ linux-3.10.33/Documentation/firmware_class/README 2013-07-01 00:13:29.000000000 +0200 >@@ -18,32 +18,45 @@ > High level behavior (mixed): > ============================ > >- kernel(driver): calls request_firmware(&fw_entry, $FIRMWARE, device) >+ 1), kernel(driver): >+ - calls request_firmware(&fw_entry, $FIRMWARE, device) >+ - kernel searchs the fimware image with name $FIRMWARE directly >+ in the below search path of root filesystem: >+ User customized search path by module parameter 'path'[1] >+ "/lib/firmware/updates/" UTS_RELEASE, >+ "/lib/firmware/updates", >+ "/lib/firmware/" UTS_RELEASE, >+ "/lib/firmware" >+ - If found, goto 7), else goto 2) >+ >+ [1], the 'path' is a string parameter which length should be less >+ than 256, user should pass 'firmware_class.path=$CUSTOMIZED_PATH' >+ if firmware_class is built in kernel(the general situation) > >- userspace: >+ 2), userspace: > - /sys/class/firmware/xxx/{loading,data} appear. > - hotplug gets called with a firmware identifier in $FIRMWARE > and the usual hotplug environment. > - hotplug: echo 1 > /sys/class/firmware/xxx/loading > >- kernel: Discard any previous partial load. >+ 3), kernel: Discard any previous partial load. > >- userspace: >+ 4), userspace: > - hotplug: cat appropriate_firmware_image > \ > /sys/class/firmware/xxx/data > >- kernel: grows a buffer in PAGE_SIZE increments to hold the image as it >+ 5), kernel: grows a buffer in PAGE_SIZE increments to hold the image as it > comes in. > >- userspace: >+ 6), userspace: > - hotplug: echo 0 > /sys/class/firmware/xxx/loading > >- kernel: request_firmware() returns and the driver has the firmware >+ 7), kernel: request_firmware() returns and the driver has the firmware > image in fw_entry->{data,size}. If something went wrong > request_firmware() returns non-zero and fw_entry is set to > NULL. > >- kernel(driver): Driver code calls release_firmware(fw_entry) releasing >+ 8), kernel(driver): Driver code calls release_firmware(fw_entry) releasing > the firmware image and any related resource. > > High level behavior (driver code): >@@ -106,3 +119,10 @@ > on the setup, so I think that the choice on what firmware to make > persistent should be left to userspace. > >+ about firmware cache: >+ -------------------- >+ After firmware cache mechanism is introduced during system sleep, >+ request_firmware can be called safely inside device's suspend and >+ resume callback, and callers need't cache the firmware by >+ themselves any more for dealing with firmware loss during system >+ resume.
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 503886
:
372090
|
372100
| 372806