Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
89cbae1f
Commit
89cbae1f
authored
Jun 18, 2003
by
Manuel Estrada Sainz
Committed by
Greg Kroah-Hartman
Jun 18, 2003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[PATCH] DRIVER: request_firmware() hotplug interface documentation
parent
58aae7d9
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
405 additions
and
0 deletions
+405
-0
Documentation/firmware_class/README
Documentation/firmware_class/README
+58
-0
Documentation/firmware_class/firmware_sample_driver.c
Documentation/firmware_class/firmware_sample_driver.c
+126
-0
Documentation/firmware_class/firmware_sample_firmware_class.c
...mentation/firmware_class/firmware_sample_firmware_class.c
+205
-0
Documentation/firmware_class/hotplug-script
Documentation/firmware_class/hotplug-script
+16
-0
No files found.
Documentation/firmware_class/README
0 → 100644
View file @
89cbae1f
request_firmware() hotplug interface:
------------------------------------
Copyright (C) 2003 Manuel Estrada Sainz <ranty@debian.org>
Why:
---
Today, the most extended way to use firmware in the Linux kernel is linking
it statically in a header file. Which has political and technical issues:
1) Some firmware is not legal to redistribute.
2) The firmware occupies memory permanently, even though it often is just
used once.
3) Some people, like the Debian crowd, don't consider some firmware free
enough and remove entire drivers (e.g.: keyspan).
about in-kernel persistence:
---------------------------
Under some circumstances, as explained below, it would be interesting to keep
firmware images in non-swappable kernel memory or even in the kernel image
(probably within initramfs).
Note that this functionality has not been implemented.
- Why OPTIONAL in-kernel persistence may be a good idea sometimes:
- If the device that needs the firmware is needed to access the
filesystem. When upon some error the device has to be reset and the
firmware reloaded, it won't be possible to get it from userspace.
e.g.:
- A diskless client with a network card that needs firmware.
- The filesystem is stored in a disk behind an scsi device
that needs firmware.
- Replacing buggy DSDT/SSDT ACPI tables on boot.
Note: this would require the persistent objects to be included
within the kernel image, probably within initramfs.
And the same device can be needed to access the filesystem or not depending
on the setup, so I think that the choice on what firmware to make
persistent should be left to userspace.
- Why register_firmware()+__init can be useful:
- For boot devices needing firmware.
- To make the transition easier:
The firmware can be declared __init and register_firmware()
called on module_init. Then the firmware is warranted to be
there even if "firmware hotplug userspace" is not there yet or
it doesn't yet provide the needed firmware.
Once the firmware is widely available in userspace, it can be
removed from the kernel. Or made optional (CONFIG_.*_FIRMWARE).
In either case, if firmware hotplug support is there, it can move the
firmware out of kernel memory into the real filesystem for later
usage.
Note: If persistence is implemented on top of initramfs,
register_firmware() may not be appropriate.
Documentation/firmware_class/firmware_sample_driver.c
0 → 100644
View file @
89cbae1f
/*
* firmware_sample_driver.c -
*
* Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
*
* Sample code on how to use request_firmware() from drivers.
*
* Note that register_firmware() is currently useless.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include "linux/firmware.h"
#define WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
char
__init
inkernel_firmware
[]
=
"let's say that this is firmware
\n
"
;
#endif
static
struct
device
ghost_device
=
{
.
name
=
"Ghost Device"
,
.
bus_id
=
"ghost0"
,
};
static
void
sample_firmware_load
(
char
*
firmware
,
int
size
)
{
u8
buf
[
size
+
1
];
memcpy
(
buf
,
firmware
,
size
);
buf
[
size
]
=
'\0'
;
printk
(
"firmware_sample_driver: firmware: %s
\n
"
,
buf
);
}
static
void
sample_probe_default
(
void
)
{
/* uses the default method to get the firmware */
const
struct
firmware
*
fw_entry
;
printk
(
"firmware_sample_driver: a ghost device got inserted :)
\n
"
);
if
(
request_firmware
(
&
fw_entry
,
"sample_driver_fw"
,
&
ghost_device
)
!=
0
)
{
printk
(
KERN_ERR
"firmware_sample_driver: Firmware not available
\n
"
);
return
;
}
sample_firmware_load
(
fw_entry
->
data
,
fw_entry
->
size
);
release_firmware
(
fw_entry
);
/* finish setting up the device */
}
static
void
sample_probe_specific
(
void
)
{
/* Uses some specific hotplug support to get the firmware from
* userspace directly into the hardware, or via some sysfs file */
/* NOTE: This currently doesn't work */
printk
(
"firmware_sample_driver: a ghost device got inserted :)
\n
"
);
if
(
request_firmware
(
NULL
,
"sample_driver_fw"
,
&
ghost_device
)
!=
0
)
{
printk
(
KERN_ERR
"firmware_sample_driver: Firmware load failed
\n
"
);
return
;
}
/* request_firmware blocks until userspace finished, so at
* this point the firmware should be already in the device */
/* finish setting up the device */
}
static
void
sample_probe_async_cont
(
const
struct
firmware
*
fw
,
void
*
context
)
{
if
(
!
fw
){
printk
(
KERN_ERR
"firmware_sample_driver: firmware load failed
\n
"
);
return
;
}
printk
(
"firmware_sample_driver: device pointer
\"
%s
\"\n
"
,
(
char
*
)
context
);
sample_firmware_load
(
fw
->
data
,
fw
->
size
);
}
static
void
sample_probe_async
(
void
)
{
/* Let's say that I can't sleep */
int
error
;
error
=
request_firmware_nowait
(
THIS_MODULE
,
"sample_driver_fw"
,
&
ghost_device
,
"my device pointer"
,
sample_probe_async_cont
);
if
(
error
){
printk
(
KERN_ERR
"firmware_sample_driver:"
" request_firmware_nowait failed
\n
"
);
}
}
static
int
sample_init
(
void
)
{
#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
register_firmware
(
"sample_driver_fw"
,
inkernel_firmware
,
sizeof
(
inkernel_firmware
));
#endif
device_initialize
(
&
ghost_device
);
/* since there is no real hardware insertion I just call the
* sample probe functions here */
sample_probe_specific
();
sample_probe_default
();
sample_probe_async
();
return
0
;
}
static
void
__exit
sample_exit
(
void
)
{
}
module_init
(
sample_init
);
module_exit
(
sample_exit
);
MODULE_LICENSE
(
"GPL"
);
Documentation/firmware_class/firmware_sample_firmware_class.c
0 → 100644
View file @
89cbae1f
/*
* firmware_sample_firmware_class.c -
*
* Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
*
* NOTE: This is just a probe of concept, if you think that your driver would
* be well served by this mechanism please contact me first.
*
* DON'T USE THIS CODE AS IS
*
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <asm/hardirq.h>
#include "linux/firmware.h"
MODULE_AUTHOR
(
"Manuel Estrada Sainz <ranty@debian.org>"
);
MODULE_DESCRIPTION
(
"Hackish sample for using firmware class directly"
);
MODULE_LICENSE
(
"GPL"
);
static
inline
struct
class_device
*
to_class_dev
(
struct
kobject
*
obj
)
{
return
container_of
(
obj
,
struct
class_device
,
kobj
);
}
static
inline
struct
class_device_attribute
*
to_class_dev_attr
(
struct
attribute
*
_attr
)
{
return
container_of
(
_attr
,
struct
class_device_attribute
,
attr
);
}
int
sysfs_create_bin_file
(
struct
kobject
*
kobj
,
struct
bin_attribute
*
attr
);
int
sysfs_remove_bin_file
(
struct
kobject
*
kobj
,
struct
bin_attribute
*
attr
);
struct
firmware_priv
{
char
fw_id
[
FIRMWARE_NAME_MAX
];
s32
loading
:
2
;
u32
abort
:
1
;
};
extern
struct
class
firmware_class
;
static
ssize_t
firmware_loading_show
(
struct
class_device
*
class_dev
,
char
*
buf
)
{
struct
firmware_priv
*
fw_priv
=
class_get_devdata
(
class_dev
);
return
sprintf
(
buf
,
"%d
\n
"
,
fw_priv
->
loading
);
}
static
ssize_t
firmware_loading_store
(
struct
class_device
*
class_dev
,
const
char
*
buf
,
size_t
count
)
{
struct
firmware_priv
*
fw_priv
=
class_get_devdata
(
class_dev
);
int
prev_loading
=
fw_priv
->
loading
;
fw_priv
->
loading
=
simple_strtol
(
buf
,
NULL
,
10
);
switch
(
fw_priv
->
loading
){
case
-
1
:
/* abort load an panic */
break
;
case
1
:
/* setup load */
break
;
case
0
:
if
(
prev_loading
==
1
){
/* finish load and get the device back to working
* state */
}
break
;
}
return
count
;
}
static
CLASS_DEVICE_ATTR
(
loading
,
0644
,
firmware_loading_show
,
firmware_loading_store
);
static
ssize_t
firmware_data_read
(
struct
kobject
*
kobj
,
char
*
buffer
,
loff_t
offset
,
size_t
count
)
{
struct
class_device
*
class_dev
=
to_class_dev
(
kobj
);
struct
firmware_priv
*
fw_priv
=
class_get_devdata
(
class_dev
);
/* read from the devices firmware memory */
return
count
;
}
static
ssize_t
firmware_data_write
(
struct
kobject
*
kobj
,
char
*
buffer
,
loff_t
offset
,
size_t
count
)
{
struct
class_device
*
class_dev
=
to_class_dev
(
kobj
);
struct
firmware_priv
*
fw_priv
=
class_get_devdata
(
class_dev
);
/* write to the devices firmware memory */
return
count
;
}
static
struct
bin_attribute
firmware_attr_data
=
{
.
attr
=
{.
name
=
"data"
,
.
mode
=
0644
},
.
size
=
0
,
.
read
=
firmware_data_read
,
.
write
=
firmware_data_write
,
};
static
int
fw_setup_class_device
(
struct
class_device
*
class_dev
,
const
char
*
fw_name
,
struct
device
*
device
)
{
int
retval
=
0
;
struct
firmware_priv
*
fw_priv
=
kmalloc
(
sizeof
(
struct
firmware_priv
),
GFP_KERNEL
);
if
(
!
fw_priv
){
retval
=
-
ENOMEM
;
goto
out
;
}
memset
(
fw_priv
,
0
,
sizeof
(
*
fw_priv
));
memset
(
class_dev
,
0
,
sizeof
(
*
class_dev
));
strncpy
(
fw_priv
->
fw_id
,
fw_name
,
FIRMWARE_NAME_MAX
);
fw_priv
->
fw_id
[
FIRMWARE_NAME_MAX
-
1
]
=
'\0'
;
strncpy
(
class_dev
->
class_id
,
device
->
bus_id
,
BUS_ID_SIZE
);
class_dev
->
class_id
[
BUS_ID_SIZE
-
1
]
=
'\0'
;
class_dev
->
dev
=
device
;
class_dev
->
class
=
&
firmware_class
,
class_set_devdata
(
class_dev
,
fw_priv
);
retval
=
class_device_register
(
class_dev
);
if
(
retval
){
printk
(
KERN_ERR
"%s: class_device_register failed
\n
"
,
__FUNCTION__
);
goto
error_free_fw_priv
;
}
retval
=
sysfs_create_bin_file
(
&
class_dev
->
kobj
,
&
firmware_attr_data
);
if
(
retval
){
printk
(
KERN_ERR
"%s: sysfs_create_bin_file failed
\n
"
,
__FUNCTION__
);
goto
error_unreg_class_dev
;
}
retval
=
class_device_create_file
(
class_dev
,
&
class_device_attr_loading
);
if
(
retval
){
printk
(
KERN_ERR
"%s: class_device_create_file failed
\n
"
,
__FUNCTION__
);
goto
error_remove_data
;
}
goto
out
;
error_remove_data:
sysfs_remove_bin_file
(
&
class_dev
->
kobj
,
&
firmware_attr_data
);
error_unreg_class_dev:
class_device_unregister
(
class_dev
);
error_free_fw_priv:
kfree
(
fw_priv
);
out:
return
retval
;
}
static
void
fw_remove_class_device
(
struct
class_device
*
class_dev
)
{
struct
firmware_priv
*
fw_priv
=
class_get_devdata
(
class_dev
);
class_device_remove_file
(
class_dev
,
&
class_device_attr_loading
);
sysfs_remove_bin_file
(
&
class_dev
->
kobj
,
&
firmware_attr_data
);
class_device_unregister
(
class_dev
);
}
static
struct
class_device
*
class_dev
;
static
struct
device
my_device
=
{
.
name
=
"Sample Device"
,
.
bus_id
=
"my_dev0"
,
};
static
int
__init
firmware_sample_init
(
void
)
{
int
error
;
device_initialize
(
&
my_device
);
class_dev
=
kmalloc
(
sizeof
(
struct
class_device
),
GFP_KERNEL
);
if
(
!
class_dev
)
return
-
ENOMEM
;
error
=
fw_setup_class_device
(
class_dev
,
"my_firmware_image"
,
&
my_device
);
if
(
error
){
kfree
(
class_dev
);
return
error
;
}
return
0
;
}
static
void
__exit
firmware_sample_exit
(
void
)
{
struct
firmware_priv
*
fw_priv
=
class_get_devdata
(
class_dev
);
fw_remove_class_device
(
class_dev
);
kfree
(
fw_priv
);
kfree
(
class_dev
);
}
module_init
(
firmware_sample_init
);
module_exit
(
firmware_sample_exit
);
Documentation/firmware_class/hotplug-script
0 → 100644
View file @
89cbae1f
#!/bin/sh
# Simple hotplug script sample:
#
# Both $DEVPATH and $FIRMWARE are already provided in the environment.
HOTPLUG_FW_DIR
=
/usr/lib/hotplug/firmware/
echo
1
>
/sysfs/
$DEVPATH
/loading
cat
$HOTPLUG_FW_DIR
/
$FIRMWARE
>
/sysfs/
$DEVPATH
/data
echo
0
>
/sysfs/
$DEVPATH
/loading
# To cancel the load in case of error:
#
# echo -1 > /sysfs/$DEVPATH/loading
#
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment