aboutsummaryrefslogtreecommitdiffstats
path: root/Documentation/firmware_class
diff options
context:
space:
mode:
Diffstat (limited to 'Documentation/firmware_class')
-rw-r--r--Documentation/firmware_class/README124
-rw-r--r--Documentation/firmware_class/firmware_sample_driver.c126
-rw-r--r--Documentation/firmware_class/firmware_sample_firmware_class.c204
-rw-r--r--Documentation/firmware_class/hotplug-script16
4 files changed, 470 insertions, 0 deletions
diff --git a/Documentation/firmware_class/README b/Documentation/firmware_class/README
new file mode 100644
index 000000000000..43e836c07ae8
--- /dev/null
+++ b/Documentation/firmware_class/README
@@ -0,0 +1,124 @@
1
2 request_firmware() hotplug interface:
3 ------------------------------------
4 Copyright (C) 2003 Manuel Estrada Sainz <ranty@debian.org>
5
6 Why:
7 ---
8
9 Today, the most extended way to use firmware in the Linux kernel is linking
10 it statically in a header file. Which has political and technical issues:
11
12 1) Some firmware is not legal to redistribute.
13 2) The firmware occupies memory permanently, even though it often is just
14 used once.
15 3) Some people, like the Debian crowd, don't consider some firmware free
16 enough and remove entire drivers (e.g.: keyspan).
17
18 High level behavior (mixed):
19 ============================
20
21 kernel(driver): calls request_firmware(&fw_entry, $FIRMWARE, device)
22
23 userspace:
24 - /sys/class/firmware/xxx/{loading,data} appear.
25 - hotplug gets called with a firmware identifier in $FIRMWARE
26 and the usual hotplug environment.
27 - hotplug: echo 1 > /sys/class/firmware/xxx/loading
28
29 kernel: Discard any previous partial load.
30
31 userspace:
32 - hotplug: cat appropriate_firmware_image > \
33 /sys/class/firmware/xxx/data
34
35 kernel: grows a buffer in PAGE_SIZE increments to hold the image as it
36 comes in.
37
38 userspace:
39 - hotplug: echo 0 > /sys/class/firmware/xxx/loading
40
41 kernel: request_firmware() returns and the driver has the firmware
42 image in fw_entry->{data,size}. If something went wrong
43 request_firmware() returns non-zero and fw_entry is set to
44 NULL.
45
46 kernel(driver): Driver code calls release_firmware(fw_entry) releasing
47 the firmware image and any related resource.
48
49 High level behavior (driver code):
50 ==================================
51
52 if(request_firmware(&fw_entry, $FIRMWARE, device) == 0)
53 copy_fw_to_device(fw_entry->data, fw_entry->size);
54 release(fw_entry);
55
56 Sample/simple hotplug script:
57 ============================
58
59 # Both $DEVPATH and $FIRMWARE are already provided in the environment.
60
61 HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/
62
63 echo 1 > /sys/$DEVPATH/loading
64 cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data
65 echo 0 > /sys/$DEVPATH/loading
66
67 Random notes:
68 ============
69
70 - "echo -1 > /sys/class/firmware/xxx/loading" will cancel the load at
71 once and make request_firmware() return with error.
72
73 - firmware_data_read() and firmware_loading_show() are just provided
74 for testing and completeness, they are not called in normal use.
75
76 - There is also /sys/class/firmware/timeout which holds a timeout in
77 seconds for the whole load operation.
78
79 - request_firmware_nowait() is also provided for convenience in
80 non-user contexts.
81
82
83 about in-kernel persistence:
84 ---------------------------
85 Under some circumstances, as explained below, it would be interesting to keep
86 firmware images in non-swappable kernel memory or even in the kernel image
87 (probably within initramfs).
88
89 Note that this functionality has not been implemented.
90
91 - Why OPTIONAL in-kernel persistence may be a good idea sometimes:
92
93 - If the device that needs the firmware is needed to access the
94 filesystem. When upon some error the device has to be reset and the
95 firmware reloaded, it won't be possible to get it from userspace.
96 e.g.:
97 - A diskless client with a network card that needs firmware.
98 - The filesystem is stored in a disk behind an scsi device
99 that needs firmware.
100 - Replacing buggy DSDT/SSDT ACPI tables on boot.
101 Note: this would require the persistent objects to be included
102 within the kernel image, probably within initramfs.
103
104 And the same device can be needed to access the filesystem or not depending
105 on the setup, so I think that the choice on what firmware to make
106 persistent should be left to userspace.
107
108 - Why register_firmware()+__init can be useful:
109 - For boot devices needing firmware.
110 - To make the transition easier:
111 The firmware can be declared __init and register_firmware()
112 called on module_init. Then the firmware is warranted to be
113 there even if "firmware hotplug userspace" is not there yet or
114 it doesn't yet provide the needed firmware.
115 Once the firmware is widely available in userspace, it can be
116 removed from the kernel. Or made optional (CONFIG_.*_FIRMWARE).
117
118 In either case, if firmware hotplug support is there, it can move the
119 firmware out of kernel memory into the real filesystem for later
120 usage.
121
122 Note: If persistence is implemented on top of initramfs,
123 register_firmware() may not be appropriate.
124
diff --git a/Documentation/firmware_class/firmware_sample_driver.c b/Documentation/firmware_class/firmware_sample_driver.c
new file mode 100644
index 000000000000..e1c56a7e6583
--- /dev/null
+++ b/Documentation/firmware_class/firmware_sample_driver.c
@@ -0,0 +1,126 @@
1/*
2 * firmware_sample_driver.c -
3 *
4 * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
5 *
6 * Sample code on how to use request_firmware() from drivers.
7 *
8 * Note that register_firmware() is currently useless.
9 *
10 */
11
12#include <linux/module.h>
13#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/device.h>
16
17#include "linux/firmware.h"
18
19#define WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
20#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
21char __init inkernel_firmware[] = "let's say that this is firmware\n";
22#endif
23
24static struct device ghost_device = {
25 .name = "Ghost Device",
26 .bus_id = "ghost0",
27};
28
29
30static void sample_firmware_load(char *firmware, int size)
31{
32 u8 buf[size+1];
33 memcpy(buf, firmware, size);
34 buf[size] = '\0';
35 printk("firmware_sample_driver: firmware: %s\n", buf);
36}
37
38static void sample_probe_default(void)
39{
40 /* uses the default method to get the firmware */
41 const struct firmware *fw_entry;
42 printk("firmware_sample_driver: a ghost device got inserted :)\n");
43
44 if(request_firmware(&fw_entry, "sample_driver_fw", &ghost_device)!=0)
45 {
46 printk(KERN_ERR
47 "firmware_sample_driver: Firmware not available\n");
48 return;
49 }
50
51 sample_firmware_load(fw_entry->data, fw_entry->size);
52
53 release_firmware(fw_entry);
54
55 /* finish setting up the device */
56}
57static void sample_probe_specific(void)
58{
59 /* Uses some specific hotplug support to get the firmware from
60 * userspace directly into the hardware, or via some sysfs file */
61
62 /* NOTE: This currently doesn't work */
63
64 printk("firmware_sample_driver: a ghost device got inserted :)\n");
65
66 if(request_firmware(NULL, "sample_driver_fw", &ghost_device)!=0)
67 {
68 printk(KERN_ERR
69 "firmware_sample_driver: Firmware load failed\n");
70 return;
71 }
72
73 /* request_firmware blocks until userspace finished, so at
74 * this point the firmware should be already in the device */
75
76 /* finish setting up the device */
77}
78static void sample_probe_async_cont(const struct firmware *fw, void *context)
79{
80 if(!fw){
81 printk(KERN_ERR
82 "firmware_sample_driver: firmware load failed\n");
83 return;
84 }
85
86 printk("firmware_sample_driver: device pointer \"%s\"\n",
87 (char *)context);
88 sample_firmware_load(fw->data, fw->size);
89}
90static void sample_probe_async(void)
91{
92 /* Let's say that I can't sleep */
93 int error;
94 error = request_firmware_nowait (THIS_MODULE,
95 "sample_driver_fw", &ghost_device,
96 "my device pointer",
97 sample_probe_async_cont);
98 if(error){
99 printk(KERN_ERR
100 "firmware_sample_driver:"
101 " request_firmware_nowait failed\n");
102 }
103}
104
105static int sample_init(void)
106{
107#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
108 register_firmware("sample_driver_fw", inkernel_firmware,
109 sizeof(inkernel_firmware));
110#endif
111 device_initialize(&ghost_device);
112 /* since there is no real hardware insertion I just call the
113 * sample probe functions here */
114 sample_probe_specific();
115 sample_probe_default();
116 sample_probe_async();
117 return 0;
118}
119static void __exit sample_exit(void)
120{
121}
122
123module_init (sample_init);
124module_exit (sample_exit);
125
126MODULE_LICENSE("GPL");
diff --git a/Documentation/firmware_class/firmware_sample_firmware_class.c b/Documentation/firmware_class/firmware_sample_firmware_class.c
new file mode 100644
index 000000000000..09eab2f1b373
--- /dev/null
+++ b/Documentation/firmware_class/firmware_sample_firmware_class.c
@@ -0,0 +1,204 @@
1/*
2 * firmware_sample_firmware_class.c -
3 *
4 * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
5 *
6 * NOTE: This is just a probe of concept, if you think that your driver would
7 * be well served by this mechanism please contact me first.
8 *
9 * DON'T USE THIS CODE AS IS
10 *
11 */
12
13#include <linux/device.h>
14#include <linux/module.h>
15#include <linux/init.h>
16#include <linux/timer.h>
17#include <linux/firmware.h>
18
19
20MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>");
21MODULE_DESCRIPTION("Hackish sample for using firmware class directly");
22MODULE_LICENSE("GPL");
23
24static inline struct class_device *to_class_dev(struct kobject *obj)
25{
26 return container_of(obj,struct class_device,kobj);
27}
28static inline
29struct class_device_attribute *to_class_dev_attr(struct attribute *_attr)
30{
31 return container_of(_attr,struct class_device_attribute,attr);
32}
33
34int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr);
35int sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr);
36
37struct firmware_priv {
38 char fw_id[FIRMWARE_NAME_MAX];
39 s32 loading:2;
40 u32 abort:1;
41};
42
43extern struct class firmware_class;
44
45static ssize_t firmware_loading_show(struct class_device *class_dev, char *buf)
46{
47 struct firmware_priv *fw_priv = class_get_devdata(class_dev);
48 return sprintf(buf, "%d\n", fw_priv->loading);
49}
50static ssize_t firmware_loading_store(struct class_device *class_dev,
51 const char *buf, size_t count)
52{
53 struct firmware_priv *fw_priv = class_get_devdata(class_dev);
54 int prev_loading = fw_priv->loading;
55
56 fw_priv->loading = simple_strtol(buf, NULL, 10);
57
58 switch(fw_priv->loading){
59 case -1:
60 /* abort load an panic */
61 break;
62 case 1:
63 /* setup load */
64 break;
65 case 0:
66 if(prev_loading==1){
67 /* finish load and get the device back to working
68 * state */
69 }
70 break;
71 }
72
73 return count;
74}
75static CLASS_DEVICE_ATTR(loading, 0644,
76 firmware_loading_show, firmware_loading_store);
77
78static ssize_t firmware_data_read(struct kobject *kobj,
79 char *buffer, loff_t offset, size_t count)
80{
81 struct class_device *class_dev = to_class_dev(kobj);
82 struct firmware_priv *fw_priv = class_get_devdata(class_dev);
83
84 /* read from the devices firmware memory */
85
86 return count;
87}
88static ssize_t firmware_data_write(struct kobject *kobj,
89 char *buffer, loff_t offset, size_t count)
90{
91 struct class_device *class_dev = to_class_dev(kobj);
92 struct firmware_priv *fw_priv = class_get_devdata(class_dev);
93
94 /* write to the devices firmware memory */
95
96 return count;
97}
98static struct bin_attribute firmware_attr_data = {
99 .attr = {.name = "data", .mode = 0644},
100 .size = 0,
101 .read = firmware_data_read,
102 .write = firmware_data_write,
103};
104static int fw_setup_class_device(struct class_device *class_dev,
105 const char *fw_name,
106 struct device *device)
107{
108 int retval = 0;
109 struct firmware_priv *fw_priv = kmalloc(sizeof(struct firmware_priv),
110 GFP_KERNEL);
111
112 if(!fw_priv){
113 retval = -ENOMEM;
114 goto out;
115 }
116 memset(fw_priv, 0, sizeof(*fw_priv));
117 memset(class_dev, 0, sizeof(*class_dev));
118
119 strncpy(fw_priv->fw_id, fw_name, FIRMWARE_NAME_MAX);
120 fw_priv->fw_id[FIRMWARE_NAME_MAX-1] = '\0';
121
122 strncpy(class_dev->class_id, device->bus_id, BUS_ID_SIZE);
123 class_dev->class_id[BUS_ID_SIZE-1] = '\0';
124 class_dev->dev = device;
125
126 class_dev->class = &firmware_class,
127 class_set_devdata(class_dev, fw_priv);
128 retval = class_device_register(class_dev);
129 if (retval){
130 printk(KERN_ERR "%s: class_device_register failed\n",
131 __FUNCTION__);
132 goto error_free_fw_priv;
133 }
134
135 retval = sysfs_create_bin_file(&class_dev->kobj, &firmware_attr_data);
136 if (retval){
137 printk(KERN_ERR "%s: sysfs_create_bin_file failed\n",
138 __FUNCTION__);
139 goto error_unreg_class_dev;
140 }
141
142 retval = class_device_create_file(class_dev,
143 &class_device_attr_loading);
144 if (retval){
145 printk(KERN_ERR "%s: class_device_create_file failed\n",
146 __FUNCTION__);
147 goto error_remove_data;
148 }
149
150 goto out;
151
152error_remove_data:
153 sysfs_remove_bin_file(&class_dev->kobj, &firmware_attr_data);
154error_unreg_class_dev:
155 class_device_unregister(class_dev);
156error_free_fw_priv:
157 kfree(fw_priv);
158out:
159 return retval;
160}
161static void fw_remove_class_device(struct class_device *class_dev)
162{
163 struct firmware_priv *fw_priv = class_get_devdata(class_dev);
164
165 class_device_remove_file(class_dev, &class_device_attr_loading);
166 sysfs_remove_bin_file(&class_dev->kobj, &firmware_attr_data);
167 class_device_unregister(class_dev);
168}
169
170static struct class_device *class_dev;
171
172static struct device my_device = {
173 .name = "Sample Device",
174 .bus_id = "my_dev0",
175};
176
177static int __init firmware_sample_init(void)
178{
179 int error;
180
181 device_initialize(&my_device);
182 class_dev = kmalloc(sizeof(struct class_device), GFP_KERNEL);
183 if(!class_dev)
184 return -ENOMEM;
185
186 error = fw_setup_class_device(class_dev, "my_firmware_image",
187 &my_device);
188 if(error){
189 kfree(class_dev);
190 return error;
191 }
192 return 0;
193
194}
195static void __exit firmware_sample_exit(void)
196{
197 struct firmware_priv *fw_priv = class_get_devdata(class_dev);
198 fw_remove_class_device(class_dev);
199 kfree(fw_priv);
200 kfree(class_dev);
201}
202module_init(firmware_sample_init);
203module_exit(firmware_sample_exit);
204
diff --git a/Documentation/firmware_class/hotplug-script b/Documentation/firmware_class/hotplug-script
new file mode 100644
index 000000000000..1990130f2ab1
--- /dev/null
+++ b/Documentation/firmware_class/hotplug-script
@@ -0,0 +1,16 @@
1#!/bin/sh
2
3# Simple hotplug script sample:
4#
5# Both $DEVPATH and $FIRMWARE are already provided in the environment.
6
7HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/
8
9echo 1 > /sys/$DEVPATH/loading
10cat $HOTPLUG_FW_DIR/$FIRMWARE > /sys/$DEVPATH/data
11echo 0 > /sys/$DEVPATH/loading
12
13# To cancel the load in case of error:
14#
15# echo -1 > /sys/$DEVPATH/loading
16#