Skip to contentSkip to conversation

filling in ACPI method access via sysfs namespace

PATCH In review Alex Williamson 4 participants 12 messages Apr 8, 2004 – May 6, 2004

Subsystems (7)
SCSI ACPI PCI Framebuffer layer +3 more subsystems
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 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

Signature · 445 lines
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;
 		}




-------------------------------------------------------
This SF.Net email is sponsored by: IBM Linux Tutorials
Free Linux tutorial presented by Daniel Robbins, President and CEO of
GenToo technologies. Learn everything from fundamentals to system
administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click
#1 lore raw

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

Signature · 257 lines
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;
 		}




-------------------------------------------------------
This SF.Net email is sponsored by: IBM Linux Tutorials
Free Linux tutorial presented by Daniel Robbins, President and CEO of
GenToo technologies. Learn everything from fundamentals to system
administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click
#2 lore raw
Quoted 9 lines · Alex Williamson

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

Signature · 8 lines
http:// if  ile.org/


-------------------------------------------------------
This SF.Net email is sponsored by: IBM Linux Tutorials
Free Linux tutorial presented by Daniel Robbins, President and CEO of
GenToo technologies. Learn everything from fundamentals to system
administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click
#3 lore raw
Quoted 2 lines · John Belmonte

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

#4 lore raw
Quoted 4 lines · Alex Williamson
Quoted 1 line · John Belmonte

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?

Quoted 8 lines

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

Signature · 1 line
http:// if  ile.org/
#5 lore raw
Quoted 4 lines · John Belmonte

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

-------------------------------------------------------
This SF.Net email is sponsored by: IBM Linux Tutorials Free Linux tutorial presented by Daniel Robbins, President and CEO of GenToo technologies. Learn everything from fundamentals to system administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click

#6 lore raw
Quoted 5 lines · Alex Williamson

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 =====

drivers/acpi/scan.c +235 -1
@@ -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 =====

drivers/base/firmware_class.c +4 -4
@@ -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 =====

drivers/i2c/chips/eeprom.c +2 -1
@@ -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 =====

drivers/pci/pci-sysfs.c +4 -2
@@ -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 =====

drivers/scsi/qla2xxx/qla_os.c +16 -16
@@ -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 =====

drivers/video/aty/radeon_base.c +4 -2
@@ -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 =====

drivers/zorro/zorro-sysfs.c +2 -2
@@ -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 =====

fs/sysfs/bin.c +2 -2
@@ -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 =====

include/linux/sysfs.h +4 -2
@@ -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);
#7 lore raw
Quoted 7 lines · Alex Williamson
Quoted 5 lines · Alex Williamson

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.

Quoted 7 lines

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.

Signature · 13 lines
"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


-------------------------------------------------------
This SF.Net email is sponsored by: IBM Linux Tutorials
Free Linux tutorial presented by Daniel Robbins, President and CEO of
GenToo technologies. Learn everything from fundamentals to system
administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click
#8 lore raw
Quoted 8 lines · Matthew Wilcox

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 =====

drivers/acpi/scan.c +209 -1
@@ -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 =====

drivers/base/firmware_class.c +4 -4
@@ -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 =====

drivers/i2c/chips/eeprom.c +2 -1
@@ -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 =====

drivers/pci/pci-sysfs.c +4 -2
@@ -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 =====

drivers/scsi/qla2xxx/qla_os.c +16 -16
@@ -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 =====

drivers/video/aty/radeon_base.c +4 -2
@@ -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 =====

drivers/zorro/zorro-sysfs.c +2 -2
@@ -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 =====

fs/sysfs/bin.c +3 -2
@@ -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 =====

include/linux/sysfs.h +4 -2
@@ -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);
#9 lore raw

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

Signature · 866 lines
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 {




-------------------------------------------------------
This SF.Net email is sponsored by: IBM Linux Tutorials
Free Linux tutorial presented by Daniel Robbins, President and CEO of
GenToo technologies. Learn everything from fundamentals to system
administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click
#10 lore raw
In reply to Alex Williamson

Hi!

Quoted 17 lines
Quoted 7 lines

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,

drivers/acpi/scan.c +1 -0
+	/*

drivers/acpi/scan.c line 68

Quoted 10 lines
drivers/acpi/scan.c +10 -0
+	 * 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 *));

This probably makes it *way* too implementation dependand.
Pavel

Signature · 8 lines
When do you have heart between your knees?


-------------------------------------------------------
This SF.Net email is sponsored by Sleepycat Software
Learn developer strategies Cisco, Motorola, Ericsson & Lucent use to deliver
higher performing products faster, at low TCO.
http://www.sleepycat.com/telcomwpreg.php?From=osdnemail3
#11 lore raw
In reply to Alex Williamson
Quoted 5 lines · Alex Williamson

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 =====

drivers/acpi/scan.c +229 -1
@@ -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;
 		}