aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/firmware_class.c78
1 files changed, 77 insertions, 1 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 6e210802c37b..e85763de928f 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -21,18 +21,85 @@
21#include <linux/firmware.h> 21#include <linux/firmware.h>
22#include <linux/slab.h> 22#include <linux/slab.h>
23#include <linux/sched.h> 23#include <linux/sched.h>
24#include <linux/file.h>
24#include <linux/list.h> 25#include <linux/list.h>
25#include <linux/async.h> 26#include <linux/async.h>
26#include <linux/pm.h> 27#include <linux/pm.h>
27#include <linux/suspend.h> 28#include <linux/suspend.h>
28#include <linux/syscore_ops.h> 29#include <linux/syscore_ops.h>
29 30
31#include <generated/utsrelease.h>
32
30#include "base.h" 33#include "base.h"
31 34
32MODULE_AUTHOR("Manuel Estrada Sainz"); 35MODULE_AUTHOR("Manuel Estrada Sainz");
33MODULE_DESCRIPTION("Multi purpose firmware loading support"); 36MODULE_DESCRIPTION("Multi purpose firmware loading support");
34MODULE_LICENSE("GPL"); 37MODULE_LICENSE("GPL");
35 38
39static const char *fw_path[] = {
40 "/lib/firmware/updates/" UTS_RELEASE,
41 "/lib/firmware/updates",
42 "/lib/firmware/" UTS_RELEASE,
43 "/lib/firmware"
44};
45
46/* Don't inline this: 'struct kstat' is biggish */
47static noinline long fw_file_size(struct file *file)
48{
49 struct kstat st;
50 if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st))
51 return -1;
52 if (!S_ISREG(st.mode))
53 return -1;
54 if (st.size != (long)st.size)
55 return -1;
56 return st.size;
57}
58
59static bool fw_read_file_contents(struct file *file, struct firmware *fw)
60{
61 loff_t pos;
62 long size;
63 char *buf;
64
65 size = fw_file_size(file);
66 if (size < 0)
67 return false;
68 buf = vmalloc(size);
69 if (!buf)
70 return false;
71 pos = 0;
72 if (vfs_read(file, buf, size, &pos) != size) {
73 vfree(buf);
74 return false;
75 }
76 fw->data = buf;
77 fw->size = size;
78 return true;
79}
80
81static bool fw_get_filesystem_firmware(struct firmware *fw, const char *name)
82{
83 int i;
84 bool success = false;
85 char *path = __getname();
86
87 for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
88 struct file *file;
89 snprintf(path, PATH_MAX, "%s/%s", fw_path[i], name);
90
91 file = filp_open(path, O_RDONLY, 0);
92 if (IS_ERR(file))
93 continue;
94 success = fw_read_file_contents(file, fw);
95 fput(file);
96 if (success)
97 break;
98 }
99 __putname(path);
100 return success;
101}
102
36/* Builtin firmware support */ 103/* Builtin firmware support */
37 104
38#ifdef CONFIG_FW_LOADER 105#ifdef CONFIG_FW_LOADER
@@ -346,7 +413,11 @@ static ssize_t firmware_loading_show(struct device *dev,
346/* firmware holds the ownership of pages */ 413/* firmware holds the ownership of pages */
347static void firmware_free_data(const struct firmware *fw) 414static void firmware_free_data(const struct firmware *fw)
348{ 415{
349 WARN_ON(!fw->priv); 416 /* Loaded directly? */
417 if (!fw->priv) {
418 vfree(fw->data);
419 return;
420 }
350 fw_free_buf(fw->priv); 421 fw_free_buf(fw->priv);
351} 422}
352 423
@@ -709,6 +780,11 @@ _request_firmware_prepare(const struct firmware **firmware_p, const char *name,
709 return NULL; 780 return NULL;
710 } 781 }
711 782
783 if (fw_get_filesystem_firmware(firmware, name)) {
784 dev_dbg(device, "firmware: direct-loading firmware %s\n", name);
785 return NULL;
786 }
787
712 ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf); 788 ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf);
713 if (!ret) 789 if (!ret)
714 fw_priv = fw_create_instance(firmware, name, device, 790 fw_priv = fw_create_instance(firmware, name, device,