Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | |