2005-02-10 16:19:04

by Stelian Pop

[permalink] [raw]
Subject: [PATCH, new ACPI driver] new sony_acpi driver

Hi,

This driver has been submitted (almost unchanged) on lkml and
on acpi-devel twice, first on July 21, 2004, then again on
September 17, 2004. It has been quietly ignored.

Privately I've had many positive feedbacks from users of this driver
(and no negative feedback), including Linux distributions who wish
to include it into their kernels. The reports are increasing in number,
it would seem that newer Sony Vaios are more and more incompatible
with sonypi and require sony_acpi to control the screen brightness.

Please integrate this patch in -mm for wider testing and into
the ACPI tree.

Original announcement follows below.

Thanks,

Stelian.

PS: I am also going to submit a bugzilla RFE for the acpi people,
I have been told they are more receptive to that.

------------------------------------------------------------------
Most of the Sony Vaio owners are happy with the current sonypi
driver, which makes them able to get/set the screen brightness,
capture the jogdial and/or special key events etc.

However, some newer Vaio series (FX series, and not only those) lack
a SPIC device in their ACPI BIOS making the sonypi driver unusable
for them.

Fortunately, there is another ACPI device, called SNC (for Sony
Notebook Control) which seems to be present in all Vaios, which
can be used to access some low-level laptop functions. From what
I understood, the SPIC device itself is built on top of SNC.

The SNC device is able to drive the screen brightness, and probably
more (what is does more is yet unknown). The attached driver is a
first shot of using the SNC directly.

In the default mode, the sony_acpi driver let's the user get/set the
screen brightness, and only that.

The screen is one of the most important power consumers in a laptop,
so being able to set its brightness is very important for many users,
making this driver useful even if it does only that.

In the debug/developer mode (which can be activated with a module
option), the driver let's the user see a few other knobs, whose
effects is however unknown. Using the debug mode we may hopefully
find what those knobs do and propose that extra functionalities in
the future versions of the driver (if someone at Sony is listening,
you know what we need from you...)

This driver does not interact with the current sonypi driver, both
drivers can be used at the same time.

Signed-of-by: Stelian Pop <[email protected]>

