public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC] dev_acpi: device driver for userspace access to ACPI
@ 2004-08-03 17:00 Alex Williamson
  2004-08-03 17:31 ` Dave Hansen
  2004-08-05  4:36 ` Greg KH
  0 siblings, 2 replies; 7+ messages in thread
From: Alex Williamson @ 2004-08-03 17:00 UTC (permalink / raw)
  To: acpi-devel; +Cc: linux-kernel


   This is by no means ready for release, but I wanted to get a sanity
check.  I'm still stuck on this idea that userspace needs access to ACPI
namespace.  Manageability apps might use this taking inventory of
devices not exposed by other means, things like X can locate chipset
components that don't live in PCI space, there's even the possibility of
making user space drivers.

  Populating the sysfs tree didn't seem to generate as much interest as
I'd hoped and I don't think it kept with the spirit of sysfs very well.
So, now I present dev_acpi (name suggestions welcome).  The link below
is a tarball with a first stab at the driver as well as a simple proof
of concept application.  It should build against any 2.6 kernel as long
as you have the include files available.  There are no kernel changes
required, thus it doesn't expose anything not already exposed as a
symbol.

   The basic concept of operation is that the ioctl operates on the ACPI
path passed into the ioctl call.  The ioctl may return the result of the
operation either in the status field of the argument or use that to
indicate the number of bytes available to read(2) for the result.  The
header file included describes the input and output for each operation.
If the status field indicates a byte count to read, the calling
application can easily size buffers, and call read(2) on the device file
to get the results.  I've also include support for write(2) that could
allow writing arguments for method calls that take input (completely
untested).  I've limited some of the output (for instance in GET_NEXT)
to try to only print out standard ACPI objects, but the filter is pretty
simple (objects beginning w/ '_').  I know the completely open interface
from the sysfs implementation scared some people.  Non-standard objects
can still be operated on, but you've got to know what to look for.

   Many of the ioctls mimic the behavior of the acpi calls that are
already exported.  What I have now is only a start at what could be
provided.  The sample, proof-of-concept app, is called acpitree.  It's
much like the tree app for listing files and directories.  It does
evaluate and print _HIDs represented by integers, but that's about it.
Here's some sample output (sorry anyone not using fixed width fonts):

>From an rx4640 ia64 server:
\\
|-- _GPE
|-- _PR_
|-- _SB_
|   |-- SBA0
|   |   |-- _HID (HWP0001)
|   |   |-- _CID
|   |   |-- _CRS
|   |   |-- _INI
|   |   |-- _UID
|   |   |-- MI0_
|   |   |   |-- _HID (IPI0001)
|   |   |   |-- _UID
|   |   |   |-- _STA
|   |   |   `-- _CRS
|   |   |-- PCI0
|   |   |   |-- _UID
|   |   |   |-- _STA
|   |   |   |-- _BBN
|   |   |   |-- _HID (HWP0002)
|   |   |   |-- _CID
|   |   |   |-- _PRT
...

>From an nc6000 laptop:
\\
|-- _GPE
|-- _PR_
|-- _SB_
|   |-- _INI
|   |-- C00C
|   |   |-- _HID (PNP0C01)
|   |   `-- _CRS
|   |-- C046
|   |   |-- _HID (PNP0A03)
|   |   |-- _ADR
|   |   |-- C047
|   |   |   |-- _ADR
|   |   |   |-- C0D1
|   |   |   |   |-- _ADR
|   |   |   |   |-- _REG
|   |   |   |   |-- _S3D
|   |   |   |   |-- _S4D
|   |   |   |   |-- _DOS
|   |   |   |   |-- C0DD
|   |   |   |   |   |-- _ADR
...

  You can find the driver and sample app here:

http://free.linux.hp.com/~awilliam/acpi/dev_acpi/dev_acpi-20040803.tar.bz2

There's a brutally short README there.  Caveat: the driver is hardcoded
to use an experimental major number, you'll have to mknod it, see the
README.

Please try it out, let me know if it sucks.  I make no guarantees it
won't kill your system, but it shouldn't unless you start evaluating
dangerous objects (ie, if you don't know what it does, don't do it).
And of course, if you have any suggestions, I welcome feedback.  Thanks,

	Alex
   
-- 
Alex Williamson                             HP Linux & Open Source Lab


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC] dev_acpi: device driver for userspace access to ACPI
  2004-08-03 17:00 [RFC] dev_acpi: device driver for userspace access to ACPI Alex Williamson
@ 2004-08-03 17:31 ` Dave Hansen
  2004-08-03 18:17   ` Alex Williamson
  2004-08-05  4:36 ` Greg KH
  1 sibling, 1 reply; 7+ messages in thread
From: Dave Hansen @ 2004-08-03 17:31 UTC (permalink / raw)
  To: Alex Williamson; +Cc: acpi-devel, Linux Kernel Mailing List

On Tue, 2004-08-03 at 10:00, Alex Williamson wrote:
>    This is by no means ready for release, but I wanted to get a sanity
> check.  I'm still stuck on this idea that userspace needs access to ACPI
> namespace.  Manageability apps might use this taking inventory of
> devices not exposed by other means, things like X can locate chipset
> components that don't live in PCI space, there's even the possibility of
> making user space drivers.

The only thing that worries me about a patch like this is that it
encourages people to write arch-specific tools that have no chance of
working on multiple platforms.  

Right now, on ppc64, we have a system for making direct calls into the
firmware, as well as a copy of the firmware's device-tree exported to
userspace.  This means that we have userspace applications that do very
generic things like counting CPUs, or activating memory in very
arch-specific ways.  

Creating more of these interfaces encourages more of these arch-specific
applications, and what we end up with are lots of tools that only work
on Intel platforms or IBM ppc, but not Linux in general.

So, what kinds of generic, arch-independent interfaces should we
implement in the kernel that would reduce the need for something like
your driver?

-- Dave



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC] dev_acpi: device driver for userspace access to ACPI
  2004-08-03 17:31 ` Dave Hansen
@ 2004-08-03 18:17   ` Alex Williamson
  2004-08-03 18:34     ` Dave Hansen
  0 siblings, 1 reply; 7+ messages in thread
From: Alex Williamson @ 2004-08-03 18:17 UTC (permalink / raw)
  To: Dave Hansen; +Cc: acpi-devel, Linux Kernel Mailing List

On Tue, 2004-08-03 at 10:31 -0700, Dave Hansen wrote:
> On Tue, 2004-08-03 at 10:00, Alex Williamson wrote:
> >    This is by no means ready for release, but I wanted to get a sanity
> > check.  I'm still stuck on this idea that userspace needs access to ACPI
> > namespace.  Manageability apps might use this taking inventory of
> > devices not exposed by other means, things like X can locate chipset
> > components that don't live in PCI space, there's even the possibility of
> > making user space drivers.
> 
> The only thing that worries me about a patch like this is that it
> encourages people to write arch-specific tools that have no chance of
> working on multiple platforms.  
> 
> Right now, on ppc64, we have a system for making direct calls into the
> firmware, as well as a copy of the firmware's device-tree exported to
> userspace.  This means that we have userspace applications that do very
> generic things like counting CPUs, or activating memory in very
> arch-specific ways.  
> 
> Creating more of these interfaces encourages more of these arch-specific
> applications, and what we end up with are lots of tools that only work
> on Intel platforms or IBM ppc, but not Linux in general.
> 
> So, what kinds of generic, arch-independent interfaces should we
> implement in the kernel that would reduce the need for something like
> your driver?

   I agree with your intent, but I'm not sure a common kernel interface
is feasible or desired.  This driver would be much more useful if it was
cleverly abstracted by a userspace library.  Should we try to make the
common layer be the library interface?  Obviously the more similar the
kernel interface, the easier, but I'm not ready to sign-up to architect
a generic interface.

   The ACPI interface could be used to do everything from switching a
