91a6902958
Well, first of all, I don't want to change so many files either. What I do: Adding a new parameter "struct bin_attribute *" in the .read/.write methods for the sysfs binary attributes. In fact, only the four lines change in fs/sysfs/bin.c and include/linux/sysfs.h do the real work. But I have to update all the files that use binary attributes to make them compatible with the new .read and .write methods. I'm not sure if I missed any. :( Why I do this: For a sysfs attribute, we can get a pointer pointing to the struct attribute in the .show/.store method, while we can't do this for the binary attributes. I don't know why this is different, but this does make it not so handy to use the binary attributes as the regular ones. So I think this patch is reasonable. :) Who benefits from it: The patch that exposes ACPI tables in sysfs requires such an improvement. All the table binary attributes share the same .read method. Parameter "struct bin_attribute *" is used to get the table signature and instance number which are used to distinguish different ACPI table binary attributes. Without this parameter, we need to offer different .read methods for different ACPI table binary attributes. This is impossible as there are various ACPI tables on different platforms, and we don't know what they are until they are loaded. Signed-off-by: Zhang Rui <rui.zhang@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
207 lines
5.1 KiB
C
207 lines
5.1 KiB
C
/*
|
|
* firmware_sample_firmware_class.c -
|
|
*
|
|
* Copyright (c) 2003 Manuel Estrada Sainz
|
|
*
|
|
* 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 <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <linux/firmware.h>
|
|
|
|
|
|
MODULE_AUTHOR("Manuel Estrada Sainz");
|
|
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,
|
|
struct bin_attribute *bin_attr,
|
|
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,
|
|
struct bin_attribute *bin_attr,
|
|
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 = {
|
|
.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);
|
|
|