2004-04-08 19:49:53

by Alex Williamson

[permalink] [raw]
Subject: [PATCH] filling in ACPI method access via sysfs namespace

Hi,

Here's a patch that adds object and control method access to devices
in the /sys/firmware/acpi/namespace/ACPI/ tree. I sent out an RFC on
this approach several days ago and got a couple of good responses.
Since then I've added a lot more methods, including several control
methods to allow you to do more than simply look at object properties.

There are a lot of potential consumers of this type of data. For
instance:

* Laptop users may be able to access some of their 'hotkey'
features from userspace (display switching, eject).
* Servers and workstations can associate PCI buses, slots, address
spaces, etc.
* Hotplug tools could discover devices dependent on other devices
prior to eject.
* Manageability tools could have easy access to device
identification and location methods.
* etc...

The code has been tried on several x86 and ia64 systems. I expanded
the existing sysfs namespace to include devices which the _STA methods
do not list as present at bootup. This helps to show things like
hotplug PCI slots that aren't currently in use and extra battery bays in
laptops that are vacant. There are several methods that return complex
data structures. I started trying to parse them into pretty text for
output, but the code bloat got too big too fast. So, I've left that for
userspace tools and just dump the raw data via this interface. Most
object methods return basic elements and are easily represented as
parsed text. Also, fair warning: calling _EJ0 methods on devices may be
unsafe. I've made the files only accessible by root to help avoid
issues in this area. A couple fun things for laptop users (YMMV):

To eject from a dock (works on an omnibook 500, exact paths will vary):
echo 0 > /sys/firmware/acpi/namespace/ACPI/_SB/PCI0/ISA0/JP1/_DCK
echo 1 > /sys/firmware/acpi/namespace/ACPI/_SB/PCI0/ISA0/JP1/_EJ0

When re-docking, do:
echo 1 > /sys/firmware/acpi/namespace/ACPI/_SB/PCI0/ISA0/JP1/_DCK

To switch displays (X can get in the way and prevent switching):
CRT only -> LCD only:
echo 0 > /sys/firmware/acpi/namespace/ACPI/_SB/PCI0/AGP/VGA/CRT/_DSS
echo 0x80000001 > /sys/firmware/acpi/namespace/ACPI/_SB/PCI0/AGP/VGA/LCD/_DSS
LCD only -> LCD+CRT:
echo 0x80000001 > /sys/firmware/acpi/namespace/ACPI/_SB/PCI0/AGP/VGA/CRT/_DSS
LCD+CRT -> CRT only:
echo 0x80000000 > /sys/firmware/acpi/namespace/ACPI/_SB/PCI0/AGP/VGA/LCD/_DSS

It's highly dependent on how your firmware works as to how complete
the behavior will be. I've seen a report from an IBM T30 that _EJ0 from
the dock won't trigger the electro-mechanical eject, but seems to do
most everything else. Bug reports welcomed (but I can't fix your
firmware if it doesn't do what you expect). Please consider integrating
upstream. Thanks,

Alex

--
Alex Williamson HP Linux & Open Source Lab

===== drivers/acpi/scan.c 1.22 vs edited =====
--- 1.22/drivers/acpi/scan.c Tue Feb 3 22:29:19 2004
+++ edited/drivers/acpi/scan.c Thu Apr 8 11:59:36 2004
@@ -7,6 +7,7 @@

#include <acpi/acpi_drivers.h>
#include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */
+#include <acpi/actypes.h>


#define _COMPONENT ACPI_BUS_COMPONENT
@@ -22,9 +23,364 @@
#define ACPI_BUS_DRIVER_NAME "ACPI Bus Driver"
#define ACPI_BUS_DEVICE_NAME "System Bus"

+#define METHOD_NAME__BCL "_BCL"
+#define METHOD_NAME__BCM "_BCM"
+#define METHOD_NAME__DCK "_DCK"
+#define METHOD_NAME__DCS "_DCS"
+#define METHOD_NAME__DGS "_DGS"
+#define METHOD_NAME__DDN "_DDN"
+#define METHOD_NAME__DSS "_DSS"
+#define METHOD_NAME__DOS "_DOS"
+#define METHOD_NAME__DOD "_DOD"
+#define METHOD_NAME__EDL "_EDL"
+#define METHOD_NAME__EJ0 "_EJ0"
+#define METHOD_NAME__EJD "_EJD"
+#define METHOD_NAME__FIX "_FIX"
+#define METHOD_NAME__HPP "_HPP"
+#define METHOD_NAME__LCK "_LCK"
+#define METHOD_NAME__LID "_LID"
+#define METHOD_NAME__MAT "_MAT"
+#define METHOD_NAME__PXM "_PXM"
+#define METHOD_NAME__RMV "_RMV"
+#define METHOD_NAME__STR "_STR"
+#define METHOD_NAME__SUN "_SUN"
+
static LIST_HEAD(acpi_device_list);
static spinlock_t acpi_device_lock = SPIN_LOCK_UNLOCKED;

+struct acpi_handle_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct acpi_device *, char *, char *);
+ ssize_t (*store)(struct acpi_device *, char *, const char *, size_t);
+};
+
+#define EISA_ID_TYPE (1<<0)
+#define UNICODE_TYPE (1<<1)
+
+static ssize_t
+parse_integer(char *buf, u32 value, unsigned int flags) {
+ if (flags & EISA_ID_TYPE) {
+ acpi_ex_eisa_id_to_string(value, buf);
+ buf[8] = '\n';
+ return 9;
+ }
+ return sprintf(buf, "%08x\n", value);
+}
+
+static ssize_t
+parse_string(char *buf, u32 length, char *pointer, unsigned int flags) {
+ length += 2;
+ return snprintf(buf, length, "%s\n", pointer);
+}
+
+static ssize_t
+parse_buffer(char *buf, u32 length, u8 *pointer, unsigned int flags) {
+ ssize_t offset = 0;
+
+ if (flags & UNICODE_TYPE) {
+ for (; length > 0 ; length -= 2) {
+ buf[offset++] = *pointer;
+ pointer += 2;
+ }
+ } else {
+ for (; length > 0 ; length--) {
+ sprintf(buf + offset, "%02x", *pointer);
+ offset += 2;
+ pointer++;
+ }
+ }
+ buf[offset++] = '\n';
+ return offset;
+}
+
+static ssize_t parse_package(char *, union acpi_object *, unsigned int);
+
+static ssize_t
+parse_element(
+ char *buf,
+ union acpi_object *element,
+ unsigned int flags)
+{
+
+ switch (element->type) {
+ case ACPI_TYPE_INTEGER:
+ return parse_integer(buf, element->integer.value, flags);
+ case ACPI_TYPE_STRING:
+ return parse_string(buf, element->string.length,
+ element->string.pointer, flags);
+ case ACPI_TYPE_BUFFER:
+ return parse_buffer(buf, element->buffer.length,
+ element->buffer.pointer, flags);
+ case ACPI_TYPE_PACKAGE:
+ return parse_package(buf, element, flags);
+ default:
+ return sprintf(buf, "Unknown Type: %d\n", element->type);
+ }
+}
+
+static ssize_t
+parse_package(char *buf, union acpi_object *element, unsigned int flags) {
+ int count = element->package.count;
+ ssize_t tmp, retval = 0;
+
+ element = element->package.elements;
+
+ for (; count > 0 ; count--, element++) {
+ tmp = parse_element(buf, element, flags);
+ buf += tmp;
+ retval += tmp;
+ }
+ return retval;
+}
+
+static ssize_t
+acpi_device_read_file (
+ struct acpi_device *device,
+ char *method,
+ char *buf)
+{
+ acpi_status status;
+ union acpi_object *element;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ ssize_t retval = 0;
+ unsigned int flags = 0;
+
+ status = acpi_evaluate_object(device->handle, method, NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ if (!strcmp(method, METHOD_NAME__HID)
+ || !strcmp(method, METHOD_NAME__CID)
+ || !strcmp(method, METHOD_NAME__FIX))
+ flags = EISA_ID_TYPE;
+
+ if (!strcmp(method, METHOD_NAME__STR))
+ flags = UNICODE_TYPE;
+
+ element = (union acpi_object *) buffer.pointer;
+ retval = parse_element(buf, element, flags);
+ acpi_os_free(buffer.pointer);
+
+ return retval;
+}
+
+static ssize_t
+acpi_device_read_raw(struct acpi_device *device, char *method, char *buf)
+{
+ acpi_status status;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+
+ status = acpi_evaluate_object(device->handle, method, NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ memcpy(buf, buffer.pointer, buffer.length);
+ acpi_os_free(buffer.pointer);
+ return buffer.length;
+}
+
+static ssize_t
+acpi_device_read_crs(struct acpi_device *device, char *method, char *buf)
+{
+ acpi_status status;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+
+ status = acpi_get_current_resources(device->handle, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ memcpy(buf, buffer.pointer, buffer.length);
+ acpi_os_free(buffer.pointer);
+ return buffer.length;
+}
+
+static ssize_t
+acpi_device_read_prs(struct acpi_device *device, char *method, char *buf)
+{
+ acpi_status status;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+
+ status = acpi_get_possible_resources(device->handle, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ memcpy(buf, buffer.pointer, buffer.length);
+ acpi_os_free(buffer.pointer);
+ return buffer.length;
+}
+
+static ssize_t
+acpi_device_read_prt(struct acpi_device *device, char *method, char *buf)
+{
+ acpi_status status;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+
+ status = acpi_get_irq_routing_table(device->handle, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ memcpy(buf, buffer.pointer, buffer.length);
+ acpi_os_free(buffer.pointer);
+ return buffer.length;
+}
+
+static ssize_t
+acpi_device_write_integer (
+ struct acpi_device *device,
+ char *method,
+ const char *buf,
+ size_t length)
+{
+ struct acpi_object_list arg_list;
+ union acpi_object arg;
+ int value;
+
+ value = simple_strtol(buf, NULL, 0);
+
+ arg_list.count = 1;
+ arg_list.pointer = &arg;
+ arg.type = ACPI_TYPE_INTEGER;
+ arg.integer.value = value;
+
+ if (ACPI_FAILURE(acpi_evaluate_object(device->handle, method, &arg_list, NULL)))
+ return -ENODEV;
+
+ return length;
+}
+
+#define acpi_handle_attr_rw(method, show_func, store_func) \
+static struct acpi_handle_attribute acpi_handle_attr##method = { \
+ .attr = {.name = METHOD_NAME_##method, \
+ .mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP}, \
+ .show = show_func, \
+ .store = store_func, \
+};
+
+#define acpi_handle_attr_ro(method, show_func) \
+static struct acpi_handle_attribute acpi_handle_attr##method = { \
+ .attr = {.name = METHOD_NAME_##method, \
+ .mode = S_IFREG | S_IRUSR | S_IRGRP}, \
+ .show = show_func, \
+};
+
+#define acpi_handle_attr_wo(method, store_func) \
+static struct acpi_handle_attribute acpi_handle_attr##method = { \
+ .attr = {.name = METHOD_NAME_##method, \
+ .mode = S_IFREG | S_IWUSR | S_IWGRP}, \
+ .store = store_func, \
+};
+
+acpi_handle_attr_ro(_ADR, acpi_device_read_file)
+acpi_handle_attr_ro(_BBN, acpi_device_read_file)
+acpi_handle_attr_ro(_BCL, acpi_device_read_file)
+acpi_handle_attr_wo(_BCM, acpi_device_write_integer)
+acpi_handle_attr_ro(_CID, acpi_device_read_file)
+acpi_handle_attr_ro(_CRS, acpi_device_read_crs)
+acpi_handle_attr_wo(_DCK, acpi_device_write_integer)
+acpi_handle_attr_ro(_DCS, acpi_device_read_file)
+acpi_handle_attr_ro(_DGS, acpi_device_read_file)
+acpi_handle_attr_ro(_DDN, acpi_device_read_file)
+acpi_handle_attr_wo(_DSS, acpi_device_write_integer)
+acpi_handle_attr_wo(_DOS, acpi_device_write_integer)
+acpi_handle_attr_ro(_DOD, acpi_device_read_file)
+acpi_handle_attr_ro(_EDL, acpi_device_read_file)
+acpi_handle_attr_wo(_EJ0, acpi_device_write_integer)
+acpi_handle_attr_ro(_EJD, acpi_device_read_file)
+acpi_handle_attr_ro(_FIX, acpi_device_read_file)
+acpi_handle_attr_ro(_HID, acpi_device_read_file)
+acpi_handle_attr_ro(_HPP, acpi_device_read_file)
+acpi_handle_attr_wo(_LCK, acpi_device_write_integer)
+acpi_handle_attr_ro(_LID, acpi_device_read_file)
+acpi_handle_attr_ro(_MAT, acpi_device_read_raw)
+acpi_handle_attr_ro(_PRS, acpi_device_read_prs)
+acpi_handle_attr_ro(_PRT, acpi_device_read_prt)
+acpi_handle_attr_ro(_PXM, acpi_device_read_file)
+acpi_handle_attr_ro(_RMV, acpi_device_read_file)
+acpi_handle_attr_ro(_SEG, acpi_device_read_file)
+acpi_handle_attr_ro(_STA, acpi_device_read_file)
+acpi_handle_attr_ro(_STR, acpi_device_read_file)
+acpi_handle_attr_ro(_SUN, acpi_device_read_file)
+acpi_handle_attr_ro(_UID, acpi_device_read_file)
+
+typedef void acpi_device_sysfs_files(struct kobject *,
+ const struct attribute *);
+
+static void
+setup_sys_fs_files (
+ struct acpi_device *dev,
+ acpi_device_sysfs_files *func)
+{
+ acpi_handle tmp = NULL;
+
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__ADR, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_ADR.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__BBN, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_BBN.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__BCL, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_BCL.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__BCM, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_BCM.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__CID, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_CID.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__CRS, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_CRS.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__DCK, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_DCK.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__DCS, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_DCS.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__DGS, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_DGS.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__DDN, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_DDN.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__DSS, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_DSS.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__DOS, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_DOS.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__DOD, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_DOD.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__EDL, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_EDL.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__EJ0, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_EJ0.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__EJD, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_EJD.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__FIX, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_FIX.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__HID, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_HID.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__HPP, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_HPP.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__LCK, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_LCK.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__LID, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_LID.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__MAT, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_MAT.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__PRS, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_PRS.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__PRT, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_PRT.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__PXM, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_PXM.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__RMV, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_RMV.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__SEG, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_SEG.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__STA, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_STA.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__STR, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_STR.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__SUN, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_SUN.attr);
+ if (ACPI_SUCCESS(acpi_get_handle(dev->handle, METHOD_NAME__UID, &tmp)))
+ (*(func))(&dev->kobj, &acpi_handle_attr_UID.attr);
+}
+
+#define create_sysfs_files(dev) \
+ setup_sys_fs_files(dev, (acpi_device_sysfs_files *)&sysfs_create_file)
+#define remove_sysfs_files(dev) \
+ setup_sys_fs_files(dev, &sysfs_remove_file)
+
static void acpi_device_release(struct kobject * kobj)
{
struct acpi_device * dev = container_of(kobj,struct acpi_device,kobj);
@@ -33,7 +389,32 @@
kfree(dev);
}

+#define to_acpi_device(n) container_of(n, struct acpi_device, kobj)
+#define to_handle_attr(n) container_of(n, struct acpi_handle_attribute, attr);
+
+static ssize_t acpi_device_attr_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct acpi_device *device = to_acpi_device(kobj);
+ struct acpi_handle_attribute *attribute = to_handle_attr(attr);
+ return attribute->show ? attribute->show(device, attribute->attr.name, buf) : 0;
+}
+
+static ssize_t acpi_device_attr_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t len)
+{
+ struct acpi_device *device = to_acpi_device(kobj);
+ struct acpi_handle_attribute *attribute = to_handle_attr(attr);
+ return attribute->store ? attribute->store(device, attribute->attr.name, buf, len) : len;
+}
+
+static struct sysfs_ops acpi_device_sysfs_ops = {
+ .show = acpi_device_attr_show,
+ .store = acpi_device_attr_store,
+};
+
static struct kobj_type ktype_acpi_ns = {
+ .sysfs_ops = &acpi_device_sysfs_ops,
.release = acpi_device_release,
};

@@ -72,6 +453,7 @@
device->kobj.ktype = &ktype_acpi_ns;
device->kobj.kset = &acpi_namespace_kset;
kobject_add(&device->kobj);
+ create_sysfs_files(device);
}

static int
@@ -79,6 +461,7 @@
struct acpi_device *device,
int type)
{
+ remove_sysfs_files(device);
kobject_unregister(&device->kobj);
return 0;
}
@@ -706,7 +1089,7 @@
switch (type) {
case ACPI_BUS_TYPE_DEVICE:
result = acpi_bus_get_status(device);
- if (ACPI_FAILURE(result) || !device->status.present) {
+ if (ACPI_FAILURE(result)) {
result = -ENOENT;
goto end;
}



2004-04-09 22:22:34

by Alex Williamson

[permalink] [raw]
Subject: Re: [PATCH] filling in ACPI method access via sysfs namespace


Here's another approach that's far less ugly than the last and is
much more powerful. The code is a little over half the size as a
bonus. Rather than specifically poking for certain methods and exposing
them, this patch exposes everything. The down side is that all reading
and writing of the files need to use binary acpi data structures. This
interface certainly provides "shoot yourself in the foot" potential, but
the access to the namespace from userspace is hard to beat. Any
thoughts on this approach versus the last? This interface and a simple
set of libraries to go along with it has a lot of potential. Thanks,

Alex

--
Alex Williamson HP Linux & Open Source Lab

===== drivers/acpi/scan.c 1.22 vs edited =====
--- 1.22/drivers/acpi/scan.c Tue Feb 3 22:29:19 2004
+++ edited/drivers/acpi/scan.c Fri Apr 9 16:06:54 2004
@@ -25,6 +25,185 @@
static LIST_HEAD(acpi_device_list);
static spinlock_t acpi_device_lock = SPIN_LOCK_UNLOCKED;

+struct acpi_handle_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct acpi_device *, char *, char *);
+ ssize_t (*store)(struct acpi_device *, char *, const char *, size_t);
+};
+
+static ssize_t
+acpi_device_read_raw(
+ struct acpi_device *device,
+ char *method,
+ char *buf)
+{
+ acpi_status status;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+
+ status = acpi_evaluate_object(device->handle, method, NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ memcpy(buf, buffer.pointer, buffer.length);
+ acpi_os_free(buffer.pointer);
+ return buffer.length;
+}
+
+static ssize_t
+acpi_device_write_raw(
+ struct acpi_device *device,
+ char *method,
+ const char *buf,
+ size_t length)
+{
+ struct acpi_object_list *arg_list;
+ union acpi_object *args, **cur_arg;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ acpi_status status;
+ u32 count, i;
+ size_t size;
+
+ args = kmalloc(length, GFP_KERNEL);
+ if (!args)
+ return -ENODEV;
+
+ memcpy(args, buf, length);
+
+ count = length / sizeof(union acpi_object);
+
+ size = sizeof(struct acpi_object_list) +
+ ((count - 1) * sizeof(union acpi_object *));
+
+ arg_list = kmalloc(size, GFP_KERNEL);
+ if (!arg_list) {
+ kfree(args);
+ return -ENODEV;
+ }
+
+ memset(arg_list, 0, size);
+ arg_list->count = count;
+
+ cur_arg = &arg_list->pointer;
+ for (i = 0 ; i < count ; i++)
+ cur_arg[i] = &args[i];
+
+ status = acpi_evaluate_object(device->handle, method, arg_list,
+ &buffer);
+ kfree(arg_list);
+ kfree(args);
+
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ /* TODO: how to return this? */
+ if (buffer.pointer)
+ acpi_os_free(buffer.pointer);
+
+ return length;
+}
+
+#define to_acpi_device(n) container_of(n, struct acpi_device, kobj)
+#define to_handle_attr(n) container_of(n, struct acpi_handle_attribute, attr);
+
+static void
+create_sysfs_files(struct acpi_device *dev)
+{
+ acpi_handle chandle = 0;
+ char pathname[ACPI_PATHNAME_MAX];
+ acpi_status status;
+ struct acpi_buffer buffer;
+ struct acpi_handle_attribute *attrib;
+ int error;
+
+ buffer.length = sizeof(pathname);
+ buffer.pointer = pathname;
+
+ while (ACPI_SUCCESS(acpi_get_next_object(ACPI_TYPE_METHOD, dev->handle,
+ chandle, &chandle))) {
+
+ memset(pathname, 0 , sizeof(pathname));
+
+ status = acpi_get_name(chandle, ACPI_SINGLE_NAME, &buffer);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ attrib = kmalloc(sizeof(struct acpi_handle_attribute),
+ GFP_KERNEL);
+ if (!attrib)
+ continue;
+
+ memset(attrib, 0, sizeof(struct acpi_handle_attribute));
+
+ attrib->attr.name = kmalloc(strlen(pathname), GFP_KERNEL);
+ if (!attrib->attr.name) {
+ kfree(attrib);
+ continue;
+ }
+
+ strcpy(attrib->attr.name, pathname);
+
+ attrib->attr.mode = S_IFREG | S_IRUSR |
+ S_IWUSR | S_IRGRP | S_IWGRP;
+ attrib->show = acpi_device_read_raw;
+ attrib->store = acpi_device_write_raw;
+
+ error = sysfs_create_file(&dev->kobj, &attrib->attr);
+ if (error) {
+ kfree(attrib->attr.name);
+ kfree(attrib);
+ continue;
+ }
+ }
+}
+
+extern struct dentry * sysfs_get_dentry(struct dentry *, const char *);
+
+static void
+remove_sysfs_files(struct acpi_device *dev)
+{
+ acpi_handle chandle = 0;
+ char pathname[ACPI_PATHNAME_MAX];
+ acpi_status status;
+ struct acpi_buffer buffer;
+ struct acpi_handle_attribute attrib, *old_attrib;
+ struct attribute *old_attr;
+ struct dentry *dentry;
+
+ buffer.length = sizeof(pathname);
+ buffer.pointer = pathname;
+ attrib.attr.name = pathname;
+
+ while (ACPI_SUCCESS(acpi_get_next_object(ACPI_TYPE_METHOD, dev->handle,
+ chandle, &chandle))) {
+
+ memset(pathname, 0 , sizeof(pathname));
+
+ status = acpi_get_name(chandle, ACPI_SINGLE_NAME, &buffer);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ down(&(dev->kobj.dentry->d_inode->i_sem));
+ dentry = sysfs_get_dentry(dev->kobj.dentry, pathname);
+ if (!IS_ERR(dentry))
+ old_attr = dentry->d_fsdata;
+ else
+ old_attr = NULL;
+ up(&(dev->kobj.dentry->d_inode->i_sem));
+
+ sysfs_remove_file(&dev->kobj, &attrib.attr);
+
+ if (old_attr) {
+ if (strncmp(pathname, old_attr->name,
+ strlen(pathname)) != 0)
+ continue;
+
+ old_attrib = to_handle_attr(old_attr);
+ kfree(old_attr->name);
+ kfree(old_attrib);
+ }
+ }
+}
+
static void acpi_device_release(struct kobject * kobj)
{
struct acpi_device * dev = container_of(kobj,struct acpi_device,kobj);
@@ -33,7 +212,31 @@
kfree(dev);
}

+static ssize_t acpi_device_attr_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct acpi_device *device = to_acpi_device(kobj);
+ struct acpi_handle_attribute *attribute = to_handle_attr(attr);
+ return attribute->show ?
+ attribute->show(device, attribute->attr.name, buf) : 0;
+}
+
+static ssize_t acpi_device_attr_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t len)
+{
+ struct acpi_device *device = to_acpi_device(kobj);
+ struct acpi_handle_attribute *attribute = to_handle_attr(attr);
+ return attribute->store ?
+ attribute->store(device, attribute->attr.name, buf, len) : 0;
+}
+
+static struct sysfs_ops acpi_device_sysfs_ops = {
+ .show = acpi_device_attr_show,
+ .store = acpi_device_attr_store,
+};
+
static struct kobj_type ktype_acpi_ns = {
+ .sysfs_ops = &acpi_device_sysfs_ops,
.release = acpi_device_release,
};

@@ -72,6 +275,7 @@
device->kobj.ktype = &ktype_acpi_ns;
device->kobj.kset = &acpi_namespace_kset;
kobject_add(&device->kobj);
+ create_sysfs_files(device);
}

