firmware: allow firmware files to be built into kernel image
authorDavid Woodhouse <dwmw2@infradead.org>
Fri, 23 May 2008 12:52:42 +0000 (13:52 +0100)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Thu, 10 Jul 2008 13:30:13 +0000 (14:30 +0100)
Some drivers have their own hacks to bypass the kernel's firmware loader
and build their firmware into the kernel; this renders those unnecessary.

Other drivers don't use the firmware loader at all, because they always
want the firmware to be available. This allows them to start using the
firmware loader.

A third set of drivers already use the firmware loader, but can't be
used without help from userspace, which sometimes requires an initrd.
This allows them to work in a static kernel.

Signed-off-by: David Woodhouse <dwmw2@infradead.org>
drivers/base/firmware_class.c
include/asm-generic/vmlinux.lds.h
include/linux/firmware.h

index 264b3a2cd8605c298cc1a1f4446edece6c1e63bc..b0be1d18fee23d5c25d865bba20c0a3560546195 100644 (file)
@@ -49,6 +49,14 @@ struct firmware_priv {
        struct timer_list timeout;
 };
 
+#ifdef CONFIG_FW_LOADER
+extern struct builtin_fw __start_builtin_fw[];
+extern struct builtin_fw __end_builtin_fw[];
+#else /* Module case. Avoid ifdefs later; it'll all optimise out */
+static struct builtin_fw *__start_builtin_fw;
+static struct builtin_fw *__end_builtin_fw;
+#endif
+
 static void
 fw_load_abort(struct firmware_priv *fw_priv)
 {
@@ -391,13 +399,12 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
        struct device *f_dev;
        struct firmware_priv *fw_priv;
        struct firmware *firmware;
+       struct builtin_fw *builtin;
        int retval;
 
        if (!firmware_p)
                return -EINVAL;
 
-       printk(KERN_INFO "firmware: requesting %s\n", name);
-
        *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
        if (!firmware) {
                printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
@@ -406,6 +413,20 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
                goto out;
        }
 
+       for (builtin = __start_builtin_fw; builtin != __end_builtin_fw;
+            builtin++) {
+               if (strcmp(name, builtin->name))
+                       continue;
+               printk(KERN_INFO "firmware: using built-in firmware %s\n",
+                      name);
+               firmware->size = builtin->size;
+               firmware->data = builtin->data;
+               return 0;
+       }
+
+       if (uevent)
+               printk(KERN_INFO "firmware: requesting %s\n", name);
+
        retval = fw_setup_device(firmware, &f_dev, name, device, uevent);
        if (retval)
                goto error_kfree_fw;
@@ -473,8 +494,16 @@ request_firmware(const struct firmware **firmware_p, const char *name,
 void
 release_firmware(const struct firmware *fw)
 {
+       struct builtin_fw *builtin;
+
        if (fw) {
+               for (builtin = __start_builtin_fw; builtin != __end_builtin_fw;
+                    builtin++) {
+                       if (fw->data == builtin->data)
+                               goto free_fw;
+               }
                vfree(fw->data);
+       free_fw:
                kfree(fw);
        }
 }
index f054778e916c08e0b1e7cc85dd028de15b21edb2..8d71a40625f31c96433396749cb549ac280aadd3 100644 (file)
                VMLINUX_SYMBOL(__end_pci_fixups_resume) = .;            \
        }                                                               \
                                                                        \
+       /* Built-in firmware blobs */                                   \
+       .builtin_fw        : AT(ADDR(.builtin_fw) - LOAD_OFFSET) {      \
+               VMLINUX_SYMBOL(__start_builtin_fw) = .;                 \
+               *(.builtin_fw)                                          \
+               VMLINUX_SYMBOL(__end_builtin_fw) = .;                   \
+       }                                                               \
+                                                                       \
        /* RapidIO route ops */                                         \
        .rio_route        : AT(ADDR(.rio_route) - LOAD_OFFSET) {        \
                VMLINUX_SYMBOL(__start_rio_route_ops) = .;              \
index 88718d60153c0332e1154ccc240cd5c15b46d618..c8ecf5b2a207371d5662234ec4eb9be9d3420c14 100644 (file)
@@ -1,7 +1,10 @@
 #ifndef _LINUX_FIRMWARE_H
 #define _LINUX_FIRMWARE_H
+
 #include <linux/module.h>
 #include <linux/types.h>
+#include <linux/compiler.h>
+
 #define FIRMWARE_NAME_MAX 30 
 #define FW_ACTION_NOHOTPLUG 0
 #define FW_ACTION_HOTPLUG 1
@@ -13,6 +16,24 @@ struct firmware {
 
 struct device;
 
+struct builtin_fw {
+       char *name;
+       void *data;
+       unsigned long size;
+};
+
+/* We have to play tricks here much like stringify() to get the
+   __COUNTER__ macro to be expanded as we want it */
+#define __fw_concat1(x, y) x##y
+#define __fw_concat(x, y) __fw_concat1(x, y)
+
+#define DECLARE_BUILTIN_FIRMWARE(name, blob)                                \
+       DECLARE_BUILTIN_FIRMWARE_SIZE(name, &(blob), sizeof(blob))
+
+#define DECLARE_BUILTIN_FIRMWARE_SIZE(name, blob, size)                             \
+       static const struct builtin_fw __fw_concat(__builtin_fw,__COUNTER__) \
+       __used __section(.builtin_fw) = { name, blob, size }
+
 #if defined(CONFIG_FW_LOADER) || (defined(CONFIG_FW_LOADER_MODULE) && defined(MODULE))
 int request_firmware(const struct firmware **fw, const char *name,
                     struct device *device);