aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-10-03 18:58:32 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-03 18:58:32 -0400
commitabb139e75c2cdbb955e840d6331cb5863e409d0e (patch)
tree2b6c986488d3a2444095153b167c0e0f82a0b487 /drivers
parentdf5a2a1fbca44bebeebb78530ac93c734f289707 (diff)
firmware: teach the kernel to load firmware files directly from the filesystem
This is a first step in allowing people to by-pass udev for loading device firmware. Current versions of udev will deadlock (causing us to block for the 30 second timeout) under some circumstances if the firmware is loaded as part of the module initialization path, and this is causing problems for media drivers in particular. The current patch hardcodes the firmware path that udev uses by default, and will fall back to the legacy udev mode if the firmware cannot be found there. We'd like to add support for both configuring the paths and the fallback behaviour, but in the meantime this hopefully fixes the immediate problem, while also giving us a way forward. [ v2: Some VFS layer interface cleanups suggested by Al Viro ] [ v3: use the default udev paths suggested by Kay Sievers ] Suggested-by: Ivan Kalvachev <ikalvachev@gmail.com> Acked-by: Greg KH <gregkh@linuxfoundation.org> Acked-by: Al Viro <viro@zeniv.linux.org.uk> Cc: Mauro Carvalho Chehab <mchehab@redhat.com> Cc: Kay Sievers <kay@redhat.com> Cc: Ming Lei <ming.lei@canonical.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-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,