diff options
-rw-r--r-- | drivers/base/firmware_class.c | 78 |
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 | ||
32 | MODULE_AUTHOR("Manuel Estrada Sainz"); | 35 | MODULE_AUTHOR("Manuel Estrada Sainz"); |
33 | MODULE_DESCRIPTION("Multi purpose firmware loading support"); | 36 | MODULE_DESCRIPTION("Multi purpose firmware loading support"); |
34 | MODULE_LICENSE("GPL"); | 37 | MODULE_LICENSE("GPL"); |
35 | 38 | ||
39 | static 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 */ | ||
47 | static 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 | |||
59 | static 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 | |||
81 | static 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 */ |
347 | static void firmware_free_data(const struct firmware *fw) | 414 | static 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, |