--- /dev/null 2005-02-10 10:35:32.824183288 +0100
+++ linux-2.6-stelian/drivers/acpi/sony_acpi.c 2005-01-31 17:05:53.000000000 +0100
@@ -0,0 +1,442 @@
+/*
+ * ACPI Sony Notebook Control Driver (SNC)
+ *
+ * Copyright (C) 2004 Stelian Pop <[email protected]>
+ *
+ * Parts of this driver inspired from asus_acpi.c, which is
+ * Copyright (C) 2002, 2003, 2004 Julien Lerouge, Karol Kozimor
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_bus.h>
+#include <asm/uaccess.h>
+
+#define ACPI_SNC_CLASS "sony"
+#define ACPI_SNC_HID "SNY5001"
+#define ACPI_SNC_DRIVER_NAME "ACPI Sony Notebook Control Driver v0.1"
+
+MODULE_AUTHOR("Stelian Pop");
+MODULE_DESCRIPTION(ACPI_SNC_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug,"set this to 1 (and RTFM) if you want to help the development of this driver");
+
+static int sony_acpi_add (struct acpi_device *device);
+static int sony_acpi_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver sony_acpi_driver = {
+ name: ACPI_SNC_DRIVER_NAME,
+ class: ACPI_SNC_CLASS,
+ ids: ACPI_SNC_HID,
+ ops: {
+ add: sony_acpi_add,
+ remove: sony_acpi_remove,
+ },
+};
+
+struct sony_snc {
+ acpi_handle handle;
+ int brt; /* brightness */
+ struct proc_dir_entry *proc_brt;
+ int cmi; /* ??? ? */
+ struct proc_dir_entry *proc_cmi;
+ int csxb; /* ??? */
+ struct proc_dir_entry *proc_csxb;
+ int ctr; /* contrast ? */
+ struct proc_dir_entry *proc_ctr;
+ int pbr; /* ??? */
+ struct proc_dir_entry *proc_pbr;
+};
+
+static struct proc_dir_entry *sony_acpi_dir;
+
+static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
+{
+ struct acpi_buffer output;
+ union acpi_object out_obj;
+ acpi_status status;
+
+ output.length = sizeof(out_obj);
+ output.pointer = &out_obj;
+
+ status = acpi_evaluate_object(handle, name, NULL, &output);
+ if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
+ *result = out_obj.integer.value;
+ return 0;
+ }
+
+ printk(KERN_WARNING "acpi_callreadfunc failed\n");
+
+ return -1;
+}
+
+static int acpi_callsetfunc(acpi_handle handle, char *name, int value, int *result)
+{
+ struct acpi_object_list params;
+ union acpi_object in_obj;
+ struct acpi_buffer output;
+ union acpi_object out_obj;
+ acpi_status status;
+
+ params.count = 1;
+ params.pointer = &in_obj;
+ in_obj.type = ACPI_TYPE_INTEGER;
+ in_obj.integer.value = value;
+
+ output.length = sizeof(out_obj);
+ output.pointer = &out_obj;
+
+ status = acpi_evaluate_object(handle, name, &params, &output);
+ if (status == AE_OK) {
+ if (result != NULL) {
+ if (out_obj.type != ACPI_TYPE_INTEGER) {
+ printk(KERN_WARNING "acpi_evaluate_object bad return type\n");
+ return -1;
+ }
+ *result = out_obj.integer.value;
+ }
+ return 0;
+ }
+
+ printk(KERN_WARNING "acpi_evaluate_object failed\n");
+
+ return -1;
+}
+
+static int parse_buffer(const char __user *buffer, unsigned long count, int *val) {
+ char s[32];
+
+ if (count > 31)
+ return -EINVAL;
+ if (copy_from_user(s, buffer, count))
+ return -EFAULT;
+ s[count] = 0;
+ if (sscanf(s, "%i", val) != 1)
+ return -EINVAL;
+ return 0;
+}
+
+static int sony_acpi_write_brt(struct file *file, const char __user *buffer, unsigned long count, void *data)
+{
+ struct sony_snc *snc = (struct sony_snc *) data;
+ int result;
+
+ if ((result = parse_buffer(buffer, count, &snc->brt)) < 0)
+ return result;
+
+ /* Accept only values between 1 and 8, or VGN-T1XP will hung */
+ if (snc->brt < 1 || snc->brt > 8)
+ return -EINVAL;
+
+ if (acpi_callsetfunc(snc->handle, "SBRT", snc->brt, NULL) < 0)
+ return -EIO;
+
+ return count;
+}
+
+static int sony_acpi_read_brt(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ struct sony_snc *snc = (struct sony_snc *) data;
+
+ if (acpi_callgetfunc(snc->handle, "GBRT", &snc->brt) < 0)
+ return -EIO;
+
+ return sprintf(page, "%d\n", snc->brt);
+}
+
+static int sony_acpi_write_cmi(struct file *file, const char __user *buffer, unsigned long count, void *data)
+{
+ struct sony_snc *snc = (struct sony_snc *) data;
+ int result;
+
+ if ((result = parse_buffer(buffer, count, &snc->cmi)) < 0)
+ return result;
+
+ if (acpi_callsetfunc(snc->handle, "SCMI", snc->cmi, &snc->cmi) < 0)
+ return -EIO;
+
+ return count;
+}
+
+static int sony_acpi_read_cmi(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ struct sony_snc *snc = (struct sony_snc *) data;
+ return sprintf(page, "%d\n", snc->cmi);
+}
+
+static int sony_acpi_write_csxb(struct file *file, const char __user *buffer, unsigned long count, void *data)
+{
+ struct sony_snc *snc = (struct sony_snc *) data;
+ int result;
+
+ if ((result = parse_buffer(buffer, count, &snc->csxb)) < 0)
+ return result;
+
+ if (acpi_callsetfunc(snc->handle, "CSXB", snc->csxb, &snc->csxb) < 0)
+ return -EIO;
+
+ return count;
+}
+
+static int sony_acpi_read_csxb(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ struct sony_snc *snc = (struct sony_snc *) data;
+ return sprintf(page, "%d\n", snc->csxb);
+}
+
+static int sony_acpi_write_ctr(struct file *file, const char __user *buffer, unsigned long count, void *data)
+{
+ struct sony_snc *snc = (struct sony_snc *) data;
+ int result;
+
+ if ((result = parse_buffer(buffer, count, &snc->ctr)) < 0)
+ return result;
+
+ if (acpi_callsetfunc(snc->handle, "SCTR", snc->ctr, NULL) < 0)
+ return -EIO;
+
+ return count;
+}
+
+static int sony_acpi_read_ctr(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ struct sony_snc *snc = (struct sony_snc *) data;
+
+ if (acpi_callgetfunc(snc->handle, "GCTR", &snc->ctr) < 0)
+ return -EIO;
+
+ return sprintf(page, "%d\n", snc->ctr);
+}
+
+static int sony_acpi_write_pbr(struct file *file, const char __user *buffer, unsigned long count, void *data)
+{
+ struct sony_snc *snc = (struct sony_snc *) data;
+ int result;
+
+ if ((result = parse_buffer(buffer, count, &snc->pbr)) < 0)
+ return result;
+
+ if (acpi_callsetfunc(snc->handle, "SPBR", snc->pbr, NULL) < 0)
+ return -EIO;
+
+ return count;
+}
+
+static int sony_acpi_read_pbr(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ struct sony_snc *snc = (struct sony_snc *) data;
+
+ if (acpi_callgetfunc(snc->handle, "GPBR", &snc->pbr) < 0)
+ return -EIO;
+
+ return sprintf(page, "%d\n", snc->pbr);
+}
+
+static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+ /* struct sony_snc *snc = (struct sony_snc *) data; */
+
+ printk("sony_snc_notify\n");
+}
+
+static acpi_status sony_walk_callback(acpi_handle handle, u32 level, void *context, void **return_value)
+{
+ struct acpi_namespace_node *node = (struct acpi_namespace_node *) handle;
+ union acpi_operand_object *operand = (union acpi_operand_object *) node->object;
+
+ printk("sony_acpi method: name: %4.4s, args %X\n",
+ node->name.ascii,
+ (u32) operand->method.param_count);
+
+ return AE_OK;
+}
+
+static int __init sony_acpi_add(struct acpi_device *device)
+{
+ acpi_status status = AE_OK;
+ struct sony_snc *snc = NULL;
+ int result;
+
+ snc = kmalloc(sizeof(struct sony_snc), GFP_KERNEL);
+ if (!snc)
+ return -ENOMEM;
+ memset(snc, 0, sizeof(struct sony_snc));
+
+ snc->handle = device->handle;
+
+ acpi_driver_data(device) = snc;
+ acpi_device_dir(device) = sony_acpi_dir;
+
+ if (debug) {
+ status = acpi_walk_namespace(ACPI_TYPE_METHOD, snc->handle, 1, sony_walk_callback, NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_WARNING "Unable to walk acpi resources\n");
+ }
+ }
+
+ snc->proc_brt = create_proc_entry("brt", 0600, acpi_device_dir(device));
+ if (!snc->proc_brt) {
+ printk(KERN_WARNING "Unable to create proc entry\n");
+ result = -EIO;
+ goto outbrt;
+ }
+
+ snc->proc_brt->write_proc = sony_acpi_write_brt;
+ snc->proc_brt->read_proc = sony_acpi_read_brt;
+ snc->proc_brt->data = acpi_driver_data(device);
+ snc->proc_brt->owner = THIS_MODULE;
+
+ if (debug) {
+ snc->proc_cmi = create_proc_entry("cmi", 0600, acpi_device_dir(device));
+ if (!snc->proc_cmi) {
+ printk(KERN_WARNING "Unable to create proc entry\n");
+ result = -EIO;
+ goto outcmi;
+ }
+
+ snc->proc_cmi->write_proc = sony_acpi_write_cmi;
+ snc->proc_cmi->read_proc = sony_acpi_read_cmi;
+ snc->proc_cmi->data = acpi_driver_data(device);
+ snc->proc_cmi->owner = THIS_MODULE;
+
+ snc->proc_csxb = create_proc_entry("csxb", 0600, acpi_device_dir(device));
+ if (!snc->proc_csxb) {
+ printk(KERN_WARNING "Unable to create proc entry\n");
+ result = -EIO;
+ goto outcsxb;
+ }
+
+ snc->proc_csxb->write_proc = sony_acpi_write_csxb;
+ snc->proc_csxb->read_proc = sony_acpi_read_csxb;
+ snc->proc_csxb->data = acpi_driver_data(device);
+ snc->proc_csxb->owner = THIS_MODULE;
+
+ snc->proc_ctr = create_proc_entry("ctr", 0600, acpi_device_dir(device));
+ if (!snc->proc_ctr) {
+ printk(KERN_WARNING "Unable to create proc entry\n");
+ result = -EIO;
+ goto outctr;
+ }
+
+ snc->proc_ctr->write_proc = sony_acpi_write_ctr;
+ snc->proc_ctr->read_proc = sony_acpi_read_ctr;
+ snc->proc_ctr->data = acpi_driver_data(device);
+ snc->proc_ctr->owner = THIS_MODULE;
+
+ snc->proc_pbr = create_proc_entry("pbr", 0600, acpi_device_dir(device));
+ if (!snc->proc_pbr) {
+ printk(KERN_WARNING "Unable to create proc entry\n");
+ result = -EIO;
+ goto outpbr;
+ }
+
+ snc->proc_pbr->write_proc = sony_acpi_write_pbr;
+ snc->proc_pbr->read_proc = sony_acpi_read_pbr;
+ snc->proc_pbr->data = acpi_driver_data(device);
+ snc->proc_pbr->owner = THIS_MODULE;
+
+ status = acpi_install_notify_handler(snc->handle,
+ ACPI_DEVICE_NOTIFY, sony_acpi_notify, snc);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_WARNING "Unable to install notify handler\n");
+ result = -ENODEV;
+ goto outnotify;
+ }
+ }
+
+ printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully installed\n");
+
+ return 0;
+
+outnotify:
+ remove_proc_entry("pbr", acpi_device_dir(device));
+outpbr:
+ remove_proc_entry("ctr", acpi_device_dir(device));
+outctr:
+ remove_proc_entry("csxb", acpi_device_dir(device));
+outcsxb:
+ remove_proc_entry("cmi", acpi_device_dir(device));
+outcmi:
+ remove_proc_entry("brt", acpi_device_dir(device));
+outbrt:
+ kfree(snc);
+ return result;
+}
+
+
+static int __exit sony_acpi_remove(struct acpi_device *device, int type)
+{
+ acpi_status status = AE_OK;
+ struct sony_snc *snc = NULL;
+
+ snc = (struct sony_snc *) acpi_driver_data(device);
+
+ if (debug) {
+ status = acpi_remove_notify_handler(snc->handle, ACPI_DEVICE_NOTIFY, sony_acpi_notify);
+ if (ACPI_FAILURE(status))
+ printk(KERN_WARNING "Unable to remove notify handler\n");
+
+ remove_proc_entry("pbr", acpi_device_dir(device));
+ remove_proc_entry("ctr", acpi_device_dir(device));
+ remove_proc_entry("csxb", acpi_device_dir(device));
+ remove_proc_entry("cmi", acpi_device_dir(device));
+ }
+ remove_proc_entry("brt", acpi_device_dir(device));
+
+ kfree(snc);
+
+ printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully removed\n");
+
+ return 0;
+}
+
+static int __init sony_acpi_init(void)
+{
+ int result;
+
+ sony_acpi_dir = proc_mkdir("sony", acpi_root_dir);
+ if (!sony_acpi_dir) {
+ printk(KERN_WARNING "Unable to create /proc entry\n");
+ return -ENODEV;
+ }
+ sony_acpi_dir->owner = THIS_MODULE;
+
+ result = acpi_bus_register_driver(&sony_acpi_driver);
+ if (result < 0) {
+ remove_proc_entry("sony", acpi_root_dir);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+
+static void __exit sony_acpi_exit(void)
+{
+ acpi_bus_unregister_driver(&sony_acpi_driver);
+ remove_proc_entry("sony", acpi_root_dir);
+}
+
+module_init(sony_acpi_init);
+module_exit(sony_acpi_exit);
--- /dev/null 2005-02-10 10:35:32.824183288 +0100
+++ linux-2.6-stelian/Documentation/sony_acpi.txt 2005-01-31 17:00:09.000000000 +0100
@@ -0,0 +1,81 @@
+ACPI Sony Notebook Control Driver (SNC) Readme
+----------------------------------------------
+ Copyright (C) 2004 Stelian Pop <[email protected]>
+
+This mini-driver drives the ACPI SNC device present in the
+ACPI BIOS of the Sony Vaio laptops.
+
+It gives access to some extra laptop functionalities. In
+its current form, the only thing this driver does is letting
+the user set or query the screen brightness.
+
+You should start by trying the sonypi driver, which does
+all this and many other things. But the sonypi driver does
+not work on all sonypi laptops, whereas sony_acpi should
+work everywhere.
+
+Usage:
+------
+
+Loading the sony_acpi.ko module will create a /proc/acpi/sony/
+directory populated with a couple of files (only one for the
+moment).
+
+You then read/write integer values from/to those files by using
+standard UNIX tools.
+
+For example:
+ # echo "1" > /proc/acpi/sony/brt
+sets the lowest screen brightness,
+ # echo "8" > /proc/acpi/sony/brt
+sets the highest screen brightness,
+ # cat /proc/acpi/sony/brt
+retrieves the current screen brightness.
+
+Development:
+------------
+
+If you want to help with the development of this driver (and
+you are not afraid of any side effects doing strange things with
+your ACPI BIOS could have on your laptop), load the driver and
+pass the option 'debug=1'.
+
+REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS.
+
+In your kernel logs you will find the list of all ACPI methods
+the SNC device has on your laptop. You can see the GBRT/SBRT methods
+used to get/set the brightness, but there are others.
+
+I HAVE NO IDEA WHAT THOSE METHODS DO.
+
+The sony_acpi driver creates, for some of those methods (the most
+current ones found on several Vaio models), an entry under
+/proc/acpi/sony/, just like the 'brt' one.
+
+Your mission, should you accept it, is to try finding out what
+those entries are for, by reading/writing random values from/to those
+files and find out what is the impact on your laptop.
+
+You can also modify the driver source and add your extra methods
+and retest.
+
+Should you find anything interesting, please report it back to me,
+I will not disavow all knowledge of your actions :)
+
+Bugs/Limitations:
+-----------------
+
+* This driver is not based on official documentation from Sony
+ (because there is none), so there is no guarantee this driver
+ will work at all, or do the right thing. Although this hasn't
+ happened to me, this driver could do very bad things to your
+ laptop, including permanent damage.
+
+* The sony_acpi and sonypi drivers do not interact at all. In the
+ future, sonypi could use sony_acpi to do (part of) its business.
+
+* spicctrl, which is the userspace tool used to communicate with the
+ sonypi driver (through /dev/sonypi) does not try to use the
+ sony_acpi driver. In the future, spicctrl could try sonypi first,
+ and if it isn't present, try sony_acpi instead.
+
--- linux-2.6-stelian/drivers/acpi/Makefile
+++ linux-2.6-linus/drivers/acpi/Makefile
@@ -54,4 +54,5 @@ obj-$(CONFIG_ACPI_NUMA) += numa.o
obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
obj-$(CONFIG_ACPI_IBM) += ibm_acpi.o
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
+obj-$(CONFIG_ACPI_SONY) += sony_acpi.o
obj-$(CONFIG_ACPI_BUS) += scan.o motherboard.o
--- linux-2.6-stelian/drivers/acpi/Kconfig
+++ linux-2.6-linus/drivers/acpi/Kconfig
@@ -242,6 +242,21 @@ config ACPI_TOSHIBA
If you have a legacy free Toshiba laptop (such as the Libretto L1
series), say Y.

+config ACPI_SONY
+ tristate "Sony Laptop Extras"
+ depends on X86
+ depends on ACPI_INTERPRETER
+ default m
+ ---help---
+ This mini-driver drives the ACPI SNC device present in the
+ ACPI BIOS of the Sony Vaio laptops.
+
+ It gives access to some extra laptop functionalities. In
+ its current form, the only thing this driver does is letting
+ the user set or query the screen brightness.
+
+ Read <Documentation/sony_acpi.txt> for more information.
+
config ACPI_CUSTOM_DSDT
bool "Include Custom DSDT"
depends on ACPI_INTERPRETER && !STANDALONE
--
Stelian Pop <[email protected]>


2005-02-10 19:39:45

by Bruno Ducrot

[permalink] [raw]
Subject: Re: [ACPI] [PATCH, new ACPI driver] new sony_acpi driver

On Thu, Feb 10, 2005 at 05:18:10PM +0100, Stelian Pop wrote:
> Hi,
>
> +ACPI Sony Notebook Control Driver (SNC) Readme
> +----------------------------------------------
> + Copyright (C) 2004 Stelian Pop <[email protected]>
> +
> +This mini-driver drives the ACPI SNC device present in the
> +ACPI BIOS of the Sony Vaio laptops.
> +
> +It gives access to some extra laptop functionalities. In
> +its current form, the only thing this driver does is letting
> +the user set or query the screen brightness.

Does those laptops support acpi_video?

--
Bruno Ducrot

-- Which is worse: ignorance or apathy?
-- Don't know. Don't care.

2005-02-11 09:15:35

by Stelian Pop

[permalink] [raw]
Subject: Re: [ACPI] [PATCH, new ACPI driver] new sony_acpi driver

On Thu, Feb 10, 2005 at 08:39:37PM +0100, Bruno Ducrot wrote:

> On Thu, Feb 10, 2005 at 05:18:10PM +0100, Stelian Pop wrote:
> > Hi,
> >
> > +ACPI Sony Notebook Control Driver (SNC) Readme
> > +----------------------------------------------
> > + Copyright (C) 2004 Stelian Pop <[email protected]>
> > +
> > +This mini-driver drives the ACPI SNC device present in the
> > +ACPI BIOS of the Sony Vaio laptops.
> > +
> > +It gives access to some extra laptop functionalities. In
> > +its current form, the only thing this driver does is letting
> > +the user set or query the screen brightness.
>
> Does those laptops support acpi_video?

No. I double checked for a few AML dumps I have here, and except
for _DOS/_DOD present in some of them no other acpi_video method is
supported.

Newer Vaio laptops may eventualy support acpi_video, but all
current (to the best of my knowledge) Vaios do support the SNC device.

Stelian.
--
Stelian Pop <[email protected]>

2005-02-11 10:31:34

by Jean Delvare

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

Hi Stelian, all,

> This driver has been submitted (almost unchanged) on lkml and
> on acpi-devel twice, first on July 21, 2004, then again on
> September 17, 2004. It has been quietly ignored.
>
> Privately I've had many positive feedbacks from users of this driver
> (and no negative feedback), including Linux distributions who wish
> to include it into their kernels. The reports are increasing in number,
> it would seem that newer Sony Vaios are more and more incompatible
> with sonypi and require sony_acpi to control the screen brightness.

I'm sorry I missed the first two announcements. I'll give a try to your
driver this evening, and report how it is working for me.

In the meantime, I'd have some comments on your patch:

> +static int debug = 0;

No need to initialize it to 0, the compiler does it for you (and more
efficiently at that).

> +module_param(debug, int, 0);
> +MODULE_PARM_DESC(debug,"set this to 1 (and RTFM) if you want to help the development of this driver");

Lack of space after comma, and line too long, split it please.

> +static struct acpi_driver sony_acpi_driver = {
> + name: ACPI_SNC_DRIVER_NAME,
> + class: ACPI_SNC_CLASS,
> + ids: ACPI_SNC_HID,
> + ops: {
> + add: sony_acpi_add,
> + remove: sony_acpi_remove,
> + },
> +};

As far as I know, you are supposed to use C99-style for structure
initialization:
.name = ACPI_SNC_DRIVER_NAME,
etc.

> + printk(KERN_WARNING "acpi_callreadfunc failed\n");

Please prepend the driver name before such messages (everywhere in the
driver). It's annoying when you see error messages in your logs and
don't know which driver caused them.

> +static int parse_buffer(const char __user *buffer, unsigned long count, int *val) {
> + char s[32];
> +
> + if (count > 31)
> + return -EINVAL;
> + if (copy_from_user(s, buffer, count))
> + return -EFAULT;
> + s[count] = 0;

= '\0' would look better IMHO.

> + if (sscanf(s, "%i", val) != 1)

Can't you use simple_strtoul instead? This would be more efficient, or
so I guess.

> --- /dev/null 2005-02-10 10:35:32.824183288 +0100
> +++ linux-2.6-stelian/Documentation/sony_acpi.txt 2005-01-31 17:00:09.000000000 +0100

Could be the time to create a Documentation/acpi directory and start
populating it. It's odd that such a large subsystem has no documentation
in the kernel tree.

> +You should start by trying the sonypi driver, which does
> +all this and many other things. But the sonypi driver does
> +not work on all sonypi laptops, whereas sony_acpi should
> +work everywhere.

s/all sonypi laptops/all Sony laptops/?

> +Loading the sony_acpi.ko module will create a /proc/acpi/sony/
> +directory populated with a couple of files (only one for the
> +moment).

s/sony_acpi\.ko/sony_acpi/
.ko is an implementation detail the user-space should not have to care
about.

> +For example:
> + # echo "1" > /proc/acpi/sony/brt

BTW, why not naming the file "brightness" instead? Most acpi files have
"long" names like that. At least people can easily figure out what the
files are for.

> +config ACPI_SONY
> + tristate "Sony Laptop Extras"
> + depends on X86
> + depends on ACPI_INTERPRETER
> + default m
> + ---help---
> + This mini-driver drives the ACPI SNC device present in the
> + ACPI BIOS of the Sony Vaio laptops.
> +
> + It gives access to some extra laptop functionalities. In
> + its current form, the only thing this driver does is letting
> + the user set or query the screen brightness.
> +
> + Read <Documentation/sony_acpi.txt> for more information.

I think I remember this should be <file:Documentation...> or something
similar.

Note that I have next to zero knowledge of the acpi internals so I
couldn't comment on that part.

Thanks,
--
Jean Delvare

2005-02-11 11:05:49

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

On Thu, 10 Feb 2005 17:18:10 +0100, Stelian Pop <[email protected]> wrote:
> +static int sony_acpi_write_brt(struct file *file, const char __user *buffer, unsigned long count, void *data)
> +{
> + struct sony_snc *snc = (struct sony_snc *) data;

The casts for void pointer conversiosn are spurious. Please drop them.

Pekka

2005-02-11 11:15:58

by Stelian Pop

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

On Fri, Feb 11, 2005 at 11:30:35AM +0100, Jean Delvare wrote:

> Hi Stelian, all,
>
> >This driver has been submitted (almost unchanged) on lkml and
> >on acpi-devel twice, first on July 21, 2004, then again on
> >September 17, 2004. It has been quietly ignored.
> >
> >Privately I've had many positive feedbacks from users of this driver
> >(and no negative feedback), including Linux distributions who wish
> >to include it into their kernels. The reports are increasing in number,
> >it would seem that newer Sony Vaios are more and more incompatible
> >with sonypi and require sony_acpi to control the screen brightness.
>
> I'm sorry I missed the first two announcements. I'll give a try to your
> driver this evening, and report how it is working for me.

thanks.

> In the meantime, I'd have some comments on your patch:
>
> >+static int debug = 0;

> No need to initialize it to 0, the compiler does it for you (and more
> efficiently at that).

sure.
>
> >+module_param(debug, int, 0);
> >+MODULE_PARM_DESC(debug,"set this to 1 (and RTFM) if you want to help the
> >development of this driver");
>
> Lack of space after comma, and line too long, split it please.

done.

> >+static struct acpi_driver sony_acpi_driver = {
> >+ name: ACPI_SNC_DRIVER_NAME,
> >+ class: ACPI_SNC_CLASS,
> >+ ids: ACPI_SNC_HID,
> >+ ops: {
> >+ add: sony_acpi_add,
> >+ remove: sony_acpi_remove,
> >+ },
> >+};
>
> As far as I know, you are supposed to use C99-style for structure
> initialization:
> .name = ACPI_SNC_DRIVER_NAME,
> etc.

of course.

> >+ printk(KERN_WARNING "acpi_callreadfunc failed\n");
>
> Please prepend the driver name before such messages (everywhere in the
> driver). It's annoying when you see error messages in your logs and
> don't know which driver caused them.

done.

> >+static int parse_buffer(const char __user *buffer, unsigned long count,
> >int *val) {
> >+ char s[32];
> >+
> >+ if (count > 31)
> >+ return -EINVAL;
> >+ if (copy_from_user(s, buffer, count))
> >+ return -EFAULT;
> >+ s[count] = 0;
>
> = '\0' would look better IMHO.
>
> >+ if (sscanf(s, "%i", val) != 1)
>
> Can't you use simple_strtoul instead? This would be more efficient, or
> so I guess.

Indeed, sscanf calls simple_strtoul.

>
> >--- /dev/null 2005-02-10 10:35:32.824183288 +0100
> >+++ linux-2.6-stelian/Documentation/sony_acpi.txt 2005-01-31
> >17:00:09.000000000 +0100
>
> Could be the time to create a Documentation/acpi directory and start
> populating it. It's odd that such a large subsystem has no documentation
> in the kernel tree.

I agree. Let's see if the acpi people follow :)

> >+You should start by trying the sonypi driver, which does
> >+all this and many other things. But the sonypi driver does
> >+not work on all sonypi laptops, whereas sony_acpi should
> >+work everywhere.
>
> s/all sonypi laptops/all Sony laptops/?

doh :)

> >+Loading the sony_acpi.ko module will create a /proc/acpi/sony/
> >+directory populated with a couple of files (only one for the
> >+moment).
>
> s/sony_acpi\.ko/sony_acpi/
> .ko is an implementation detail the user-space should not have to care
> about.

yup.

> >+For example:
> >+ # echo "1" > /proc/acpi/sony/brt
>
> BTW, why not naming the file "brightness" instead? Most acpi files have
> "long" names like that. At least people can easily figure out what the
> files are for.

Agreed.

> >+config ACPI_SONY
> >+ tristate "Sony Laptop Extras"
> >+ depends on X86
> >+ depends on ACPI_INTERPRETER
> >+ default m
> >+ ---help---
> >+ This mini-driver drives the ACPI SNC device present in the
> >+ ACPI BIOS of the Sony Vaio laptops.
> >+
> >+ It gives access to some extra laptop functionalities. In
> >+ its current form, the only thing this driver does is letting
> >+ the user set or query the screen brightness.
> >+
> >+ Read <Documentation/sony_acpi.txt> for more information.
>
> I think I remember this should be <file:Documentation...> or something
> similar.

correct.

> Note that I have next to zero knowledge of the acpi internals so I
> couldn't comment on that part.

Hey, that's not so bad :)

An updated version will follow.

Stelian.
--
Stelian Pop <[email protected]>

2005-02-11 11:36:27

by Stelian Pop

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

Based on feedback from Jean Delvare and Pekka Enberg, here is an
updated version.

Changes from the previous version include:
- do not initialize to 0 a static variable
- trim to 80 columns
- do not do spurious void * casts
- use c99 style struct initialization
- use simple_strtoul instead of sscanf
- move documentation to new directory Documentation/acpi
- name the file 'brightness' instead of 'brt'

Signed-of-by: Stelian Pop <[email protected]>

drivers/acpi/sony_acpi.c | 471 +++++++++++++++++++++
Documentation/acpi/sony_acpi.txt | 81 +++
drivers/acpi/Kconfig | 15
drivers/acpi/Makefile | 1

4 files changed, 568 insertions(+)

--- /dev/null 2005-02-11 10:21:21.043997848 +0100
+++ linux-2.6-stelian/drivers/acpi/sony_acpi.c 2005-02-11 12:24:41.000000000 +0100
@@ -0,0 +1,471 @@
+/*
+ * ACPI Sony Notebook Control Driver (SNC)
+ *
+ * Copyright (C) 2004 Stelian Pop <[email protected]>
+ *
+ * Parts of this driver inspired from asus_acpi.c, which is
+ * Copyright (C) 2002, 2003, 2004 Julien Lerouge, Karol Kozimor
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_bus.h>
+#include <asm/uaccess.h>
+
+#define ACPI_SNC_CLASS "sony"
+#define ACPI_SNC_HID "SNY5001"
+#define ACPI_SNC_DRIVER_NAME "ACPI Sony Notebook Control Driver v0.1"
+
+#define LOG_PFX KERN_WARNING "sony_acpi: "
+
+MODULE_AUTHOR("Stelian Pop");
+MODULE_DESCRIPTION(ACPI_SNC_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
+ "the development of this driver");
+
+static int sony_acpi_add (struct acpi_device *device);
+static int sony_acpi_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver sony_acpi_driver = {
+ .name = ACPI_SNC_DRIVER_NAME,
+ .class = ACPI_SNC_CLASS,
+ .ids = ACPI_SNC_HID,
+ .ops = {
+ .add = sony_acpi_add,
+ .remove = sony_acpi_remove,
+ },
+};
+
+struct sony_snc {
+ acpi_handle handle;
+ int brt; /* brightness */
+ struct proc_dir_entry *proc_brt;
+ int cmi; /* ??? ? */
+ struct proc_dir_entry *proc_cmi;
+ int csxb; /* ??? */
+ struct proc_dir_entry *proc_csxb;
+ int ctr; /* contrast ? */
+ struct proc_dir_entry *proc_ctr;
+ int pbr; /* ??? */
+ struct proc_dir_entry *proc_pbr;
+};
+
+static struct proc_dir_entry *sony_acpi_dir;
+
+static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
+{
+ struct acpi_buffer output;
+ union acpi_object out_obj;
+ acpi_status status;
+
+ output.length = sizeof(out_obj);
+ output.pointer = &out_obj;
+
+ status = acpi_evaluate_object(handle, name, NULL, &output);
+ if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
+ *result = out_obj.integer.value;
+ return 0;
+ }
+
+ printk(LOG_PFX "acpi_callreadfunc failed\n");
+
+ return -1;
+}
+
+static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
+ int *result)
+{
+ struct acpi_object_list params;
+ union acpi_object in_obj;
+ struct acpi_buffer output;
+ union acpi_object out_obj;
+ acpi_status status;
+
+ params.count = 1;
+ params.pointer = &in_obj;
+ in_obj.type = ACPI_TYPE_INTEGER;
+ in_obj.integer.value = value;
+
+ output.length = sizeof(out_obj);
+ output.pointer = &out_obj;
+
+ status = acpi_evaluate_object(handle, name, &params, &output);
+ if (status == AE_OK) {
+ if (result != NULL) {
+ if (out_obj.type != ACPI_TYPE_INTEGER) {
+ printk(LOG_PFX "acpi_evaluate_object bad "
+ "return type\n");
+ return -1;
+ }
+ *result = out_obj.integer.value;
+ }
+ return 0;
+ }
+
+ printk(LOG_PFX "acpi_evaluate_object failed\n");
+
+ return -1;
+}
+
+static int parse_buffer(const char __user *buffer, unsigned long count,
+ int *val) {
+ char s[32];
+ int ret;
+
+ if (count > 31)
+ return -EINVAL;
+ if (copy_from_user(s, buffer, count))
+ return -EFAULT;
+ s[count] = '\0';
+ ret = simple_strtoul(s, NULL, 10);
+ *val = ret;
+ return 0;
+}
+
+static int sony_acpi_write_brt(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct sony_snc *snc = data;
+ int result;
+
+ if ((result = parse_buffer(buffer, count, &snc->brt)) < 0)
+ return result;
+
+ /* Accept only values between 1 and 8, or VGN-T1XP will hung */
+ if (snc->brt < 1 || snc->brt > 8)
+ return -EINVAL;
+
+ if (acpi_callsetfunc(snc->handle, "SBRT", snc->brt, NULL) < 0)
+ return -EIO;
+
+ return count;
+}
+
+static int sony_acpi_read_brt(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct sony_snc *snc = data;
+
+ if (acpi_callgetfunc(snc->handle, "GBRT", &snc->brt) < 0)
+ return -EIO;
+
+ return sprintf(page, "%d\n", snc->brt);
+}
+
+static int sony_acpi_write_cmi(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct sony_snc *snc = data;
+ int result;
+
+ if ((result = parse_buffer(buffer, count, &snc->cmi)) < 0)
+ return result;
+
+ if (acpi_callsetfunc(snc->handle, "SCMI", snc->cmi, &snc->cmi) < 0)
+ return -EIO;
+
+ return count;
+}
+
+static int sony_acpi_read_cmi(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct sony_snc *snc = data;
+ return sprintf(page, "%d\n", snc->cmi);
+}
+
+static int sony_acpi_write_csxb(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct sony_snc *snc = data;
+ int result;
+
+ if ((result = parse_buffer(buffer, count, &snc->csxb)) < 0)
+ return result;
+
+ if (acpi_callsetfunc(snc->handle, "CSXB", snc->csxb, &snc->csxb) < 0)
+ return -EIO;
+
+ return count;
+}
+
+static int sony_acpi_read_csxb(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct sony_snc *snc = data;
+ return sprintf(page, "%d\n", snc->csxb);
+}
+
+static int sony_acpi_write_ctr(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct sony_snc *snc = data;
+ int result;
+
+ if ((result = parse_buffer(buffer, count, &snc->ctr)) < 0)
+ return result;
+
+ if (acpi_callsetfunc(snc->handle, "SCTR", snc->ctr, NULL) < 0)
+ return -EIO;
+
+ return count;
+}
+
+static int sony_acpi_read_ctr(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct sony_snc *snc = data;
+
+ if (acpi_callgetfunc(snc->handle, "GCTR", &snc->ctr) < 0)
+ return -EIO;
+
+ return sprintf(page, "%d\n", snc->ctr);
+}
+
+static int sony_acpi_write_pbr(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct sony_snc *snc = data;
+ int result;
+
+ if ((result = parse_buffer(buffer, count, &snc->pbr)) < 0)
+ return result;
+
+ if (acpi_callsetfunc(snc->handle, "SPBR", snc->pbr, NULL) < 0)
+ return -EIO;
+
+ return count;
+}
+
+static int sony_acpi_read_pbr(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct sony_snc *snc = data;
+
+ if (acpi_callgetfunc(snc->handle, "GPBR", &snc->pbr) < 0)
+ return -EIO;
+
+ return sprintf(page, "%d\n", snc->pbr);
+}
+
+static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+ /* struct sony_snc *snc = data; */
+
+ printk(LOG_PFX "sony_snc_notify\n");
+}
+
+static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
+ void *context, void **return_value)
+{
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *operand;
+
+ node = (struct acpi_namespace_node *) handle;
+ operand = (union acpi_operand_object *) node->object;
+
+ printk(LOG_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
+ (u32) operand->method.param_count);
+
+ return AE_OK;
+}
+
+static int __init sony_acpi_add(struct acpi_device *device)
+{
+ acpi_status status = AE_OK;
+ struct sony_snc *snc = NULL;
+ int result;
+
+ snc = kmalloc(sizeof(struct sony_snc), GFP_KERNEL);
+ if (!snc)
+ return -ENOMEM;
+ memset(snc, 0, sizeof(struct sony_snc));
+
+ snc->handle = device->handle;
+
+ acpi_driver_data(device) = snc;
+ acpi_device_dir(device) = sony_acpi_dir;
+
+ if (debug) {
+ status = acpi_walk_namespace(ACPI_TYPE_METHOD, snc->handle, 1,
+ sony_walk_callback, NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ printk(LOG_PFX "unable to walk acpi resources\n");
+ }
+ }
+
+ snc->proc_brt = create_proc_entry("brightness", 0600,
+ acpi_device_dir(device));
+ if (!snc->proc_brt) {
+ printk(LOG_PFX "unable to create proc entry\n");
+ result = -EIO;
+ goto outbrt;
+ }
+
+ snc->proc_brt->write_proc = sony_acpi_write_brt;
+ snc->proc_brt->read_proc = sony_acpi_read_brt;
+ snc->proc_brt->data = acpi_driver_data(device);
+ snc->proc_brt->owner = THIS_MODULE;
+
+ if (debug) {
+ snc->proc_cmi = create_proc_entry("cmi", 0600,
+ acpi_device_dir(device));
+ if (!snc->proc_cmi) {
+ printk(LOG_PFX "unable to create proc entry\n");
+ result = -EIO;
+ goto outcmi;
+ }
+
+ snc->proc_cmi->write_proc = sony_acpi_write_cmi;
+ snc->proc_cmi->read_proc = sony_acpi_read_cmi;
+ snc->proc_cmi->data = acpi_driver_data(device);
+ snc->proc_cmi->owner = THIS_MODULE;
+
+ snc->proc_csxb = create_proc_entry("csxb", 0600,
+ acpi_device_dir(device));
+ if (!snc->proc_csxb) {
+ printk(LOG_PFX "unable to create proc entry\n");
+ result = -EIO;
+ goto outcsxb;
+ }
+
+ snc->proc_csxb->write_proc = sony_acpi_write_csxb;
+ snc->proc_csxb->read_proc = sony_acpi_read_csxb;
+ snc->proc_csxb->data = acpi_driver_data(device);
+ snc->proc_csxb->owner = THIS_MODULE;
+
+ snc->proc_ctr = create_proc_entry("ctr", 0600,
+ acpi_device_dir(device));
+ if (!snc->proc_ctr) {
+ printk(LOG_PFX "unable to create proc entry\n");
+ result = -EIO;
+ goto outctr;
+ }
+
+ snc->proc_ctr->write_proc = sony_acpi_write_ctr;
+ snc->proc_ctr->read_proc = sony_acpi_read_ctr;
+ snc->proc_ctr->data = acpi_driver_data(device);
+ snc->proc_ctr->owner = THIS_MODULE;
+
+ snc->proc_pbr = create_proc_entry("pbr", 0600,
+ acpi_device_dir(device));
+ if (!snc->proc_pbr) {
+ printk(LOG_PFX "unable to create proc entry\n");
+ result = -EIO;
+ goto outpbr;
+ }
+
+ snc->proc_pbr->write_proc = sony_acpi_write_pbr;
+ snc->proc_pbr->read_proc = sony_acpi_read_pbr;
+ snc->proc_pbr->data = acpi_driver_data(device);
+ snc->proc_pbr->owner = THIS_MODULE;
+
+ status = acpi_install_notify_handler(snc->handle,
+ ACPI_DEVICE_NOTIFY, sony_acpi_notify, snc);
+ if (ACPI_FAILURE(status)) {
+ printk(LOG_PFX "unable to install notify "
+ "handler\n");
+ result = -ENODEV;
+ goto outnotify;
+ }
+ }
+
+ printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully installed\n");
+
+ return 0;
+
+outnotify:
+ remove_proc_entry("pbr", acpi_device_dir(device));
+outpbr:
+ remove_proc_entry("ctr", acpi_device_dir(device));
+outctr:
+ remove_proc_entry("csxb", acpi_device_dir(device));
+outcsxb:
+ remove_proc_entry("cmi", acpi_device_dir(device));
+outcmi:
+ remove_proc_entry("brightness", acpi_device_dir(device));
+outbrt:
+ kfree(snc);
+ return result;
+}
+
+
+static int __exit sony_acpi_remove(struct acpi_device *device, int type)
+{
+ acpi_status status = AE_OK;
+ struct sony_snc *snc = NULL;
+
+ snc = (struct sony_snc *) acpi_driver_data(device);
+
+ if (debug) {
+ status = acpi_remove_notify_handler(snc->handle,
+ ACPI_DEVICE_NOTIFY,
+ sony_acpi_notify);
+ if (ACPI_FAILURE(status))
+ printk(LOG_PFX "unable to remove notify handler\n");
+
+ remove_proc_entry("pbr", acpi_device_dir(device));
+ remove_proc_entry("ctr", acpi_device_dir(device));
+ remove_proc_entry("csxb", acpi_device_dir(device));
+ remove_proc_entry("cmi", acpi_device_dir(device));
+ }
+ remove_proc_entry("brightness", acpi_device_dir(device));
+
+ kfree(snc);
+
+ printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully removed\n");
+
+ return 0;
+}
+
+static int __init sony_acpi_init(void)
+{
+ int result;
+
+ sony_acpi_dir = proc_mkdir("sony", acpi_root_dir);
+ if (!sony_acpi_dir) {
+ printk(LOG_PFX "unable to create /proc entry\n");
+ return -ENODEV;
+ }
+ sony_acpi_dir->owner = THIS_MODULE;
+
+ result = acpi_bus_register_driver(&sony_acpi_driver);
+ if (result < 0) {
+ remove_proc_entry("sony", acpi_root_dir);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+
+static void __exit sony_acpi_exit(void)
+{
+ acpi_bus_unregister_driver(&sony_acpi_driver);
+ remove_proc_entry("sony", acpi_root_dir);
+}
+
+module_init(sony_acpi_init);
+module_exit(sony_acpi_exit);
--- /dev/null 2005-02-11 10:21:21.043997848 +0100
+++ linux-2.6-stelian/Documentation/acpi/sony_acpi.txt 2005-02-11 12:08:58.000000000 +0100
@@ -0,0 +1,81 @@
+ACPI Sony Notebook Control Driver (SNC) Readme
+----------------------------------------------
+ Copyright (C) 2004 Stelian Pop <[email protected]>
+
+This mini-driver drives the ACPI SNC device present in the
+ACPI BIOS of the Sony Vaio laptops.
+
+It gives access to some extra laptop functionalities. In
+its current form, the only thing this driver does is letting
+the user set or query the screen brightness.
+
+You should start by trying the sonypi driver, which does
+all this and many other things. But the sonypi driver does
+not work on all Sony laptops, whereas sony_acpi should
+work everywhere.
+
+Usage:
+------
+
+Loading the sony_acpi module will create a /proc/acpi/sony/
+directory populated with a couple of files (only one for the
+moment).
+
+You then read/write integer values from/to those files by using
+standard UNIX tools.
+
+For example:
+ # echo "1" > /proc/acpi/sony/brightness
+sets the lowest screen brightness,
+ # echo "8" > /proc/acpi/sony/brightness
+sets the highest screen brightness,
+ # cat /proc/acpi/sony/brightness
+retrieves the current screen brightness.
+
+Development:
+------------
+
+If you want to help with the development of this driver (and
+you are not afraid of any side effects doing strange things with
+your ACPI BIOS could have on your laptop), load the driver and
+pass the option 'debug=1'.
+
+REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS.
+
+In your kernel logs you will find the list of all ACPI methods
+the SNC device has on your laptop. You can see the GBRT/SBRT methods
+used to get/set the brightness, but there are others.
+
+I HAVE NO IDEA WHAT THOSE METHODS DO.
+
+The sony_acpi driver creates, for some of those methods (the most
+current ones found on several Vaio models), an entry under
+/proc/acpi/sony/, just like the 'brightness' one.
+
+Your mission, should you accept it, is to try finding out what
+those entries are for, by reading/writing random values from/to those
+files and find out what is the impact on your laptop.
+
+You can also modify the driver source and add your extra methods
+and retest.
+
+Should you find anything interesting, please report it back to me,
+I will not disavow all knowledge of your actions :)
+
+Bugs/Limitations:
+-----------------
+
+* This driver is not based on official documentation from Sony
+ (because there is none), so there is no guarantee this driver
+ will work at all, or do the right thing. Although this hasn't
+ happened to me, this driver could do very bad things to your
+ laptop, including permanent damage.
+
+* The sony_acpi and sonypi drivers do not interact at all. In the
+ future, sonypi could use sony_acpi to do (part of) its business.
+
+* spicctrl, which is the userspace tool used to communicate with the
+ sonypi driver (through /dev/sonypi) does not try to use the
+ sony_acpi driver. In the future, spicctrl could try sonypi first,
+ and if it isn't present, try sony_acpi instead.
+
--- linux-2.6-linus/drivers/acpi/Kconfig 2005-01-31 16:55:23.000000000 +0100
+++ linux-2.6-stelian/drivers/acpi/Kconfig 2005-02-11 12:10:36.000000000 +0100
@@ -242,6 +242,21 @@
If you have a legacy free Toshiba laptop (such as the Libretto L1
series), say Y.

+config ACPI_SONY
+ tristate "Sony Laptop Extras"
+ depends on X86
+ depends on ACPI_INTERPRETER
+ default m
+ ---help---
+ This mini-driver drives the ACPI SNC device present in the
+ ACPI BIOS of the Sony Vaio laptops.
+
+ It gives access to some extra laptop functionalities. In
+ its current form, the only thing this driver does is letting
+ the user set or query the screen brightness.
+
+ Read <file:Documentation/acpi/sony_acpi.txt> for more information.
+
config ACPI_CUSTOM_DSDT
bool "Include Custom DSDT"
depends on ACPI_INTERPRETER && !STANDALONE
--- linux-2.6-linus/drivers/acpi/Makefile 2005-01-31 16:55:23.000000000 +0100
+++ linux-2.6-stelian/drivers/acpi/Makefile 2005-01-31 17:00:10.000000000 +0100
@@ -54,4 +54,5 @@
obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
obj-$(CONFIG_ACPI_IBM) += ibm_acpi.o
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
+obj-$(CONFIG_ACPI_SONY) += sony_acpi.o
obj-$(CONFIG_ACPI_BUS) += scan.o motherboard.o
--
Stelian Pop <[email protected]>

2005-02-11 12:02:22

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

On Fri, 11 Feb 2005 12:36:37 +0100, Stelian Pop <[email protected]> wrote:
> +static int __init sony_acpi_add(struct acpi_device *device)
> +{
> + acpi_status status = AE_OK;
> + struct sony_snc *snc = NULL;
> + int result;
> +
> + snc = kmalloc(sizeof(struct sony_snc), GFP_KERNEL);
> + if (!snc)
> + return -ENOMEM;
> + memset(snc, 0, sizeof(struct sony_snc));

Nitpick: use kcalloc() instead of kmalloc() and memset().

Pekka

2005-02-12 13:20:54

by Jean Delvare

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

> Based on feedback from Jean Delvare and Pekka Enberg, here is an
> updated version.

Works for me (Vaio PCG-GR214EP). Tested with 2.6.11-rc3-bk8.

I then enabled the debug mode. I couldn't find anything relevant WRT
what each additional file is supposed to do, but I still have noticed a
number of things you might be interested in.

ctr doesn't seem to affect the contrast for me. I also noticed that the
value seems to be stored on 8 bits. Higher bits are ignored (e.g. write
300, read 44). Default value is 64.

cmi behaves strangely. Its default value is 0. Whatever I write to it
(including 0), next read returns 131.

pbr seems to be stored on 4 bits. Higher bits are ignored (e.g. write
20, read 4). Default value is 0.

csxb is the dangerous one. Default 0. Writing 41 to it deadlocked my
system. Writing 42 changed pbr from 0 to 8 as a side effect. Each write
generates an error in the logs:
sony_acpi: acpi_evaluate_object failed

cmi and csxb are reset to 0 on sony_acpi module cycling. brightness, ctr
and pbr are preserved.

Hope that helps. Let me know if you want me to test specific things.
--
Jean Delvare

2005-02-14 10:06:01

by Stelian Pop

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

On Sat, Feb 12, 2005 at 02:21:03PM +0100, Jean Delvare wrote:

> > Based on feedback from Jean Delvare and Pekka Enberg, here is an
> > updated version.
>
> Works for me (Vaio PCG-GR214EP). Tested with 2.6.11-rc3-bk8.
>
> I then enabled the debug mode. I couldn't find anything relevant WRT
> what each additional file is supposed to do, but I still have noticed a
> number of things you might be interested in.
[...]

I have some interesting information from one user, who noticed that:

* pbr is the power-on brightness. It's the brightness that the
laptop uses at power-on time.

* cdp is the CD-ROM power. Writing 0 to cdp turns off the cdrom in
order to save a bit of power consumption.

I'll add this information in the docs.

Stelian.
--
Stelian Pop <[email protected]>

2005-02-14 10:53:13

by Matthew Garrett

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

Stelian Pop <[email protected]> wrote:

> Privately I've had many positive feedbacks from users of this driver
> (and no negative feedback), including Linux distributions who wish
> to include it into their kernels. The reports are increasing in number,
> it would seem that newer Sony Vaios are more and more incompatible
> with sonypi and require sony_acpi to control the screen brightness.

The sonypi driver seems to be necessary to catch Vaio hotkey events,
including the sleep button. I've checked a couple of DSDTs, and it seems
that the more recent Vaios are lacking the SPIC entries but still don't
have the sleep button defined. Is there any chance of this driver being
able to catch hotkey events?

Related to that, I have a nastyish hack which lets the sonypi driver
generate ACPI events whenever a hotkey is pressed. Despite not strictly
being ACPI events, this makes it much easier to integrate sonypi stuff
with general ACPI support. I'll send it if you're interested.

--
Matthew Garrett | [email protected]

2005-02-14 10:56:59

by Stelian Pop

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

On Mon, Feb 14, 2005 at 10:53:10AM +0000, Matthew Garrett wrote:

> Stelian Pop <[email protected]> wrote:
>
> > Privately I've had many positive feedbacks from users of this driver
> > (and no negative feedback), including Linux distributions who wish
> > to include it into their kernels. The reports are increasing in number,
> > it would seem that newer Sony Vaios are more and more incompatible
> > with sonypi and require sony_acpi to control the screen brightness.
>
> The sonypi driver seems to be necessary to catch Vaio hotkey events,
> including the sleep button. I've checked a couple of DSDTs, and it seems
> that the more recent Vaios are lacking the SPIC entries but still don't
> have the sleep button defined. Is there any chance of this driver being
> able to catch hotkey events?

I don't believe so.

> Related to that, I have a nastyish hack which lets the sonypi driver
> generate ACPI events whenever a hotkey is pressed. Despite not strictly
> being ACPI events, this makes it much easier to integrate sonypi stuff
> with general ACPI support. I'll send it if you're interested.

Wouldn't be more useful to make the ACPI hotkeys generate an
input event (like sonypi does) and integrate all this at the input
level ?

Stelian.
--
Stelian Pop <[email protected]>

2005-02-14 12:14:50

by Jean Delvare

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver


Hi Stelian, all,

On 2005-02-14, Stelian Pop wrote:
> I have some interesting information from one user, who noticed that:
>
> * pbr is the power-on brightness. It's the brightness that the
> laptop uses at power-on time.

Hey, that makes full sense. After playing around with the debug mode, I
noticed that the brightness was at the minimum level on next boot. I
thought it was related to the fact that I didn't have the opportunity
to stop my system cleanly, but it is indeed possible that I had written
0 to pbr before the crash. Will test this evening.

This reminds me of a related thing I had noticed some times ago but
couldn't explain back then. Brightness changes made under Linux using
spicctrl always seemed to be temporary (lost over reboot) while those
made under Windows on the same laptop were permanent (preserved over
reboot). Now I have to believe that spicctrl was only changing brt,
while the Windows tool was probably changing both brt and pbr?

So, what about either renaming pbr to brightness_default, or making the
brightness file dual-valued (several acpi files do that already)? And I
guess that the pbr value would need to be limited to the 0-8 range just
like is done for brt.

> * cdp is the CD-ROM power. Writing 0 to cdp turns off the cdrom in
> order to save a bit of power consumption.

I don't seem to have cdp on my system. Is this something I need to
manually activate in the driver, or does it simply mean that my laptop
doesn't support that feature?

Thanks,
--
Jean Delvare

2005-02-14 12:37:20

by Stelian Pop

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

On Mon, Feb 14, 2005 at 01:13:30PM +0100, Jean Delvare wrote:

>
> Hi Stelian, all,
>
> On 2005-02-14, Stelian Pop wrote:
> > I have some interesting information from one user, who noticed that:
> >
> > * pbr is the power-on brightness. It's the brightness that the
> > laptop uses at power-on time.
>
> Hey, that makes full sense. After playing around with the debug mode, I
> noticed that the brightness was at the minimum level on next boot. I
> thought it was related to the fact that I didn't have the opportunity
> to stop my system cleanly, but it is indeed possible that I had written
> 0 to pbr before the crash. Will test this evening.
>
> This reminds me of a related thing I had noticed some times ago but
> couldn't explain back then. Brightness changes made under Linux using
> spicctrl always seemed to be temporary (lost over reboot) while those
> made under Windows on the same laptop were permanent (preserved over
> reboot). Now I have to believe that spicctrl was only changing brt,
> while the Windows tool was probably changing both brt and pbr?
>
> So, what about either renaming pbr to brightness_default,

something like that, yes.

> or making the
> brightness file dual-valued (several acpi files do that already)?

I don't like much this.

> And I
> guess that the pbr value would need to be limited to the 0-8 range just
> like is done for brt.

Probably, I haven't tested it myself yet, so I don't know.

> > * cdp is the CD-ROM power. Writing 0 to cdp turns off the cdrom in
> > order to save a bit of power consumption.
>
> I don't seem to have cdp on my system. Is this something I need to
> manually activate in the driver, or does it simply mean that my laptop
> doesn't support that feature?

sony_acpi doesn't create this node. But if it is supported on your
system you should see 'method: name: GCDP' and 'method: name: SCDP'
in the logs because sony_acpi does enumerate all the methods it
finds for the snc device.

If you see it you can create the cdp entry with a bit of copy&paste
in the driver code...

Stelian.
--
Stelian Pop <[email protected]>

2005-02-14 18:44:20

by Jean Delvare

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

Hi all,

> > > * pbr is the power-on brightness. It's the brightness that the
> > > laptop uses at power-on time.
> >
> > Will test this evening.

I can confirm, that works for me too.

> > > * cdp is the CD-ROM power. Writing 0 to cdp turns off the cdrom in
> > > order to save a bit of power consumption.
> >
> > I don't seem to have cdp on my system. Is this something I need to
> > manually activate in the driver, or does it simply mean that my
> > laptop doesn't support that feature?
>
> sony_acpi doesn't create this node. But if it is supported on your
> system you should see 'method: name: GCDP' and 'method: name: SCDP'
> in the logs because sony_acpi does enumerate all the methods it
> finds for the snc device.

I don't have this one. The logs say:

sony_acpi: method: name: GPID, args 0
sony_acpi: method: name: GBRT, args 0
sony_acpi: method: name: SBRT, args 1
sony_acpi: method: name: GPBR, args 0
sony_acpi: method: name: SPBR, args 1
sony_acpi: method: name: GCTR, args 0
sony_acpi: method: name: SCTR, args 1
sony_acpi: method: name: GPCR, args 0
sony_acpi: method: name: SPCR, args 1
sony_acpi: method: name: GCMI, args 1
sony_acpi: method: name: SCMI, args 1
sony_acpi: method: name: PWAK, args 0
sony_acpi: method: name: PWRN, args 0
sony_acpi: method: name: CSXB, args 1

So, let alone the ones the driver already exposes when loaded with
debug=1, I have:
GPID, GPCR/SPCR, PWAK and PWRN.

A few random comments:
* GPID could be "get product id"?
* I'll give a try to GPCR/SPCR, seems to be another get/set pair.
* Isn't is strange that GCMI takes one argument?
* CSXB is obviously not part of a standard get/set pair, which might
(somewhat) explain why it crashed my system the other day.

I'll report if I can find more.

Thanks,
--
Jean Delvare

2005-02-14 20:31:42

by Vojtech Pavlik

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

On Mon, Feb 14, 2005 at 11:58:37AM +0100, Stelian Pop wrote:

> > Stelian Pop <[email protected]> wrote:
> >
> > > Privately I've had many positive feedbacks from users of this driver
> > > (and no negative feedback), including Linux distributions who wish
> > > to include it into their kernels. The reports are increasing in number,
> > > it would seem that newer Sony Vaios are more and more incompatible
> > > with sonypi and require sony_acpi to control the screen brightness.
> >
> > The sonypi driver seems to be necessary to catch Vaio hotkey events,
> > including the sleep button. I've checked a couple of DSDTs, and it seems
> > that the more recent Vaios are lacking the SPIC entries but still don't
> > have the sleep button defined. Is there any chance of this driver being
> > able to catch hotkey events?
>
> I don't believe so.
>
> > Related to that, I have a nastyish hack which lets the sonypi driver
> > generate ACPI events whenever a hotkey is pressed. Despite not strictly
> > being ACPI events, this makes it much easier to integrate sonypi stuff
> > with general ACPI support. I'll send it if you're interested.
>
> Wouldn't be more useful to make the ACPI hotkeys generate an
> input event (like sonypi does) and integrate all this at the input
> level ?

Yes, I'd like to see that. The other possible way is have the input
layer generate ACPI events for power-related keys.

--
Vojtech Pavlik
SuSE Labs, SuSE CR

2005-02-15 15:31:15

by Brown, Len

[permalink] [raw]
Subject: Re: [ACPI] [PATCH, new ACPI driver] new sony_acpi driver

On Thu, 2005-02-10 at 11:18, Stelian Pop wrote:,

>
> PS: I am also going to submit a bugzilla RFE for the acpi people,
> I have been told they are more receptive to that.

I guess that refers to me:-)

E-mail is fine, but the unfortunate reality is that due to simple
volume, it is lossy. The reason we like bugzilla is that it never
forgets:-)

So thanks for taking the extra time to file a bug report
http://bugzilla.kernel.org/show_bug.cgi?id=4193

If it turns out that there is no way a generic solution
can handle the SNC Sony laptops, then I agree that a
platform specific wart is the only way to go. But
it would be best if we can make the exotic Sony/SNC
look more generic to the user so that the user
(and the distro supporting them) don't need to learn
special things to handle this system.

thanks,
-Len


2005-02-15 15:38:00

by Stelian Pop

[permalink] [raw]
Subject: Re: [ACPI] [PATCH, new ACPI driver] new sony_acpi driver

On Tue, Feb 15, 2005 at 10:30:49AM -0500, Len Brown wrote:

> On Thu, 2005-02-10 at 11:18, Stelian Pop wrote:,
>
> >
> > PS: I am also going to submit a bugzilla RFE for the acpi people,
> > I have been told they are more receptive to that.
>
> I guess that refers to me:-)

Hey, you *are* more receptive to bugzilla then !

> E-mail is fine, but the unfortunate reality is that due to simple
> volume, it is lossy. The reason we like bugzilla is that it never
> forgets:-)
>
> So thanks for taking the extra time to file a bug report
> http://bugzilla.kernel.org/show_bug.cgi?id=4193
>
> If it turns out that there is no way a generic solution
> can handle the SNC Sony laptops, then I agree that a

Note: this is not a subset of the Sony laptops, we are talking
about all the Sony laptops here.

> platform specific wart is the only way to go. But
> it would be best if we can make the exotic Sony/SNC
> look more generic to the user so that the user
> (and the distro supporting them) don't need to learn
> special things to handle this system.

I agree, but unfortunately I don't think it's possible to handle
them in a generic way. However, my understanding of the ACPI layer
is limited, so I very well be wrong.

I attached two DSDT in bugzilla, I have a few more if you want them.

Stelian.
--
Stelian Pop <[email protected]>

2005-02-15 16:21:21

by Romano Giannetti

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

On Mon, Feb 14, 2005 at 09:32:11PM +0100, Vojtech Pavlik wrote:
>
> Yes, I'd like to see that. The other possible way is have the input
> layer generate ACPI events for power-related keys.
>

I beg your pardon, but I have a very strange problem with ACPI event on a
Sony laptop. Probably it's completely unraleted, but if you have time to
have a look, it is on bugzilla too:

http://bugme.osdl.org/show_bug.cgi?id=4124

Thanks!
Romano

--
Romano Giannetti - Univ. Pontificia Comillas (Madrid, Spain)
Electronic Engineer - phone +34 915 422 800 ext 2416 fax +34 915 596 569

2005-02-16 14:40:27

by Stelian Pop

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

On Tue, Feb 15, 2005 at 05:14:12PM +0100, Romano Giannetti wrote:

> On Mon, Feb 14, 2005 at 09:32:11PM +0100, Vojtech Pavlik wrote:
> >
> > Yes, I'd like to see that. The other possible way is have the input
> > layer generate ACPI events for power-related keys.
> >
>
> I beg your pardon, but I have a very strange problem with ACPI event on a
> Sony laptop. Probably it's completely unraleted, but if you have time to
> have a look, it is on bugzilla too:
>
> http://bugme.osdl.org/show_bug.cgi?id=4124

Strange indeed.

First thing to test is to disable sonypi (either rebuild a kernel
without it or rename the module so it will not get loaded again),
then reboot and see if you still have problems.

If you do, the problem is ACPI/input related.

If you don't, the strangeness comes from some interraction with
sonypi.

Stelian.
--
Stelian Pop <[email protected]>

2005-02-16 15:41:26

by Romano Giannetti

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

On Wed, Feb 16, 2005 at 03:41:59PM +0100, Stelian Pop wrote:
> On Tue, Feb 15, 2005 at 05:14:12PM +0100, Romano Giannetti wrote:

> > http://bugme.osdl.org/show_bug.cgi?id=4124
>
> Strange indeed.
>
> First thing to test is to disable sonypi (either rebuild a kernel
> without it or rename the module so it will not get loaded again),
> then reboot and see if you still have problems.
>

I have it compiled in, but it is not loaded: on a freshly booted kernel,
dmesg | grep sonypi results in nothing, and if I try to load it manually:

# modprobe sonypi
FATAL: Error inserting sonypi (/lib/modules/2.6.11-rc1/kernel/drivers/char/sonypi.ko): No such device

and in syslog:

sonypi: request_region failed

which is correct, because this laptop doesn't have a sonypi devive, it is
a PCG-FX701. I will nuke the module and try to reboot, but I do not think it
will make any difference.

> If you do, the problem is ACPI/input related.
>
> If you don't, the strangeness comes from some interraction with
> sonypi.

It's happened between 2.6.7 and 2.6.9; the only differences I can see in the
.config are the following:

+CONFIG_ACPI_BLACKLIST_YEAR=0
+CONFIG_SERIO_RAW=m

... but I do not think they could make any difference. I will compile a
2.6.11-rc4 and try, but...

Anyway, I have put the dsdt from cat /proc/acpi/dsdt in

http://www.dea.icai.upco.es/romano/linux/mydsdt.bin

Thank you for the help.

Romano

--
Romano Giannetti - Univ. Pontificia Comillas (Madrid, Spain)
Electronic Engineer - phone +34 915 422 800 ext 2416 fax +34 915 596 569

2005-02-16 15:41:13

by Stelian Pop

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

On Mon, Feb 14, 2005 at 07:42:35PM +0100, Jean Delvare wrote:

> Hi all,
>
> > > > * pbr is the power-on brightness. It's the brightness that the
> > > > laptop uses at power-on time.
> > >
> > > Will test this evening.
>
> I can confirm, that works for me too.

All right, here is a third version of the driver, which adds the
'brightness_default' entry and rewrites a big part of the code in
a more extensible way.

Stelian.

--- linux-2.6-linus/drivers/acpi/sony_acpi.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6-stelian/drivers/acpi/sony_acpi.c 2005-02-16 16:28:36.000000000 +0100
@@ -0,0 +1,392 @@
+/*
+ * ACPI Sony Notebook Control Driver (SNC)
+ *
+ * Copyright (C) 2004-2005 Stelian Pop <[email protected]>
+ *
+ * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
+ * which are copyrighted by their respective authors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_bus.h>
+#include <asm/uaccess.h>
+
+#define ACPI_SNC_CLASS "sony"
+#define ACPI_SNC_HID "SNY5001"
+#define ACPI_SNC_DRIVER_NAME "ACPI Sony Notebook Control Driver v0.2"
+
+#define LOG_PFX KERN_WARNING "sony_acpi: "
+
+MODULE_AUTHOR("Stelian Pop");
+MODULE_DESCRIPTION(ACPI_SNC_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
+ "the development of this driver");
+
+static int sony_acpi_add (struct acpi_device *device);
+static int sony_acpi_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver sony_acpi_driver = {
+ .name = ACPI_SNC_DRIVER_NAME,
+ .class = ACPI_SNC_CLASS,
+ .ids = ACPI_SNC_HID,
+ .ops = {
+ .add = sony_acpi_add,
+ .remove = sony_acpi_remove,
+ },
+};
+
+static acpi_handle sony_acpi_handle;
+static struct proc_dir_entry *sony_acpi_dir;
+
+static struct sony_acpi_value {
+ char *name; /* name of the entry */
+ struct proc_dir_entry *proc; /* /proc entry */
+ char *acpiget;/* name of the ACPI get function */
+ char *acpiset;/* name of the ACPI get function */
+ int min; /* minimum allowed value or -1 */
+ int max; /* maximum allowed value or -1 */
+ int debug; /* active only in debug mode ? */
+} sony_acpi_values[] = {
+ {
+ .name = "brightness",
+ .acpiget = "GBRT",
+ .acpiset = "SBRT",
+ .min = 1,
+ .max = 8,
+ .debug = 0,
+ },
+ {
+ .name = "brightness_default",
+ .acpiget = "GPBR",
+ .acpiset = "SPBR",
+ .min = 1,
+ .max = 8,
+ .debug = 0,
+ },
+ {
+ .name = "cdpower",
+ .acpiget = "GCDP",
+ .acpiset = "SCDP",
+ .min = -1,
+ .max = -1,
+ .debug = 0,
+ },
+ {
+ .name = "PID",
+ .acpiget = "GPID",
+ .debug = 1,
+ },
+ {
+ .name = "CTR",
+ .acpiget = "GCTR",
+ .acpiset = "SCTR",
+ .min = -1,
+ .max = -1,
+ .debug = 1,
+ },
+ {
+ .name = "PCR",
+ .acpiget = "GPCR",
+ .acpiset = "SPCR",
+ .min = -1,
+ .max = -1,
+ .debug = 1,
+ },
+ {
+ .name = "CMI",
+ .acpiget = "GCMI",
+ .acpiset = "SCMI",
+ .min = -1,
+ .max = -1,
+ .debug = 1,
+ },
+ {
+ .name = NULL,
+ }
+};
+
+static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
+{
+ struct acpi_buffer output;
+ union acpi_object out_obj;
+ acpi_status status;
+
+ output.length = sizeof(out_obj);
+ output.pointer = &out_obj;
+
+ status = acpi_evaluate_object(handle, name, NULL, &output);
+ if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
+ *result = out_obj.integer.value;
+ return 0;
+ }
+
+ printk(LOG_PFX "acpi_callreadfunc failed\n");
+
+ return -1;
+}
+
+static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
+ int *result)
+{
+ struct acpi_object_list params;
+ union acpi_object in_obj;
+ struct acpi_buffer output;
+ union acpi_object out_obj;
+ acpi_status status;
+
+ params.count = 1;
+ params.pointer = &in_obj;
+ in_obj.type = ACPI_TYPE_INTEGER;
+ in_obj.integer.value = value;
+
+ output.length = sizeof(out_obj);
+ output.pointer = &out_obj;
+
+ status = acpi_evaluate_object(handle, name, &params, &output);
+ if (status == AE_OK) {
+ if (result != NULL) {
+ if (out_obj.type != ACPI_TYPE_INTEGER) {
+ printk(LOG_PFX "acpi_evaluate_object bad "
+ "return type\n");
+ return -1;
+ }
+ *result = out_obj.integer.value;
+ }
+ return 0;
+ }
+
+ printk(LOG_PFX "acpi_evaluate_object failed\n");
+
+ return -1;
+}
+
+static int parse_buffer(const char __user *buffer, unsigned long count,
+ int *val) {
+ char s[32];
+ int ret;
+
+ if (count > 31)
+ return -EINVAL;
+ if (copy_from_user(s, buffer, count))
+ return -EFAULT;
+ s[count] = '\0';
+ ret = simple_strtoul(s, NULL, 10);
+ *val = ret;
+ return 0;
+}
+
+static int sony_acpi_read(char* page, char** start, off_t off, int count,
+ int* eof, void *data)
+{
+ struct sony_acpi_value *item = data;
+ int value;
+
+ if (!item->acpiget)
+ return -EIO;
+
+ if (acpi_callgetfunc(sony_acpi_handle, item->acpiget, &value) < 0)
+ return -EIO;
+
+ return sprintf(page, "%d\n", value);
+}
+
+static int sony_acpi_write(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct sony_acpi_value *item = data;
+ int result;
+ int value;
+
+ if (!item->acpiset)
+ return -EIO;
+
+ if ((result = parse_buffer(buffer, count, &value)) < 0)
+ return result;
+
+ if (item->min != -1 && value < item->min)
+ return -EINVAL;
+ if (item->max != -1 && value > item->max)
+ return -EINVAL;
+
+ if (acpi_callsetfunc(sony_acpi_handle, item->acpiset, value, NULL) < 0)
+ return -EIO;
+
+ return count;
+}
+
+static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+ printk(LOG_PFX "sony_acpi_notify\n");
+}
+
+static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
+ void *context, void **return_value)
+{
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *operand;
+
+ node = (struct acpi_namespace_node *) handle;
+ operand = (union acpi_operand_object *) node->object;
+
+ printk(LOG_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
+ (u32) operand->method.param_count);
+
+ return AE_OK;
+}
+
+static int __init sony_acpi_add(struct acpi_device *device)
+{
+ acpi_status status;
+ int result;
+ struct sony_acpi_value *item;
+
+ sony_acpi_handle = device->handle;
+
+ acpi_driver_data(device) = NULL;
+ acpi_device_dir(device) = sony_acpi_dir;
+
+ if (debug) {
+ status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_acpi_handle,
+ 1, sony_walk_callback, NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ printk(LOG_PFX "unable to walk acpi resources\n");
+ result = -ENODEV;
+ goto outwalk;
+ }
+
+ status = acpi_install_notify_handler(sony_acpi_handle,
+ ACPI_DEVICE_NOTIFY,
+ sony_acpi_notify,
+ NULL);
+ if (ACPI_FAILURE(status)) {
+ printk(LOG_PFX "unable to install notify handler\n");
+ result = -ENODEV;
+ goto outnotify;
+ }
+ }
+
+ for (item = sony_acpi_values; item->name; ++item) {
+ acpi_handle handle;
+
+ if (!debug && item->debug)
+ continue;
+
+ if (item->acpiget &&
+ ACPI_FAILURE(acpi_get_handle(sony_acpi_handle,
+ item->acpiget, &handle)))
+ continue;
+
+ if (item->acpiset &&
+ ACPI_FAILURE(acpi_get_handle(sony_acpi_handle,
+ item->acpiset, &handle)))
+ continue;
+
+ item->proc = create_proc_entry(item->name, 0600,
+ acpi_device_dir(device));
+ if (!item->proc) {
+ printk(LOG_PFX "unable to create proc entry\n");
+ result = -EIO;
+ goto outproc;
+ }
+
+ item->proc->read_proc = sony_acpi_read;
+ item->proc->write_proc = sony_acpi_write;
+ item->proc->data = item;
+ item->proc->owner = THIS_MODULE;
+ }
+
+ printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully installed\n");
+
+ return 0;
+
+outproc:
+ if (debug) {
+ status = acpi_remove_notify_handler(sony_acpi_handle,
+ ACPI_DEVICE_NOTIFY,
+ sony_acpi_notify);
+ if (ACPI_FAILURE(status))
+ printk(LOG_PFX "unable to remove notify handler\n");
+ }
+outnotify:
+ for (item = sony_acpi_values; item->name; ++item)
+ if (item->proc)
+ remove_proc_entry(item->name, acpi_device_dir(device));
+outwalk:
+ return result;
+}
+
+
+static int __exit sony_acpi_remove(struct acpi_device *device, int type)
+{
+ acpi_status status;
+ struct sony_acpi_value *item;
+
+ if (debug) {
+ status = acpi_remove_notify_handler(sony_acpi_handle,
+ ACPI_DEVICE_NOTIFY,
+ sony_acpi_notify);
+ if (ACPI_FAILURE(status))
+ printk(LOG_PFX "unable to remove notify handler\n");
+ }
+
+ for (item = sony_acpi_values; item->name; ++item)
+ if (item->proc)
+ remove_proc_entry(item->name, acpi_device_dir(device));
+
+ printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully removed\n");
+
+ return 0;
+}
+
+static int __init sony_acpi_init(void)
+{
+ int result;
+
+ sony_acpi_dir = proc_mkdir("sony", acpi_root_dir);
+ if (!sony_acpi_dir) {
+ printk(LOG_PFX "unable to create /proc entry\n");
+ return -ENODEV;
+ }
+ sony_acpi_dir->owner = THIS_MODULE;
+
+ result = acpi_bus_register_driver(&sony_acpi_driver);
+ if (result < 0) {
+ remove_proc_entry("sony", acpi_root_dir);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+
+static void __exit sony_acpi_exit(void)
+{
+ acpi_bus_unregister_driver(&sony_acpi_driver);
+ remove_proc_entry("sony", acpi_root_dir);
+}
+
+module_init(sony_acpi_init);
+module_exit(sony_acpi_exit);
--- linux-2.6-linus/drivers/acpi/Kconfig 2005-01-31 16:55:23.000000000 +0100
+++ linux-2.6-stelian/drivers/acpi/Kconfig 2005-02-11 12:10:36.000000000 +0100
@@ -242,6 +242,21 @@
If you have a legacy free Toshiba laptop (such as the Libretto L1
series), say Y.

+config ACPI_SONY
+ tristate "Sony Laptop Extras"
+ depends on X86
+ depends on ACPI_INTERPRETER
+ default m
+ ---help---
+ This mini-driver drives the ACPI SNC device present in the
+ ACPI BIOS of the Sony Vaio laptops.
+
+ It gives access to some extra laptop functionalities. In
+ its current form, the only thing this driver does is letting
+ the user set or query the screen brightness.
+
+ Read <file:Documentation/acpi/sony_acpi.txt> for more information.
+
config ACPI_CUSTOM_DSDT
bool "Include Custom DSDT"
depends on ACPI_INTERPRETER && !STANDALONE
--- linux-2.6-linus/drivers/acpi/Makefile 2005-01-31 16:55:23.000000000 +0100
+++ linux-2.6-stelian/drivers/acpi/Makefile 2005-01-31 17:00:10.000000000 +0100
@@ -54,4 +54,5 @@
obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
obj-$(CONFIG_ACPI_IBM) += ibm_acpi.o
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
+obj-$(CONFIG_ACPI_SONY) += sony_acpi.o
obj-$(CONFIG_ACPI_BUS) += scan.o motherboard.o
--- linux-2.6-linus/Documentation/acpi/sony_acpi.txt 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6-stelian/Documentation/acpi/sony_acpi.txt 2005-02-16 16:36:24.000000000 +0100
@@ -0,0 +1,87 @@
+ACPI Sony Notebook Control Driver (SNC) Readme
+----------------------------------------------
+ Copyright (C) 2004- 2005 Stelian Pop <[email protected]>
+
+This mini-driver drives the ACPI SNC device present in the
+ACPI BIOS of the Sony Vaio laptops.
+
+It gives access to some extra laptop functionalities. In
+its current form, this driver is mainly useful for controlling the
+screen brightness, but it may do more in the future.
+
+You should probably start by trying the sonypi driver, and try
+sony_acpi only if sonypi doesn't work for you.
+
+Usage:
+------
+
+Loading the sony_acpi module will create a /proc/acpi/sony/
+directory populated with a couple of files.
+
+You then read/write integer values from/to those files by using
+standard UNIX tools.
+
+The files are:
+ brightness current screen brightness
+ brightness_default screen brightness which will be set
+ when the laptop will be rebooted
+ cdpower power on/off the internal CD drive
+
+Note that some files may be missing if they are not supported
+by your particular laptop model.
+
+Example usage:
+ # echo "1" > /proc/acpi/sony/brightness
+sets the lowest screen brightness,
+ # echo "8" > /proc/acpi/sony/brightness
+sets the highest screen brightness,
+ # cat /proc/acpi/sony/brightness
+retrieves the current screen brightness.
+
+Development:
+------------
+
+If you want to help with the development of this driver (and
+you are not afraid of any side effects doing strange things with
+your ACPI BIOS could have on your laptop), load the driver and
+pass the option 'debug=1'.
+
+REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS.
+
+In your kernel logs you will find the list of all ACPI methods
+the SNC device has on your laptop. You can see the GBRT/SBRT methods
+used to get/set the brightness, but there are others.
+
+I HAVE NO IDEA WHAT THOSE METHODS DO.
+
+The sony_acpi driver creates, for some of those methods (the most
+current ones found on several Vaio models), an entry under
+/proc/acpi/sony/, just like the 'brightness' one. You can create
+other entries corresponding to your own laptop methods by further
+editing the source (see the 'sony_acpi_values' table, and add a new
+structure to this table with your get/set method names).
+
+Your mission, should you accept it, is to try finding out what
+those entries are for, by reading/writing random values from/to those
+files and find out what is the impact on your laptop.
+
+Should you find anything interesting, please report it back to me,
+I will not disavow all knowledge of your actions :)
+
+Bugs/Limitations:
+-----------------
+
+* This driver is not based on official documentation from Sony
+ (because there is none), so there is no guarantee this driver
+ will work at all, or do the right thing. Although this hasn't
+ happened to me, this driver could do very bad things to your
+ laptop, including permanent damage.
+
+* The sony_acpi and sonypi drivers do not interact at all. In the
+ future, sonypi could use sony_acpi to do (part of) its business.
+
+* spicctrl, which is the userspace tool used to communicate with the
+ sonypi driver (through /dev/sonypi) does not try to use the
+ sony_acpi driver. In the future, spicctrl could try sonypi first,
+ and if it isn't present, try sony_acpi instead.
+
--
Stelian Pop <[email protected]>

2005-02-16 19:37:34

by Emmanuel Fleury

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

Stelian Pop wrote:
> Based on feedback from Jean Delvare and Pekka Enberg, here is an
> updated version.
>
> Changes from the previous version include:
> - do not initialize to 0 a static variable
> - trim to 80 columns
> - do not do spurious void * casts
> - use c99 style struct initialization
> - use simple_strtoul instead of sscanf
> - move documentation to new directory Documentation/acpi
> - name the file 'brightness' instead of 'brt'

Works straight for me (on a Vaio PCG-C1MZX), I can adjust brightness by
feeding the proc/ interface.

I have to try the debug mode.

Regards
--
Emmanuel Fleury

Computer Science Department, | Office: B1-201
Aalborg University, | Phone: +45 96 35 72 23
Fredriks Bajersvej 7E, | Fax: +45 98 15 98 89
9220 Aalborg East, Denmark | Email: [email protected]

2005-02-16 19:40:34

by Bruno Ducrot

[permalink] [raw]
Subject: Re: [ACPI] [PATCH, new ACPI driver] new sony_acpi driver

> > platform specific wart is the only way to go. But
> > it would be best if we can make the exotic Sony/SNC
> > look more generic to the user so that the user
> > (and the distro supporting them) don't need to learn
> > special things to handle this system.
>
> I agree, but unfortunately I don't think it's possible to handle
> them in a generic way. However, my understanding of the ACPI layer
> is limited, so I very well be wrong.
>
> I attached two DSDT in bugzilla, I have a few more if you want them.
>

I will (re)work some part of the acpi_video stuff in order to make it
more generic (its design is to separate a kind of 'video bus', then
'video devices' can attach. For now, its support only one kind of device,
the acpi one. See acpi_viedo.c). The original goal was to atleast
attach others acpi specific drivers (the toshiba at that time).

Problem though is my time which unfortunately is missing currently
(professional stuff, other free projects and even real life) and
I must admit I don't intend to work on acpi_video right now.

Cheers,

--
Bruno Ducrot

-- Which is worse: ignorance or apathy?
-- Don't know. Don't care.

2005-02-16 23:19:17

by Pavel Machek

[permalink] [raw]
Subject: Re: [ACPI] Re: [PATCH, new ACPI driver] new sony_acpi driver

Hi!

> > > Related to that, I have a nastyish hack which lets the sonypi driver
> > > generate ACPI events whenever a hotkey is pressed. Despite not strictly
> > > being ACPI events, this makes it much easier to integrate sonypi stuff
> > > with general ACPI support. I'll send it if you're interested.
> >
> > Wouldn't be more useful to make the ACPI hotkeys generate an
> > input event (like sonypi does) and integrate all this at the input
> > level ?
>
> Yes, I'd like to see that. The other possible way is have the input
> layer generate ACPI events for power-related keys.

No; ACPI events are ugly hack. They should die, die, die....

We should probably switch even stuff like acpi power button to input
layer, etc.

Pavel
--
People were complaining that M$ turns users into beta-testers...
...jr ghea gurz vagb qrirybcref, naq gurl frrz gb yvxr vg gung jnl!

2005-02-18 18:38:45

by Jean Delvare

[permalink] [raw]
Subject: Re: [PATCH, new ACPI driver] new sony_acpi driver

Hi Stellian,

> All right, here is a third version of the driver, which adds the
> 'brightness_default' entry and rewrites a big part of the code in
> a more extensible way.

Tested, works for me (debug mode not tested).

Thanks,
--
Jean Delvare