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, |