static int
@@ -79,6 +283,7 @@
struct acpi_device *device,
int type)
{
+ remove_sysfs_files(device);
kobject_unregister(&device->kobj);
return 0;
}
@@ -706,7 +911,7 @@
switch (type) {
case ACPI_BUS_TYPE_DEVICE:
result = acpi_bus_get_status(device);
- if (ACPI_FAILURE(result) || !device->status.present) {
+ if (ACPI_FAILURE(result)) {
result = -ENOENT;
goto end;
}


2004-04-10 01:52:41

by John Belmonte

[permalink] [raw]
Subject: Re: [ACPI] Re: [PATCH] filling in ACPI method access via sysfs namespace

Alex Williamson wrote:
> Here's another approach that's far less ugly than the last and is
> much more powerful. The code is a little over half the size as a
> bonus. Rather than specifically poking for certain methods and exposing
> them, this patch exposes everything. The down side is that all reading
> and writing of the files need to use binary acpi data structures. This
> interface certainly provides "shoot yourself in the foot" potential, but
> the access to the namespace from userspace is hard to beat. Any
> thoughts on this approach versus the last? This interface and a simple
> set of libraries to go along with it has a lot of potential. Thanks,

The limitation of this interface is that it's not able to call an ACPI
method with some arguments and get the return value, correct?

-John


--
http:// if ile.org/

2004-04-10 03:12:54

by Alex Williamson

[permalink] [raw]
Subject: Re: [ACPI] Re: [PATCH] filling in ACPI method access via sysfs namespace

On Fri, 2004-04-09 at 19:52, John Belmonte wrote:
> The limitation of this interface is that it's not able to call an ACPI
> method with some arguments and get the return value, correct?

Yes, that's unfortunately a limitation. Most of the standard
interfaces either take no parameters or have no return value, so they
fit nicely into this framework. I'm open to suggestions on how to work
around this. We could make the store function save off the method
parameters, then the show function would call the method with the saved
parameters and return the results. Obviously there are some userspace
ordering issues that could make this complicated, but it's easy to code
on the kernel side. Other ideas? Thanks,

Alex

2004-04-10 05:32:11

by John Belmonte

[permalink] [raw]
Subject: Re: [ACPI] Re: [PATCH] filling in ACPI method access via sysfs namespace

Alex Williamson wrote:
> On Fri, 2004-04-09 at 19:52, John Belmonte wrote:
>
>>The limitation of this interface is that it's not able to call an ACPI
>>method with some arguments and get the return value, correct?
>
> Yes, that's unfortunately a limitation. Most of the standard
> interfaces either take no parameters or have no return value, so they
> fit nicely into this framework. I'm open to suggestions on how to work
> around this. We could make the store function save off the method
> parameters, then the show function would call the method with the saved
> parameters and return the results. Obviously there are some userspace
> ordering issues that could make this complicated, but it's easy to code
> on the kernel side. Other ideas? Thanks,

You may want to look at the acpi-devel thread "[rfc] generic testing
ACPI module", where this issue was discussed
(http://sourceforge.net/mailarchive/message.php?msg_id=7455349).

-John


--
http:// if ile.org/

2004-04-10 05:32:50

by Alex Williamson

[permalink] [raw]
Subject: Re: [ACPI] Re: [PATCH] filling in ACPI method access via sysfs namespace

On Fri, 2004-04-09 at 21:12, Alex Williamson wrote:

> We could make the store function save off the method
> parameters, then the show function would call the method with the saved
> parameters and return the results. Obviously there are some userspace
> ordering issues that could make this complicated, but it's easy to code
> on the kernel side.

Here's an implementation of this. It's a little klunky, but
certainly fills in the functionality.

Alex

===== drivers/acpi/scan.c 1.22 vs edited =====
--- 1.22/drivers/acpi/scan.c Tue Feb 3 22:29:19 2004
+++ edited/drivers/acpi/scan.c Fri Apr 9 23:02:48 2004
@@ -25,6 +25,208 @@
static LIST_HEAD(acpi_device_list);
static spinlock_t acpi_device_lock = SPIN_LOCK_UNLOCKED;

+struct acpi_handle_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct acpi_device *, struct acpi_handle_attribute *, char *);
+ ssize_t (*store)(struct acpi_device *, struct acpi_handle_attribute *, const char *, size_t);
+ union acpi_object *args;
+ size_t length;
+ spinlock_t lock;
+};
+
+static ssize_t
+acpi_device_write_raw(
+ struct acpi_device *device,
+ struct acpi_handle_attribute *attrib,
+ const char *buf,
+ size_t length)
+{
+ spin_lock(&attrib->lock);
+ if (attrib->args) {
+ kfree(attrib->args);
+ attrib->args = NULL;
+ attrib->length = 0;
+ }
+
+ attrib->args = kmalloc(length, GFP_KERNEL);
+ if (!attrib->args) {
+ spin_unlock(&attrib->lock);
+ return -ENODEV;
+ }
+
+ memcpy(attrib->args, buf, length);
+ attrib->length = length;
+ spin_unlock(&attrib->lock);
+
+ return length;
+}
+
+static ssize_t
+acpi_device_read_raw(
+ struct acpi_device *device,
+ struct acpi_handle_attribute *attrib,
+ char *buf)
+{
+ struct acpi_object_list *arg_list = NULL;
+ union acpi_object **cur_arg, *args = NULL;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ acpi_status status;
+ u32 count, i;
+ size_t size;
+
+ spin_lock(&attrib->lock);
+ count = attrib->length / sizeof(union acpi_object);
+
+ if (count) {
+ size = sizeof(struct acpi_object_list) +
+ ((count - 1) * sizeof(union acpi_object *));
+
+ arg_list = kmalloc(size, GFP_KERNEL);
+ if (!arg_list) {
+ spin_unlock(&attrib->lock);
+ return -ENODEV;
+ }
+
+ memset(arg_list, 0, size);
+ arg_list->count = count;
+
+ args = kmalloc(attrib->length, GFP_KERNEL);
+ if (!args) {
+ kfree(arg_list);
+ spin_unlock(&attrib->lock);
+ return -ENODEV;
+ }
+ memcpy(args, attrib->args, attrib->length);
+
+ cur_arg = &arg_list->pointer;
+ for (i = 0 ; i < count ; i++)
+ cur_arg[i] = &args[i];
+
+ kfree(attrib->args);
+ attrib->args = NULL;
+ attrib->length = 0;
+ }
+ spin_unlock(&attrib->lock);
+
+ status = acpi_evaluate_object(device->handle, attrib->attr.name,
+ arg_list, &buffer);
+ if (arg_list)
+ kfree(arg_list);
+ if (args)
+ kfree(args);
+
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ memcpy(buf, buffer.pointer, buffer.length);
+ acpi_os_free(buffer.pointer);
+
+ return buffer.length;
+}
+
+#define to_acpi_device(n) container_of(n, struct acpi_device, kobj)
+#define to_handle_attr(n) container_of(n, struct acpi_handle_attribute, attr);
+
+static void
+create_sysfs_files(struct acpi_device *dev)
+{
+ acpi_handle chandle = 0;
+ char pathname[ACPI_PATHNAME_MAX];
+ acpi_status status;
+ struct acpi_buffer buffer;
+ struct acpi_handle_attribute *attrib;
+ int error;
+
+ buffer.length = sizeof(pathname);
+ buffer.pointer = pathname;
+
+ while (ACPI_SUCCESS(acpi_get_next_object(ACPI_TYPE_METHOD, dev->handle,
+ chandle, &chandle))) {
+
+ memset(pathname, 0 , sizeof(pathname));
+
+ status = acpi_get_name(chandle, ACPI_SINGLE_NAME, &buffer);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ attrib = kmalloc(sizeof(struct acpi_handle_attribute),
+ GFP_KERNEL);
+ if (!attrib)
+ continue;
+
+ memset(attrib, 0, sizeof(struct acpi_handle_attribute));
+
+ attrib->attr.name = kmalloc(strlen(pathname), GFP_KERNEL);
+ if (!attrib->attr.name) {
+ kfree(attrib);
+ continue;
+ }
+
+ strcpy(attrib->attr.name, pathname);
+
+ attrib->attr.mode = S_IFREG | S_IRUSR |
+ S_IWUSR | S_IRGRP | S_IWGRP;
+ attrib->show = acpi_device_read_raw;
+ attrib->store = acpi_device_write_raw;
+ spin_lock_init(&attrib->lock);
+
+ error = sysfs_create_file(&dev->kobj, &attrib->attr);
+ if (error) {
+ kfree(attrib->attr.name);
+ kfree(attrib);
+ continue;
+ }
+ }
+}
+
+extern struct dentry * sysfs_get_dentry(struct dentry *, const char *);
+
+static void
+remove_sysfs_files(struct acpi_device *dev)
+{
+ acpi_handle chandle = 0;
+ char pathname[ACPI_PATHNAME_MAX];
+ acpi_status status;
+ struct acpi_buffer buffer;
+ struct acpi_handle_attribute attrib, *old_attrib;
+ struct attribute *old_attr;
+ struct dentry *dentry;
+
+ buffer.length = sizeof(pathname);
+ buffer.pointer = pathname;
+ attrib.attr.name = pathname;
+
+ while (ACPI_SUCCESS(acpi_get_next_object(ACPI_TYPE_METHOD, dev->handle,
+ chandle, &chandle))) {
+
+ memset(pathname, 0 , sizeof(pathname));
+
+ status = acpi_get_name(chandle, ACPI_SINGLE_NAME, &buffer);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ down(&(dev->kobj.dentry->d_inode->i_sem));
+ dentry = sysfs_get_dentry(dev->kobj.dentry, pathname);
+ if (!IS_ERR(dentry))
+ old_attr = dentry->d_fsdata;
+ else
+ old_attr = NULL;
+ up(&(dev->kobj.dentry->d_inode->i_sem));
+
+ sysfs_remove_file(&dev->kobj, &attrib.attr);
+
+ if (old_attr) {
+ if (strncmp(pathname, old_attr->name,
+ strlen(pathname)) != 0)
+ continue;
+
+ old_attrib = to_handle_attr(old_attr);
+ kfree(old_attr->name);
+ kfree(old_attrib);
+ }
+ }
+}
+
static void acpi_device_release(struct kobject * kobj)
{
struct acpi_device * dev = container_of(kobj,struct acpi_device,kobj);
@@ -33,7 +235,31 @@
kfree(dev);
}

+static ssize_t acpi_device_attr_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct acpi_device *device = to_acpi_device(kobj);
+ struct acpi_handle_attribute *attribute = to_handle_attr(attr);
+ return attribute->show ?
+ attribute->show(device, attribute, buf) : 0;
+}
+
+static ssize_t acpi_device_attr_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t len)
+{
+ struct acpi_device *device = to_acpi_device(kobj);
+ struct acpi_handle_attribute *attribute = to_handle_attr(attr);
+ return attribute->store ?
+ attribute->store(device, attribute, buf, len) : 0;
+}
+
+static struct sysfs_ops acpi_device_sysfs_ops = {
+ .show = acpi_device_attr_show,
+ .store = acpi_device_attr_store,
+};
+
static struct kobj_type ktype_acpi_ns = {
+ .sysfs_ops = &acpi_device_sysfs_ops,
.release = acpi_device_release,
};

@@ -72,6 +298,7 @@
device->kobj.ktype = &ktype_acpi_ns;
device->kobj.kset = &acpi_namespace_kset;
kobject_add(&device->kobj);
+ create_sysfs_files(device);
}

static int
@@ -79,6 +306,7 @@
struct acpi_device *device,
int type)
{
+ remove_sysfs_files(device);
kobject_unregister(&device->kobj);
return 0;
}
@@ -706,7 +934,7 @@
switch (type) {
case ACPI_BUS_TYPE_DEVICE:
result = acpi_bus_get_status(device);
- if (ACPI_FAILURE(result) || !device->status.present) {
+ if (ACPI_FAILURE(result)) {
result = -ENOENT;
goto end;
}


2004-04-10 20:43:05

by Alex Williamson

[permalink] [raw]
Subject: Re: [ACPI] Re: [PATCH] filling in ACPI method access via sysfs namespace

On Fri, 2004-04-09 at 23:31, John Belmonte wrote:
>
> You may want to look at the acpi-devel thread "[rfc] generic testing
> ACPI module", where this issue was discussed
> (http://sourceforge.net/mailarchive/message.php?msg_id=7455349).

Ok, I took a look. The open/write/read/close interface seems to be
the best approach. It shouldn't be too hard, except the read/write
interfaces don't pass in the attribute pointer like they do for the
show/store interface. Is this a bug?

A nice feature of the current implementation is that I have one show
function that reads the method name out of the attribute structure.
With the bin_file interface, I'd need to have a different read/write
function for every possible method on a kobject. I also lose the
convenience of being able extend the container structure to meet my
needs. Am I strecthing the use of the bin_file too far or should these
interfaces pass the attribute pointer? Thanks,

Alex

2004-04-11 03:20:40

by Alex Williamson

[permalink] [raw]
Subject: Re: [ACPI] Re: [PATCH] filling in ACPI method access via sysfs namespace

On Sat, 2004-04-10 at 14:42, Alex Williamson wrote:
>
> Ok, I took a look. The open/write/read/close interface seems to be
> the best approach. It shouldn't be too hard, except the read/write
> interfaces don't pass in the attribute pointer like they do for the
> show/store interface.

I made the assumption that this wasn't that big of a stretch to the
bin file interface and extended it to add the arribute pointer. Below
is what I came up with. There are luckly only a handful of places using
the current sysfs bin file interface, so the additional changes are
pretty small. I'm guessing I can probably get rid of the spinlocks I
added for the previous implementation, but they're not hurting anything
for now. Am I getting closer? Thanks,

Alex

drivers/acpi/scan.c | 236 +++++++++++++++++++++++++++++++++++++++-
drivers/base/firmware_class.c | 8 -
drivers/i2c/chips/eeprom.c | 3
drivers/pci/pci-sysfs.c | 6 -
drivers/scsi/qla2xxx/qla_os.c | 32 ++---
drivers/video/aty/radeon_base.c | 6 -
drivers/zorro/zorro-sysfs.c | 4
fs/sysfs/bin.c | 4
include/linux/sysfs.h | 6 -
9 files changed, 273 insertions(+), 32 deletions(-)

===== drivers/acpi/scan.c 1.22 vs edited =====
--- 1.22/drivers/acpi/scan.c Tue Feb 3 22:29:19 2004
+++ edited/drivers/acpi/scan.c Sat Apr 10 20:55:30 2004
@@ -25,6 +25,238 @@
static LIST_HEAD(acpi_device_list);
static spinlock_t acpi_device_lock = SPIN_LOCK_UNLOCKED;

+struct acpi_handle_attribute {
+ struct attribute attr;
+ size_t size;
+ ssize_t (*read)(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
+ ssize_t (*write)(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
+ union acpi_object *args;
+ size_t length;
+ spinlock_t lock;
+};
+
+#define to_acpi_device(n) container_of(n, struct acpi_device, kobj)
+#define to_handle_attr(n) container_of(n, struct acpi_handle_attribute, attr);
+
+static ssize_t
+acpi_device_write_raw(
+ struct kobject *kobj,
+ struct attribute *attr,
+ char *buf,
+ loff_t off,
+ size_t length)
+{
+ struct acpi_handle_attribute *attrib = to_handle_attr(attr);
+
+ /*
+ * Not sure if offsets should be ignored or enforced, suppose we could
+ * pass multiple arguments that way, but this interface already
+ * accepts multiple arguments packed into an acpi_object array
+ */
+ if (off)
+ return -ENODEV;
+
+ spin_lock(&attrib->lock);
+ if (attrib->args) {
+ kfree(attrib->args);
+ attrib->args = NULL;
+ attrib->length = 0;
+ }
+
+ attrib->args = kmalloc(length, GFP_KERNEL);
+ if (!attrib->args) {
+ spin_unlock(&attrib->lock);
+ return -ENODEV;
+ }
+
+ memcpy(attrib->args, buf, length);
+ attrib->length = length;
+ spin_unlock(&attrib->lock);
+
+ return length;
+}
+
+static ssize_t
+acpi_device_read_raw(
+ struct kobject *kobj,
+ struct attribute *attr,
+ char *buf,
+ loff_t off,
+ size_t length)
+{
+ struct acpi_device *device = to_acpi_device(kobj);
+ struct acpi_handle_attribute *attrib = to_handle_attr(attr);
+ struct acpi_object_list *arg_list = NULL;
+ union acpi_object **cur_arg, *args = NULL;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ acpi_status status;
+ u32 count, i;
+ size_t size;
+
+ /* Ditto, see comment above */
+ if (off)
+ return -ENODEV;
+
+ spin_lock(&attrib->lock);
+
+ /*
+ * For convenience, handle cases where the last argument
+ * is too small.
+ */
+ count = attrib->length / sizeof(union acpi_object);
+ if (attrib->length % sizeof(union acpi_object))
+ count++;
+
+ if (count) {
+ size = sizeof(struct acpi_object_list) +
+ ((count - 1) * sizeof(union acpi_object *));
+
+ arg_list = kmalloc(size, GFP_KERNEL);
+ if (!arg_list) {
+ spin_unlock(&attrib->lock);
+ return -ENODEV;
+ }
+
+ memset(arg_list, 0, size);
+ arg_list->count = count;
+
+ size = count * sizeof(union acpi_object);
+ args = kmalloc(size, GFP_KERNEL);
+ if (!args) {
+ kfree(arg_list);
+ spin_unlock(&attrib->lock);
+ return -ENODEV;
+ }
+ memset(args, 0, size);
+ memcpy(args, attrib->args, min(attrib->length, size));
+
+ cur_arg = &arg_list->pointer;
+ for (i = 0 ; i < count ; i++)
+ cur_arg[i] = &args[i];
+
+ kfree(attrib->args);
+ attrib->args = NULL;
+ attrib->length = 0;
+ }
+ spin_unlock(&attrib->lock);
+
+ status = acpi_evaluate_object(device->handle, attrib->attr.name,
+ arg_list, &buffer);
+ if (arg_list)
+ kfree(arg_list);
+ if (args)
+ kfree(args);
+
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ size = min((unsigned long)buffer.length, PAGE_SIZE);
+ memcpy(buf, buffer.pointer, size);
+ acpi_os_free(buffer.pointer);
+
+ return size;
+}
+
+static void
+create_sysfs_files(struct acpi_device *dev)
+{
+ acpi_handle chandle = 0;
+ char pathname[ACPI_PATHNAME_MAX];
+ acpi_status status;
+ struct acpi_buffer buffer;
+ struct bin_attribute *attrib;
+ int error;
+
+ buffer.length = sizeof(pathname);
+ buffer.pointer = pathname;
+
+ while (ACPI_SUCCESS(acpi_get_next_object(ACPI_TYPE_METHOD, dev->handle,
+ chandle, &chandle))) {
+
+ memset(pathname, 0 , sizeof(pathname));
+
+ status = acpi_get_name(chandle, ACPI_SINGLE_NAME, &buffer);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ attrib = kmalloc(sizeof(struct acpi_handle_attribute),
+ GFP_KERNEL);
+ if (!attrib)
+ continue;
+
+ memset(attrib, 0, sizeof(struct acpi_handle_attribute));
+
+ attrib->attr.name = kmalloc(strlen(pathname), GFP_KERNEL);
+ if (!attrib->attr.name) {
+ kfree(attrib);
+ continue;
+ }
+
+ strcpy(attrib->attr.name, pathname);
+
+ attrib->size = PAGE_SIZE;
+ attrib->attr.mode = S_IFREG | S_IRUSR |
+ S_IWUSR | S_IRGRP | S_IWGRP;
+ attrib->read = acpi_device_read_raw;
+ attrib->write = acpi_device_write_raw;
+ spin_lock_init(&((struct acpi_handle_attribute *)attrib)->lock);
+
+ error = sysfs_create_bin_file(&dev->kobj, attrib);
+ if (error) {
+ kfree(attrib->attr.name);
+ kfree(attrib);
+ continue;
+ }
+ }
+}
+
+extern struct dentry * sysfs_get_dentry(struct dentry *, const char *);
+
+static void
+remove_sysfs_files(struct acpi_device *dev)
+{
+ acpi_handle chandle = 0;
+ char pathname[ACPI_PATHNAME_MAX];
+ acpi_status status;
+ struct acpi_buffer buffer;
+ struct bin_attribute *old_attrib;
+ struct dentry *dentry;
+
+ buffer.length = sizeof(pathname);
+ buffer.pointer = pathname;
+
+ while (ACPI_SUCCESS(acpi_get_next_object(ACPI_TYPE_METHOD, dev->handle,
+ chandle, &chandle))) {
+
+ memset(pathname, 0 , sizeof(pathname));
+
+ status = acpi_get_name(chandle, ACPI_SINGLE_NAME, &buffer);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ down(&(dev->kobj.dentry->d_inode->i_sem));
+ dentry = sysfs_get_dentry(dev->kobj.dentry, pathname);
+ if (!IS_ERR(dentry))
+ old_attrib = dentry->d_fsdata;
+ else
+ old_attrib = NULL;
+ up(&(dev->kobj.dentry->d_inode->i_sem));
+
+ if (old_attrib) {
+ if (strncmp(pathname, old_attrib->attr.name,
+ strlen(pathname)) != 0)
+ continue;
+
+ sysfs_remove_bin_file(&dev->kobj, old_attrib);
+
+ kfree(old_attrib->attr.name);
+ kfree(old_attrib);
+ }
+ }
+}
+
static void acpi_device_release(struct kobject * kobj)
{
struct acpi_device * dev = container_of(kobj,struct acpi_device,kobj);
@@ -72,6 +304,7 @@
device->kobj.ktype = &ktype_acpi_ns;
device->kobj.kset = &acpi_namespace_kset;
kobject_add(&device->kobj);
+ create_sysfs_files(device);
}

static int
@@ -79,6 +312,7 @@
struct acpi_device *device,
int type)
{
+ remove_sysfs_files(device);
kobject_unregister(&device->kobj);
return 0;
}
@@ -706,7 +940,7 @@
switch (type) {
case ACPI_BUS_TYPE_DEVICE:
result = acpi_bus_get_status(device);
- if (ACPI_FAILURE(result) || !device->status.present) {
+ if (ACPI_FAILURE(result)) {
result = -ENOENT;
goto end;
}
===== drivers/base/firmware_class.c 1.16 vs edited =====
--- 1.16/drivers/base/firmware_class.c Mon Mar 1 20:01:49 2004
+++ edited/drivers/base/firmware_class.c Sat Apr 10 15:31:43 2004
@@ -167,8 +167,8 @@
firmware_loading_show, firmware_loading_store);

static ssize_t
-firmware_data_read(struct kobject *kobj,
- char *buffer, loff_t offset, size_t count)
+firmware_data_read(struct kobject *kobj, struct attribute *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);
@@ -227,8 +227,8 @@
* the driver as a firmware image.
**/
static ssize_t
-firmware_data_write(struct kobject *kobj,
- char *buffer, loff_t offset, size_t count)
+firmware_data_write(struct kobject *kobj, struct attribute *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);
===== drivers/i2c/chips/eeprom.c 1.6 vs edited =====
--- 1.6/drivers/i2c/chips/eeprom.c Mon Mar 15 03:38:36 2004
+++ edited/drivers/i2c/chips/eeprom.c Sat Apr 10 15:32:02 2004
@@ -122,7 +122,8 @@
up(&data->update_lock);
}

-static ssize_t eeprom_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
+static ssize_t eeprom_read(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
struct eeprom_data *data = i2c_get_clientdata(client);
===== drivers/pci/pci-sysfs.c 1.8 vs edited =====
--- 1.8/drivers/pci/pci-sysfs.c Mon Feb 9 21:55:42 2004
+++ edited/drivers/pci/pci-sysfs.c Sat Apr 10 20:39:02 2004
@@ -63,7 +63,8 @@
static DEVICE_ATTR(resource,S_IRUGO,pci_show_resources,NULL);

static ssize_t
-pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
+pci_read_config(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
unsigned int size = 64;
@@ -117,7 +118,8 @@
}

static ssize_t
-pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
+pci_write_config(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
unsigned int size = count;
===== drivers/scsi/qla2xxx/qla_os.c 1.6 vs edited =====
--- 1.6/drivers/scsi/qla2xxx/qla_os.c Wed Feb 18 20:42:35 2004
+++ edited/drivers/scsi/qla2xxx/qla_os.c Sat Apr 10 15:34:14 2004
@@ -435,10 +435,10 @@
int qla2x00_allocate_sp_pool( scsi_qla_host_t *ha);
void qla2x00_free_sp_pool(scsi_qla_host_t *ha);

-static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *, char *, loff_t,
- size_t);
-static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *, char *, loff_t,
- size_t);
+static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
+static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
static struct bin_attribute sysfs_fw_dump_attr = {
.attr = {
.name = "fw_dump",
@@ -448,10 +448,10 @@
.read = qla2x00_sysfs_read_fw_dump,
.write = qla2x00_sysfs_write_fw_dump,
};
-static ssize_t qla2x00_sysfs_read_nvram(struct kobject *, char *, loff_t,
- size_t);
-static ssize_t qla2x00_sysfs_write_nvram(struct kobject *, char *, loff_t,
- size_t);
+static ssize_t qla2x00_sysfs_read_nvram(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
+static ssize_t qla2x00_sysfs_write_nvram(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
static struct bin_attribute sysfs_nvram_attr = {
.attr = {
.name = "nvram",
@@ -473,8 +473,8 @@


/* SysFS attributes. */
-static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *kobj, char *buf,
- loff_t off, size_t count)
+static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *kobj,
+ struct attribute *attr, char *buf, loff_t off, size_t count)
{
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
struct device, kobj)));
@@ -491,8 +491,8 @@
return (count);
}

-static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *kobj, char *buf,
- loff_t off, size_t count)
+static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *kobj,
+ struct attribute *attr, char *buf, loff_t off, size_t count)
{
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
struct device, kobj)));
@@ -546,8 +546,8 @@
return (count);
}