laptop display between the interfaces to listing and configuring/de-
configuring specific pieces of hardware.  There will be a set of
functionality that's common across multiple interfaces, but I don't want
to prevent the usage that is very specific to the underlying
implementation.

	Alex  

-- 
Alex Williamson                             HP Linux & Open Source Lab


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC] dev_acpi: device driver for userspace access to ACPI
  2004-08-03 18:17   ` Alex Williamson
@ 2004-08-03 18:34     ` Dave Hansen
  2004-08-03 21:02       ` Alex Williamson
  0 siblings, 1 reply; 7+ messages in thread
From: Dave Hansen @ 2004-08-03 18:34 UTC (permalink / raw)
  To: Alex Williamson; +Cc: acpi-devel, Linux Kernel Mailing List

On Tue, 2004-08-03 at 11:17, Alex Williamson wrote:
> On Tue, 2004-08-03 at 10:31 -0700, Dave Hansen wrote:
> > So, what kinds of generic, arch-independent interfaces should we
> > implement in the kernel that would reduce the need for something like
> > your driver?
> 
>    I agree with your intent, but I'm not sure a common kernel interface
> is feasible or desired.  This driver would be much more useful if it was
> cleverly abstracted by a userspace library.  Should we try to make the
> common layer be the library interface?  Obviously the more similar the
> kernel interface, the easier, but I'm not ready to sign-up to architect
> a generic interface.

Instead of architecting a generic interface, might you simply exclude
access from your driver to things that already have generic interfaces? 
I think there are things that we exclude from /proc/device-tree on ppc64
because there's a generic equivalent elsewhere.  

>    The ACPI interface could be used to do everything from switching a
> laptop display between the interfaces to listing and configuring/de-
> configuring specific pieces of hardware.  There will be a set of
> functionality that's common across multiple interfaces, but I don't want
> to prevent the usage that is very specific to the underlying
> implementation.

There are certainly some very platform-specific things that obviously
need to be done with direct access to the firmware, and that we don't
want to pollute the kernel with.  Parsing some of the firmware error
logs on ppc64 comes to mind.  You just need to be *very* careful with
the application authors because it's such a big gun :)

-- Dave


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC] dev_acpi: device driver for userspace access to ACPI
  2004-08-03 18:34     ` Dave Hansen
@ 2004-08-03 21:02       ` Alex Williamson
  0 siblings, 0 replies; 7+ messages in thread
From: Alex Williamson @ 2004-08-03 21:02 UTC (permalink / raw)
  To: Dave Hansen; +Cc: acpi-devel, Linux Kernel Mailing List

On Tue, 2004-08-03 at 11:34 -0700, Dave Hansen wrote:

> Instead of architecting a generic interface, might you simply exclude
> access from your driver to things that already have generic interfaces? 
> I think there are things that we exclude from /proc/device-tree on ppc64
> because there's a generic equivalent elsewhere.  
> 

   The access interfaces I'm exposing are pretty simple building block
type features.  It's a toolset to poke at namespace, not a predefined
set of device specific functions.  I'm sure you can mix them all
together and duplicate something that already exists, but trying to
kludge in limitations sounds futile and would reduce the usefulness of
the entire interface.  Besides, given a choice, I kinda doubt people are
going to choose to implement something that requires them to know about
ACPI ;^)

> There are certainly some very platform-specific things that obviously
> need to be done with direct access to the firmware, and that we don't
> want to pollute the kernel with.  Parsing some of the firmware error
> logs on ppc64 comes to mind.  You just need to be *very* careful with
> the application authors because it's such a big gun :)

   This is certainly a big gun, but in the software world, I'd rather
have a big gun available than no gun at all.  Thanks for the comments,

	Alex

-- 
Alex Williamson                             HP Linux & Open Source Lab


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC] dev_acpi: device driver for userspace access to ACPI
  2004-08-03 17:00 [RFC] dev_acpi: device driver for userspace access to ACPI Alex Williamson
  2004-08-03 17:31 ` Dave Hansen
@ 2004-08-05  4:36 ` Greg KH
  2004-08-05 15:52   ` Alex Williamson
  1 sibling, 1 reply; 7+ messages in thread
From: Greg KH @ 2004-08-05  4:36 UTC (permalink / raw)
  To: Alex Williamson; +Cc: acpi-devel, linux-kernel

On Tue, Aug 03, 2004 at 11:00:26AM -0600, Alex Williamson wrote:
> 
>   Populating the sysfs tree didn't seem to generate as much interest as
> I'd hoped and I don't think it kept with the spirit of sysfs very well.

I'm sorry if I didn't speak up at the time, but I still think that your
sysfs patches were the right way to go.  Why do you think they don't
keep with the spirit of sysfs?  Do you have a pointer to your last
patch that exported the acpi table info through sysfs?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC] dev_acpi: device driver for userspace access to ACPI
  2004-08-05  4:36 ` Greg KH
@ 2004-08-05 15:52   ` Alex Williamson
  0 siblings, 0 replies; 7+ messages in thread
From: Alex Williamson @ 2004-08-05 15:52 UTC (permalink / raw)
  To: Greg KH; +Cc: acpi-devel, linux-kernel

On Wed, 2004-08-04 at 21:36 -0700, Greg KH wrote:
> On Tue, Aug 03, 2004 at 11:00:26AM -0600, Alex Williamson wrote:
> > 
> >   Populating the sysfs tree didn't seem to generate as much interest as
> > I'd hoped and I don't think it kept with the spirit of sysfs very well.
> 
> I'm sorry if I didn't speak up at the time, but I still think that your
> sysfs patches were the right way to go.  Why do you think they don't
> keep with the spirit of sysfs?  Do you have a pointer to your last
> patch that exported the acpi table info through sysfs?

   The sysfs patch definitely has a higher coolness factor than the
device file approach.  For the most part, the functionality is the same,
so I'm happy to go either way.  The kludgy part of the sysfs patch is
the way it's kind of hacking an ioctl interface into the read()/write()
interface available.  A read of an object file can return various things
based on what's been written.  The same is true of the device file
patch, but the device file patch is far less invasive and ends up being
a nice standalone module.  I had to tweak some things in sysfs to get it
to behave the way I wanted (like add some backing store), which made me
start thinking perhaps I should do it another way.  I toyed with the
idea of actually making an acpifs, but I couldn't come up with a big
enough win over the sysfs approach to go down that path.

   If the sysfs version isn't making too many people cringe, I'd prefer
to pursue that direction.  Here's a pointer to the last version I
posted:

http://marc.theaimsgroup.com/?l=linux-kernel&m=108239031314885&w=2

I also attached the latest version I have below, it's newer, but still
dated from April.  I believe it still applies and works.  let me know if
you think it has a chance.  Thanks,

	Alex

-- 
Alex Williamson                             HP Linux & Open Source Lab

 drivers/acpi/scan.c             |  661 +++++++++++++++++++++++++++++++++++++++-
 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, 711 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 26 22:11:02 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,662 @@
 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;
+};
+
+static void
+fixup_string(
+	unsigned long		start,
+	acpi_integer		length,
+	union acpi_object	*element)
+{
+	unsigned long offset;
+	unsigned long **ptr;
+
+	offset = (unsigned long)(element->string.pointer) - start;
+
+	if (offset > length)
+		offset = 0;
+
+	ptr = (unsigned long **)&element->string.pointer;
+	*ptr = (unsigned long *)offset;
+}
+
+static void
+fixup_buffer(
+	unsigned long		start,
+	acpi_integer		length,
+	union acpi_object	*element)
+{
+	unsigned long offset;
+	unsigned long **ptr;
+
+	offset = (unsigned long)(element->buffer.pointer) - start;
+
+	if (offset > length)
+		offset = 0;
+
+	ptr = (unsigned long **)&element->buffer.pointer;
+	*ptr = (unsigned long *)offset;
+}
+
+static void fixup_package(unsigned long, acpi_integer, union acpi_object *);
+
+/*
+ * strings, buffers, and packages contain pointers.  These should just
+ * be pointing further down in the buffer, so before passing to user
+ * space, change the pointers into offsets from the beginning of the buffer.
+ */
+static void
+fixup_element(
+	unsigned long		start,
+	acpi_integer		length,
+	union acpi_object	*element)
+{
+	unsigned long buf_left;
+
+	buf_left = (start + length)  - (unsigned long)element;
+	if (buf_left < sizeof(union acpi_object))
+		return;
+
+	switch (element->type) {
+		case ACPI_TYPE_STRING:
+			return fixup_string(start, length, element);
+		case ACPI_TYPE_BUFFER:
+			return fixup_buffer(start, length, element);
+		case ACPI_TYPE_PACKAGE:
+			return fixup_package(start, length, element);
+		default:
+			/* No fixup necessary */
+			return;
+	}
+}
+
+static void
+fixup_package(
+	unsigned long		start,
+	acpi_integer		length,
+	union acpi_object	*element)
+{
+	u32 count;
+	union acpi_object *next_element;
+	unsigned long offset;
+	unsigned long **ptr;
+
+	count = element->package.count;
+
+	next_element = element->package.elements;
+
+	for ( ; count > 0 ; count-- ) {
+		fixup_element(start, length, next_element);
+		next_element++;
+	}
+
+	offset = (unsigned long)(element->package.elements) - start;
+
+	if (offset > length)
+		offset = 0;
+
+	ptr = (unsigned long **)&element->package.elements;
+	*ptr = (unsigned long *)offset;
+}
+
+/* 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;
+
+		fixup_element((unsigned long)buffer.pointer,
+		              buffer.length, buffer.pointer);
+
+		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 +729,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 +737,7 @@
 	struct acpi_device	*device, 
 	int			type)
 {
+	remove_sysfs_files(device);
 	kobject_unregister(&device->kobj);
 	return 0;
 }
@@ -706,7 +1365,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.17 vs edited =====
--- 1.17/drivers/base/firmware_class.c	Wed Apr 21 14:42:04 2004
+++ edited/drivers/base/firmware_class.c	Mon Apr 26 20:19:15 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.9 vs edited =====
--- 1.9/drivers/i2c/chips/eeprom.c	Wed Apr 21 14:42:04 2004
+++ edited/drivers/i2c/chips/eeprom.c	Mon Apr 26 20:19:16 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	Mon Apr 26 20:19:16 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.16 vs edited =====
--- 1.16/drivers/scsi/qla2xxx/qla_os.c	Wed Apr 21 14:42:05 2004
+++ edited/drivers/scsi/qla2xxx/qla_os.c	Mon Apr 26 20:19:17 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",
@@ -408,10 +408,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",
@@ -434,8 +434,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)));
@@ -452,8 +452,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)));
@@ -507,8 +507,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)));
@@ -534,8 +534,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	Mon Apr 26 20:19:21 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	Mon Apr 26 20:19:22 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.13 vs edited =====
--- 1.13/fs/sysfs/bin.c	Wed Apr 21 14:42:04 2004
+++ edited/fs/sysfs/bin.c	Mon Apr 26 20:22:27 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,
@@ -113,7 +113,11 @@
 		goto Error;
 
 	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 Error;
 
@@ -137,7 +141,12 @@
 	if (kobj) 
 		kobject_put(kobj);
 	module_put(attr->attr.owner);
-	kfree(buffer);
+
+	if (attr->release)
+		attr->release(kobj, &attr->attr, buffer);
+	else
+		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	Mon Apr 26 20:19:29 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 {



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2004-08-05 15:54 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-08-03 17:00 [RFC] dev_acpi: device driver for userspace access to ACPI Alex Williamson
2004-08-03 17:31 ` Dave Hansen
2004-08-03 18:17   ` Alex Williamson
2004-08-03 18:34     ` Dave Hansen
2004-08-03 21:02       ` Alex Williamson
2004-08-05  4:36 ` Greg KH
2004-08-05 15:52   ` Alex Williamson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox