Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | |
| 2 | request_firmware() hotplug interface: |
| 3 | ------------------------------------ |
Markus Rechberger | 87d37a4 | 2007-06-04 18:45:44 +0200 | [diff] [blame] | 4 | Copyright (C) 2003 Manuel Estrada Sainz |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | |
Ming Lei | 10bd4c7 | 2012-10-24 10:49:33 +0800 | [diff] [blame] | 21 | 1), kernel(driver): |
| 22 | - calls request_firmware(&fw_entry, $FIRMWARE, device) |
| 23 | - kernel searchs the fimware image with name $FIRMWARE directly |
| 24 | in the below search path of root filesystem: |
Ming Lei | 2760284 | 2012-11-03 17:47:58 +0800 | [diff] [blame] | 25 | User customized search path by module parameter 'path'[1] |
Ming Lei | 10bd4c7 | 2012-10-24 10:49:33 +0800 | [diff] [blame] | 26 | "/lib/firmware/updates/" UTS_RELEASE, |
| 27 | "/lib/firmware/updates", |
| 28 | "/lib/firmware/" UTS_RELEASE, |
| 29 | "/lib/firmware" |
| 30 | - If found, goto 7), else goto 2) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 | |
Ming Lei | 2760284 | 2012-11-03 17:47:58 +0800 | [diff] [blame] | 32 | [1], the 'path' is a string parameter which length should be less |
| 33 | than 256, user should pass 'firmware_class.path=$CUSTOMIZED_PATH' |
| 34 | if firmware_class is built in kernel(the general situation) |
| 35 | |
Ming Lei | 10bd4c7 | 2012-10-24 10:49:33 +0800 | [diff] [blame] | 36 | 2), userspace: |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 37 | - /sys/class/firmware/xxx/{loading,data} appear. |
| 38 | - hotplug gets called with a firmware identifier in $FIRMWARE |
| 39 | and the usual hotplug environment. |
| 40 | - hotplug: echo 1 > /sys/class/firmware/xxx/loading |
| 41 | |
Ming Lei | 10bd4c7 | 2012-10-24 10:49:33 +0800 | [diff] [blame] | 42 | 3), kernel: Discard any previous partial load. |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 43 | |
Ming Lei | 10bd4c7 | 2012-10-24 10:49:33 +0800 | [diff] [blame] | 44 | 4), userspace: |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 45 | - hotplug: cat appropriate_firmware_image > \ |
| 46 | /sys/class/firmware/xxx/data |
| 47 | |
Ming Lei | 10bd4c7 | 2012-10-24 10:49:33 +0800 | [diff] [blame] | 48 | 5), kernel: grows a buffer in PAGE_SIZE increments to hold the image as it |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 49 | comes in. |
| 50 | |
Ming Lei | 10bd4c7 | 2012-10-24 10:49:33 +0800 | [diff] [blame] | 51 | 6), userspace: |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 52 | - hotplug: echo 0 > /sys/class/firmware/xxx/loading |
| 53 | |
Ming Lei | 10bd4c7 | 2012-10-24 10:49:33 +0800 | [diff] [blame] | 54 | 7), kernel: request_firmware() returns and the driver has the firmware |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 55 | image in fw_entry->{data,size}. If something went wrong |
| 56 | request_firmware() returns non-zero and fw_entry is set to |
| 57 | NULL. |
| 58 | |
Ming Lei | 10bd4c7 | 2012-10-24 10:49:33 +0800 | [diff] [blame] | 59 | 8), kernel(driver): Driver code calls release_firmware(fw_entry) releasing |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 60 | the firmware image and any related resource. |
| 61 | |
| 62 | High level behavior (driver code): |
| 63 | ================================== |
| 64 | |
| 65 | if(request_firmware(&fw_entry, $FIRMWARE, device) == 0) |
| 66 | copy_fw_to_device(fw_entry->data, fw_entry->size); |
Kees Cook | b142518 | 2014-07-14 14:38:11 -0700 | [diff] [blame] | 67 | release_firmware(fw_entry); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 68 | |
| 69 | Sample/simple hotplug script: |
| 70 | ============================ |
| 71 | |
| 72 | # Both $DEVPATH and $FIRMWARE are already provided in the environment. |
| 73 | |
| 74 | HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/ |
| 75 | |
| 76 | echo 1 > /sys/$DEVPATH/loading |
Kees Cook | b142518 | 2014-07-14 14:38:11 -0700 | [diff] [blame] | 77 | cat $HOTPLUG_FW_DIR/$FIRMWARE > /sys/$DEVPATH/data |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 78 | echo 0 > /sys/$DEVPATH/loading |
| 79 | |
| 80 | Random notes: |
| 81 | ============ |
| 82 | |
| 83 | - "echo -1 > /sys/class/firmware/xxx/loading" will cancel the load at |
| 84 | once and make request_firmware() return with error. |
| 85 | |
| 86 | - firmware_data_read() and firmware_loading_show() are just provided |
| 87 | for testing and completeness, they are not called in normal use. |
| 88 | |
| 89 | - There is also /sys/class/firmware/timeout which holds a timeout in |
| 90 | seconds for the whole load operation. |
| 91 | |
| 92 | - request_firmware_nowait() is also provided for convenience in |
Ming Lei | 7fcab09 | 2009-05-29 11:33:19 +0800 | [diff] [blame] | 93 | user contexts to request firmware asynchronously, but can't be called |
| 94 | in atomic contexts. |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 95 | |
| 96 | |
| 97 | about in-kernel persistence: |
| 98 | --------------------------- |
| 99 | Under some circumstances, as explained below, it would be interesting to keep |
| 100 | firmware images in non-swappable kernel memory or even in the kernel image |
| 101 | (probably within initramfs). |
| 102 | |
| 103 | Note that this functionality has not been implemented. |
| 104 | |
| 105 | - Why OPTIONAL in-kernel persistence may be a good idea sometimes: |
| 106 | |
| 107 | - If the device that needs the firmware is needed to access the |
| 108 | filesystem. When upon some error the device has to be reset and the |
| 109 | firmware reloaded, it won't be possible to get it from userspace. |
| 110 | e.g.: |
| 111 | - A diskless client with a network card that needs firmware. |
| 112 | - The filesystem is stored in a disk behind an scsi device |
| 113 | that needs firmware. |
| 114 | - Replacing buggy DSDT/SSDT ACPI tables on boot. |
| 115 | Note: this would require the persistent objects to be included |
| 116 | within the kernel image, probably within initramfs. |
| 117 | |
| 118 | And the same device can be needed to access the filesystem or not depending |
| 119 | on the setup, so I think that the choice on what firmware to make |
| 120 | persistent should be left to userspace. |
| 121 | |
Ming Lei | 6a92785 | 2012-11-03 17:48:16 +0800 | [diff] [blame] | 122 | about firmware cache: |
| 123 | -------------------- |
| 124 | After firmware cache mechanism is introduced during system sleep, |
| 125 | request_firmware can be called safely inside device's suspend and |
Kees Cook | b142518 | 2014-07-14 14:38:11 -0700 | [diff] [blame] | 126 | resume callback, and callers needn't cache the firmware by |
Ming Lei | 6a92785 | 2012-11-03 17:48:16 +0800 | [diff] [blame] | 127 | themselves any more for dealing with firmware loss during system |
| 128 | resume. |