-static ssize_t qla2x00_sysfs_read_nvram(struct kobject *kobj, char *buf,
- loff_t off, size_t count)
+static ssize_t qla2x00_sysfs_read_nvram(struct kobject *kobj,
+ struct attribute *attr, char *buf, loff_t off, size_t count)
{
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
struct device, kobj)));
@@ -573,8 +573,8 @@
return (count);
}

-static ssize_t qla2x00_sysfs_write_nvram(struct kobject *kobj, char *buf,
- loff_t off, size_t count)
+static ssize_t qla2x00_sysfs_write_nvram(struct kobject *kobj,
+ struct attribute *attr, char *buf, loff_t off, size_t count)
{
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
struct device, kobj)));
===== drivers/video/aty/radeon_base.c 1.12 vs edited =====
--- 1.12/drivers/video/aty/radeon_base.c Fri Mar 5 03:40:50 2004
+++ edited/drivers/video/aty/radeon_base.c Sat Apr 10 15:35:04 2004
@@ -2014,7 +2014,8 @@
}


-static ssize_t radeon_show_edid1(struct kobject *kobj, char *buf, loff_t off, size_t count)
+static ssize_t radeon_show_edid1(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct pci_dev *pdev = to_pci_dev(dev);
@@ -2025,7 +2026,8 @@
}


-static ssize_t radeon_show_edid2(struct kobject *kobj, char *buf, loff_t off, size_t count)
+static ssize_t radeon_show_edid2(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct pci_dev *pdev = to_pci_dev(dev);
===== drivers/zorro/zorro-sysfs.c 1.1 vs edited =====
--- 1.1/drivers/zorro/zorro-sysfs.c Sun Jan 18 23:35:41 2004
+++ edited/drivers/zorro/zorro-sysfs.c Sat Apr 10 15:35:55 2004
@@ -47,8 +47,8 @@

static DEVICE_ATTR(resource, S_IRUGO, zorro_show_resource, NULL);

-static ssize_t zorro_read_config(struct kobject *kobj, char *buf, loff_t off,
- size_t count)
+static ssize_t zorro_read_config(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct zorro_dev *z = to_zorro_dev(container_of(kobj, struct device,
kobj));
===== fs/sysfs/bin.c 1.12 vs edited =====
--- 1.12/fs/sysfs/bin.c Fri Aug 29 10:40:51 2003
+++ edited/fs/sysfs/bin.c Sat Apr 10 15:36:38 2004
@@ -20,7 +20,7 @@
struct bin_attribute * attr = dentry->d_fsdata;
struct kobject * kobj = dentry->d_parent->d_fsdata;

- return attr->read(kobj, buffer, off, count);
+ return attr->read(kobj, &attr->attr, buffer, off, count);
}

static ssize_t
@@ -63,7 +63,7 @@
struct bin_attribute *attr = dentry->d_fsdata;
struct kobject *kobj = dentry->d_parent->d_fsdata;

- return attr->write(kobj, buffer, offset, count);
+ return attr->write(kobj, &attr->attr, buffer, offset, count);
}

static ssize_t write(struct file * file, const char __user * userbuf,
===== include/linux/sysfs.h 1.32 vs edited =====
--- 1.32/include/linux/sysfs.h Tue Aug 12 09:53:51 2003
+++ edited/include/linux/sysfs.h Sat Apr 10 16:32:12 2004
@@ -21,8 +21,10 @@
struct bin_attribute {
struct attribute attr;
size_t size;
- ssize_t (*read)(struct kobject *, char *, loff_t, size_t);
- ssize_t (*write)(struct kobject *, char *, loff_t, size_t);
+ ssize_t (*read)(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
+ ssize_t (*write)(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
};

int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr);


2004-04-11 22:29:44

by Matthew Wilcox

[permalink] [raw]
Subject: Re: [ACPI] Re: [PATCH] filling in ACPI method access via sysfs namespace

On Sat, Apr 10, 2004 at 09:20:19PM -0600, Alex Williamson wrote:
> On Sat, 2004-04-10 at 14:42, Alex Williamson wrote:
> >
> > Ok, I took a look. The open/write/read/close interface seems to be
> > the best approach. It shouldn't be too hard, except the read/write
> > interfaces don't pass in the attribute pointer like they do for the
> > show/store interface.
>
> I made the assumption that this wasn't that big of a stretch to the
> bin file interface and extended it to add the arribute pointer. Below
> is what I came up with. There are luckly only a handful of places using
> the current sysfs bin file interface, so the additional changes are
> pretty small. I'm guessing I can probably get rid of the spinlocks I
> added for the previous implementation, but they're not hurting anything
> for now. Am I getting closer? Thanks,

Yes, definitely closer to my way of thinking ...

It seems unintuitive that you have to read the file for the method to
take effect. How about having the write function invoke the method and
(if there is a result) store it for later read-back via the read function?
It should be discarded on close, of course. A read() on a file with
no stored result should invoke the ACPI method (on the assumption this
is a parameter-less method) and return the result directly. Closing a
file should discard any result from the method.

--
"Next the statesmen will invent cheap lies, putting the blame upon
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince
himself that the war is just, and will thank God for the better sleep
he enjoys after this process of grotesque self-deception." -- Mark Twain

2004-04-12 03:31:50

by Alex Williamson

[permalink] [raw]
Subject: Re: [ACPI] Re: [PATCH] filling in ACPI method access via sysfs namespace

On Sun, 2004-04-11 at 16:29, Matthew Wilcox wrote:
>
> It seems unintuitive that you have to read the file for the method to
> take effect. How about having the write function invoke the method and
> (if there is a result) store it for later read-back via the read function?
> It should be discarded on close, of course. A read() on a file with
> no stored result should invoke the ACPI method (on the assumption this
> is a parameter-less method) and return the result directly. Closing a
> file should discard any result from the method.

How's this? It behaves the way you described, but might be doing
some questionable things with the buffer to get there. Is there a
better place to store the return data than back into the buf passed to
write() (aka file->private_data)? Without adding callbacks to
open/close, I'm not sure how else we can dispose of the results on
close. Thanks,

Alex

drivers/acpi/scan.c | 210 +++++++++++++++++++++++++++++++++++++++-
drivers/base/firmware_class.c | 8 -
drivers/i2c/chips/eeprom.c | 3
drivers/pci/pci-sysfs.c | 6 -
drivers/scsi/qla2xxx/qla_os.c | 32 +++---
drivers/video/aty/radeon_base.c | 6 -
drivers/zorro/zorro-sysfs.c | 4
fs/sysfs/bin.c | 5
include/linux/sysfs.h | 6 -
9 files changed, 248 insertions(+), 32 deletions(-)

===== drivers/acpi/scan.c 1.22 vs edited =====
--- 1.22/drivers/acpi/scan.c Tue Feb 3 22:29:19 2004
+++ edited/drivers/acpi/scan.c Sun Apr 11 21:22:45 2004
@@ -25,6 +25,212 @@
static LIST_HEAD(acpi_device_list);
static spinlock_t acpi_device_lock = SPIN_LOCK_UNLOCKED;

+#define to_acpi_device(n) container_of(n, struct acpi_device, kobj)
+#define to_bin_attr(n) container_of(n, struct bin_attribute, attr);
+
+#define LENGTH_OFFSET (PAGE_SIZE - sizeof(acpi_size))
+
+static ssize_t
+acpi_device_write_raw(
+ struct kobject *kobj,
+ struct attribute *attr,
+ char *buf,
+ loff_t off,
+ size_t length)
+{
+ struct acpi_device *device = to_acpi_device(kobj);
+ struct bin_attribute *attrib = to_bin_attr(attr);
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct acpi_object_list *arg_list = NULL;
+ union acpi_object **cur_arg, *args = NULL;
+ acpi_status status;
+ u32 count, i;
+ size_t size;
+
+ /*
+ * Not sure if offsets should be ignored or enforced, suppose we could
+ * pass multiple arguments that way, but this interface already
+ * accepts multiple arguments packed into an acpi_object array
+ */
+ if (off)
+ return -ENODEV;
+
+ /*
+ * For convenience, handle cases where the last argument
+ * is too small.
+ */
+ count = length / sizeof(union acpi_object);
+ if (length % sizeof(union acpi_object))
+ count++;
+
+ if (count) {
+ size = sizeof(struct acpi_object_list) +
+ ((count - 1) * sizeof(union acpi_object *));
+
+ arg_list = kmalloc(size, GFP_KERNEL);
+ if (!arg_list)
+ return -ENODEV;
+
+ memset(arg_list, 0, size);
+ arg_list->count = count;
+
+ args = (union acpi_object *)buf;
+
+ cur_arg = &arg_list->pointer;
+ for (i = 0 ; i < count ; i++)
+ cur_arg[i] = &args[i];
+ }
+
+ status = acpi_evaluate_object(device->handle, attrib->attr.name,
+ arg_list, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ if (arg_list)
+ kfree(arg_list);
+
+ memset(buf, 0, PAGE_SIZE);
+ memcpy(buf, buffer.pointer, min((unsigned long)buffer.length,
+ LENGTH_OFFSET));
+ memcpy(buf + LENGTH_OFFSET, &buffer.length, sizeof(acpi_size));
+ acpi_os_free(buffer.pointer);
+ return length;
+}
+
+static ssize_t
+acpi_device_read_raw(
+ struct kobject *kobj,
+ struct attribute *attr,
+ char *buf,
+ loff_t off,
+ size_t length)
+{
+ struct acpi_device *device = to_acpi_device(kobj);
+ struct bin_attribute *attrib = to_bin_attr(attr);
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ acpi_size prev_length;
+ acpi_status status;
+ size_t size;
+
+ /* Ditto, see comment above */
+ if (off)
+ return -ENODEV;
+
+ memcpy(&prev_length, buf + LENGTH_OFFSET, sizeof(acpi_size));
+
+ if (prev_length) {
+ memset(buf + LENGTH_OFFSET, 0, sizeof(acpi_size));
+ return min((unsigned long)prev_length, LENGTH_OFFSET);
+ }
+
+ status = acpi_evaluate_object(device->handle, attrib->attr.name,
+ NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ size = min((unsigned long)buffer.length, LENGTH_OFFSET);
+ memset(buf, 0, PAGE_SIZE);
+ memcpy(buf, buffer.pointer, size);
+ acpi_os_free(buffer.pointer);
+ return size;
+}
+
+static void
+create_sysfs_files(struct acpi_device *dev)
+{
+ acpi_handle chandle = 0;
+ char pathname[ACPI_PATHNAME_MAX];
+ acpi_status status;
+ struct acpi_buffer buffer;
+ struct bin_attribute *attrib;
+ int error;
+
+ buffer.length = sizeof(pathname);
+ buffer.pointer = pathname;
+
+ while (ACPI_SUCCESS(acpi_get_next_object(ACPI_TYPE_METHOD, dev->handle,
+ chandle, &chandle))) {
+
+ memset(pathname, 0 , sizeof(pathname));
+
+ status = acpi_get_name(chandle, ACPI_SINGLE_NAME, &buffer);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ attrib = kmalloc(sizeof(struct bin_attribute), GFP_KERNEL);
+ if (!attrib)
+ continue;
+
+ memset(attrib, 0, sizeof(struct bin_attribute));
+
+ attrib->attr.name = kmalloc(strlen(pathname), GFP_KERNEL);
+ if (!attrib->attr.name) {
+ kfree(attrib);
+ continue;
+ }
+
+ strcpy(attrib->attr.name, pathname);
+
+ attrib->size = LENGTH_OFFSET;
+ attrib->attr.mode = S_IFREG | S_IRUSR |
+ S_IWUSR | S_IRGRP | S_IWGRP;
+ attrib->read = acpi_device_read_raw;
+ attrib->write = acpi_device_write_raw;
+
+ error = sysfs_create_bin_file(&dev->kobj, attrib);
+ if (error) {
+ kfree(attrib->attr.name);
+ kfree(attrib);
+ continue;
+ }
+ }
+}
+
+extern struct dentry * sysfs_get_dentry(struct dentry *, const char *);
+
+static void
+remove_sysfs_files(struct acpi_device *dev)
+{
+ acpi_handle chandle = 0;
+ char pathname[ACPI_PATHNAME_MAX];
+ acpi_status status;
+ struct acpi_buffer buffer;
+ struct bin_attribute *old_attrib;
+ struct dentry *dentry;
+
+ buffer.length = sizeof(pathname);
+ buffer.pointer = pathname;
+
+ while (ACPI_SUCCESS(acpi_get_next_object(ACPI_TYPE_METHOD, dev->handle,
+ chandle, &chandle))) {
+
+ memset(pathname, 0 , sizeof(pathname));
+
+ status = acpi_get_name(chandle, ACPI_SINGLE_NAME, &buffer);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ down(&(dev->kobj.dentry->d_inode->i_sem));
+ dentry = sysfs_get_dentry(dev->kobj.dentry, pathname);
+ if (!IS_ERR(dentry))
+ old_attrib = dentry->d_fsdata;
+ else
+ old_attrib = NULL;
+ up(&(dev->kobj.dentry->d_inode->i_sem));
+
+ if (old_attrib) {
+ if (strncmp(pathname, old_attrib->attr.name,
+ strlen(pathname)) != 0)
+ continue;
+
+ sysfs_remove_bin_file(&dev->kobj, old_attrib);
+
+ kfree(old_attrib->attr.name);
+ kfree(old_attrib);
+ }
+ }
+}
+
static void acpi_device_release(struct kobject * kobj)
{
struct acpi_device * dev = container_of(kobj,struct acpi_device,kobj);
@@ -72,6 +278,7 @@
device->kobj.ktype = &ktype_acpi_ns;
device->kobj.kset = &acpi_namespace_kset;
kobject_add(&device->kobj);
+ create_sysfs_files(device);
}

static int
@@ -79,6 +286,7 @@
struct acpi_device *device,
int type)
{
+ remove_sysfs_files(device);
kobject_unregister(&device->kobj);
return 0;
}
@@ -706,7 +914,7 @@
switch (type) {
case ACPI_BUS_TYPE_DEVICE:
result = acpi_bus_get_status(device);
- if (ACPI_FAILURE(result) || !device->status.present) {
+ if (ACPI_FAILURE(result)) {
result = -ENOENT;
goto end;
}
===== drivers/base/firmware_class.c 1.16 vs edited =====
--- 1.16/drivers/base/firmware_class.c Mon Mar 1 20:01:49 2004
+++ edited/drivers/base/firmware_class.c Sun Apr 11 20:16:41 2004
@@ -167,8 +167,8 @@
firmware_loading_show, firmware_loading_store);

static ssize_t
-firmware_data_read(struct kobject *kobj,
- char *buffer, loff_t offset, size_t count)
+firmware_data_read(struct kobject *kobj, struct attribute *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);
@@ -227,8 +227,8 @@
* the driver as a firmware image.
**/
static ssize_t
-firmware_data_write(struct kobject *kobj,
- char *buffer, loff_t offset, size_t count)
+firmware_data_write(struct kobject *kobj, struct attribute *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);
===== drivers/i2c/chips/eeprom.c 1.6 vs edited =====
--- 1.6/drivers/i2c/chips/eeprom.c Mon Mar 15 03:38:36 2004
+++ edited/drivers/i2c/chips/eeprom.c Sun Apr 11 20:16:41 2004
@@ -122,7 +122,8 @@
up(&data->update_lock);
}

-static ssize_t eeprom_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
+static ssize_t eeprom_read(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
struct eeprom_data *data = i2c_get_clientdata(client);
===== drivers/pci/pci-sysfs.c 1.8 vs edited =====
--- 1.8/drivers/pci/pci-sysfs.c Mon Feb 9 21:55:42 2004
+++ edited/drivers/pci/pci-sysfs.c Sun Apr 11 20:16:41 2004
@@ -63,7 +63,8 @@
static DEVICE_ATTR(resource,S_IRUGO,pci_show_resources,NULL);

static ssize_t
-pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
+pci_read_config(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
unsigned int size = 64;
@@ -117,7 +118,8 @@
}

static ssize_t
-pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
+pci_write_config(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
unsigned int size = count;
===== drivers/scsi/qla2xxx/qla_os.c 1.6 vs edited =====
--- 1.6/drivers/scsi/qla2xxx/qla_os.c Wed Feb 18 20:42:35 2004
+++ edited/drivers/scsi/qla2xxx/qla_os.c Sun Apr 11 20:16:41 2004
@@ -435,10 +435,10 @@
int qla2x00_allocate_sp_pool( scsi_qla_host_t *ha);
void qla2x00_free_sp_pool(scsi_qla_host_t *ha);

-static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *, char *, loff_t,
- size_t);
-static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *, char *, loff_t,
- size_t);
+static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
+static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
static struct bin_attribute sysfs_fw_dump_attr = {
.attr = {
.name = "fw_dump",
@@ -448,10 +448,10 @@
.read = qla2x00_sysfs_read_fw_dump,
.write = qla2x00_sysfs_write_fw_dump,
};
-static ssize_t qla2x00_sysfs_read_nvram(struct kobject *, char *, loff_t,
- size_t);
-static ssize_t qla2x00_sysfs_write_nvram(struct kobject *, char *, loff_t,
- size_t);
+static ssize_t qla2x00_sysfs_read_nvram(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
+static ssize_t qla2x00_sysfs_write_nvram(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
static struct bin_attribute sysfs_nvram_attr = {
.attr = {
.name = "nvram",
@@ -473,8 +473,8 @@


/* SysFS attributes. */
-static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *kobj, char *buf,
- loff_t off, size_t count)
+static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *kobj,
+ struct attribute *attr, char *buf, loff_t off, size_t count)
{
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
struct device, kobj)));
@@ -491,8 +491,8 @@
return (count);
}

-static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *kobj, char *buf,
- loff_t off, size_t count)
+static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *kobj,
+ struct attribute *attr, char *buf, loff_t off, size_t count)
{
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
struct device, kobj)));
@@ -546,8 +546,8 @@
return (count);
}

-static ssize_t qla2x00_sysfs_read_nvram(struct kobject *kobj, char *buf,
- loff_t off, size_t count)
+static ssize_t qla2x00_sysfs_read_nvram(struct kobject *kobj,
+ struct attribute *attr, char *buf, loff_t off, size_t count)
{
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
struct device, kobj)));
@@ -573,8 +573,8 @@
return (count);
}

-static ssize_t qla2x00_sysfs_write_nvram(struct kobject *kobj, char *buf,
- loff_t off, size_t count)
+static ssize_t qla2x00_sysfs_write_nvram(struct kobject *kobj,
+ struct attribute *attr, char *buf, loff_t off, size_t count)
{
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
struct device, kobj)));
===== drivers/video/aty/radeon_base.c 1.12 vs edited =====
--- 1.12/drivers/video/aty/radeon_base.c Fri Mar 5 03:40:50 2004
+++ edited/drivers/video/aty/radeon_base.c Sun Apr 11 20:16:41 2004
@@ -2014,7 +2014,8 @@
}


-static ssize_t radeon_show_edid1(struct kobject *kobj, char *buf, loff_t off, size_t count)
+static ssize_t radeon_show_edid1(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct pci_dev *pdev = to_pci_dev(dev);
@@ -2025,7 +2026,8 @@
}


-static ssize_t radeon_show_edid2(struct kobject *kobj, char *buf, loff_t off, size_t count)
+static ssize_t radeon_show_edid2(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct pci_dev *pdev = to_pci_dev(dev);
===== drivers/zorro/zorro-sysfs.c 1.1 vs edited =====
--- 1.1/drivers/zorro/zorro-sysfs.c Sun Jan 18 23:35:41 2004
+++ edited/drivers/zorro/zorro-sysfs.c Sun Apr 11 20:16:41 2004
@@ -47,8 +47,8 @@

static DEVICE_ATTR(resource, S_IRUGO, zorro_show_resource, NULL);

-static ssize_t zorro_read_config(struct kobject *kobj, char *buf, loff_t off,
- size_t count)
+static ssize_t zorro_read_config(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct zorro_dev *z = to_zorro_dev(container_of(kobj, struct device,
kobj));
===== fs/sysfs/bin.c 1.12 vs edited =====
--- 1.12/fs/sysfs/bin.c Fri Aug 29 10:40:51 2003
+++ edited/fs/sysfs/bin.c Sun Apr 11 20:16:41 2004
@@ -20,7 +20,7 @@
struct bin_attribute * attr = dentry->d_fsdata;
struct kobject * kobj = dentry->d_parent->d_fsdata;

- return attr->read(kobj, buffer, off, count);
+ return attr->read(kobj, &attr->attr, buffer, off, count);
}

static ssize_t
@@ -63,7 +63,7 @@
struct bin_attribute *attr = dentry->d_fsdata;
struct kobject *kobj = dentry->d_parent->d_fsdata;

- return attr->write(kobj, buffer, offset, count);
+ return attr->write(kobj, &attr->attr, buffer, offset, count);
}

static ssize_t write(struct file * file, const char __user * userbuf,
@@ -109,6 +109,7 @@

error = -ENOMEM;
file->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ memset(file->private_data, 0, PAGE_SIZE);
if (!file->private_data)
goto Done;

===== include/linux/sysfs.h 1.32 vs edited =====
--- 1.32/include/linux/sysfs.h Tue Aug 12 09:53:51 2003
+++ edited/include/linux/sysfs.h Sun Apr 11 20:16:41 2004
@@ -21,8 +21,10 @@
struct bin_attribute {
struct attribute attr;
size_t size;
- ssize_t (*read)(struct kobject *, char *, loff_t, size_t);
- ssize_t (*write)(struct kobject *, char *, loff_t, size_t);
+ ssize_t (*read)(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
+ ssize_t (*write)(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
};

int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr);


2004-04-19 15:47:31

by Alex Williamson

[permalink] [raw]
Subject: Re: [ACPI] Re: [PATCH] filling in ACPI method access via sysfs namespace


Here's the latest version of the patch. I've made a lot of changes:

* Added more files, now shows all objects, not just methods.
* Returned to read() being the only path that evaluates objects.
The logic seems much easier to do it this way, and allows for
things like multiple writes at different offsets.
* Added open/release callbacks to sysfs bin file support to allow
us to allocate our own private_data structure. This allows us
to add enough extra pointers to have persistent data per open,
and not have it waste memory when not open. Net result is
userspace should not have to worry about multiple accesses to
the files.
* Added several "special" commands that can be triggered by
writing a reserved type to the file. Features so far:
* Return type of object
* Return number of arguments for methods
* Return buffer containing AML for methods
* Replace AML with code contained in write buffer
* Return sizeof(union acpi_object)
* Added a list to track creation or bin_attribute structures.
There's really nothing unique about them except the name field.
If one with the same name has already been created, we now reuse
it. On my laptop, the memory savings from this is fairly small
(nc6000, 734 file entries, 456 unique). However, on some larger
ia64 systems, it's massive (2 cell rx8620, 6933 file entries,
396 unique).

Please try out this new version and let me know what you think.
Thanks,

Alex
--
Alex Williamson HP Linux & Open Source Lab

drivers/acpi/scan.c | 561 +++++++++++++++++++++++++++++++++++++++-
drivers/base/firmware_class.c | 8
drivers/i2c/chips/eeprom.c | 3
drivers/pci/pci-sysfs.c | 6
drivers/scsi/qla2xxx/qla_os.c | 32 +-
drivers/video/aty/radeon_base.c | 6
drivers/zorro/zorro-sysfs.c | 4
fs/sysfs/bin.c | 17 -
include/linux/sysfs.h | 8
9 files changed, 611 insertions(+), 34 deletions(-)

===== drivers/acpi/scan.c 1.22 vs edited =====
--- 1.22/drivers/acpi/scan.c Tue Feb 3 22:29:19 2004
+++ edited/drivers/acpi/scan.c Mon Apr 19 08:59:19 2004
@@ -7,6 +7,7 @@

#include <acpi/acpi_drivers.h>
#include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */
+#include <acpi/acnamesp.h> /* for acpi_ns_map_handle_to_node() */


#define _COMPONENT ACPI_BUS_COMPONENT
@@ -25,6 +26,562 @@
static LIST_HEAD(acpi_device_list);
static spinlock_t acpi_device_lock = SPIN_LOCK_UNLOCKED;

+static LIST_HEAD(acpi_bin_file_list);
+static spinlock_t acpi_bin_file_lock = SPIN_LOCK_UNLOCKED;
+
+struct acpi_bin_files {
+ struct list_head list;
+ struct bin_attribute *bin_attrib;
+ u32 use_count;
+};
+
+struct acpi_private_data {
+ char buf[PAGE_SIZE];
+ size_t write_len;
+ size_t read_len;
+ char *write_buf;
+ char *read_buf;
+};
+
+#define to_acpi_device(n) container_of(n, struct acpi_device, kobj)
+#define to_bin_attr(n) container_of(n, struct bin_attribute, attr)
+
+static ssize_t
+acpi_device_write_raw(
+ struct kobject *kobj,
+ struct attribute *attr,
+ char *buf,
+ loff_t off,
+ size_t length)
+{
+ struct acpi_private_data *data = (struct acpi_private_data *)buf;
+ char *new_buf;
+ size_t new_size;
+
+ /* Writing always clears anything left in the read buffer */
+ if (data->read_len) {
+ acpi_os_free(data->read_buf);
+ data->read_len = 0;
+ }
+
+ /* Allow of multiple writes to build up a buffer */
+ new_size = max(data->write_len, (size_t)(off + length));
+ new_buf = kmalloc(new_size, GFP_KERNEL);
+ if (!new_buf)
+ return -ENOMEM;
+
+ memset(new_buf, 0, new_size);
+ memcpy(new_buf, data->write_buf, data->write_len);
+ memcpy(new_buf + off, buf, length);
+
+ if (data->write_len)
+ kfree(data->write_buf);
+
+ data->write_buf = new_buf;
+ data->write_len = new_size;
+
+ return length;
+}
+
+struct acpi_device_special_cmd {
+ acpi_object_type type;
+ unsigned int cmd;
+ char *args;
+};
+
+/* These probably need to go in a header file if this goes live */
+#define OBJ_LEN 0x0
+#define GET_TYPE 0x1
+#define GET_ARG_CNT 0x2
+#define GET_AML 0x3
+#define SET_AML 0x4
+
+static ssize_t
+acpi_device_read_special(
+ acpi_handle handle,
+ char *attr_name,
+ struct acpi_buffer *buffer,
+ char *buf,
+ size_t length)
+{
+ struct acpi_device_special_cmd *special =
+ (struct acpi_device_special_cmd *)buf;
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *obj_desc;
+ acpi_object_type type;
+ acpi_handle chandle = NULL;
+ acpi_status status;
+ ssize_t ret_val;
+
+ switch (special->cmd) {
+ /*
+ * OBJ_LEN: Return the length of a union acpi_object. Hopefully
+ * useful for synchronization between kernel & userspace.
+ */
+ case OBJ_LEN:
+ buffer->pointer = acpi_os_allocate(sizeof(unsigned int));
+
+ if (!buffer->pointer)
+ return -ENOMEM;
+
+ buffer->length = sizeof(unsigned int);
+ *(unsigned int *)(buffer->pointer) = sizeof(union acpi_object);
+
+ return buffer->length;
+
+ /*
+ * GET_TYPE: Return the type of an object.
+ */
+ case GET_TYPE:
+ status = acpi_get_handle(handle, attr_name, &chandle);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ buffer->pointer = acpi_os_allocate(sizeof(acpi_object_type));
+
+ if (!buffer->pointer)
+ return -ENOMEM;
+
+ buffer->length = sizeof(acpi_object_type);
+
+ status = acpi_get_type(chandle, buffer->pointer);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_free(buffer->pointer);
+ buffer->length = 0;
+ return -ENODEV;
+ }
+ return buffer->length;
+
+ /*
+ * GET_ARG_CNT: If object is a method, return the number of arguments
+ * it takes.
+ */
+ case GET_ARG_CNT:
+ /*
+ * GET_AML: If object is a method, return a buffer with the raw AML.
+ */
+ case GET_AML:
+ /*
+ * SET_AML: If object is a method, write new AML.
+ */
+ case SET_AML:
+ ret_val = -ENODEV;
+ status = acpi_get_handle(handle, attr_name, &chandle);
+ if (ACPI_FAILURE(status))
+ return ret_val;
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE (status))
+ return ret_val;
+
+ node = acpi_ns_map_handle_to_node(chandle);
+ if (!node)
+ goto aml_out;
+
+ type = acpi_ns_get_type(node);
+ /* FIXME: do we care about aliases? */
+ if (type != ACPI_TYPE_METHOD)
+ goto aml_out;
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc)
+ goto aml_out;
+
+ ret_val = -ENOMEM;
+ if (special->cmd == GET_ARG_CNT) {
+ buffer->pointer =
+ acpi_os_allocate(sizeof(unsigned int));
+
+ if (!buffer->pointer)
+ goto aml_out;
+
+ buffer->length = sizeof(unsigned int);
+
+ *(unsigned int *)(buffer->pointer) =
+ obj_desc->method.param_count;
+
+ ret_val = buffer->length;
+
+ } else if (special->cmd == GET_AML) {
+ buffer->pointer =
+ acpi_os_allocate(obj_desc->method.aml_length);
+
+ if (!buffer->pointer)
+ goto aml_out;
+
+ buffer->length = obj_desc->method.aml_length;
+
+ memcpy(buffer->pointer, obj_desc->method.aml_start,
+ obj_desc->method.aml_length);
+
+ ret_val = buffer->length;
+
+ } else if (special->cmd == SET_AML) {
+ u8 *new_aml;
+ size_t size;
+
+ size = offsetof(struct acpi_device_special_cmd, args);
+ size = length - size;
+ new_aml = kmalloc(size, GFP_KERNEL);
+ if (!new_aml)
+ goto aml_out;
+
+ /*
+ * FIXME: Memory leak, does the old data
+ * get kmalloc'd? I'll assume this
+ * interface isn't going to get stress
+ * enough to care for now.
+ * if (obj_desc->method.aml_length)
+ * kfree(obj_desc->method.aml_start);
+ */
+
+ memcpy(new_aml, &special->args, size);
+ obj_desc->method.aml_start = new_aml;
+ obj_desc->method.aml_length = size;
+ ret_val = buffer->length = 0;
+ }
+aml_out:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return ret_val;
+
+ default:
+ buffer->length = 0;
+ return 0;
+ }
+}
+
+static ssize_t
+acpi_device_read_raw(
+ struct kobject *kobj,
+ struct attribute *attr,
+ char *buf,
+ loff_t off,
+ size_t length)
+{
+ struct acpi_device *device = to_acpi_device(kobj);
+ struct bin_attribute *attrib = to_bin_attr(attr);
+ struct acpi_private_data *data = (struct acpi_private_data *)buf;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct acpi_object_list *arg_list = NULL;
+ union acpi_object **cur_arg, *args = NULL;
+ unsigned int count, i;
+ acpi_status status;
+ size_t size;
+
+ /* Check for special command */
+ if (data->write_len > sizeof(acpi_object_type) && !off) {
+ struct acpi_device_special_cmd *special;
+
+ special = ((struct acpi_device_special_cmd *)data->write_buf);
+
+ if (special->type == ACPI_TYPE_NOT_FOUND) {
+ ssize_t ret_val;
+ ret_val = acpi_device_read_special(device->handle,
+ attrib->attr.name,
+ &buffer,
+ data->write_buf,
+ data->write_len);
+ if (ret_val < 0)
+ return ret_val;
+
+ if (data->read_len)
+ acpi_os_free(data->read_buf);
+
+ data->read_buf = buffer.pointer;
+ data->read_len = buffer.length;
+ goto return_data;
+ }
+ }
+
+ /* An offset of zero implies new-read */
+ if (!off) {
+ /* Check if parameters are written in the write_buf */
+ count = data->write_len / sizeof(union acpi_object);
+ if (data->write_len % sizeof(union acpi_object))
+ count++;
+
+ if (count) {
+ size = sizeof(struct acpi_object_list) +
+ ((count - 1) * sizeof(union acpi_object *));
+
+ arg_list = kmalloc(size, GFP_KERNEL);
+ if (!arg_list)
+ return -ENOMEM;
+
+ memset(arg_list, 0, size);
+ arg_list->count = count;
+
+ args = (union acpi_object *)data->write_buf;
+
+ /*
+ * Make argument pointers point at the right offsets
+ * into the write_buf. Note args can only be union
+ * acpi_object in size.
+ */
+ cur_arg = &arg_list->pointer;
+ for (i = 0 ; i < count ; i++)
+ cur_arg[i] = &args[i];
+ }
+
+ status = acpi_evaluate_object(device->handle, attrib->attr.name,
+ arg_list, &buffer);
+ if (arg_list)
+ kfree(arg_list);
+ if (data->read_len)
+ acpi_os_free(data->read_buf);
+
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ data->read_buf = buffer.pointer;
+ data->read_len = buffer.length;
+ }
+
+return_data:
+
+ /* Write buffer always gets cleared on a successful read */
+ if (data->write_len) {
+ kfree(data->write_buf);
+ data->write_len = 0;
+ }
+
+ /* Return only what we're asked for */
+ if (off > data->read_len)
+ return 0;
+ if (off + length > data->read_len)
+ length = data->read_len - off;
+
+ memcpy(buf, data->read_buf + off, length);
+
+ return length;
+}
+
+static char *
+acpi_device_open_raw(
+ struct kobject *kobj,
+ struct attribute *attr)
+{
+ struct acpi_private_data *data;
+
+ data = kmalloc(sizeof(struct acpi_private_data), GFP_KERNEL);
+ if (!data)
+ return NULL;
+
+ memset(data, 0, sizeof(struct acpi_private_data));
+ return (char *)data;
+}
+
+static void
+acpi_device_release_raw(
+ struct kobject *kobj,
+ struct attribute *attr,
+ char *buf)
+{
+ struct acpi_private_data *data = (struct acpi_private_data *)buf;
+
+ if (data->read_len)
+ acpi_os_free(data->read_buf);
+ if (data->write_len)
+ kfree(data->write_buf);
+
+ kfree(data);
+
+ return;
+}
+
+static struct acpi_bin_files *
+find_bin_file(char *name) {
+ struct list_head *node, *next;
+ struct acpi_bin_files *bin_file;
+
+ list_for_each_safe(node, next, &acpi_bin_file_list) {
+ bin_file = container_of(node, struct acpi_bin_files, list);
+
+ if (!strcmp(name, bin_file->bin_attrib->attr.name))
+ return bin_file;
+ }
+ return NULL;
+}
+
+static void
+create_sysfs_files(struct acpi_device *dev)
+{
+ acpi_handle chandle = 0;
+ char pathname[ACPI_PATHNAME_MAX];
+ acpi_status status;
+ struct acpi_buffer buffer;
+ struct bin_attribute *attrib;
+ acpi_object_type type;
+ struct acpi_bin_files *bin_file;
+ int error;
+
+ buffer.length = sizeof(pathname);
+ buffer.pointer = pathname;
+
+ while (ACPI_SUCCESS(acpi_get_next_object(ACPI_TYPE_ANY, dev->handle,
+ chandle, &chandle))) {
+
+ status = acpi_get_type(chandle, &type);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ switch (type) {
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_THERMAL:
+ case ACPI_TYPE_POWER:
+ case ACPI_TYPE_INVALID:
+ case ACPI_TYPE_NOT_FOUND:
+ continue;
+ }
+
+ memset(pathname, 0 , sizeof(pathname));
+
+ status = acpi_get_name(chandle, ACPI_SINGLE_NAME, &buffer);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ attrib = NULL;
+
+ spin_lock(&acpi_bin_file_lock);
+
+ /*
+ * Check if we already have a bin_attribute w/ this name.
+ * If so, reuse it and save some memory.
+ */
+ bin_file = find_bin_file(pathname);
+
+ if (bin_file) {
+ attrib = bin_file->bin_attrib;
+ bin_file->use_count++;
+ } else {
+ attrib = kmalloc(sizeof(struct bin_attribute),
+ GFP_KERNEL);
+ if (!attrib)
+ continue;
+
+ memset(attrib, 0, sizeof(struct bin_attribute));
+
+ attrib->attr.name = kmalloc(strlen(pathname),
+ GFP_KERNEL);
+ if (!attrib->attr.name) {
+ kfree(attrib);
+ continue;
+ }
+
+ strcpy(attrib->attr.name, pathname);
+
+ attrib->attr.mode = S_IFREG | S_IRUSR | S_IRGRP |
+ S_IWUSR | S_IWGRP;
+ attrib->read = acpi_device_read_raw;
+ attrib->write = acpi_device_write_raw;
+ attrib->open = acpi_device_open_raw;
+ attrib->release = acpi_device_release_raw;
+
+ bin_file = kmalloc(sizeof(struct acpi_bin_files),
+ GFP_KERNEL);
+
+ if (!bin_file) {
+ kfree(attrib);
+ kfree(bin_file);
+ continue;
+ }
+
+ INIT_LIST_HEAD(&bin_file->list);
+ bin_file->bin_attrib = attrib;
+ bin_file->use_count = 1;
+
+ list_add_tail(&bin_file->list, &acpi_bin_file_list);
+ }
+ spin_unlock(&acpi_bin_file_lock);
+
+ error = sysfs_create_bin_file(&dev->kobj, attrib);
+ if (error) {
+ spin_lock(&acpi_bin_file_lock);
+ bin_file->use_count--;
+ if (!bin_file->use_count) {
+ list_del(&bin_file->list);
+ kfree(attrib->attr.name);
+ kfree(attrib);
+ kfree(bin_file);
+ }
+ spin_unlock(&acpi_bin_file_lock);
+ continue;
+ }
+ }
+}
+
+extern struct dentry * sysfs_get_dentry(struct dentry *, const char *);
+
+static void
+remove_sysfs_files(struct acpi_device *dev)
+{
+ acpi_handle chandle = 0;
+ char pathname[ACPI_PATHNAME_MAX];
+ acpi_status status;
+ struct acpi_buffer buffer;
+ struct bin_attribute *old_attrib;
+ struct dentry *dentry;
+ acpi_object_type type;
+ struct acpi_bin_files *bin_file;
+
+ buffer.length = sizeof(pathname);
+ buffer.pointer = pathname;
+
+ while (ACPI_SUCCESS(acpi_get_next_object(ACPI_TYPE_ANY, dev->handle,
+ chandle, &chandle))) {
+
+ status = acpi_get_type(chandle, &type);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ switch (type) {
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_THERMAL:
+ case ACPI_TYPE_POWER:
+ case ACPI_TYPE_INVALID:
+ case ACPI_TYPE_NOT_FOUND:
+ continue;
+ }
+
+ memset(pathname, 0 , sizeof(pathname));
+
+ status = acpi_get_name(chandle, ACPI_SINGLE_NAME, &buffer);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ down(&(dev->kobj.dentry->d_inode->i_sem));
+ dentry = sysfs_get_dentry(dev->kobj.dentry, pathname);
+ if (!IS_ERR(dentry))
+ old_attrib = dentry->d_fsdata;
+ else
+ old_attrib = NULL;
+ up(&(dev->kobj.dentry->d_inode->i_sem));
+
+ if (old_attrib) {
+ if (strcmp(pathname, old_attrib->attr.name))
+ continue;
+
+ sysfs_remove_bin_file(&dev->kobj, old_attrib);
+
+ spin_lock(&acpi_bin_file_lock);
+ bin_file = find_bin_file(pathname);
+ if (!bin_file)
+ continue;
+
+ bin_file->use_count--;
+ if (!bin_file->use_count) {
+ list_del(&bin_file->list);
+ kfree(old_attrib->attr.name);
+ kfree(old_attrib);
+ kfree(bin_file);
+ }
+ spin_unlock(&acpi_bin_file_lock);
+ }
+ }
+}
+
static void acpi_device_release(struct kobject * kobj)
{
struct acpi_device * dev = container_of(kobj,struct acpi_device,kobj);
@@ -72,6 +629,7 @@
device->kobj.ktype = &ktype_acpi_ns;
device->kobj.kset = &acpi_namespace_kset;
kobject_add(&device->kobj);
+ create_sysfs_files(device);
}

static int
@@ -79,6 +637,7 @@
struct acpi_device *device,
int type)
{
+ remove_sysfs_files(device);
kobject_unregister(&device->kobj);
return 0;
}
@@ -706,7 +1265,7 @@
switch (type) {
case ACPI_BUS_TYPE_DEVICE:
result = acpi_bus_get_status(device);
- if (ACPI_FAILURE(result) || !device->status.present) {
+ if (ACPI_FAILURE(result)) {
result = -ENOENT;
goto end;
}
===== drivers/base/firmware_class.c 1.16 vs edited =====
--- 1.16/drivers/base/firmware_class.c Mon Mar 1 20:01:49 2004
+++ edited/drivers/base/firmware_class.c Sun Apr 18 18:30:40 2004
@@ -167,8 +167,8 @@
firmware_loading_show, firmware_loading_store);

static ssize_t
-firmware_data_read(struct kobject *kobj,
- char *buffer, loff_t offset, size_t count)
+firmware_data_read(struct kobject *kobj, struct attribute *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);
@@ -227,8 +227,8 @@
* the driver as a firmware image.
**/
static ssize_t
-firmware_data_write(struct kobject *kobj,
- char *buffer, loff_t offset, size_t count)
+firmware_data_write(struct kobject *kobj, struct attribute *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);
===== drivers/i2c/chips/eeprom.c 1.8 vs edited =====
--- 1.8/drivers/i2c/chips/eeprom.c Sat Apr 10 04:33:41 2004
+++ edited/drivers/i2c/chips/eeprom.c Sun Apr 18 18:30:40 2004
@@ -123,7 +123,8 @@
up(&data->update_lock);
}

-static ssize_t eeprom_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
+static ssize_t eeprom_read(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
struct eeprom_data *data = i2c_get_clientdata(client);
===== drivers/pci/pci-sysfs.c 1.9 vs edited =====
--- 1.9/drivers/pci/pci-sysfs.c Fri Mar 26 09:11:04 2004
+++ edited/drivers/pci/pci-sysfs.c Sun Apr 18 18:30:40 2004
@@ -63,7 +63,8 @@
static DEVICE_ATTR(resource,S_IRUGO,pci_show_resources,NULL);

static ssize_t
-pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
+pci_read_config(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
unsigned int size = 64;
@@ -117,7 +118,8 @@
}

static ssize_t
-pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
+pci_write_config(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
unsigned int size = count;
===== drivers/scsi/qla2xxx/qla_os.c 1.15 vs edited =====
--- 1.15/drivers/scsi/qla2xxx/qla_os.c Mon Mar 22 02:13:23 2004
+++ edited/drivers/scsi/qla2xxx/qla_os.c Sun Apr 18 18:30:41 2004
@@ -394,10 +394,10 @@
int qla2x00_allocate_sp_pool( scsi_qla_host_t *ha);
void qla2x00_free_sp_pool(scsi_qla_host_t *ha);

-static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *, char *, loff_t,
- size_t);
-static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *, char *, loff_t,
- size_t);
+static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
+static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
static struct bin_attribute sysfs_fw_dump_attr = {
.attr = {
.name = "fw_dump",
@@ -407,10 +407,10 @@
.read = qla2x00_sysfs_read_fw_dump,
.write = qla2x00_sysfs_write_fw_dump,
};
-static ssize_t qla2x00_sysfs_read_nvram(struct kobject *, char *, loff_t,
- size_t);
-static ssize_t qla2x00_sysfs_write_nvram(struct kobject *, char *, loff_t,
- size_t);
+static ssize_t qla2x00_sysfs_read_nvram(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
+static ssize_t qla2x00_sysfs_write_nvram(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
static struct bin_attribute sysfs_nvram_attr = {
.attr = {
.name = "nvram",
@@ -432,8 +432,8 @@


/* SysFS attributes. */
-static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *kobj, char *buf,
- loff_t off, size_t count)
+static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *kobj,
+ struct attribute *attr, char *buf, loff_t off, size_t count)
{
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
struct device, kobj)));
@@ -450,8 +450,8 @@
return (count);
}

-static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *kobj, char *buf,
- loff_t off, size_t count)
+static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *kobj,
+ struct attribute *attr, char *buf, loff_t off, size_t count)
{
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
struct device, kobj)));
@@ -505,8 +505,8 @@
return (count);
}

-static ssize_t qla2x00_sysfs_read_nvram(struct kobject *kobj, char *buf,
- loff_t off, size_t count)
+static ssize_t qla2x00_sysfs_read_nvram(struct kobject *kobj,
+ struct attribute *attr, char *buf, loff_t off, size_t count)
{
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
struct device, kobj)));
@@ -532,8 +532,8 @@
return (count);
}

-static ssize_t qla2x00_sysfs_write_nvram(struct kobject *kobj, char *buf,
- loff_t off, size_t count)
+static ssize_t qla2x00_sysfs_write_nvram(struct kobject *kobj,
+ struct attribute *attr, char *buf, loff_t off, size_t count)
{
struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
struct device, kobj)));
===== drivers/video/aty/radeon_base.c 1.12 vs edited =====
--- 1.12/drivers/video/aty/radeon_base.c Fri Mar 5 03:40:50 2004
+++ edited/drivers/video/aty/radeon_base.c Sun Apr 18 18:30:41 2004
@@ -2014,7 +2014,8 @@
}


-static ssize_t radeon_show_edid1(struct kobject *kobj, char *buf, loff_t off, size_t count)
+static ssize_t radeon_show_edid1(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct pci_dev *pdev = to_pci_dev(dev);
@@ -2025,7 +2026,8 @@
}


-static ssize_t radeon_show_edid2(struct kobject *kobj, char *buf, loff_t off, size_t count)
+static ssize_t radeon_show_edid2(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct pci_dev *pdev = to_pci_dev(dev);
===== drivers/zorro/zorro-sysfs.c 1.1 vs edited =====
--- 1.1/drivers/zorro/zorro-sysfs.c Sun Jan 18 23:35:41 2004
+++ edited/drivers/zorro/zorro-sysfs.c Sun Apr 18 18:30:42 2004
@@ -47,8 +47,8 @@

static DEVICE_ATTR(resource, S_IRUGO, zorro_show_resource, NULL);

-static ssize_t zorro_read_config(struct kobject *kobj, char *buf, loff_t off,
- size_t count)
+static ssize_t zorro_read_config(struct kobject *kobj, struct attribute *attr,
+ char *buf, loff_t off, size_t count)
{
struct zorro_dev *z = to_zorro_dev(container_of(kobj, struct device,
kobj));
===== fs/sysfs/bin.c 1.12 vs edited =====
--- 1.12/fs/sysfs/bin.c Fri Aug 29 10:40:51 2003
+++ edited/fs/sysfs/bin.c Sun Apr 18 18:30:43 2004
@@ -20,7 +20,7 @@
struct bin_attribute * attr = dentry->d_fsdata;
struct kobject * kobj = dentry->d_parent->d_fsdata;

- return attr->read(kobj, buffer, off, count);
+ return attr->read(kobj, &attr->attr, buffer, off, count);
}

static ssize_t
@@ -63,7 +63,7 @@
struct bin_attribute *attr = dentry->d_fsdata;
struct kobject *kobj = dentry->d_parent->d_fsdata;

- return attr->write(kobj, buffer, offset, count);
+ return attr->write(kobj, &attr->attr, buffer, offset, count);
}

static ssize_t write(struct file * file, const char __user * userbuf,
@@ -108,7 +108,11 @@
goto Done;

error = -ENOMEM;
- file->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (attr->open)
+ file->private_data = attr->open(kobj, &attr->attr);
+ else
+ file->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
+
if (!file->private_data)
goto Done;

@@ -123,11 +127,16 @@
static int release(struct inode * inode, struct file * file)
{
struct kobject * kobj = file->f_dentry->d_parent->d_fsdata;
+ struct bin_attribute * attr = file->f_dentry->d_fsdata;
u8 * buffer = file->private_data;

+ if (attr->release)
+ attr->release(kobj, &attr->attr, buffer);
+ else
+ kfree(buffer);
+
if (kobj)
kobject_put(kobj);
- kfree(buffer);
return 0;
}

===== include/linux/sysfs.h 1.33 vs edited =====
--- 1.33/include/linux/sysfs.h Mon Apr 12 11:55:33 2004
+++ edited/include/linux/sysfs.h Sun Apr 18 18:30:44 2004
@@ -27,8 +27,12 @@
struct bin_attribute {
struct attribute attr;
size_t size;
- ssize_t (*read)(struct kobject *, char *, loff_t, size_t);
- ssize_t (*write)(struct kobject *, char *, loff_t, size_t);
+ ssize_t (*read)(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
+ ssize_t (*write)(struct kobject *, struct attribute *,
+ char *, loff_t, size_t);
+ char *(*open)(struct kobject *, struct attribute *);
+ void (*release)(struct kobject *, struct attribute *, char *);
};

struct sysfs_ops {