Commit 39760aa6 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://ldm.bkbits.net/linux-2.5-core

into home.transmeta.com:/home/torvalds/v2.5/linux
parents ad126509 8dcd0564
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.
/*
* 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");
/*
* 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);
#!/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
#
...@@ -5,15 +5,8 @@ Patrick Mochel <mochel@osdl.org> ...@@ -5,15 +5,8 @@ Patrick Mochel <mochel@osdl.org>
Updated: 3 June 2003 Updated: 3 June 2003
Copyright (c) Patrick Mochel Copyright (c) 2003 Patrick Mochel
Copyright (c) Open Source Development Labs Copyright (c) 2003 Open Source Development Labs
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
0. Introduction 0. Introduction
......
...@@ -653,6 +653,8 @@ source "drivers/parport/Kconfig" ...@@ -653,6 +653,8 @@ source "drivers/parport/Kconfig"
endmenu endmenu
source "drivers/base/Kconfig"
source "drivers/mtd/Kconfig" source "drivers/mtd/Kconfig"
source "drivers/pnp/Kconfig" source "drivers/pnp/Kconfig"
......
...@@ -703,6 +703,8 @@ endchoice ...@@ -703,6 +703,8 @@ endchoice
source "fs/Kconfig.binfmt" source "fs/Kconfig.binfmt"
source "drivers/base/Kconfig"
config PM config PM
bool "Power Management support" bool "Power Management support"
---help--- ---help---
......
...@@ -297,6 +297,8 @@ config CMDLINE ...@@ -297,6 +297,8 @@ config CMDLINE
endmenu endmenu
source "drivers/base/Kconfig"
source "drivers/parport/Kconfig" source "drivers/parport/Kconfig"
source "drivers/pnp/Kconfig" source "drivers/pnp/Kconfig"
......
...@@ -515,6 +515,8 @@ config ETRAX_POWERBUTTON_BIT ...@@ -515,6 +515,8 @@ config ETRAX_POWERBUTTON_BIT
endmenu endmenu
source "drivers/base/Kconfig"
# bring in Etrax built-in drivers # bring in Etrax built-in drivers
source "arch/cris/drivers/Kconfig" source "arch/cris/drivers/Kconfig"
......
...@@ -145,6 +145,8 @@ source "fs/Kconfig.binfmt" ...@@ -145,6 +145,8 @@ source "fs/Kconfig.binfmt"
endmenu endmenu
source "drivers/base/Kconfig"
source "drivers/block/Kconfig" source "drivers/block/Kconfig"
source "drivers/ide/Kconfig" source "drivers/ide/Kconfig"
......
...@@ -1194,6 +1194,8 @@ source "fs/Kconfig.binfmt" ...@@ -1194,6 +1194,8 @@ source "fs/Kconfig.binfmt"
endmenu endmenu
source "drivers/base/Kconfig"
source "drivers/mtd/Kconfig" source "drivers/mtd/Kconfig"
source "drivers/parport/Kconfig" source "drivers/parport/Kconfig"
......
...@@ -281,19 +281,19 @@ unsigned long get_cmos_time(void) ...@@ -281,19 +281,19 @@ unsigned long get_cmos_time(void)
return retval; return retval;
} }
static struct sysdev_class rtc_sysclass = { static struct sysdev_class pit_sysclass = {
set_kset_name("rtc"), set_kset_name("pit"),
}; };
/* XXX this driverfs stuff should probably go elsewhere later -john */ /* XXX this driverfs stuff should probably go elsewhere later -john */
static struct sys_device device_i8253 = { static struct sys_device device_i8253 = {
.id = 0, .id = 0,
.cls = &rtc_sysclass, .cls = &pit_sysclass,
}; };
static int time_init_device(void) static int time_init_device(void)
{ {
int error = sysdev_class_register(&rtc_sysclass); int error = sysdev_class_register(&pit_sysclass);
if (!error) if (!error)
error = sys_device_register(&device_i8253); error = sys_device_register(&device_i8253);
return error; return error;
......
...@@ -534,6 +534,8 @@ endif ...@@ -534,6 +534,8 @@ endif
endmenu endmenu
source "drivers/base/Kconfig"
if !IA64_HP_SIM if !IA64_HP_SIM
source "drivers/mtd/Kconfig" source "drivers/mtd/Kconfig"
......
...@@ -573,6 +573,8 @@ endif ...@@ -573,6 +573,8 @@ endif
endmenu endmenu
source "drivers/base/Kconfig"
source "drivers/mtd/Kconfig" source "drivers/mtd/Kconfig"
source "drivers/block/Kconfig" source "drivers/block/Kconfig"
......
...@@ -515,6 +515,8 @@ config PM ...@@ -515,6 +515,8 @@ config PM
endmenu endmenu
source "drivers/base/Kconfig"
source "drivers/mtd/Kconfig" source "drivers/mtd/Kconfig"
source "drivers/parport/Kconfig" source "drivers/parport/Kconfig"
......
...@@ -744,6 +744,8 @@ if ISA ...@@ -744,6 +744,8 @@ if ISA
source "drivers/pnp/Kconfig" source "drivers/pnp/Kconfig"
endif endif
source "drivers/base/Kconfig"
source "drivers/mtd/Kconfig" source "drivers/mtd/Kconfig"
source "drivers/parport/Kconfig" source "drivers/parport/Kconfig"
......
...@@ -361,6 +361,8 @@ endmenu ...@@ -361,6 +361,8 @@ endmenu
source "drivers/pci/Kconfig" source "drivers/pci/Kconfig"
source "drivers/base/Kconfig"
source "drivers/mtd/Kconfig" source "drivers/mtd/Kconfig"
source "drivers/parport/Kconfig" source "drivers/parport/Kconfig"
......
...@@ -170,6 +170,8 @@ source "fs/Kconfig.binfmt" ...@@ -170,6 +170,8 @@ source "fs/Kconfig.binfmt"
endmenu endmenu
source "drivers/base/Kconfig"
# source "drivers/mtd/Kconfig" # source "drivers/mtd/Kconfig"
source "drivers/parport/Kconfig" source "drivers/parport/Kconfig"
......
...@@ -1163,6 +1163,8 @@ config PIN_TLB ...@@ -1163,6 +1163,8 @@ config PIN_TLB
depends on ADVANCED_OPTIONS && 8xx depends on ADVANCED_OPTIONS && 8xx
endmenu endmenu
source "drivers/base/Kconfig"
source "drivers/mtd/Kconfig" source "drivers/mtd/Kconfig"
source "drivers/pnp/Kconfig" source "drivers/pnp/Kconfig"
......
...@@ -241,6 +241,8 @@ config CMDLINE ...@@ -241,6 +241,8 @@ config CMDLINE
endmenu endmenu
source "drivers/base/Kconfig"
source "drivers/mtd/Kconfig" source "drivers/mtd/Kconfig"
source "drivers/parport/Kconfig" source "drivers/parport/Kconfig"
......
...@@ -229,6 +229,8 @@ config PCMCIA ...@@ -229,6 +229,8 @@ config PCMCIA
bool bool
default n default n
source "drivers/base/Kconfig"
menu "SCSI support" menu "SCSI support"
config SCSI config SCSI
......
...@@ -767,6 +767,8 @@ source "fs/Kconfig.binfmt" ...@@ -767,6 +767,8 @@ source "fs/Kconfig.binfmt"
endmenu endmenu
source "drivers/base/Kconfig"
source "drivers/mtd/Kconfig" source "drivers/mtd/Kconfig"
source "drivers/parport/Kconfig" source "drivers/parport/Kconfig"
......
...@@ -319,6 +319,8 @@ config PRINTER ...@@ -319,6 +319,8 @@ config PRINTER
endmenu endmenu
source "drivers/base/Kconfig"
source "drivers/video/Kconfig" source "drivers/video/Kconfig"
source "drivers/mtd/Kconfig" source "drivers/mtd/Kconfig"
......
...@@ -521,6 +521,8 @@ config WATCHDOG_RIO ...@@ -521,6 +521,8 @@ config WATCHDOG_RIO
endmenu endmenu
source "drivers/base/Kconfig"
source "drivers/video/Kconfig" source "drivers/video/Kconfig"
source "drivers/serial/Kconfig" source "drivers/serial/Kconfig"
......
...@@ -171,6 +171,8 @@ endmenu ...@@ -171,6 +171,8 @@ endmenu
source "init/Kconfig" source "init/Kconfig"
source "drivers/base/Kconfig"
source "arch/um/Kconfig_char" source "arch/um/Kconfig_char"
source "arch/um/Kconfig_block" source "arch/um/Kconfig_block"
......
...@@ -249,6 +249,8 @@ endmenu ...@@ -249,6 +249,8 @@ endmenu
############################################################################# #############################################################################
source "drivers/base/Kconfig"
source drivers/mtd/Kconfig source drivers/mtd/Kconfig
source drivers/parport/Kconfig source drivers/parport/Kconfig
......
...@@ -397,6 +397,8 @@ config UID16 ...@@ -397,6 +397,8 @@ config UID16
endmenu endmenu
source "drivers/base/Kconfig"
source "drivers/mtd/Kconfig" source "drivers/mtd/Kconfig"
source "drivers/parport/Kconfig" source "drivers/parport/Kconfig"
......
menu "Generic Driver Options"
config FW_LOADER
tristate "Hotplug firmware loading support"
---help---
This option is provided for the case where no in-kernel-tree modules
require hotplug firmware loading support, but a module built outside
the kernel tree does.
endmenu
...@@ -3,4 +3,5 @@ ...@@ -3,4 +3,5 @@
obj-y := core.o sys.o interface.o power.o bus.o \ obj-y := core.o sys.o interface.o power.o bus.o \
driver.o class.o platform.o \ driver.o class.o platform.o \
cpu.o firmware.o init.o map.o cpu.o firmware.o init.o map.o
obj-$(CONFIG_FW_LOADER) += firmware_class.o
obj-$(CONFIG_NUMA) += node.o memblk.o obj-$(CONFIG_NUMA) += node.o memblk.o
/*
* firmware_class.c - Multi purpose firmware loading support
*
* Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
*
* Please see Documentation/firmware_class/ for more information.
*
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/vmalloc.h>
#include <asm/hardirq.h>
#include <linux/firmware.h>
#include "base.h"
MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>");
MODULE_DESCRIPTION("Multi purpose firmware loading support");
MODULE_LICENSE("GPL");
static int loading_timeout = 10; /* In seconds */
struct firmware_priv {
char fw_id[FIRMWARE_NAME_MAX];
struct completion completion;
struct bin_attribute attr_data;
struct firmware *fw;
int loading;
int abort;
int alloc_size;
struct timer_list timeout;
};
static ssize_t
firmware_timeout_show(struct class *class, char *buf)
{
return sprintf(buf, "%d\n", loading_timeout);
}
/**
* firmware_timeout_store:
* Description:
* Sets the number of seconds to wait for the firmware. Once
* this expires an error will be return to the driver and no
* firmware will be provided.
*
* Note: zero means 'wait for ever'
*
**/
static ssize_t
firmware_timeout_store(struct class *class, const char *buf, size_t count)
{
loading_timeout = simple_strtol(buf, NULL, 10);
return count;
}
static CLASS_ATTR(timeout, 0644, firmware_timeout_show, firmware_timeout_store);
static void fw_class_dev_release(struct class_device *class_dev);
int firmware_class_hotplug(struct class_device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
static struct class firmware_class = {
.name = "firmware",
.hotplug = firmware_class_hotplug,
.release = fw_class_dev_release,
};
int
firmware_class_hotplug(struct class_device *class_dev, char **envp,
int num_envp, char *buffer, int buffer_size)
{
struct firmware_priv *fw_priv = class_get_devdata(class_dev);
int i = 0;
char *scratch = buffer;
if (buffer_size < (FIRMWARE_NAME_MAX + 10))
return -ENOMEM;
if (num_envp < 1)
return -ENOMEM;
envp[i++] = scratch;
scratch += sprintf(scratch, "FIRMWARE=%s", fw_priv->fw_id) + 1;
return 0;
}
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);
}
/**
* firmware_loading_store: - loading control file
* Description:
* The relevant values are:
*
* 1: Start a load, discarding any previous partial load.
* 0: Conclude the load and handle the data to the driver code.
* -1: Conclude the load with an error and discard any written data.
**/
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:
fw_priv->abort = 1;
wmb();
complete(&fw_priv->completion);
break;
case 1:
kfree(fw_priv->fw->data);
fw_priv->fw->data = NULL;
fw_priv->fw->size = 0;
fw_priv->alloc_size = 0;
break;
case 0:
if (prev_loading == 1)
complete(&fw_priv->completion);
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);
struct firmware *fw = fw_priv->fw;
if (offset > fw->size)
return 0;
if (offset + count > fw->size)
count = fw->size - offset;
memcpy(buffer, fw->data + offset, count);
return count;
}
static int
fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
{
u8 *new_data;
if (min_size <= fw_priv->alloc_size)
return 0;
new_data = vmalloc(fw_priv->alloc_size + PAGE_SIZE);
if (!new_data) {
printk(KERN_ERR "%s: unable to alloc buffer\n", __FUNCTION__);
/* Make sure that we don't keep incomplete data */
fw_priv->abort = 1;
return -ENOMEM;
}
fw_priv->alloc_size += PAGE_SIZE;
if (fw_priv->fw->data) {
memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
vfree(fw_priv->fw->data);
}
fw_priv->fw->data = new_data;
BUG_ON(min_size > fw_priv->alloc_size);
return 0;
}
/**
* firmware_data_write:
*
* Description:
*
* Data written to the 'data' attribute will be later handled to
* the driver as a firmware image.
**/
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);
struct firmware *fw = fw_priv->fw;
int retval;
retval = fw_realloc_buffer(fw_priv, offset + count);
if (retval)
return retval;
memcpy(fw->data + offset, buffer, count);
fw->size = max_t(size_t, offset + count, fw->size);
return count;
}
static struct bin_attribute firmware_attr_data_tmpl = {
.attr = {.name = "data", .mode = 0644},
.size = 0,
.read = firmware_data_read,
.write = firmware_data_write,
};
static void
fw_class_dev_release(struct class_device *class_dev)
{
kfree(class_dev);
}
static void
firmware_class_timeout(u_long data)
{
struct firmware_priv *fw_priv = (struct firmware_priv *) data;
fw_priv->abort = 1;
wmb();
complete(&fw_priv->completion);
}
static inline void
fw_setup_class_device_id(struct class_device *class_dev, struct device *dev)
{
/* XXX warning we should watch out for name collisions */
strncpy(class_dev->class_id, dev->bus_id, BUS_ID_SIZE);
class_dev->class_id[BUS_ID_SIZE - 1] = '\0';
}
static int
fw_setup_class_device(struct class_device **class_dev_p,
const char *fw_name, struct device *device)
{
int retval = 0;
struct firmware_priv *fw_priv = kmalloc(sizeof (struct firmware_priv),
GFP_KERNEL);
struct class_device *class_dev = kmalloc(sizeof (struct class_device),
GFP_KERNEL);
if (!fw_priv || !class_dev) {
retval = -ENOMEM;
goto error_kfree;
}
memset(fw_priv, 0, sizeof (*fw_priv));
memset(class_dev, 0, sizeof (*class_dev));
init_completion(&fw_priv->completion);
memcpy(&fw_priv->attr_data, &firmware_attr_data_tmpl,
sizeof (firmware_attr_data_tmpl));
strncpy(&fw_priv->fw_id[0], fw_name, FIRMWARE_NAME_MAX);
fw_priv->fw_id[FIRMWARE_NAME_MAX - 1] = '\0';
fw_setup_class_device_id(class_dev, device);
class_dev->dev = device;
fw_priv->timeout.function = firmware_class_timeout;
fw_priv->timeout.data = (u_long) fw_priv;
init_timer(&fw_priv->timeout);
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_kfree;
}
retval = sysfs_create_bin_file(&class_dev->kobj, &fw_priv->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;
}
fw_priv->fw = kmalloc(sizeof (struct firmware), GFP_KERNEL);
if (!fw_priv->fw) {
printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
__FUNCTION__);
retval = -ENOMEM;
goto error_remove_loading;
}
memset(fw_priv->fw, 0, sizeof (*fw_priv->fw));
goto out;
error_remove_loading:
class_device_remove_file(class_dev, &class_device_attr_loading);
error_remove_data:
sysfs_remove_bin_file(&class_dev->kobj, &fw_priv->attr_data);
error_unreg_class_dev:
class_device_unregister(class_dev);
error_kfree:
kfree(fw_priv);
kfree(class_dev);
*class_dev_p = NULL;
out:
*class_dev_p = class_dev;
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, &fw_priv->attr_data);
class_device_unregister(class_dev);
}
/**
* request_firmware: - request firmware to hotplug and wait for it
* Description:
* @firmware will be used to return a firmware image by the name
* of @name for device @device.
*
* Should be called from user context where sleeping is allowed.
*
* @name will be use as $FIRMWARE in the hotplug environment and
* should be distinctive enough not to be confused with any other
* firmware image for this or any other device.
**/
int
request_firmware(const struct firmware **firmware, const char *name,
struct device *device)
{
struct class_device *class_dev;
struct firmware_priv *fw_priv;
int retval;
if (!firmware)
return -EINVAL;
*firmware = NULL;
retval = fw_setup_class_device(&class_dev, name, device);
if (retval)
goto out;
fw_priv = class_get_devdata(class_dev);
if (loading_timeout) {
fw_priv->timeout.expires = jiffies + loading_timeout * HZ;
add_timer(&fw_priv->timeout);
}
wait_for_completion(&fw_priv->completion);
del_timer(&fw_priv->timeout);
fw_remove_class_device(class_dev);
if (fw_priv->fw->size && !fw_priv->abort) {
*firmware = fw_priv->fw;
} else {
retval = -ENOENT;
vfree(fw_priv->fw->data);
kfree(fw_priv->fw);
}
kfree(fw_priv);
out:
return retval;
}
/**
* release_firmware: - release the resource associated with a firmware image
**/
void
release_firmware(const struct firmware *fw)
{
if (fw) {
vfree(fw->data);
kfree(fw);
}
}
/**
* register_firmware: - provide a firmware image for later usage
*
* Description:
* Make sure that @data will be available by requesting firmware @name.
*
* Note: This will not be possible until some kind of persistence
* is available.
**/
void
register_firmware(const char *name, const u8 *data, size_t size)
{
/* This is meaningless without firmware caching, so until we
* decide if firmware caching is reasonable just leave it as a
* noop */
}
/* Async support */
struct firmware_work {
struct work_struct work;
struct module *module;
const char *name;
struct device *device;
void *context;
void (*cont)(const struct firmware *fw, void *context);
};
static void
request_firmware_work_func(void *arg)
{
struct firmware_work *fw_work = arg;
const struct firmware *fw;
if (!arg)
return;
request_firmware(&fw, fw_work->name, fw_work->device);
fw_work->cont(fw, fw_work->context);
release_firmware(fw);
module_put(fw_work->module);
kfree(fw_work);
}
/**
* request_firmware_nowait:
*
* Description:
* Asynchronous variant of request_firmware() for contexts where
* it is not possible to sleep.
*
* @cont will be called asynchronously when the firmware request is over.
*
* @context will be passed over to @cont.
*
* @fw may be %NULL if firmware request fails.
*
**/
int
request_firmware_nowait(
struct module *module,
const char *name, struct device *device, void *context,
void (*cont)(const struct firmware *fw, void *context))
{
struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
GFP_ATOMIC);
if (!fw_work)
return -ENOMEM;
if (!try_module_get(module)) {
kfree(fw_work);
return -EFAULT;
}
*fw_work = (struct firmware_work) {
.module = module,
.name = name,
.device = device,
.context = context,
.cont = cont,
};
INIT_WORK(&fw_work->work, request_firmware_work_func, fw_work);
schedule_work(&fw_work->work);
return 0;
}
static int __init
firmware_class_init(void)
{
int error;
error = class_register(&firmware_class);
if (error) {
printk(KERN_ERR "%s: class_register failed\n", __FUNCTION__);
}
error = class_create_file(&firmware_class, &class_attr_timeout);
if (error) {
printk(KERN_ERR "%s: class_create_file failed\n",
__FUNCTION__);
class_unregister(&firmware_class);
}
return error;
}
static void __exit
firmware_class_exit(void)
{
class_remove_file(&firmware_class, &class_attr_timeout);
class_unregister(&firmware_class);
}
module_init(firmware_class_init);
module_exit(firmware_class_exit);
EXPORT_SYMBOL(release_firmware);
EXPORT_SYMBOL(request_firmware);
EXPORT_SYMBOL(request_firmware_nowait);
EXPORT_SYMBOL(register_firmware);
EXPORT_SYMBOL(firmware_class);
...@@ -74,6 +74,8 @@ void sysdev_remove_file(struct sys_device * s, struct sysdev_attribute * a) ...@@ -74,6 +74,8 @@ void sysdev_remove_file(struct sys_device * s, struct sysdev_attribute * a)
sysfs_remove_file(&s->kobj,&a->attr); sysfs_remove_file(&s->kobj,&a->attr);
} }
EXPORT_SYMBOL(sysdev_create_file);
EXPORT_SYMBOL(sysdev_remove_file);
/* /*
* declare system_subsys * declare system_subsys
...@@ -171,6 +173,9 @@ int sys_device_register(struct sys_device * sysdev) ...@@ -171,6 +173,9 @@ int sys_device_register(struct sys_device * sysdev)
/* Make sure the kset is set */ /* Make sure the kset is set */
sysdev->kobj.kset = &cls->kset; sysdev->kobj.kset = &cls->kset;
/* But make sure we point to the right type for sysfs translation */
sysdev->kobj.ktype = &ktype_sysdev;
/* set the kobject name */ /* set the kobject name */
snprintf(sysdev->kobj.name,KOBJ_NAME_LEN,"%s%d", snprintf(sysdev->kobj.name,KOBJ_NAME_LEN,"%s%d",
cls->kset.kobj.name,sysdev->id); cls->kset.kobj.name,sysdev->id);
...@@ -218,9 +223,6 @@ void sys_device_unregister(struct sys_device * sysdev) ...@@ -218,9 +223,6 @@ void sys_device_unregister(struct sys_device * sysdev)
if (drv->remove) if (drv->remove)
drv->remove(sysdev); drv->remove(sysdev);
} }
list_del_init(&sysdev->entry);
up_write(&system_subsys.rwsem); up_write(&system_subsys.rwsem);
kobject_unregister(&sysdev->kobj); kobject_unregister(&sysdev->kobj);
......
#ifndef _LINUX_FIRMWARE_H
#define _LINUX_FIRMWARE_H
#include <linux/module.h>
#include <linux/types.h>
#define FIRMWARE_NAME_MAX 30
struct firmware {
size_t size;
u8 *data;
};
int request_firmware(const struct firmware **fw, const char *name,
struct device *device);
int request_firmware_nowait(
struct module *module,
const char *name, struct device *device, void *context,
void (*cont)(const struct firmware *fw, void *context));
void release_firmware(const struct firmware *fw);
void register_firmware(const char *name, const u8 *data, size_t size);
#endif
...@@ -72,7 +72,6 @@ struct sys_device { ...@@ -72,7 +72,6 @@ struct sys_device {
u32 id; u32 id;
struct sysdev_class * cls; struct sysdev_class * cls;
struct kobject kobj; struct kobject kobj;
struct list_head entry;
}; };
extern int sys_device_register(struct sys_device *); extern int sys_device_register(struct sys_device *);
......
...@@ -100,6 +100,9 @@ static void fill_kobj_path(struct kset *kset, struct kobject *kobj, char *path, ...@@ -100,6 +100,9 @@ static void fill_kobj_path(struct kset *kset, struct kobject *kobj, char *path,
#define BUFFER_SIZE 1024 /* should be enough memory for the env */ #define BUFFER_SIZE 1024 /* should be enough memory for the env */
#define NUM_ENVP 32 /* number of env pointers */ #define NUM_ENVP 32 /* number of env pointers */
static unsigned long sequence_num;
static spinlock_t sequence_lock = SPIN_LOCK_UNLOCKED;
static void kset_hotplug(const char *action, struct kset *kset, static void kset_hotplug(const char *action, struct kset *kset,
struct kobject *kobj) struct kobject *kobj)
{ {
...@@ -112,6 +115,7 @@ static void kset_hotplug(const char *action, struct kset *kset, ...@@ -112,6 +115,7 @@ static void kset_hotplug(const char *action, struct kset *kset,
int kobj_path_length; int kobj_path_length;
char *kobj_path = NULL; char *kobj_path = NULL;
char *name = NULL; char *name = NULL;
unsigned long seq;
/* If the kset has a filter operation, call it. If it returns /* If the kset has a filter operation, call it. If it returns
failure, no hotplug event is required. */ failure, no hotplug event is required. */
...@@ -152,6 +156,13 @@ static void kset_hotplug(const char *action, struct kset *kset, ...@@ -152,6 +156,13 @@ static void kset_hotplug(const char *action, struct kset *kset,
envp [i++] = scratch; envp [i++] = scratch;
scratch += sprintf(scratch, "ACTION=%s", action) + 1; scratch += sprintf(scratch, "ACTION=%s", action) + 1;
spin_lock(&sequence_lock);
seq = sequence_num++;
spin_unlock(&sequence_lock);
envp [i++] = scratch;
scratch += sprintf(scratch, "SEQNUM=%ld", seq) + 1;
kobj_path_length = get_kobj_path_length (kset, kobj); kobj_path_length = get_kobj_path_length (kset, kobj);
kobj_path = kmalloc (kobj_path_length, GFP_KERNEL); kobj_path = kmalloc (kobj_path_length, GFP_KERNEL);
if (!kobj_path) if (!kobj_path)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment