linux-acpi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Panasonic Let's Note laptop extras driver
@ 2008-07-02  9:00 Harald Welte
  2008-07-02 10:19 ` Matthew Garrett
                   ` (2 more replies)
  0 siblings, 3 replies; 28+ messages in thread
From: Harald Welte @ 2008-07-02  9:00 UTC (permalink / raw)
  To: linux-acpi; +Cc: Hiroshi Miura, hadi

Hi!

Ever since I bought my Panasonic Let's Note CF-R5 in September 2006
(like other well-known kernel hackers such as Jamal Selim and Stephen
Hemminger) I'm using the (apparently abandoned) out-of-tree pcc_acpi.c
driver.  Various changes in the kernel API's have required me to
merge/forward-port it from one kernel release to the other.

In more than three attempts (two of them in the last 6 months), I have
tried to contact the main author (Hiroshi Miura, see Cc) and asked if he
had plans to submit the driver for mainline inclusion or if he'd support
any such submission attempt by me.

The co-author David Bronaugh has promptly responded and is fine with
merging it mainline.

Though we are now in the unfortunate situation where the main author is
no longer reachable, I don't want to delay this even longer and want to
get this driver merged.  The original code was explicitly GPL licensed,
so there is no legal reason to 

For the time being, I'm also willing to be the maintainer of this
driver.

Any feedback is welcome.  Please excuse obvious mistakes.  While I have
a lot of kernel experience, I have not done any work on ACPI before.

There is one particular issue:

* in order to fully support the backlight interface and get rid of the
  clumsy separation of 'ac' (with power supply) and 'dc' (battery only) 
  brightness settings, the driver would need to receive a notification
  whenever the ACPI power supply status changes.  I know how to get to
  that notification in userspace, but in kernelspace I'm not so sure.
  Is there a notifier chain to which I can attach?

Thanks for your review,


Signed-off-by: Harald Welte <laforge@gnumonks.org>

diff --git a/MAINTAINERS b/MAINTAINERS
index 8f0ec46..05d449e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3108,6 +3108,11 @@ M:	olof@lixom.net
 L:	i2c@lm-sensors.org
 S:	Maintained
 
+PANASONIC LAPTOP ACPI EXTRAS DRIVER
+P:	Harald Welte
+M:	laforge@gnumonks.org
+S:	Maintained
+
 PARALLEL PORT SUPPORT
 L:	linux-parport@lists.infradead.org (subscribers-only)
 S:	Orphan
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index c52fca8..1da4bc8 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -259,6 +259,17 @@ config ACPI_ASUS
 	  NOTE: This driver is deprecated and will probably be removed soon,
 	  use asus-laptop instead.
 
+config ACPI_PANASONIC
+	tristate "Panasonic Laptop Extras"
+	depends on X86
+	select BACKLIGHT_CLASS_DEVICE
+	---help---
+	  This driver adds support for access to certain system settings
+	  on Panasonic Let's Note laptops.  
+
+	  If you have a Panasonic Let's note laptop (such as the R1(N variant),
+	  R2, R3, R5, T2, W2 and Y2 series), say Y.
+
 config ACPI_TOSHIBA
 	tristate "Toshiba Laptop Extras"
 	depends on X86
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 40b0fca..04c1b5d 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_ACPI_WMI)		+= wmi.o
 obj-$(CONFIG_ACPI_ASUS)		+= asus_acpi.o
 obj-$(CONFIG_ACPI_TOSHIBA)	+= toshiba_acpi.o
 obj-$(CONFIG_ACPI_HOTPLUG_MEMORY)	+= acpi_memhotplug.o
+obj-$(CONFIG_ACPI_PANASONIC)	+= pcc_acpi.o
 obj-$(CONFIG_ACPI_PROCFS_POWER)	+= cm_sbs.o
 obj-$(CONFIG_ACPI_SBS)		+= sbshc.o
 obj-$(CONFIG_ACPI_SBS)		+= sbs.o
diff --git a/drivers/acpi/pcc_acpi.c b/drivers/acpi/pcc_acpi.c
new file mode 100644
index 0000000..11ddc79
--- /dev/null
+++ b/drivers/acpi/pcc_acpi.c
@@ -0,0 +1,1062 @@
+/*
+ *  Panasonic HotKey and LCD brightness control driver
+ *  (C) 2004 Hiroshi Miura <miura@da-cha.org>
+ *  (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/
+ *  (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>
+ *  (C) 2004 David Bronaugh <dbronaugh>
+ *  (C) 2006-2008 Harald Welte <laforge@gnumonks.org>
+ *
+ *  derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  publicshed by the Free Software Foundation.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *---------------------------------------------------------------------------
+ *
+ * ChangeLog:
+ *
+ *      Jun.27, 2008	Harald Welte <laforge@gnumonks.org>
+ *      	-v0.92	merge with 2.6.26-rc6 input API changes
+ *      		remove broken <= 2.6.15 kernel support
+ *      		resolve all compiler warnings
+ *      		add support for backlight api
+ *      		major code restructuring
+ *      		various coding style fixes (checkpatch.pl)
+ *
+ * 	Dac.28, 2007	Harald Welte <laforge@gnumonks.org>
+ * 		-v0.91	merge with 2.6.24-rc6 ACPI changes
+ *
+ * 	Nov.04, 2006	Hiroshi Miura <miura@da-cha.org>
+ * 		-v0.9	remove warning about section reference.
+ * 			remove acpi_os_free
+ * 			add /proc/acpi/pcc/brightness interface for HAL access
+ * 			merge dbronaugh's enhancement
+ * 			Aug.17, 2004 David Bronaugh (dbronaugh)
+ *  				- Added screen brightness setting interface
+ *				  Thanks to FreeBSD crew (acpi_panasonic.c)
+ * 				  for the ideas I needed to accomplish it
+ *
+ *	May.29, 2006	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.8.4 follow to change keyinput structure
+ *			thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,
+ *			Jacob Bower <jacob.bower@ic.ac.uk> and
+ *			Hiroshi Yokota for providing solutions.
+ *
+ *	Oct.02, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.8.2	merge code of YOKOTA Hiroshi
+ *					<yokota@netlab.is.tsukuba.ac.jp>.
+ *			Add sticky key mode interface.
+ *			Refactoring acpi_pcc_generete_keyinput().
+ *
+ *	Sep.15, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.8	Generate key input event on input subsystem.
+ *			This is based on yet another driver written by
+ *							Ryuta Nakanishi.
+ *
+ *	Sep.10, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.7	Change proc interface functions using seq_file
+ *			facility as same as other ACPI drivers.
+ *
+ *	Aug.28, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.6.4 Fix a silly error with status checking
+ *
+ *	Aug.25, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.6.3 replace read_acpi_int by standard function
+ *							acpi_evaluate_integer
+ *			some clean up and make smart copyright notice.
+ *			fix return value of pcc_acpi_get_key()
+ *			fix checking return value of acpi_bus_register_driver()
+ *
+ *      Aug.22, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              -v0.6.2 Add check on ACPI data (num_sifr)
+ *                      Coding style cleanups, better error messages/handling
+ *			Fixed an off-by-one error in memory allocation
+ *
+ *      Aug.21, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              -v0.6.1 Fix a silly error with status checking
+ *
+ *      Aug.20, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              - v0.6  Correct brightness controls to reflect reality
+ *                      based on information gleaned by Hiroshi Miura
+ *                      and discussions with Hiroshi Miura
+ *
+ *	Aug.10, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.5  support LCD brightness control
+ *			based on the disclosed information by MEI.
+ *
+ *	Jul.25, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.4  first post version
+ *		        add function to retrive SIFR
+ *
+ *	Jul.24, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.3  get proper status of hotkey
+ *
+ *      Jul.22, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.2  add HotKey handler
+ *
+ *      Jul.17, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.1  start from toshiba_acpi driver written by John Belmonte
+ *
+ */
+
+#define ACPI_PCC_VERSION	"0.92"
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/backlight.h>
+#include <linux/ctype.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/input.h>
+
+
+#ifndef ACPI_HOTKEY_COMPONENT
+#define ACPI_HOTKEY_COMPONENT	0x10000000
+#endif
+
+#define _COMPONENT		ACPI_HOTKEY_COMPONENT
+ACPI_MODULE_NAME("pcc_acpi")
+
+MODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte");
+MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Lets Note laptops");
+MODULE_LICENSE("GPL");
+
+#define LOGPREFIX "pcc_acpi: "
+
+/* Define ACPI PATHs */
+/* Lets note hotkeys */
+#define METHOD_HKEY_QUERY	"HINF"
+#define METHOD_HKEY_SQTY	"SQTY"
+#define METHOD_HKEY_SINF	"SINF"
+#define METHOD_HKEY_SSET	"SSET"
+#define HKEY_NOTIFY		 0x80
+
+/* definitions for /proc/ interface */
+#define ACPI_PCC_DRIVER_NAME	"PCC Extra Driver"
+#define ACPI_PCC_DEVICE_NAME	"PCCExtra"
+#define ACPI_PCC_CLASS		"pcc"
+#define PROC_PCC		ACPI_PCC_CLASS
+
+#define ACPI_PCC_INPUT_PHYS	"panasonic/hkey0"
+
+/* This is transitional definition */
+#ifndef KEY_BATT
+# define KEY_BATT 227
+#endif
+
+#define PROC_STR_MAX_LEN  8
+
+/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
+   ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
+*/
+enum SINF_BITS { SINF_NUM_BATTERIES = 0,
+		 SINF_LCD_TYPE,
+		 SINF_AC_MAX_BRIGHT,
+		 SINF_AC_MIN_BRIGHT,
+		 SINF_AC_CUR_BRIGHT,
+		 SINF_DC_MAX_BRIGHT,
+		 SINF_DC_MIN_BRIGHT,
+		 SINF_DC_CUR_BRIGHT,
+		 SINF_MUTE,
+		 SINF_RESERVED,
+		 SINF_ENV_STATE,
+		 SINF_STICKY_KEY = 0x80,
+	};
+/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
+
+static int acpi_pcc_hotkey_add(struct acpi_device *device);
+static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type);
+static int acpi_pcc_hotkey_resume(struct acpi_device *device);
+
+static const struct acpi_device_id pcc_device_ids[] = {
+	{ "MAT0012", 0},
+	{ "MAT0013", 0},
+	{ "MAT0018", 0},
+	{ "MAT0019", 0},
+	{ "", 0},
+};
+
+static struct acpi_driver acpi_pcc_driver = {
+	.name =		ACPI_PCC_DRIVER_NAME,
+	.class =	ACPI_PCC_CLASS,
+	.ids =		pcc_device_ids,
+	.ops =		{
+				.add =		acpi_pcc_hotkey_add,
+				.remove =	acpi_pcc_hotkey_remove,
+				.resume =       acpi_pcc_hotkey_resume,
+			},
+};
+
+struct acpi_hotkey {
+	acpi_handle		handle;
+	struct acpi_device	*device;
+	struct proc_dir_entry   *proc_dir_entry;
+	unsigned long		num_sifr;
+	u32 			*sinf;
+	unsigned long		status;
+	struct input_dev	*input_dev;
+	int			sticky_mode;
+	struct backlight_device	*backlight;
+};
+
+struct pcc_keyinput {
+	struct acpi_hotkey      *hotkey;
+	int key_mode;
+};
+
+/* method access functions */
+static int acpi_pcc_write_sset(struct acpi_hotkey *hotkey, int func, int val)
+{
+	union acpi_object in_objs[] = {
+		{ .integer.type  = ACPI_TYPE_INTEGER,
+		  .integer.value = func, },
+		{ .integer.type  = ACPI_TYPE_INTEGER,
+		  .integer.value = val, },
+	};
+	struct acpi_object_list params = {
+		.count   = ARRAY_SIZE(in_objs),
+		.pointer = in_objs,
+	};
+	acpi_status status = AE_OK;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_write_sset");
+
+	status = acpi_evaluate_object(hotkey->handle, METHOD_HKEY_SSET,
+				      &params, NULL);
+
+	return status == AE_OK;
+}
+
+static inline int acpi_pcc_get_sqty(struct acpi_device *device)
+{
+	unsigned long s;
+	acpi_status status;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_get_sqty");
+
+	status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,
+				       NULL, &s);
+	if (ACPI_SUCCESS(status))
+		return s;
+	else {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "evaluation error HKEY.SQTY\n"));
+		return -EINVAL;
+	}
+}
+
+static int acpi_pcc_retrieve_biosdata(struct acpi_hotkey *hotkey, u32 *sinf)
+{
+	acpi_status status;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *hkey = NULL;
+	int i;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_retrieve_biosdata");
+
+	status = acpi_evaluate_object(hotkey->handle, METHOD_HKEY_SINF, 0,
+				      &buffer);
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "evaluation error HKEY.SINF\n"));
+		return 0;
+	}
+
+	hkey = buffer.pointer;
+	if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
+		goto end;
+	}
+
+	if (hotkey->num_sifr < hkey->package.count) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				 "SQTY reports bad SINF length\n"));
+		status = AE_ERROR;
+		goto end;
+	}
+
+	for (i = 0; i < hkey->package.count; i++) {
+		union acpi_object *element = &(hkey->package.elements[i]);
+		if (likely(element->type == ACPI_TYPE_INTEGER)) {
+			sinf[i] = element->integer.value;
+		} else
+			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+					 "Invalid HKEY.SINF data\n"));
+	}
+	sinf[hkey->package.count] = -1;
+
+end:
+	kfree(buffer.pointer);
+	return status == AE_OK;
+}
+
+static int acpi_pcc_read_sinf_field(struct seq_file *seq, int field)
+{
+	struct acpi_hotkey *hotkey = (struct acpi_hotkey *) seq->private;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_read_sinf_field");
+
+	if (!acpi_pcc_retrieve_biosdata(hotkey, hotkey->sinf)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				 "Couldn't retrieve BIOS data\n"));
+		return -EIO;
+	}
+
+	return seq_printf(seq, "%u\n", hotkey->sinf[field]);
+}
+
+/* backlight API interface functions */
+
+static int bl_get(struct backlight_device *bd)
+{
+	struct acpi_hotkey *hotkey = bl_get_data(bd);
+
+	if (!acpi_pcc_retrieve_biosdata(hotkey, hotkey->sinf))
+		return -EIO;
+
+	return hotkey->sinf[SINF_AC_CUR_BRIGHT];
+}
+
+static int bl_set_status(struct backlight_device *bd)
+{
+	struct acpi_hotkey *hotkey = bl_get_data(bd);
+	int bright = bd->props.brightness;
+
+	if (!acpi_pcc_retrieve_biosdata(hotkey, hotkey->sinf))
+		return -EIO;
+
+	if (bright < hotkey->sinf[SINF_AC_MIN_BRIGHT] ||
+	    bright > hotkey->sinf[SINF_AC_MAX_BRIGHT])
+		return -EINVAL;
+
+	return acpi_pcc_write_sset(hotkey, SINF_AC_CUR_BRIGHT, bright);
+}
+
+static struct backlight_ops pcc_backlight_ops = {
+	.get_brightness	= bl_get,
+	.update_status	= bl_set_status,
+};
+
+
+/* user interface functions */
+
+/* read methods */
+#define PCC_SINF_READ_F(_name_, FUNC) \
+static int _name_(struct seq_file *seq, void *offset) \
+{ \
+	return acpi_pcc_read_sinf_field(seq, (FUNC)); \
+}
+
+PCC_SINF_READ_F(acpi_pcc_numbatteries_show,      SINF_NUM_BATTERIES);
+PCC_SINF_READ_F(acpi_pcc_lcdtype_show,           SINF_LCD_TYPE);
+PCC_SINF_READ_F(acpi_pcc_ac_brightness_max_show, SINF_AC_MAX_BRIGHT);
+PCC_SINF_READ_F(acpi_pcc_ac_brightness_min_show, SINF_AC_MIN_BRIGHT);
+PCC_SINF_READ_F(acpi_pcc_ac_brightness_show,     SINF_AC_CUR_BRIGHT);
+PCC_SINF_READ_F(acpi_pcc_dc_brightness_max_show, SINF_DC_MAX_BRIGHT);
+PCC_SINF_READ_F(acpi_pcc_dc_brightness_min_show, SINF_DC_MIN_BRIGHT);
+PCC_SINF_READ_F(acpi_pcc_dc_brightness_show,     SINF_DC_CUR_BRIGHT);
+PCC_SINF_READ_F(acpi_pcc_brightness_show,        SINF_AC_CUR_BRIGHT);
+PCC_SINF_READ_F(acpi_pcc_mute_show,              SINF_MUTE);
+
+static int acpi_pcc_sticky_key_show(struct seq_file *seq, void *offset)
+{
+	struct acpi_hotkey *hotkey = seq->private;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_sticky_key_show");
+
+	if (!hotkey || !hotkey->device)
+		return 0;
+
+	seq_printf(seq, "%d\n", hotkey->sticky_mode);
+
+	return 0;
+}
+
+static int acpi_pcc_keyinput_show(struct seq_file *seq, void *offset)
+{
+	struct acpi_hotkey 	*hotkey = (struct acpi_hotkey *) seq->private;
+	struct input_dev 	*hotk_input_dev = hotkey->input_dev;
+	struct pcc_keyinput 	*keyinput = input_get_drvdata(hotk_input_dev);
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_keyinput_show");
+
+	seq_printf(seq, "%d\n", keyinput->key_mode);
+
+	return 0;
+}
+
+static int acpi_pcc_version_show(struct seq_file *seq, void *offset)
+{
+	struct acpi_hotkey *hotkey = (struct acpi_hotkey *) seq->private;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_version_show");
+
+	if (!hotkey || !hotkey->device)
+		return 0;
+
+	seq_printf(seq, "%s version %s\n", ACPI_PCC_DRIVER_NAME,
+		   ACPI_PCC_VERSION);
+	seq_printf(seq, "%li functions\n", hotkey->num_sifr);
+
+	return 0;
+}
+
+/* write methods */
+static ssize_t acpi_pcc_write_single_flag(struct file *file,
+					  const char __user *buffer,
+					  size_t count, int sinf_func)
+{
+	struct seq_file	*seq = file->private_data;
+	struct acpi_hotkey *hotkey = seq->private;
+	char write_string[PROC_STR_MAX_LEN];
+	u32 val;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_write_single_flag");
+
+	if (!hotkey || (count > sizeof(write_string) - 1))
+		return -EINVAL;
+
+	if (copy_from_user(write_string, buffer, count))
+		return -EFAULT;
+
+	write_string[count] = '\0';
+
+	if (sscanf(write_string, "%i", &val) == 1 &&
+	    (val == 0 || val == 1))
+		acpi_pcc_write_sset(hotkey, sinf_func, val);
+
+	return count;
+}
+
+static unsigned long write_brightness(struct file *file,
+				      const char __user *buffer,
+				      size_t count, int min_index,
+				      int max_index, int cur_index)
+{
+	struct seq_file	*seq = (struct seq_file *)file->private_data;
+	struct acpi_hotkey *hotkey = (struct acpi_hotkey *)seq->private;
+	char write_string[PROC_STR_MAX_LEN];
+	u32 bright;
+	u32 *sinf = kmalloc(sizeof(u32) * (hotkey->num_sifr + 1), GFP_KERNEL);
+
+	ACPI_FUNCTION_TRACE("write_brightness");
+
+	if (!hotkey || (count > sizeof(write_string) - 1))
+		return -EINVAL;
+
+	if (!sinf) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Couldn't allocate %li bytes\n",
+				  sizeof(u32) * hotkey->num_sifr));
+		return -EFAULT;
+	}
+
+	if (copy_from_user(write_string, buffer, count))
+		return -EFAULT;
+
+	write_string[count] = '\0';
+
+	if (!acpi_pcc_retrieve_biosdata(hotkey, sinf)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Couldn't retrieve BIOS data\n"));
+		goto end;
+	}
+
+	if (sscanf(write_string, "%i", &bright) == 1 &&
+	    bright >= sinf[min_index] && bright <= sinf[max_index])
+			acpi_pcc_write_sset(hotkey, cur_index, bright);
+
+end:
+	kfree(sinf);
+	return count;
+}
+
+static ssize_t acpi_pcc_write_ac_brightness(struct file *file,
+					    const char __user *buffer,
+					    size_t count, loff_t *ppos)
+{
+	return write_brightness(file, buffer, count, SINF_AC_MIN_BRIGHT,
+				SINF_AC_MAX_BRIGHT, SINF_AC_CUR_BRIGHT);
+}
+
+static ssize_t acpi_pcc_write_dc_brightness(struct file *file,
+					    const char __user *buffer,
+					    size_t count, loff_t *ppos)
+{
+	return write_brightness(file, buffer, count, SINF_DC_MIN_BRIGHT,
+				SINF_DC_MAX_BRIGHT, SINF_DC_CUR_BRIGHT);
+}
+
+static ssize_t acpi_pcc_write_mute(struct file *file,
+				   const char __user *buffer,
+				   size_t count, loff_t *ppos)
+{
+	return acpi_pcc_write_single_flag(file, buffer, count, SINF_MUTE);
+}
+
+static ssize_t acpi_pcc_write_sticky_key(struct file *file,
+					 const char __user *buffer,
+					 size_t count, loff_t *ppos)
+{
+	return acpi_pcc_write_single_flag(file, buffer, count, SINF_STICKY_KEY);
+}
+
+static ssize_t acpi_pcc_write_keyinput(struct file *file,
+				       const char __user *buffer,
+				       size_t count, loff_t *ppos)
+{
+	struct seq_file		*seq = (struct seq_file *)file->private_data;
+	struct acpi_hotkey	*hotkey = (struct acpi_hotkey *)seq->private;
+	struct pcc_keyinput 	*keyinput;
+	char			write_string[PROC_STR_MAX_LEN];
+	int			key_mode;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_write_keyinput");
+
+	if (!hotkey || (count > sizeof(write_string) - 1))
+		return -EINVAL;
+
+	if (copy_from_user(write_string, buffer, count))
+		return -EFAULT;
+
+	write_string[count] = '\0';
+
+	if (sscanf(write_string, "%i", &key_mode) == 1 &&
+	    (key_mode == 0 || key_mode == 1)) {
+		keyinput = input_get_drvdata(hotkey->input_dev);
+		keyinput->key_mode = key_mode;
+	}
+
+	return count;
+}
+
+/* hotkey driver */
+static void acpi_pcc_generete_keyinput(struct acpi_hotkey *hotkey)
+{
+	struct input_dev    *hotk_input_dev = hotkey->input_dev;
+	struct pcc_keyinput *keyinput = input_get_drvdata(hotk_input_dev);
+	int hinf = hotkey->status;
+	int key_code, hkey_num;
+	const int key_map[] = {
+		/*  0 */ -1,
+		/*  1 */ KEY_BRIGHTNESSDOWN,
+		/*  2 */ KEY_BRIGHTNESSUP,
+		/*  3 */ KEY_DISPLAYTOGGLE,
+		/*  4 */ KEY_MUTE,
+		/*  5 */ KEY_VOLUMEDOWN,
+		/*  6 */ KEY_VOLUMEUP,
+		/*  7 */ KEY_SLEEP,
+		/*  8 */ -1, /* Change CPU boost: do nothing */
+		/*  9 */ KEY_BATT,
+		/* 10 */ KEY_SUSPEND,
+	};
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_generete_keyinput");
+
+	if (keyinput->key_mode == 0)
+		return;
+
+	hkey_num = hinf & 0xf;
+
+	if (hkey_num < 0 || hkey_num > ARRAY_SIZE(key_map)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "hotkey number out of range: %d\n",
+				  hkey_num));
+		return;
+	}
+
+	key_code = key_map[hkey_num];
+
+	if (key_code != -1) {
+		int pushed = (hinf & 0x80) ? TRUE : FALSE;
+
+		input_report_key(hotk_input_dev, key_code, pushed);
+		input_sync(hotk_input_dev);
+	}
+}
+
+static int acpi_pcc_hotkey_get_key(struct acpi_hotkey *hotkey)
+{
+	unsigned long result;
+	acpi_status status = AE_OK;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_get_key");
+
+	status = acpi_evaluate_integer(hotkey->handle, METHOD_HKEY_QUERY,
+				       NULL, &result);
+	if (likely(ACPI_SUCCESS(status)))
+		hotkey->status = result;
+	else
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				 "error getting hotkey status\n"));
+
+	return status == AE_OK;
+}
+
+static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct acpi_hotkey *hotkey = (struct acpi_hotkey *) data;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_notify");
+
+	switch (event) {
+	case HKEY_NOTIFY:
+		if (acpi_pcc_hotkey_get_key(hotkey)) {
+			/* generate event like '"pcc HKEY 00000080 00000084"'
+			 * when Fn+F4 pressed */
+			acpi_bus_generate_proc_event(hotkey->device, event,
+						     hotkey->status);
+		}
+		acpi_pcc_generete_keyinput(hotkey);
+		break;
+	default:
+		/* nothing to do */
+		break;
+	}
+}
+
+/* "seq" initializer */
+#define SEQ_OPEN_FS(_open_func_name_, _show_func_name_) \
+static int _open_func_name_(struct inode *inode, struct file *file) \
+{								      \
+	return single_open(file, _show_func_name_, PDE(inode)->data);  \
+}
+
+/* /proc/acpi interface */
+SEQ_OPEN_FS(acpi_pcc_dc_brightness_open_fs, acpi_pcc_dc_brightness_show);
+SEQ_OPEN_FS(acpi_pcc_numbatteries_open_fs, acpi_pcc_numbatteries_show);
+SEQ_OPEN_FS(acpi_pcc_lcdtype_open_fs, acpi_pcc_lcdtype_show);
+SEQ_OPEN_FS(acpi_pcc_ac_brightness_max_open_fs,
+	    acpi_pcc_ac_brightness_max_show);
+SEQ_OPEN_FS(acpi_pcc_ac_brightness_min_open_fs,
+	    acpi_pcc_ac_brightness_min_show);
+SEQ_OPEN_FS(acpi_pcc_ac_brightness_open_fs, acpi_pcc_ac_brightness_show);
+SEQ_OPEN_FS(acpi_pcc_dc_brightness_max_open_fs,
+	    acpi_pcc_dc_brightness_max_show);
+SEQ_OPEN_FS(acpi_pcc_dc_brightness_min_open_fs,
+	    acpi_pcc_dc_brightness_min_show);
+SEQ_OPEN_FS(acpi_pcc_brightness_open_fs, acpi_pcc_brightness_show);
+SEQ_OPEN_FS(acpi_pcc_mute_open_fs, acpi_pcc_mute_show);
+SEQ_OPEN_FS(acpi_pcc_version_open_fs, acpi_pcc_version_show);
+SEQ_OPEN_FS(acpi_pcc_keyinput_open_fs, acpi_pcc_keyinput_show);
+SEQ_OPEN_FS(acpi_pcc_sticky_key_open_fs, acpi_pcc_sticky_key_show);
+
+struct _proc_item {
+	const char *name;
+	struct file_operations fops;
+	mode_t flag;
+};
+
+/* "seq" fops template for read-only files. */
+#define SEQ_FILEOPS_R(_open_func_name_) \
+{ \
+	.open	 = _open_func_name_,		  \
+	.read	 = seq_read,			  \
+	.llseek	 = seq_lseek,			  \
+	.release = single_release,		  \
+}
+
+/* "seq" fops template for read-write files. */
+#define SEQ_FILEOPS_RW(_open_func_name_, _write_func_name_) \
+{ \
+	.open	 = _open_func_name_ ,		  \
+	.read	 = seq_read,			  \
+	.write	 = _write_func_name_,		  \
+	.llseek	 = seq_lseek,			  \
+	.release = single_release,		  \
+}
+
+/* Note: These functions map *exactly* to the SINF/SSET functions */
+static struct _proc_item pcc_proc_items_sifr[] = {
+	{
+		"num_batteries",
+		SEQ_FILEOPS_R(acpi_pcc_numbatteries_open_fs),
+		S_IRUGO,
+	}, {
+		"lcd_type",
+		SEQ_FILEOPS_R(acpi_pcc_lcdtype_open_fs),
+		S_IRUGO,
+	}, {
+		"ac_brightness_max",
+		SEQ_FILEOPS_R(acpi_pcc_ac_brightness_max_open_fs),
+		S_IRUGO,
+	}, {
+		"ac_brightness_min",
+		SEQ_FILEOPS_R(acpi_pcc_ac_brightness_min_open_fs),
+		S_IRUGO,
+	}, {
+		"ac_brightness",
+		SEQ_FILEOPS_RW(acpi_pcc_ac_brightness_open_fs,
+			       acpi_pcc_write_ac_brightness),
+		S_IFREG | S_IRUGO | S_IWUSR,
+	}, {
+		"dc_brightness_max",
+		SEQ_FILEOPS_R(acpi_pcc_dc_brightness_max_open_fs),
+		S_IRUGO,
+	}, {
+		"dc_brightness_min",
+		SEQ_FILEOPS_R(acpi_pcc_dc_brightness_min_open_fs),
+		S_IRUGO,
+	}, {
+		"dc_brightness",
+		SEQ_FILEOPS_RW(acpi_pcc_dc_brightness_open_fs,
+			       acpi_pcc_write_dc_brightness),
+		S_IFREG | S_IRUGO | S_IWUSR,
+	}, {
+		"brightness",
+		SEQ_FILEOPS_RW(acpi_pcc_brightness_open_fs,
+			       acpi_pcc_write_dc_brightness),
+		S_IFREG | S_IRUGO | S_IWUSR,
+	}, {
+		"mute",
+		SEQ_FILEOPS_RW(acpi_pcc_mute_open_fs,
+			       acpi_pcc_write_mute),
+		S_IFREG | S_IRUGO | S_IWUSR,
+	},
+	{ NULL, { 0 }, 0 },
+};
+
+static struct _proc_item pcc_proc_items[] = {
+	{
+		"sticky_key",
+		SEQ_FILEOPS_RW(acpi_pcc_sticky_key_open_fs,
+				acpi_pcc_write_sticky_key),
+		S_IFREG | S_IRUGO | S_IWUSR,
+	}, {
+		"version",
+		SEQ_FILEOPS_R(acpi_pcc_version_open_fs),
+		S_IRUGO,
+	}, {
+		"keyinput",
+		SEQ_FILEOPS_RW(acpi_pcc_keyinput_open_fs,
+				acpi_pcc_write_keyinput),
+		S_IFREG | S_IRUGO | S_IWUSR,
+	},
+	{ NULL, { 0 }, 0 },
+};
+
+static int acpi_pcc_add_device(struct acpi_device *device,
+			       struct _proc_item *proc_items, int num)
+{
+	struct acpi_hotkey *hotkey = acpi_driver_data(device);
+	struct proc_dir_entry *proc;
+	struct _proc_item *item;
+	int i;
+
+	for (item = proc_items, i = 0; item->name && i < num; ++item, ++i) {
+		proc = create_proc_entry(item->name, item->flag,
+					 hotkey->proc_dir_entry);
+		if (likely(proc)) {
+			proc->proc_fops = &item->fops;
+			proc->data = hotkey;
+			proc->owner = THIS_MODULE;
+		} else {
+			while (i-- > 0) {
+				item--;
+				remove_proc_entry(item->name,
+						  hotkey->proc_dir_entry);
+			}
+			return -ENODEV;
+		}
+	}
+	return 0;
+}
+
+static int acpi_pcc_proc_init(struct acpi_device *device)
+{
+	struct proc_dir_entry *acpi_pcc_dir;
+	struct acpi_hotkey *hotkey = acpi_driver_data(device);
+	acpi_status status;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_proc_init");
+
+	acpi_pcc_dir = proc_mkdir(PROC_PCC, acpi_root_dir);
+
+	if (unlikely(!acpi_pcc_dir)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Couldn't create dir in /proc\n"));
+		return -ENODEV;
+	}
+
+	acpi_pcc_dir->owner = THIS_MODULE;
+	hotkey->proc_dir_entry = acpi_pcc_dir;
+
+	status = acpi_pcc_add_device(device, pcc_proc_items_sifr,
+				     hotkey->num_sifr);
+	status |= acpi_pcc_add_device(device, pcc_proc_items,
+			sizeof(pcc_proc_items)/sizeof(struct _proc_item));
+	if (unlikely(status)) {
+		remove_proc_entry(PROC_PCC, acpi_root_dir);
+		hotkey->proc_dir_entry = NULL;
+		return -ENODEV;
+	}
+
+	return status;
+}
+
+static void acpi_pcc_remove_device(struct acpi_device *device,
+				   struct _proc_item *proc_items, int num)
+{
+	struct acpi_hotkey *hotkey = acpi_driver_data(device);
+	struct _proc_item *item;
+	int i;
+
+	for (item = proc_items, i = 0;
+	     item->name != NULL && i < num;
+	     ++item, ++i)
+		remove_proc_entry(item->name, hotkey->proc_dir_entry);
+}
+
+static int acpi_pcc_init_input(struct acpi_hotkey *hotkey)
+{
+	struct input_dev *hotk_input_dev;
+	struct pcc_keyinput *pcc_keyinput;
+	int rc;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_init_input");
+
+	hotk_input_dev = input_allocate_device();
+	if (!hotk_input_dev) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Couldn't allocate input device for hotkey"));
+		return -ENOMEM;
+	}
+
+	pcc_keyinput = kmalloc(sizeof(struct pcc_keyinput), GFP_KERNEL);
+
+	if (pcc_keyinput == NULL) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Couldn't allocate mem for hotkey"));
+		input_free_device(hotk_input_dev);
+		return -ENOMEM;
+	}
+
+	hotk_input_dev->evbit[0] = BIT(EV_KEY);
+
+	set_bit(KEY_BRIGHTNESSDOWN, hotk_input_dev->keybit);
+	set_bit(KEY_BRIGHTNESSUP, hotk_input_dev->keybit);
+	set_bit(KEY_MUTE, hotk_input_dev->keybit);
+	set_bit(KEY_VOLUMEDOWN, hotk_input_dev->keybit);
+	set_bit(KEY_VOLUMEUP, hotk_input_dev->keybit);
+	set_bit(KEY_SLEEP, hotk_input_dev->keybit);
+	set_bit(KEY_BATT, hotk_input_dev->keybit);
+	set_bit(KEY_SUSPEND, hotk_input_dev->keybit);
+
+	hotk_input_dev->name = ACPI_PCC_DRIVER_NAME;
+	hotk_input_dev->phys = ACPI_PCC_INPUT_PHYS;
+	hotk_input_dev->id.bustype = 0x1a; /* XXX FIXME: BUS_I8042? */
+	hotk_input_dev->id.vendor = 0x0001;
+	hotk_input_dev->id.product = 0x0001;
+	hotk_input_dev->id.version = 0x0100;
+
+	pcc_keyinput->key_mode = 1; /* default on */
+	pcc_keyinput->hotkey = hotkey;
+
+	input_set_drvdata(hotk_input_dev, pcc_keyinput);
+
+	hotkey->input_dev = hotk_input_dev;
+
+	rc = input_register_device(hotk_input_dev);
+	if (rc < 0) {
+		kfree(pcc_keyinput);
+		input_free_device(hotk_input_dev);
+	}
+
+	return rc;
+}
+
+/* kernel module interface */
+
+static int acpi_pcc_hotkey_resume(struct acpi_device *device)
+{
+	struct acpi_hotkey *hotkey = acpi_driver_data(device);
+	acpi_status status = AE_OK;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_resume");
+
+	if (device == NULL || hotkey == NULL)
+		return -EINVAL;
+
+	ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
+			  hotkey->sticky_mode));
+
+	status = acpi_pcc_write_sset(hotkey, SINF_STICKY_KEY,
+				     hotkey->sticky_mode);
+
+	return status == AE_OK ? 0 : -EINVAL;
+}
+
+static int acpi_pcc_hotkey_add(struct acpi_device *device)
+{
+	acpi_status status;
+	struct acpi_hotkey *hotkey;
+	int num_sifr, result;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_add");
+
+	if (!device)
+		return -EINVAL;
+
+	num_sifr = acpi_pcc_get_sqty(device);
+
+	if (num_sifr > 255) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large"));
+		return -ENODEV;
+	}
+
+	hotkey = kmalloc(sizeof(struct acpi_hotkey), GFP_KERNEL);
+	if (!hotkey) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Couldn't allocate mem for hotkey"));
+		return -ENOMEM;
+	}
+	memset(hotkey, 0, sizeof(struct acpi_hotkey));
+
+	hotkey->sinf = kmalloc(sizeof(u32) * (num_sifr + 1), GFP_KERNEL);
+	if (!hotkey->sinf) {
+		result = -ENOMEM;
+		goto out_hotkey;
+	}
+
+	hotkey->device = device;
+	hotkey->handle = device->handle;
+	hotkey->num_sifr = num_sifr;
+	acpi_driver_data(device) = hotkey;
+	strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
+	strcpy(acpi_device_class(device), ACPI_PCC_CLASS);
+
+	status = acpi_install_notify_handler (
+			hotkey->handle,
+			ACPI_DEVICE_NOTIFY,
+			acpi_pcc_hotkey_notify,
+			hotkey);
+
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error installing notify handler\n"));
+		result = -ENODEV;
+		goto out_sinf;
+	}
+
+	result = acpi_pcc_init_input(hotkey);
+	if (result) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error installing keyinput handler\n"));
+		goto out_notify;
+	}
+
+	hotkey->backlight = backlight_device_register("panasonic", NULL,
+						      hotkey,
+						      &pcc_backlight_ops);
+	if (IS_ERR(hotkey->backlight))
+		goto out_input;
+
+	if (!acpi_pcc_retrieve_biosdata(hotkey, hotkey->sinf)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				 "Couldn't retrieve BIOS data\n"));
+		goto out_backlight;
+	}
+
+	hotkey->backlight->props.max_brightness =
+					hotkey->sinf[SINF_AC_MAX_BRIGHT];
+	hotkey->backlight->props.brightness = hotkey->sinf[SINF_AC_CUR_BRIGHT];
+
+	result = acpi_pcc_proc_init(device);
+	if (result)
+		goto out_backlight;
+
+	return 0;
+
+out_backlight:
+	backlight_device_unregister(hotkey->backlight);
+out_notify:
+	acpi_remove_notify_handler(hotkey->handle, ACPI_DEVICE_NOTIFY,
+				   acpi_pcc_hotkey_notify);
+out_input:
+	input_unregister_device(hotkey->input_dev);
+	kfree(input_get_drvdata(hotkey->input_dev));
+	input_free_device(hotkey->input_dev);
+out_sinf:
+	kfree(hotkey->sinf);
+out_hotkey:
+	kfree(hotkey);
+
+	return result;
+}
+
+static int __init acpi_pcc_init(void)
+{
+	int result = 0;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_init");
+
+	if (acpi_disabled)
+		return -ENODEV;
+
+	result = acpi_bus_register_driver(&acpi_pcc_driver);
+	if (result < 0) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error registering hotkey driver\n"));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
+{
+	struct acpi_hotkey *hotkey = acpi_driver_data(device);
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_remove");
+
+	if (!device || !hotkey)
+		return -EINVAL;
+
+	if (hotkey->proc_dir_entry) {
+		acpi_pcc_remove_device(device, pcc_proc_items_sifr,
+				       hotkey->num_sifr);
+		acpi_pcc_remove_device(device, pcc_proc_items,
+			sizeof(pcc_proc_items)/sizeof(struct _proc_item));
+		remove_proc_entry(PROC_PCC, acpi_root_dir);
+	}
+
+	backlight_device_unregister(hotkey->backlight);
+
+	acpi_remove_notify_handler(hotkey->handle, ACPI_DEVICE_NOTIFY,
+				   acpi_pcc_hotkey_notify);
+
+	input_unregister_device(hotkey->input_dev);
+	kfree(input_get_drvdata(hotkey->input_dev));
+	input_free_device(hotkey->input_dev);
+
+	kfree(hotkey->sinf);
+	kfree(hotkey);
+
+	return 0;
+}
+
+static void __exit acpi_pcc_exit(void)
+{
+	ACPI_FUNCTION_TRACE("acpi_pcc_exit");
+
+	acpi_bus_unregister_driver(&acpi_pcc_driver);
+}
+
+module_init(acpi_pcc_init);
+module_exit(acpi_pcc_exit);
-- 
- Harald Welte <laforge@gnumonks.org>           http://laforge.gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
                                                  (ETSI EN 300 175-7 Ch. A6)

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver
  2008-07-02  9:00 [PATCH] Panasonic Let's Note laptop extras driver Harald Welte
@ 2008-07-02 10:19 ` Matthew Garrett
  2008-07-02 13:03   ` Harald Welte
  2008-07-03 16:52   ` Harald Welte
  2008-07-03 17:35 ` [PATCH] Panasonic Let's Note laptop extras driver v0.93 Harald Welte
  2008-07-04  3:44 ` [PATCH] Panasonic Let's Note laptop extras driver v0.94 Harald Welte
  2 siblings, 2 replies; 28+ messages in thread
From: Matthew Garrett @ 2008-07-02 10:19 UTC (permalink / raw)
  To: Harald Welte; +Cc: linux-acpi, Hiroshi Miura, hadi

On Wed, Jul 02, 2008 at 05:00:44PM +0800, Harald Welte wrote:

> * in order to fully support the backlight interface and get rid of the
>   clumsy separation of 'ac' (with power supply) and 'dc' (battery only) 
>   brightness settings, the driver would need to receive a notification
>   whenever the ACPI power supply status changes.  I know how to get to
>   that notification in userspace, but in kernelspace I'm not so sure.
>   Is there a notifier chain to which I can attach?

I don't think so, but it ought to be easy enough to glue into the 
power_supply class. The easiest thing for the moment is probably just to 
write values to both the ac and dc classes and let userspace handle the 
transition. If they're both in sync, it doesn't matter which you read 
back from.

> +/* This is transitional definition */
> +#ifndef KEY_BATT
> +# define KEY_BATT 227
> +#endif

Shouldn't be needed in-tree.

> +	const int key_map[] = {
> +		/*  0 */ -1,
> +		/*  1 */ KEY_BRIGHTNESSDOWN,
> +		/*  2 */ KEY_BRIGHTNESSUP,
> +		/*  3 */ KEY_DISPLAYTOGGLE,
> +		/*  4 */ KEY_MUTE,
> +		/*  5 */ KEY_VOLUMEDOWN,
> +		/*  6 */ KEY_VOLUMEUP,
> +		/*  7 */ KEY_SLEEP,
> +		/*  8 */ -1, /* Change CPU boost: do nothing */
> +		/*  9 */ KEY_BATT,
> +		/* 10 */ KEY_SUSPEND,
> +	};

Hm. Does this mean that the keymap isn't changable at runtime?

> +		if (acpi_pcc_hotkey_get_key(hotkey)) {
> +			/* generate event like '"pcc HKEY 00000080 00000084"'
> +			 * when Fn+F4 pressed */

Probably better not to add further events to /proc now - we'd like to 
deprecate the interface.

> +struct _proc_item {
> +	const char *name;
> +	struct file_operations fops;
> +	mode_t flag;
> +};

And shouldn't be adding any new files to /proc. Anything that can't be 
expressed via the existing generic sysfs classes should be an attribute 
on a sysfs platform device.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver
  2008-07-02 10:19 ` Matthew Garrett
@ 2008-07-02 13:03   ` Harald Welte
  2008-07-02 13:17     ` Matthew Garrett
  2008-07-03 16:52   ` Harald Welte
  1 sibling, 1 reply; 28+ messages in thread
From: Harald Welte @ 2008-07-02 13:03 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: linux-acpi, Hiroshi Miura, hadi

[-- Attachment #1: Type: text/plain, Size: 2824 bytes --]

Hi Matthew,

On Wed, Jul 02, 2008 at 11:19:19AM +0100, Matthew Garrett wrote:
> On Wed, Jul 02, 2008 at 05:00:44PM +0800, Harald Welte wrote:
> 
> > * in order to fully support the backlight interface and get rid of the
> >   clumsy separation of 'ac' (with power supply) and 'dc' (battery only) 
> >   brightness settings, the driver would need to receive a notification
> >   whenever the ACPI power supply status changes.  I know how to get to
> >   that notification in userspace, but in kernelspace I'm not so sure.
> >   Is there a notifier chain to which I can attach?
> 
> I don't think so, but it ought to be easy enough to glue into the 
> power_supply class. The easiest thing for the moment is probably just to 
> write values to both the ac and dc classes and let userspace handle the 
> transition. If they're both in sync, it doesn't matter which you read 
> back from.

mh, ok, that actually makes a lot of sense.  Will do that.

> > +/* This is transitional definition */
> > +#ifndef KEY_BATT
> > +# define KEY_BATT 227
> > +#endif
> 
> Shouldn't be needed in-tree.

true.

> > +	const int key_map[] = {
> > +		/*  0 */ -1,
> > +		/*  1 */ KEY_BRIGHTNESSDOWN,
> > +		/*  2 */ KEY_BRIGHTNESSUP,
> > +		/*  3 */ KEY_DISPLAYTOGGLE,
> > +		/*  4 */ KEY_MUTE,
> > +		/*  5 */ KEY_VOLUMEDOWN,
> > +		/*  6 */ KEY_VOLUMEUP,
> > +		/*  7 */ KEY_SLEEP,
> > +		/*  8 */ -1, /* Change CPU boost: do nothing */
> > +		/*  9 */ KEY_BATT,
> > +		/* 10 */ KEY_SUSPEND,
> > +	};
> 
> Hm. Does this mean that the keymap isn't changable at runtime?

i wasn't aware that those kind of keymaps could be changeable at
runtime.  Will read up on my homework ;)

> > +		if (acpi_pcc_hotkey_get_key(hotkey)) {
> > +			/* generate event like '"pcc HKEY 00000080 00000084"'
> > +			 * when Fn+F4 pressed */
> 
> Probably better not to add further events to /proc now - we'd like to 
> deprecate the interface.

ok.  I just kept all the exsting interfaces of the pcc_acpi driver
(/proc based) and added the new backlight API to it.

> And shouldn't be adding any new files to /proc. Anything that can't be 
> expressed via the existing generic sysfs classes should be an attribute 
> on a sysfs platform device.

that's what I generally know from other drivers (and how I do it in
other kernel code).  But I wasn't sure what the policy with regard to
this was for ACPI.

Thanks for your quick feedback, will re-post after all issues have been
adressed.

-- 
- Harald Welte <laforge@gnumonks.org>           http://laforge.gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
                                                  (ETSI EN 300 175-7 Ch. A6)

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver
  2008-07-02 13:03   ` Harald Welte
@ 2008-07-02 13:17     ` Matthew Garrett
  0 siblings, 0 replies; 28+ messages in thread
From: Matthew Garrett @ 2008-07-02 13:17 UTC (permalink / raw)
  To: Harald Welte; +Cc: linux-acpi, Hiroshi Miura, hadi

On Wed, Jul 02, 2008 at 09:03:15PM +0800, Harald Welte wrote:
> Hi Matthew,
> > 
> > Hm. Does this mean that the keymap isn't changable at runtime?
> 
> i wasn't aware that those kind of keymaps could be changeable at
> runtime.  Will read up on my homework ;)

Especially when you have a case where there's a scancode that isn't 
mapped to anything by default, it's helpful to let it be changed at 
runtime. I usually just copy the code from wistron_btns whenever I'm 
implementing this.

> > And shouldn't be adding any new files to /proc. Anything that can't be 
> > expressed via the existing generic sysfs classes should be an attribute 
> > on a sysfs platform device.
> 
> that's what I generally know from other drivers (and how I do it in
> other kernel code).  But I wasn't sure what the policy with regard to
> this was for ACPI.

The long-term goal is to kill off /proc/acpi entirely, so adding new 
things is kind of frowned upon :)

-- 
Matthew Garrett | mjg59@srcf.ucam.org

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver
  2008-07-02 10:19 ` Matthew Garrett
  2008-07-02 13:03   ` Harald Welte
@ 2008-07-03 16:52   ` Harald Welte
  2008-07-03 17:08     ` Matthew Garrett
  1 sibling, 1 reply; 28+ messages in thread
From: Harald Welte @ 2008-07-03 16:52 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: linux-acpi, Hiroshi Miura, hadi

[-- Attachment #1: Type: text/plain, Size: 1139 bytes --]

On Wed, Jul 02, 2008 at 11:19:19AM +0100, Matthew Garrett wrote:

> > +		if (acpi_pcc_hotkey_get_key(hotkey)) {
> > +			/* generate event like '"pcc HKEY 00000080 00000084"'
> > +			 * when Fn+F4 pressed */
> 
> Probably better not to add further events to /proc now - we'd like to 
> deprecate the interface.

the userspace scripts that acpid ships already support those events.  So
if the kernel driver provides events in this way, the brightness changes
will work out-of-the-box.

So I vote for keeping those events in the driver - just as long as there
is no new standard mechanism for signalling brightness up/down button
events to userspace.

I was pondering to emulate the events that the "video" acpi class
driver sends to userspace.  But that also doesn't really seems right the
correct solution.

-- 
- Harald Welte <laforge@gnumonks.org>           http://laforge.gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
                                                  (ETSI EN 300 175-7 Ch. A6)

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver
  2008-07-03 16:52   ` Harald Welte
@ 2008-07-03 17:08     ` Matthew Garrett
  2008-07-04  3:14       ` Henrique de Moraes Holschuh
  0 siblings, 1 reply; 28+ messages in thread
From: Matthew Garrett @ 2008-07-03 17:08 UTC (permalink / raw)
  To: Harald Welte; +Cc: linux-acpi, Hiroshi Miura, hadi

On Fri, Jul 04, 2008 at 12:52:19AM +0800, Harald Welte wrote:

> So I vote for keeping those events in the driver - just as long as there
> is no new standard mechanism for signalling brightness up/down button
> events to userspace.

There is - they should just be sent as KEY_BRIGHTNESSUP and 
KEY_BRIGHTNESSDOWN.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

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

* [PATCH] Panasonic Let's Note laptop extras driver v0.93
  2008-07-02  9:00 [PATCH] Panasonic Let's Note laptop extras driver Harald Welte
  2008-07-02 10:19 ` Matthew Garrett
@ 2008-07-03 17:35 ` Harald Welte
  2008-07-03 19:23   ` Matthew Garrett
  2008-07-04  3:44 ` [PATCH] Panasonic Let's Note laptop extras driver v0.94 Harald Welte
  2 siblings, 1 reply; 28+ messages in thread
From: Harald Welte @ 2008-07-03 17:35 UTC (permalink / raw)
  To: linux-acpi; +Cc: Hiroshi Miura

Hi!

This is the version of the driver incorporating changes as requested
from Matthew Garrett.

Ever since I bought my Panasonic Let's Note CF-R5 in September 2006
(like other well-known kernel hackers such as Jamal Selim and Stephen
Hemminger) I'm using the (apparently abandoned) out-of-tree pcc_acpi.c
driver.  Various changes in the kernel API's have required me to
merge/forward-port it from one kernel release to the other.

In more than three attempts (two of them in the last 6 months), I have
tried to contact the main author (Hiroshi Miura, see Cc) and asked if he
had plans to submit the driver for mainline inclusion or if he'd support
any such submission attempt by me.

The co-author David Bronaugh has promptly responded and is fine with
merging it mainline.

Though we are now in the unfortunate situation where the main author is
no longer reachable, I don't want to delay this even longer and want to
get this driver merged.  The original code was explicitly GPL licensed,
so there is no legal requirement to have explicit permission from
Hiroshi Miura.

For the time being, I'm also willing to be the maintainer of this
driver.

Any feedback is welcome.  Please excuse obvious mistakes.  While I have
a lot of kernel experience, I have not done any work on ACPI before.

Thanks for your review,

Signed-off-by: Harald Welte <laforge@gnumonks.org>

---


diff --git a/MAINTAINERS b/MAINTAINERS
index 8f0ec46..05d449e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3108,6 +3108,11 @@ M:	olof@lixom.net
 L:	i2c@lm-sensors.org
 S:	Maintained
 
+PANASONIC LAPTOP ACPI EXTRAS DRIVER
+P:	Harald Welte
+M:	laforge@gnumonks.org
+S:	Maintained
+
 PARALLEL PORT SUPPORT
 L:	linux-parport@lists.infradead.org (subscribers-only)
 S:	Orphan
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index c52fca8..1da4bc8 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -259,6 +259,17 @@ config ACPI_ASUS
 	  NOTE: This driver is deprecated and will probably be removed soon,
 	  use asus-laptop instead.
 
+config ACPI_PANASONIC
+	tristate "Panasonic Laptop Extras"
+	depends on X86
+	select BACKLIGHT_CLASS_DEVICE
+	---help---
+	  This driver adds support for access to certain system settings
+	  on Panasonic Let's Note laptops.  
+
+	  If you have a Panasonic Let's note laptop (such as the R1(N variant),
+	  R2, R3, R5, T2, W2 and Y2 series), say Y.
+
 config ACPI_TOSHIBA
 	tristate "Toshiba Laptop Extras"
 	depends on X86
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 40b0fca..04c1b5d 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_ACPI_WMI)		+= wmi.o
 obj-$(CONFIG_ACPI_ASUS)		+= asus_acpi.o
 obj-$(CONFIG_ACPI_TOSHIBA)	+= toshiba_acpi.o
 obj-$(CONFIG_ACPI_HOTPLUG_MEMORY)	+= acpi_memhotplug.o
+obj-$(CONFIG_ACPI_PANASONIC)	+= pcc_acpi.o
 obj-$(CONFIG_ACPI_PROCFS_POWER)	+= cm_sbs.o
 obj-$(CONFIG_ACPI_SBS)		+= sbshc.o
 obj-$(CONFIG_ACPI_SBS)		+= sbs.o
diff --git a/drivers/acpi/pcc_acpi.c b/drivers/acpi/pcc_acpi.c
new file mode 100644
index 0000000..9316723
--- /dev/null
+++ b/drivers/acpi/pcc_acpi.c
@@ -0,0 +1,758 @@
+/*
+ *  Panasonic HotKey and LCD brightness control driver
+ *  (C) 2004 Hiroshi Miura <miura@da-cha.org>
+ *  (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/
+ *  (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>
+ *  (C) 2004 David Bronaugh <dbronaugh>
+ *  (C) 2006-2008 Harald Welte <laforge@gnumonks.org>
+ *
+ *  derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  publicshed by the Free Software Foundation.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *---------------------------------------------------------------------------
+ *
+ * ChangeLog:
+ * 	Jul.03, 2008	Harald Welte <laforge@gnumonks.org>
+ * 		-v0.932	replace /proc interface with device attributes
+ *
+ *      Jun.27, 2008	Harald Welte <laforge@gnumonks.org>
+ *      	-v0.92	merge with 2.6.26-rc6 input API changes
+ *      		remove broken <= 2.6.15 kernel support
+ *      		resolve all compiler warnings
+ *      		various coding style fixes (checkpatch.pl)
+ *      		add support for backlight api
+ *      		major code restructuring
+ *
+ * 	Dac.28, 2007	Harald Welte <laforge@gnumonks.org>
+ * 		-v0.91	merge with 2.6.24-rc6 ACPI changes
+ *
+ * 	Nov.04, 2006	Hiroshi Miura <miura@da-cha.org>
+ * 		-v0.9	remove warning about section reference.
+ * 			remove acpi_os_free
+ * 			add /proc/acpi/pcc/brightness interface for HAL access
+ * 			merge dbronaugh's enhancement
+ * 			Aug.17, 2004 David Bronaugh (dbronaugh)
+ *  				- Added screen brightness setting interface
+ *				  Thanks to FreeBSD crew (acpi_panasonic.c)
+ * 				  for the ideas I needed to accomplish it
+ *
+ *	May.29, 2006	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.8.4 follow to change keyinput structure
+ *			thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,
+ *			Jacob Bower <jacob.bower@ic.ac.uk> and
+ *			Hiroshi Yokota for providing solutions.
+ *
+ *	Oct.02, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.8.2	merge code of YOKOTA Hiroshi
+ *					<yokota@netlab.is.tsukuba.ac.jp>.
+ *			Add sticky key mode interface.
+ *			Refactoring acpi_pcc_generate_keyinput().
+ *
+ *	Sep.15, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.8	Generate key input event on input subsystem.
+ *			This is based on yet another driver written by
+ *							Ryuta Nakanishi.
+ *
+ *	Sep.10, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.7	Change proc interface functions using seq_file
+ *			facility as same as other ACPI drivers.
+ *
+ *	Aug.28, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.6.4 Fix a silly error with status checking
+ *
+ *	Aug.25, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.6.3 replace read_acpi_int by standard function
+ *							acpi_evaluate_integer
+ *			some clean up and make smart copyright notice.
+ *			fix return value of pcc_acpi_get_key()
+ *			fix checking return value of acpi_bus_register_driver()
+ *
+ *      Aug.22, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              -v0.6.2 Add check on ACPI data (num_sifr)
+ *                      Coding style cleanups, better error messages/handling
+ *			Fixed an off-by-one error in memory allocation
+ *
+ *      Aug.21, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              -v0.6.1 Fix a silly error with status checking
+ *
+ *      Aug.20, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              - v0.6  Correct brightness controls to reflect reality
+ *                      based on information gleaned by Hiroshi Miura
+ *                      and discussions with Hiroshi Miura
+ *
+ *	Aug.10, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.5  support LCD brightness control
+ *			based on the disclosed information by MEI.
+ *
+ *	Jul.25, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.4  first post version
+ *		        add function to retrive SIFR
+ *
+ *	Jul.24, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.3  get proper status of hotkey
+ *
+ *      Jul.22, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.2  add HotKey handler
+ *
+ *      Jul.17, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.1  start from toshiba_acpi driver written by John Belmonte
+ *
+ */
+
+#define ACPI_PCC_VERSION	"0.93"
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/backlight.h>
+#include <linux/ctype.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/input.h>
+
+
+#ifndef ACPI_HOTKEY_COMPONENT
+#define ACPI_HOTKEY_COMPONENT	0x10000000
+#endif
+
+#define _COMPONENT		ACPI_HOTKEY_COMPONENT
+ACPI_MODULE_NAME("pcc_acpi")
+
+MODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte");
+MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");
+MODULE_LICENSE("GPL");
+
+#define LOGPREFIX "pcc_acpi: "
+
+/* Define ACPI PATHs */
+/* Lets note hotkeys */
+#define METHOD_HKEY_QUERY	"HINF"
+#define METHOD_HKEY_SQTY	"SQTY"
+#define METHOD_HKEY_SINF	"SINF"
+#define METHOD_HKEY_SSET	"SSET"
+#define HKEY_NOTIFY		 0x80
+
+/* definitions for /proc/ interface */
+#define ACPI_PCC_DRIVER_NAME	"PCC Extra Driver"
+#define ACPI_PCC_DEVICE_NAME	"PCCExtra"
+#define ACPI_PCC_CLASS		"pcc"
+#define PROC_PCC		ACPI_PCC_CLASS
+
+#define ACPI_PCC_INPUT_PHYS	"panasonic/hkey0"
+
+#define PROC_STR_MAX_LEN  8
+
+/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
+   ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
+*/
+enum SINF_BITS { SINF_NUM_BATTERIES = 0,
+		 SINF_LCD_TYPE,
+		 SINF_AC_MAX_BRIGHT,
+		 SINF_AC_MIN_BRIGHT,
+		 SINF_AC_CUR_BRIGHT,
+		 SINF_DC_MAX_BRIGHT,
+		 SINF_DC_MIN_BRIGHT,
+		 SINF_DC_CUR_BRIGHT,
+		 SINF_MUTE,
+		 SINF_RESERVED,
+		 SINF_ENV_STATE,
+		 SINF_STICKY_KEY = 0x80,
+	};
+/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
+
+static int acpi_pcc_hotkey_add(struct acpi_device *device);
+static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type);
+static int acpi_pcc_hotkey_resume(struct acpi_device *device);
+
+static const struct acpi_device_id pcc_device_ids[] = {
+	{ "MAT0012", 0},
+	{ "MAT0013", 0},
+	{ "MAT0018", 0},
+	{ "MAT0019", 0},
+	{ "", 0},
+};
+
+static struct acpi_driver acpi_pcc_driver = {
+	.name =		ACPI_PCC_DRIVER_NAME,
+	.class =	ACPI_PCC_CLASS,
+	.ids =		pcc_device_ids,
+	.ops =		{
+				.add =		acpi_pcc_hotkey_add,
+				.remove =	acpi_pcc_hotkey_remove,
+				.resume =       acpi_pcc_hotkey_resume,
+			},
+};
+
+#define KEYMAP_SIZE		11
+static const int initial_keymap[KEYMAP_SIZE] = {
+	/*  0 */ -1,
+	/*  1 */ KEY_BRIGHTNESSDOWN,
+	/*  2 */ KEY_BRIGHTNESSUP,
+	/*  3 */ KEY_DISPLAYTOGGLE,
+	/*  4 */ KEY_MUTE,
+	/*  5 */ KEY_VOLUMEDOWN,
+	/*  6 */ KEY_VOLUMEUP,
+	/*  7 */ KEY_SLEEP,
+	/*  8 */ -1, /* Change CPU boost: do nothing */
+	/*  9 */ KEY_BATTERY,
+	/* 10 */ KEY_SUSPEND,
+};
+
+struct pcc_acpi {
+	acpi_handle		handle;
+	unsigned long		num_sifr;
+	int			sticky_mode;
+	u32 			*sinf;
+	struct acpi_device	*device;
+	struct input_dev	*input_dev;
+	struct backlight_device	*backlight;
+	int			keymap[KEYMAP_SIZE];
+};
+
+struct pcc_keyinput {
+	struct acpi_hotkey      *hotkey;
+};
+
+/* method access functions */
+static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
+{
+	union acpi_object in_objs[] = {
+		{ .integer.type  = ACPI_TYPE_INTEGER,
+		  .integer.value = func, },
+		{ .integer.type  = ACPI_TYPE_INTEGER,
+		  .integer.value = val, },
+	};
+	struct acpi_object_list params = {
+		.count   = ARRAY_SIZE(in_objs),
+		.pointer = in_objs,
+	};
+	acpi_status status = AE_OK;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_write_sset");
+
+	status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET,
+				      &params, NULL);
+
+	return status == AE_OK;
+}
+
+static inline int acpi_pcc_get_sqty(struct acpi_device *device)
+{
+	unsigned long s;
+	acpi_status status;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_get_sqty");
+
+	status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,
+				       NULL, &s);
+	if (ACPI_SUCCESS(status))
+		return s;
+	else {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "evaluation error HKEY.SQTY\n"));
+		return -EINVAL;
+	}
+}
+
+static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
+{
+	acpi_status status;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *hkey = NULL;
+	int i;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_retrieve_biosdata");
+
+	status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, 0,
+				      &buffer);
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "evaluation error HKEY.SINF\n"));
+		return 0;
+	}
+
+	hkey = buffer.pointer;
+	if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
+		goto end;
+	}
+
+	if (pcc->num_sifr < hkey->package.count) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				 "SQTY reports bad SINF length\n"));
+		status = AE_ERROR;
+		goto end;
+	}
+
+	for (i = 0; i < hkey->package.count; i++) {
+		union acpi_object *element = &(hkey->package.elements[i]);
+		if (likely(element->type == ACPI_TYPE_INTEGER)) {
+			sinf[i] = element->integer.value;
+		} else
+			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+					 "Invalid HKEY.SINF data\n"));
+	}
+	sinf[hkey->package.count] = -1;
+
+end:
+	kfree(buffer.pointer);
+	return status == AE_OK;
+}
+
+/* backlight API interface functions */
+
+/* This driver currently treats AC and DC brightness identical,
+ * since we don't need to invent an interface to the core ACPI
+ * logic to receive events in case a power supply is plugged in
+ * or removed */
+
+static int bl_get(struct backlight_device *bd)
+{
+	struct pcc_acpi *pcc = bl_get_data(bd);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return pcc->sinf[SINF_AC_CUR_BRIGHT];
+}
+
+static int bl_set_status(struct backlight_device *bd)
+{
+	struct pcc_acpi *pcc = bl_get_data(bd);
+	int bright = bd->props.brightness;
+	int rc;
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
+		bright = pcc->sinf[SINF_AC_MIN_BRIGHT];
+
+	if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT])
+		bright = pcc->sinf[SINF_DC_MIN_BRIGHT];
+
+	if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] ||
+	    bright > pcc->sinf[SINF_AC_MAX_BRIGHT])
+		return -EINVAL;
+
+	rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright);
+	if (rc < 0)
+		return rc;
+
+	return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright);
+}
+
+static struct backlight_ops pcc_backlight_ops = {
+	.get_brightness	= bl_get,
+	.update_status	= bl_set_status,
+};
+
+
+/* sysfs user interface functions */
+
+static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return sprintf(buf, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
+}
+
+static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return sprintf(buf, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
+}
+
+static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return sprintf(buf, "%u\n", pcc->sinf[SINF_MUTE]);
+}
+
+static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return sprintf(buf, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
+}
+
+static ssize_t set_sticky(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+	int val;
+
+	if (count && sscanf(buf, "%i", &val) == 1 &&
+	    (val == 0 || val == 1)) {
+		acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val);
+		pcc->sticky_mode = val;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(numbatt, S_IRUGO, show_numbatt, NULL);
+static DEVICE_ATTR(lcdtype, S_IRUGO, show_lcdtype, NULL);
+static DEVICE_ATTR(mute, S_IRUGO, show_mute, NULL);
+static DEVICE_ATTR(sticky_key, S_IRUGO | S_IWUSR, show_sticky, set_sticky);
+
+static struct attribute *pcc_sysfs_entries[] = {
+	&dev_attr_numbatt.attr,
+	&dev_attr_lcdtype.attr,
+	&dev_attr_mute.attr,
+	&dev_attr_sticky_key.attr,
+	NULL,
+};
+
+static struct attribute_group pcc_attr_group = {
+	.name	= NULL,		/* put in device directory */
+	.attrs	= pcc_sysfs_entries,
+};
+
+
+/* hotkey input device driver */
+
+static int pcc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+{
+	struct pcc_acpi *pcc = input_get_drvdata(dev);
+
+	if (scancode > ARRAY_SIZE(pcc->keymap))
+		return -EINVAL;
+
+	*keycode = pcc->keymap[scancode];
+
+	return 0;
+}
+
+static int pcc_setkeycode(struct input_dev *dev, int scancode, int keycode)
+{
+	struct pcc_acpi *pcc = input_get_drvdata(dev);
+
+	if (scancode > ARRAY_SIZE(pcc->keymap))
+		return -EINVAL;
+
+	if (keycode < 0 || keycode > KEY_MAX)
+		return -EINVAL;
+
+	pcc->keymap[scancode] = keycode;
+
+	return 0;
+}
+
+static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
+{
+	struct input_dev *hotk_input_dev = pcc->input_dev;
+	int rc;
+	int key_code, hkey_num;
+	unsigned long result;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_generate_keyinput");
+
+	rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
+				   NULL, &result);
+	if (!ACPI_SUCCESS(rc)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				 "error getting hotkey status\n"));
+		return;
+	}
+
+	acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
+
+	hkey_num = result & 0xf;
+
+	if (hkey_num < 0 || hkey_num > ARRAY_SIZE(pcc->keymap)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "hotkey number out of range: %d\n",
+				  hkey_num));
+		return;
+	}
+
+	key_code = pcc->keymap[hkey_num];
+
+	if (key_code != -1) {
+		int pushed = (result & 0x80) ? TRUE : FALSE;
+
+		input_report_key(hotk_input_dev, key_code, pushed);
+		input_sync(hotk_input_dev);
+	}
+
+	return;
+}
+
+static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct pcc_acpi *pcc = (struct pcc_acpi *) data;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_notify");
+
+	switch (event) {
+	case HKEY_NOTIFY:
+		acpi_pcc_generate_keyinput(pcc);
+		break;
+	default:
+		/* nothing to do */
+		break;
+	}
+}
+
+static int acpi_pcc_init_input(struct pcc_acpi *pcc)
+{
+	int rc;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_init_input");
+
+	pcc->input_dev = input_allocate_device();
+	if (!pcc->input_dev) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Couldn't allocate input device for hotkey"));
+		return -ENOMEM;
+	}
+
+	pcc->input_dev->evbit[0] = BIT(EV_KEY);
+
+	set_bit(KEY_BRIGHTNESSDOWN, pcc->input_dev->keybit);
+	set_bit(KEY_BRIGHTNESSUP, pcc->input_dev->keybit);
+	set_bit(KEY_MUTE, pcc->input_dev->keybit);
+	set_bit(KEY_VOLUMEDOWN, pcc->input_dev->keybit);
+	set_bit(KEY_VOLUMEUP, pcc->input_dev->keybit);
+	set_bit(KEY_SLEEP, pcc->input_dev->keybit);
+	set_bit(KEY_BATTERY, pcc->input_dev->keybit);
+	set_bit(KEY_SUSPEND, pcc->input_dev->keybit);
+	set_bit(KEY_DISPLAYTOGGLE, pcc->input_dev->keybit);
+
+	pcc->input_dev->name = ACPI_PCC_DRIVER_NAME;
+	pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS;
+	pcc->input_dev->id.bustype = 0x1a; /* XXX FIXME: BUS_I8042? */
+	pcc->input_dev->id.vendor = 0x0001;
+	pcc->input_dev->id.product = 0x0001;
+	pcc->input_dev->id.version = 0x0100;
+	pcc->input_dev->getkeycode = pcc_getkeycode;
+	pcc->input_dev->setkeycode = pcc_setkeycode;
+
+	/* load initial keymap */
+	memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap));
+
+	input_set_drvdata(pcc->input_dev, pcc);
+
+	rc = input_register_device(pcc->input_dev);
+	if (rc < 0)
+		input_free_device(pcc->input_dev);
+
+	return rc;
+}
+
+/* kernel module interface */
+
+static int acpi_pcc_hotkey_resume(struct acpi_device *device)
+{
+	struct pcc_acpi *pcc = acpi_driver_data(device);
+	acpi_status status = AE_OK;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_resume");
+
+	if (device == NULL || pcc == NULL)
+		return -EINVAL;
+
+	ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
+			  pcc->sticky_mode));
+
+	status = acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
+
+	return status == AE_OK ? 0 : -EINVAL;
+}
+
+static int acpi_pcc_hotkey_add(struct acpi_device *device)
+{
+	acpi_status status;
+	struct pcc_acpi *pcc;
+	int num_sifr, result;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_add");
+
+	if (!device)
+		return -EINVAL;
+
+	num_sifr = acpi_pcc_get_sqty(device);
+
+	if (num_sifr > 255) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large"));
+		return -ENODEV;
+	}
+
+	pcc = kmalloc(sizeof(struct pcc_acpi), GFP_KERNEL);
+	if (!pcc) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Couldn't allocate mem for pcc"));
+		return -ENOMEM;
+	}
+	memset(pcc, 0, sizeof(struct pcc_acpi));
+
+	pcc->sinf = kmalloc(sizeof(u32) * (num_sifr + 1), GFP_KERNEL);
+	if (!pcc->sinf) {
+		result = -ENOMEM;
+		goto out_hotkey;
+	}
+
+	pcc->device = device;
+	pcc->handle = device->handle;
+	pcc->num_sifr = num_sifr;
+	acpi_driver_data(device) = pcc;
+	strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
+	strcpy(acpi_device_class(device), ACPI_PCC_CLASS);
+
+	result = acpi_pcc_init_input(pcc);
+	if (result) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error installing keyinput handler\n"));
+		goto out_sinf;
+	}
+
+	/* initialize hotkey input device */
+	status = acpi_install_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
+					     acpi_pcc_hotkey_notify, pcc);
+
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error installing notify handler\n"));
+		result = -ENODEV;
+		goto out_input;
+	}
+
+	/* initialize backlight */
+	pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
+						   &pcc_backlight_ops);
+	if (IS_ERR(pcc->backlight))
+		goto out_notify;
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				 "Couldn't retrieve BIOS data\n"));
+		goto out_backlight;
+	}
+
+	/* read the initial brightness setting from the hardware */
+	pcc->backlight->props.max_brightness =
+					pcc->sinf[SINF_AC_MAX_BRIGHT];
+	pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
+
+	/* read the initial sticky key mode from the hardware */
+	pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY];
+
+	/* add sysfs attributes */
+	result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
+	if (result)
+		goto out_backlight;
+
+	return 0;
+
+out_backlight:
+	backlight_device_unregister(pcc->backlight);
+out_notify:
+	acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
+				   acpi_pcc_hotkey_notify);
+out_input:
+	input_unregister_device(pcc->input_dev);
+	kfree(input_get_drvdata(pcc->input_dev));
+	input_free_device(pcc->input_dev);
+out_sinf:
+	kfree(pcc->sinf);
+out_hotkey:
+	kfree(pcc);
+
+	return result;
+}
+
+static int __init acpi_pcc_init(void)
+{
+	int result = 0;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_init");
+
+	if (acpi_disabled)
+		return -ENODEV;
+
+	result = acpi_bus_register_driver(&acpi_pcc_driver);
+	if (result < 0) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error registering hotkey driver\n"));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
+{
+	struct pcc_acpi *pcc = acpi_driver_data(device);
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_remove");
+
+	if (!device || !pcc)
+		return -EINVAL;
+
+	sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);
+
+	backlight_device_unregister(pcc->backlight);
+
+	acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
+				   acpi_pcc_hotkey_notify);
+
+	input_unregister_device(pcc->input_dev);
+	kfree(input_get_drvdata(pcc->input_dev));
+	input_free_device(pcc->input_dev);
+
+	kfree(pcc->sinf);
+	kfree(pcc);
+
+	return 0;
+}
+
+static void __exit acpi_pcc_exit(void)
+{
+	ACPI_FUNCTION_TRACE("acpi_pcc_exit");
+
+	acpi_bus_unregister_driver(&acpi_pcc_driver);
+}
+
+module_init(acpi_pcc_init);
+module_exit(acpi_pcc_exit);
-- 
- Harald Welte <laforge@gnumonks.org>           http://laforge.gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
                                                  (ETSI EN 300 175-7 Ch. A6)

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.93
  2008-07-03 17:35 ` [PATCH] Panasonic Let's Note laptop extras driver v0.93 Harald Welte
@ 2008-07-03 19:23   ` Matthew Garrett
  2008-07-04  1:31     ` Harald Welte
  0 siblings, 1 reply; 28+ messages in thread
From: Matthew Garrett @ 2008-07-03 19:23 UTC (permalink / raw)
  To: Harald Welte; +Cc: linux-acpi, Hiroshi Miura

On Fri, Jul 04, 2008 at 01:35:42AM +0800, Harald Welte wrote:
> +
> +static int pcc_setkeycode(struct input_dev *dev, int scancode, int keycode)
> +{
> +	struct pcc_acpi *pcc = input_get_drvdata(dev);
> +
> +	if (scancode > ARRAY_SIZE(pcc->keymap))
> +		return -EINVAL;
> +
> +	if (keycode < 0 || keycode > KEY_MAX)
> +		return -EINVAL;
> +
> +	pcc->keymap[scancode] = keycode;

You probably want to set_bit the input device here, and also clean up 
any now-unused keycodes from there. I'd also suggest sending KEY_PROG1 
by default on the CPU throttle button - some users will find it easier 
if it already generates a keycode. Other than that, looks good.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.93
  2008-07-03 19:23   ` Matthew Garrett
@ 2008-07-04  1:31     ` Harald Welte
  0 siblings, 0 replies; 28+ messages in thread
From: Harald Welte @ 2008-07-04  1:31 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: linux-acpi, Hiroshi Miura

[-- Attachment #1: Type: text/plain, Size: 1103 bytes --]

Hi Matthew,

On Thu, Jul 03, 2008 at 08:23:03PM +0100, Matthew Garrett wrote:
> On Fri, Jul 04, 2008 at 01:35:42AM +0800, Harald Welte wrote:
> > +
> > +static int pcc_setkeycode(struct input_dev *dev, int scancode, int keycode)
> > [...]
> 
> You probably want to set_bit the input device here, and also clean up 
> any now-unused keycodes from there. I'd also suggest sending KEY_PROG1 
> by default on the CPU throttle button - some users will find it easier 
> if it already generates a keycode. Other than that, looks good.

Ok, I've added this.  Thanks for noticing.  I'm waiting on your feedback
with regard to the /proc events vs. input event device and acpid
handling.  Once I am clear on this, I'll submit the hopefully final
version that can be merged.

Cheers,
-- 
- Harald Welte <laforge@gnumonks.org>           http://laforge.gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
                                                  (ETSI EN 300 175-7 Ch. A6)

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver
  2008-07-03 17:08     ` Matthew Garrett
@ 2008-07-04  3:14       ` Henrique de Moraes Holschuh
  2008-07-04  4:43         ` Harald Welte
  0 siblings, 1 reply; 28+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-07-04  3:14 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: Harald Welte, linux-acpi, Hiroshi Miura, hadi

On Thu, 03 Jul 2008, Matthew Garrett wrote:
> On Fri, Jul 04, 2008 at 12:52:19AM +0800, Harald Welte wrote:
> > So I vote for keeping those events in the driver - just as long as there
> > is no new standard mechanism for signalling brightness up/down button
> > events to userspace.
> 
> There is - they should just be sent as KEY_BRIGHTNESSUP and 
> KEY_BRIGHTNESSDOWN.

Once bitten, twice shy.  I understand these buttons in the Panasonic Let's
Note do NOTHING in firmware/hardware (i.e. if you don't hook something to
them, brightness is NOT changed)?

Because if they DO anything else than just staying there prettly and telling
you they have been pressed, then you are **NOT** to issue
KEY_BRIGHTNESSUP/DOWN.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

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

* [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-07-02  9:00 [PATCH] Panasonic Let's Note laptop extras driver Harald Welte
  2008-07-02 10:19 ` Matthew Garrett
  2008-07-03 17:35 ` [PATCH] Panasonic Let's Note laptop extras driver v0.93 Harald Welte
@ 2008-07-04  3:44 ` Harald Welte
  2008-07-04  9:10   ` Matthew Garrett
  2008-09-22 22:38   ` Len Brown
  2 siblings, 2 replies; 28+ messages in thread
From: Harald Welte @ 2008-07-04  3:44 UTC (permalink / raw)
  To: linux-acpi; +Cc: Hiroshi Miura

Hi!

<disclaimer>
This is hopefully the last version before mainline inclusion.  I hereby
officially request that this driver is merged!
</disclaimer>

============

This is a driver for ACPI extras such as hotkeys and backlight
brightness control on various Panasonic "Let's Note" series laptop
computers.

It exports the backlight via the backlight class device API,
and the hotkeys as input event device.  Some more esoteric
items like number of installed batteries are exported via sysfs
device attributes.

Hotkey events also generate old-style ACPI enents through
/proc/acpi/event to interoperate with current versions of acpid.

Signed-off-by: Harald Welte <laforge@gnumonks.org>

---

non-changelog description:

Ever since I bought my Panasonic Let's Note CF-R5 in September 2006
(like other well-known kernel hackers such as Jamal Selim and Stephen
Hemminger) I'm using the (apparently abandoned) out-of-tree pcc_acpi.c
driver.  Various changes in the kernel API's have required me to
merge/forward-port it from one kernel release to the other.

In more than three attempts (two of them in the last 6 months), I have
tried to contact the main author (Hiroshi Miura, see Cc) and asked if he
had plans to submit the driver for mainline inclusion or if he'd support
any such submission attempt by me.

The co-author David Bronaugh has promptly responded and is fine with
merging it mainline.

Though we are now in the unfortunate situation where the main author is
no longer reachable, I don't want to delay this even longer and want to
get this driver merged.  The original code was explicitly GPL licensed,
so there is no legal requirement to have explicit permission from
Hiroshi Miura.

For the time being, I'm also willing to be the maintainer of this
driver.

Any feedback is welcome.  Please excuse obvious mistakes.  While I have
a lot of kernel experience, I have not done any work on ACPI before.

Thanks for your review,

diff --git a/MAINTAINERS b/MAINTAINERS
index e6c06fa..df59ad5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3108,6 +3108,11 @@ M:	olof@lixom.net
 L:	i2c@lm-sensors.org
 S:	Maintained
 
+PANASONIC LAPTOP ACPI EXTRAS DRIVER
+P:	Harald Welte
+M:	laforge@gnumonks.org
+S:	Maintained
+
 PARALLEL PORT SUPPORT
 L:	linux-parport@lists.infradead.org (subscribers-only)
 S:	Orphan
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index c52fca8..1da4bc8 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -259,6 +259,17 @@ config ACPI_ASUS
 	  NOTE: This driver is deprecated and will probably be removed soon,
 	  use asus-laptop instead.
 
+config ACPI_PANASONIC
+	tristate "Panasonic Laptop Extras"
+	depends on X86
+	select BACKLIGHT_CLASS_DEVICE
+	---help---
+	  This driver adds support for access to certain system settings
+	  on Panasonic Let's Note laptops.  
+
+	  If you have a Panasonic Let's note laptop (such as the R1(N variant),
+	  R2, R3, R5, T2, W2 and Y2 series), say Y.
+
 config ACPI_TOSHIBA
 	tristate "Toshiba Laptop Extras"
 	depends on X86
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 40b0fca..04c1b5d 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_ACPI_WMI)		+= wmi.o
 obj-$(CONFIG_ACPI_ASUS)		+= asus_acpi.o
 obj-$(CONFIG_ACPI_TOSHIBA)	+= toshiba_acpi.o
 obj-$(CONFIG_ACPI_HOTPLUG_MEMORY)	+= acpi_memhotplug.o
+obj-$(CONFIG_ACPI_PANASONIC)	+= pcc_acpi.o
 obj-$(CONFIG_ACPI_PROCFS_POWER)	+= cm_sbs.o
 obj-$(CONFIG_ACPI_SBS)		+= sbshc.o
 obj-$(CONFIG_ACPI_SBS)		+= sbs.o
diff --git a/drivers/acpi/pcc_acpi.c b/drivers/acpi/pcc_acpi.c
new file mode 100644
index 0000000..ee0702f
--- /dev/null
+++ b/drivers/acpi/pcc_acpi.c
@@ -0,0 +1,779 @@
+/*
+ *  Panasonic HotKey and LCD brightness control driver
+ *  (C) 2004 Hiroshi Miura <miura@da-cha.org>
+ *  (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/
+ *  (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>
+ *  (C) 2004 David Bronaugh <dbronaugh>
+ *  (C) 2006-2008 Harald Welte <laforge@gnumonks.org>
+ *
+ *  derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  publicshed by the Free Software Foundation.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *---------------------------------------------------------------------------
+ *
+ * ChangeLog:
+ * 	Jul.04, 2008	Harald Welte <laforge@gnumonks.org>
+ * 		-v0.94	replace /proc interface with device attributes
+ * 			support {set,get}keycode on th input device
+ *
+ *      Jun.27, 2008	Harald Welte <laforge@gnumonks.org>
+ *      	-v0.92	merge with 2.6.26-rc6 input API changes
+ *      		remove broken <= 2.6.15 kernel support
+ *      		resolve all compiler warnings
+ *      		various coding style fixes (checkpatch.pl)
+ *      		add support for backlight api
+ *      		major code restructuring
+ *
+ * 	Dac.28, 2007	Harald Welte <laforge@gnumonks.org>
+ * 		-v0.91	merge with 2.6.24-rc6 ACPI changes
+ *
+ * 	Nov.04, 2006	Hiroshi Miura <miura@da-cha.org>
+ * 		-v0.9	remove warning about section reference.
+ * 			remove acpi_os_free
+ * 			add /proc/acpi/pcc/brightness interface for HAL access
+ * 			merge dbronaugh's enhancement
+ * 			Aug.17, 2004 David Bronaugh (dbronaugh)
+ *  				- Added screen brightness setting interface
+ *				  Thanks to FreeBSD crew (acpi_panasonic.c)
+ * 				  for the ideas I needed to accomplish it
+ *
+ *	May.29, 2006	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.8.4 follow to change keyinput structure
+ *			thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,
+ *			Jacob Bower <jacob.bower@ic.ac.uk> and
+ *			Hiroshi Yokota for providing solutions.
+ *
+ *	Oct.02, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.8.2	merge code of YOKOTA Hiroshi
+ *					<yokota@netlab.is.tsukuba.ac.jp>.
+ *			Add sticky key mode interface.
+ *			Refactoring acpi_pcc_generate_keyinput().
+ *
+ *	Sep.15, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.8	Generate key input event on input subsystem.
+ *			This is based on yet another driver written by
+ *							Ryuta Nakanishi.
+ *
+ *	Sep.10, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.7	Change proc interface functions using seq_file
+ *			facility as same as other ACPI drivers.
+ *
+ *	Aug.28, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.6.4 Fix a silly error with status checking
+ *
+ *	Aug.25, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.6.3 replace read_acpi_int by standard function
+ *							acpi_evaluate_integer
+ *			some clean up and make smart copyright notice.
+ *			fix return value of pcc_acpi_get_key()
+ *			fix checking return value of acpi_bus_register_driver()
+ *
+ *      Aug.22, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              -v0.6.2 Add check on ACPI data (num_sifr)
+ *                      Coding style cleanups, better error messages/handling
+ *			Fixed an off-by-one error in memory allocation
+ *
+ *      Aug.21, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              -v0.6.1 Fix a silly error with status checking
+ *
+ *      Aug.20, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              - v0.6  Correct brightness controls to reflect reality
+ *                      based on information gleaned by Hiroshi Miura
+ *                      and discussions with Hiroshi Miura
+ *
+ *	Aug.10, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.5  support LCD brightness control
+ *			based on the disclosed information by MEI.
+ *
+ *	Jul.25, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.4  first post version
+ *		        add function to retrive SIFR
+ *
+ *	Jul.24, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.3  get proper status of hotkey
+ *
+ *      Jul.22, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.2  add HotKey handler
+ *
+ *      Jul.17, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.1  start from toshiba_acpi driver written by John Belmonte
+ *
+ */
+
+#define ACPI_PCC_VERSION	"0.94"
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/backlight.h>
+#include <linux/ctype.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/input.h>
+
+
+#ifndef ACPI_HOTKEY_COMPONENT
+#define ACPI_HOTKEY_COMPONENT	0x10000000
+#endif
+
+#define _COMPONENT		ACPI_HOTKEY_COMPONENT
+ACPI_MODULE_NAME("pcc_acpi")
+
+MODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte");
+MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");
+MODULE_LICENSE("GPL");
+
+#define LOGPREFIX "pcc_acpi: "
+
+/* Define ACPI PATHs */
+/* Lets note hotkeys */
+#define METHOD_HKEY_QUERY	"HINF"
+#define METHOD_HKEY_SQTY	"SQTY"
+#define METHOD_HKEY_SINF	"SINF"
+#define METHOD_HKEY_SSET	"SSET"
+#define HKEY_NOTIFY		 0x80
+
+/* definitions for /proc/ interface */
+#define ACPI_PCC_DRIVER_NAME	"PCC Extra Driver"
+#define ACPI_PCC_DEVICE_NAME	"PCCExtra"
+#define ACPI_PCC_CLASS		"pcc"
+#define PROC_PCC		ACPI_PCC_CLASS
+
+#define ACPI_PCC_INPUT_PHYS	"panasonic/hkey0"
+
+#define PROC_STR_MAX_LEN  8
+
+/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
+   ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
+*/
+enum SINF_BITS { SINF_NUM_BATTERIES = 0,
+		 SINF_LCD_TYPE,
+		 SINF_AC_MAX_BRIGHT,
+		 SINF_AC_MIN_BRIGHT,
+		 SINF_AC_CUR_BRIGHT,
+		 SINF_DC_MAX_BRIGHT,
+		 SINF_DC_MIN_BRIGHT,
+		 SINF_DC_CUR_BRIGHT,
+		 SINF_MUTE,
+		 SINF_RESERVED,
+		 SINF_ENV_STATE,
+		 SINF_STICKY_KEY = 0x80,
+	};
+/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
+
+static int acpi_pcc_hotkey_add(struct acpi_device *device);
+static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type);
+static int acpi_pcc_hotkey_resume(struct acpi_device *device);
+
+static const struct acpi_device_id pcc_device_ids[] = {
+	{ "MAT0012", 0},
+	{ "MAT0013", 0},
+	{ "MAT0018", 0},
+	{ "MAT0019", 0},
+	{ "", 0},
+};
+
+static struct acpi_driver acpi_pcc_driver = {
+	.name =		ACPI_PCC_DRIVER_NAME,
+	.class =	ACPI_PCC_CLASS,
+	.ids =		pcc_device_ids,
+	.ops =		{
+				.add =		acpi_pcc_hotkey_add,
+				.remove =	acpi_pcc_hotkey_remove,
+				.resume =       acpi_pcc_hotkey_resume,
+			},
+};
+
+#define KEYMAP_SIZE		11
+static const int initial_keymap[KEYMAP_SIZE] = {
+	/*  0 */ -1,
+	/*  1 */ KEY_BRIGHTNESSDOWN,
+	/*  2 */ KEY_BRIGHTNESSUP,
+	/*  3 */ KEY_DISPLAYTOGGLE,
+	/*  4 */ KEY_MUTE,
+	/*  5 */ KEY_VOLUMEDOWN,
+	/*  6 */ KEY_VOLUMEUP,
+	/*  7 */ KEY_SLEEP,
+	/*  8 */ KEY_PROG1, /* Change CPU boost */
+	/*  9 */ KEY_BATTERY,
+	/* 10 */ KEY_SUSPEND,
+};
+
+struct pcc_acpi {
+	acpi_handle		handle;
+	unsigned long		num_sifr;
+	int			sticky_mode;
+	u32 			*sinf;
+	struct acpi_device	*device;
+	struct input_dev	*input_dev;
+	struct backlight_device	*backlight;
+	int			keymap[KEYMAP_SIZE];
+};
+
+struct pcc_keyinput {
+	struct acpi_hotkey      *hotkey;
+};
+
+/* method access functions */
+static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
+{
+	union acpi_object in_objs[] = {
+		{ .integer.type  = ACPI_TYPE_INTEGER,
+		  .integer.value = func, },
+		{ .integer.type  = ACPI_TYPE_INTEGER,
+		  .integer.value = val, },
+	};
+	struct acpi_object_list params = {
+		.count   = ARRAY_SIZE(in_objs),
+		.pointer = in_objs,
+	};
+	acpi_status status = AE_OK;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_write_sset");
+
+	status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET,
+				      &params, NULL);
+
+	return status == AE_OK;
+}
+
+static inline int acpi_pcc_get_sqty(struct acpi_device *device)
+{
+	unsigned long s;
+	acpi_status status;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_get_sqty");
+
+	status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,
+				       NULL, &s);
+	if (ACPI_SUCCESS(status))
+		return s;
+	else {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "evaluation error HKEY.SQTY\n"));
+		return -EINVAL;
+	}
+}
+
+static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
+{
+	acpi_status status;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *hkey = NULL;
+	int i;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_retrieve_biosdata");
+
+	status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, 0,
+				      &buffer);
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "evaluation error HKEY.SINF\n"));
+		return 0;
+	}
+
+	hkey = buffer.pointer;
+	if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
+		goto end;
+	}
+
+	if (pcc->num_sifr < hkey->package.count) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				 "SQTY reports bad SINF length\n"));
+		status = AE_ERROR;
+		goto end;
+	}
+
+	for (i = 0; i < hkey->package.count; i++) {
+		union acpi_object *element = &(hkey->package.elements[i]);
+		if (likely(element->type == ACPI_TYPE_INTEGER)) {
+			sinf[i] = element->integer.value;
+		} else
+			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+					 "Invalid HKEY.SINF data\n"));
+	}
+	sinf[hkey->package.count] = -1;
+
+end:
+	kfree(buffer.pointer);
+	return status == AE_OK;
+}
+
+/* backlight API interface functions */
+
+/* This driver currently treats AC and DC brightness identical,
+ * since we don't need to invent an interface to the core ACPI
+ * logic to receive events in case a power supply is plugged in
+ * or removed */
+
+static int bl_get(struct backlight_device *bd)
+{
+	struct pcc_acpi *pcc = bl_get_data(bd);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return pcc->sinf[SINF_AC_CUR_BRIGHT];
+}
+
+static int bl_set_status(struct backlight_device *bd)
+{
+	struct pcc_acpi *pcc = bl_get_data(bd);
+	int bright = bd->props.brightness;
+	int rc;
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
+		bright = pcc->sinf[SINF_AC_MIN_BRIGHT];
+
+	if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT])
+		bright = pcc->sinf[SINF_DC_MIN_BRIGHT];
+
+	if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] ||
+	    bright > pcc->sinf[SINF_AC_MAX_BRIGHT])
+		return -EINVAL;
+
+	rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright);
+	if (rc < 0)
+		return rc;
+
+	return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright);
+}
+
+static struct backlight_ops pcc_backlight_ops = {
+	.get_brightness	= bl_get,
+	.update_status	= bl_set_status,
+};
+
+
+/* sysfs user interface functions */
+
+static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return sprintf(buf, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
+}
+
+static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return sprintf(buf, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
+}
+
+static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return sprintf(buf, "%u\n", pcc->sinf[SINF_MUTE]);
+}
+
+static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return sprintf(buf, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
+}
+
+static ssize_t set_sticky(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+	int val;
+
+	if (count && sscanf(buf, "%i", &val) == 1 &&
+	    (val == 0 || val == 1)) {
+		acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val);
+		pcc->sticky_mode = val;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(numbatt, S_IRUGO, show_numbatt, NULL);
+static DEVICE_ATTR(lcdtype, S_IRUGO, show_lcdtype, NULL);
+static DEVICE_ATTR(mute, S_IRUGO, show_mute, NULL);
+static DEVICE_ATTR(sticky_key, S_IRUGO | S_IWUSR, show_sticky, set_sticky);
+
+static struct attribute *pcc_sysfs_entries[] = {
+	&dev_attr_numbatt.attr,
+	&dev_attr_lcdtype.attr,
+	&dev_attr_mute.attr,
+	&dev_attr_sticky_key.attr,
+	NULL,
+};
+
+static struct attribute_group pcc_attr_group = {
+	.name	= NULL,		/* put in device directory */
+	.attrs	= pcc_sysfs_entries,
+};
+
+
+/* hotkey input device driver */
+
+static int pcc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+{
+	struct pcc_acpi *pcc = input_get_drvdata(dev);
+
+	if (scancode > ARRAY_SIZE(pcc->keymap))
+		return -EINVAL;
+
+	*keycode = pcc->keymap[scancode];
+
+	return 0;
+}
+
+static int keymap_get_by_keycode(struct pcc_acpi *pcc, int keycode)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) {
+		if (pcc->keymap[i] == keycode)
+			return i+1;
+	}
+
+	return 0;
+}
+
+static int pcc_setkeycode(struct input_dev *dev, int scancode, int keycode)
+{
+	struct pcc_acpi *pcc = input_get_drvdata(dev);
+	int oldkeycode;
+
+	if (scancode > ARRAY_SIZE(pcc->keymap))
+		return -EINVAL;
+
+	if (keycode < 0 || keycode > KEY_MAX)
+		return -EINVAL;
+
+	oldkeycode = pcc->keymap[scancode];
+	pcc->keymap[scancode] = keycode;
+
+	set_bit(keycode, dev->keybit);
+
+	if (!keymap_get_by_keycode(pcc, oldkeycode))
+		clear_bit(oldkeycode, dev->keybit);
+
+	return 0;
+}
+
+static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
+{
+	struct input_dev *hotk_input_dev = pcc->input_dev;
+	int rc;
+	int key_code, hkey_num;
+	unsigned long result;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_generate_keyinput");
+
+	rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
+				   NULL, &result);
+	if (!ACPI_SUCCESS(rc)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				 "error getting hotkey status\n"));
+		return;
+	}
+
+	acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
+
+	hkey_num = result & 0xf;
+
+	if (hkey_num < 0 || hkey_num > ARRAY_SIZE(pcc->keymap)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "hotkey number out of range: %d\n",
+				  hkey_num));
+		return;
+	}
+
+	key_code = pcc->keymap[hkey_num];
+
+	if (key_code != -1) {
+		int pushed = (result & 0x80) ? TRUE : FALSE;
+
+		input_report_key(hotk_input_dev, key_code, pushed);
+		input_sync(hotk_input_dev);
+	}
+
+	return;
+}
+
+static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct pcc_acpi *pcc = (struct pcc_acpi *) data;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_notify");
+
+	switch (event) {
+	case HKEY_NOTIFY:
+		acpi_pcc_generate_keyinput(pcc);
+		break;
+	default:
+		/* nothing to do */
+		break;
+	}
+}
+
+static int acpi_pcc_init_input(struct pcc_acpi *pcc)
+{
+	int rc;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_init_input");
+
+	pcc->input_dev = input_allocate_device();
+	if (!pcc->input_dev) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Couldn't allocate input device for hotkey"));
+		return -ENOMEM;
+	}
+
+	pcc->input_dev->evbit[0] = BIT(EV_KEY);
+
+	set_bit(KEY_BRIGHTNESSDOWN, pcc->input_dev->keybit);
+	set_bit(KEY_BRIGHTNESSUP, pcc->input_dev->keybit);
+	set_bit(KEY_MUTE, pcc->input_dev->keybit);
+	set_bit(KEY_VOLUMEDOWN, pcc->input_dev->keybit);
+	set_bit(KEY_VOLUMEUP, pcc->input_dev->keybit);
+	set_bit(KEY_SLEEP, pcc->input_dev->keybit);
+	set_bit(KEY_BATTERY, pcc->input_dev->keybit);
+	set_bit(KEY_SUSPEND, pcc->input_dev->keybit);
+	set_bit(KEY_DISPLAYTOGGLE, pcc->input_dev->keybit);
+	set_bit(KEY_PROG1, pcc->input_dev->keybit);
+
+	pcc->input_dev->name = ACPI_PCC_DRIVER_NAME;
+	pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS;
+	pcc->input_dev->id.bustype = 0x1a; /* XXX FIXME: BUS_I8042? */
+	pcc->input_dev->id.vendor = 0x0001;
+	pcc->input_dev->id.product = 0x0001;
+	pcc->input_dev->id.version = 0x0100;
+	pcc->input_dev->getkeycode = pcc_getkeycode;
+	pcc->input_dev->setkeycode = pcc_setkeycode;
+
+	/* load initial keymap */
+	memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap));
+
+	input_set_drvdata(pcc->input_dev, pcc);
+
+	rc = input_register_device(pcc->input_dev);
+	if (rc < 0)
+		input_free_device(pcc->input_dev);
+
+	return rc;
+}
+
+/* kernel module interface */
+
+static int acpi_pcc_hotkey_resume(struct acpi_device *device)
+{
+	struct pcc_acpi *pcc = acpi_driver_data(device);
+	acpi_status status = AE_OK;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_resume");
+
+	if (device == NULL || pcc == NULL)
+		return -EINVAL;
+
+	ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
+			  pcc->sticky_mode));
+
+	status = acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
+
+	return status == AE_OK ? 0 : -EINVAL;
+}
+
+static int acpi_pcc_hotkey_add(struct acpi_device *device)
+{
+	acpi_status status;
+	struct pcc_acpi *pcc;
+	int num_sifr, result;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_add");
+
+	if (!device)
+		return -EINVAL;
+
+	num_sifr = acpi_pcc_get_sqty(device);
+
+	if (num_sifr > 255) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large"));
+		return -ENODEV;
+	}
+
+	pcc = kmalloc(sizeof(struct pcc_acpi), GFP_KERNEL);
+	if (!pcc) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Couldn't allocate mem for pcc"));
+		return -ENOMEM;
+	}
+	memset(pcc, 0, sizeof(struct pcc_acpi));
+
+	pcc->sinf = kmalloc(sizeof(u32) * (num_sifr + 1), GFP_KERNEL);
+	if (!pcc->sinf) {
+		result = -ENOMEM;
+		goto out_hotkey;
+	}
+
+	pcc->device = device;
+	pcc->handle = device->handle;
+	pcc->num_sifr = num_sifr;
+	acpi_driver_data(device) = pcc;
+	strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
+	strcpy(acpi_device_class(device), ACPI_PCC_CLASS);
+
+	result = acpi_pcc_init_input(pcc);
+	if (result) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error installing keyinput handler\n"));
+		goto out_sinf;
+	}
+
+	/* initialize hotkey input device */
+	status = acpi_install_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
+					     acpi_pcc_hotkey_notify, pcc);
+
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error installing notify handler\n"));
+		result = -ENODEV;
+		goto out_input;
+	}
+
+	/* initialize backlight */
+	pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
+						   &pcc_backlight_ops);
+	if (IS_ERR(pcc->backlight))
+		goto out_notify;
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				 "Couldn't retrieve BIOS data\n"));
+		goto out_backlight;
+	}
+
+	/* read the initial brightness setting from the hardware */
+	pcc->backlight->props.max_brightness =
+					pcc->sinf[SINF_AC_MAX_BRIGHT];
+	pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
+
+	/* read the initial sticky key mode from the hardware */
+	pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY];
+
+	/* add sysfs attributes */
+	result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
+	if (result)
+		goto out_backlight;
+
+	return 0;
+
+out_backlight:
+	backlight_device_unregister(pcc->backlight);
+out_notify:
+	acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
+				   acpi_pcc_hotkey_notify);
+out_input:
+	input_unregister_device(pcc->input_dev);
+	kfree(input_get_drvdata(pcc->input_dev));
+	input_free_device(pcc->input_dev);
+out_sinf:
+	kfree(pcc->sinf);
+out_hotkey:
+	kfree(pcc);
+
+	return result;
+}
+
+static int __init acpi_pcc_init(void)
+{
+	int result = 0;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_init");
+
+	if (acpi_disabled)
+		return -ENODEV;
+
+	result = acpi_bus_register_driver(&acpi_pcc_driver);
+	if (result < 0) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error registering hotkey driver\n"));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
+{
+	struct pcc_acpi *pcc = acpi_driver_data(device);
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_remove");
+
+	if (!device || !pcc)
+		return -EINVAL;
+
+	sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);
+
+	backlight_device_unregister(pcc->backlight);
+
+	acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
+				   acpi_pcc_hotkey_notify);
+
+	input_unregister_device(pcc->input_dev);
+	kfree(input_get_drvdata(pcc->input_dev));
+	input_free_device(pcc->input_dev);
+
+	kfree(pcc->sinf);
+	kfree(pcc);
+
+	return 0;
+}
+
+static void __exit acpi_pcc_exit(void)
+{
+	ACPI_FUNCTION_TRACE("acpi_pcc_exit");
+
+	acpi_bus_unregister_driver(&acpi_pcc_driver);
+}
+
+module_init(acpi_pcc_init);
+module_exit(acpi_pcc_exit);
-- 
- Harald Welte <laforge@gnumonks.org>           http://laforge.gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
                                                  (ETSI EN 300 175-7 Ch. A6)

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver
  2008-07-04  3:14       ` Henrique de Moraes Holschuh
@ 2008-07-04  4:43         ` Harald Welte
  2008-07-04 11:55           ` Henrique de Moraes Holschuh
  0 siblings, 1 reply; 28+ messages in thread
From: Harald Welte @ 2008-07-04  4:43 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh
  Cc: Matthew Garrett, linux-acpi, Hiroshi Miura, hadi

[-- Attachment #1: Type: text/plain, Size: 1462 bytes --]

On Fri, Jul 04, 2008 at 12:14:04AM -0300, Henrique de Moraes Holschuh wrote:
> On Thu, 03 Jul 2008, Matthew Garrett wrote:
> > On Fri, Jul 04, 2008 at 12:52:19AM +0800, Harald Welte wrote:
> > > So I vote for keeping those events in the driver - just as long as there
> > > is no new standard mechanism for signalling brightness up/down button
> > > events to userspace.
> > 
> > There is - they should just be sent as KEY_BRIGHTNESSUP and 
> > KEY_BRIGHTNESSDOWN.
> 
> Once bitten, twice shy.  I understand these buttons in the Panasonic Let's
> Note do NOTHING in firmware/hardware (i.e. if you don't hook something to
> them, brightness is NOT changed)?

no, they don't do anything in firmware/hardware.  They just generate
events, and the OS/driver need to take care of any action to perform.

> Because if they DO anything else than just staying there prettly and telling
> you they have been pressed, then you are **NOT** to issue
> KEY_BRIGHTNESSUP/DOWN.

thanks for making this clear.  However, this doesn't apply to the
panasonic laptop hotkeys.  They really don't do anything unless the
os/driver takes care of it.

-- 
- Harald Welte <laforge@gnumonks.org>           http://laforge.gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
                                                  (ETSI EN 300 175-7 Ch. A6)

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-07-04  3:44 ` [PATCH] Panasonic Let's Note laptop extras driver v0.94 Harald Welte
@ 2008-07-04  9:10   ` Matthew Garrett
  2008-07-04 12:07     ` Henrique de Moraes Holschuh
  2008-07-12 10:36     ` Harald Welte
  2008-09-22 22:38   ` Len Brown
  1 sibling, 2 replies; 28+ messages in thread
From: Matthew Garrett @ 2008-07-04  9:10 UTC (permalink / raw)
  To: Harald Welte; +Cc: linux-acpi, Hiroshi Miura

I'd still have a preference for not including the proc_generate_event 
stuff, but I'm not sure that there's any real consensus on that so I'm 
not going to block it.

Acked-by: mjg@redhat.com

-- 
Matthew Garrett | mjg59@srcf.ucam.org

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver
  2008-07-04  4:43         ` Harald Welte
@ 2008-07-04 11:55           ` Henrique de Moraes Holschuh
  0 siblings, 0 replies; 28+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-07-04 11:55 UTC (permalink / raw)
  To: Harald Welte; +Cc: Matthew Garrett, linux-acpi, Hiroshi Miura, hadi

On Fri, 04 Jul 2008, Harald Welte wrote:
> > Once bitten, twice shy.  I understand these buttons in the Panasonic Let's
> > Note do NOTHING in firmware/hardware (i.e. if you don't hook something to
> > them, brightness is NOT changed)?
> 
> no, they don't do anything in firmware/hardware.  They just generate
> events, and the OS/driver need to take care of any action to perform.

Ah, good.  That's much easier to deal with, yes.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-07-04  9:10   ` Matthew Garrett
@ 2008-07-04 12:07     ` Henrique de Moraes Holschuh
  2008-07-04 12:11       ` Matthew Garrett
  2008-07-12 10:36     ` Harald Welte
  1 sibling, 1 reply; 28+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-07-04 12:07 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: Harald Welte, linux-acpi, Hiroshi Miura

On Fri, 04 Jul 2008, Matthew Garrett wrote:
> I'd still have a preference for not including the proc_generate_event 
> stuff, but I'm not sure that there's any real consensus on that so I'm 
> not going to block it.

FWIW, thinkpad-acpi had to keep the proc_generate_event stuff (it is an
established ABI for thinkpad-acpi since 2.6.12 or thereabouts...), but it
does NOT issue those events over netlink at all, so anyone switching to the
new ACPI event infrastructure is forced to also switch to the input-device
infrastructure to get hotkey presses.

And once /proc/acpi is gone, the old way will be gone.

Unless someone posts a recipe here of how to get HAL to connect to an input
device, and how to tell it to run code when a KEY_* event is received (which
you could add to the driver documentation), I'd say proc_generate_event will
have to be tolerated.  It is all most people know how to deal with in
userspace.

So, FW(little)IW...
Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-07-04 12:07     ` Henrique de Moraes Holschuh
@ 2008-07-04 12:11       ` Matthew Garrett
  2008-07-04 12:22         ` Henrique de Moraes Holschuh
  0 siblings, 1 reply; 28+ messages in thread
From: Matthew Garrett @ 2008-07-04 12:11 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh; +Cc: Harald Welte, linux-acpi, Hiroshi Miura

On Fri, Jul 04, 2008 at 09:07:54AM -0300, Henrique de Moraes Holschuh wrote:

> Unless someone posts a recipe here of how to get HAL to connect to an input
> device, and how to tell it to run code when a KEY_* event is received (which
> you could add to the driver documentation), I'd say proc_generate_event will
> have to be tolerated.  It is all most people know how to deal with in
> userspace.

hal has bound to input devices for years. It won't run code directly, 
since that's a policy decision, but lshal -m should show events come 
from input devices.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-07-04 12:11       ` Matthew Garrett
@ 2008-07-04 12:22         ` Henrique de Moraes Holschuh
  2008-07-04 12:33           ` Matthew Garrett
  0 siblings, 1 reply; 28+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-07-04 12:22 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: Harald Welte, linux-acpi, Hiroshi Miura

On Fri, 04 Jul 2008, Matthew Garrett wrote:
> On Fri, Jul 04, 2008 at 09:07:54AM -0300, Henrique de Moraes Holschuh wrote:
> > Unless someone posts a recipe here of how to get HAL to connect to an input
> > device, and how to tell it to run code when a KEY_* event is received (which
> > you could add to the driver documentation), I'd say proc_generate_event will
> > have to be tolerated.  It is all most people know how to deal with in
> > userspace.
> 
> hal has bound to input devices for years. It won't run code directly, 
> since that's a policy decision, but lshal -m should show events come 
> from input devices.

Is there an easy way to duplicate what acpid does (i.e. give HAL some config
file that tells it to run an extrenal script when it gets a certain event)?
If there is not, it is no wonder people don't want to go away from acpid and
we have to keep proc_generate_event around...

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-07-04 12:22         ` Henrique de Moraes Holschuh
@ 2008-07-04 12:33           ` Matthew Garrett
  2008-07-04 20:18             ` Henrique de Moraes Holschuh
  0 siblings, 1 reply; 28+ messages in thread
From: Matthew Garrett @ 2008-07-04 12:33 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh; +Cc: Harald Welte, linux-acpi, Hiroshi Miura

On Fri, Jul 04, 2008 at 09:22:47AM -0300, Henrique de Moraes Holschuh wrote:
> On Fri, 04 Jul 2008, Matthew Garrett wrote:
> > hal has bound to input devices for years. It won't run code directly, 
> > since that's a policy decision, but lshal -m should show events come 
> > from input devices.
> 
> Is there an easy way to duplicate what acpid does (i.e. give HAL some config
> file that tells it to run an extrenal script when it gets a certain event)?
> If there is not, it is no wonder people don't want to go away from acpid and
> we have to keep proc_generate_event around...

Right, that's why I'm not encouraging its removal from any existing 
drivers at the moment. I was under the impression that there was an 
acpid that could listen for input devices somewhere, but I'm having 
trouble finding that now. Hm.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-07-04 12:33           ` Matthew Garrett
@ 2008-07-04 20:18             ` Henrique de Moraes Holschuh
  0 siblings, 0 replies; 28+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-07-04 20:18 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: Harald Welte, linux-acpi, Hiroshi Miura

On Fri, 04 Jul 2008, Matthew Garrett wrote:
> On Fri, Jul 04, 2008 at 09:22:47AM -0300, Henrique de Moraes Holschuh wrote:
> > On Fri, 04 Jul 2008, Matthew Garrett wrote:
> > > hal has bound to input devices for years. It won't run code directly, 
> > > since that's a policy decision, but lshal -m should show events come 
> > > from input devices.
> > 
> > Is there an easy way to duplicate what acpid does (i.e. give HAL some config
> > file that tells it to run an extrenal script when it gets a certain event)?
> > If there is not, it is no wonder people don't want to go away from acpid and
> > we have to keep proc_generate_event around...
> 
> Right, that's why I'm not encouraging its removal from any existing 
> drivers at the moment. I was under the impression that there was an 
> acpid that could listen for input devices somewhere, but I'm having 
> trouble finding that now. Hm.

Can't we add that to HAL?  It is not ACPI specific, and it IS useful as all
heck for 'system-wide' behaviour (otherwise one could use whatever the
desktop environment's default "special key mapping" utility is).

Another major extreme annoyance is the X.org evdev driver.  Besides the
(very unfortunate) fact that it simply doesn't work right, that thing
insists on doing an exclusive grab of any input device you give it (for
security reasons, I suppose), which actually means you need to handle the
special input events inside X as well.

Things like that make me wonder if I shouldn't be issuing the thinkpad-acpi
events that are mapped to input events also over netlink.  Well, I will wait
until we get a proper replacement for proc-based acpid, first.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-07-04  9:10   ` Matthew Garrett
  2008-07-04 12:07     ` Henrique de Moraes Holschuh
@ 2008-07-12 10:36     ` Harald Welte
  2008-07-12 10:56       ` Matthew Garrett
  1 sibling, 1 reply; 28+ messages in thread
From: Harald Welte @ 2008-07-12 10:36 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: linux-acpi, Hiroshi Miura

[-- Attachment #1: Type: text/plain, Size: 828 bytes --]

On Fri, Jul 04, 2008 at 10:10:31AM +0100, Matthew Garrett wrote:
> I'd still have a preference for not including the proc_generate_event 
> stuff, but I'm not sure that there's any real consensus on that so I'm 
> not going to block it.
> 
> Acked-by: mjg@redhat.com

so how do we proceed from here?  I've already asked for its inclusion
but have not heard any statement from the ACPI maintainer[s].  Should I
re-submit it to some other e-mail address?

Or are there still issues that I should address?

-- 
- Harald Welte <laforge@gnumonks.org>           http://laforge.gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
                                                  (ETSI EN 300 175-7 Ch. A6)

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-07-12 10:36     ` Harald Welte
@ 2008-07-12 10:56       ` Matthew Garrett
  0 siblings, 0 replies; 28+ messages in thread
From: Matthew Garrett @ 2008-07-12 10:56 UTC (permalink / raw)
  To: Harald Welte; +Cc: linux-acpi, Hiroshi Miura

On Sat, Jul 12, 2008 at 06:36:48PM +0800, Harald Welte wrote:

> so how do we proceed from here?  I've already asked for its inclusion
> but have not heard any statement from the ACPI maintainer[s].  Should I
> re-submit it to some other e-mail address?
> 
> Or are there still issues that I should address?

Andi's the ACPI maintainer while Len's on sabbatical, so you might want 
to Cc: him?

-- 
Matthew Garrett | mjg59@srcf.ucam.org

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

* re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
@ 2008-07-12 11:53 Kyler Laird
  0 siblings, 0 replies; 28+ messages in thread
From: Kyler Laird @ 2008-07-12 11:53 UTC (permalink / raw)
  To: linux-acpi

I just applied the 2008-07-04 3:44:19 to 2.6.26-rc9 and it seems to work
well for my Panasonic CF-74.  It'll be wonderful to have this in the
mainline kernel!  (I won't need to add anything now.  Yea!)

In userspace, note that this requires a change from the proc interface
to sys for /etc/acpi/panabright.sh.  In Ubuntu Hardy I also disabled
/etc/acpi/mutebtn.sh (by setting the first line to "#!/bin/true")
because there seems to be a mute toggle elsewhere and the two toggle
events were canceling each other.

Thanks!

--kyler

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-07-04  3:44 ` [PATCH] Panasonic Let's Note laptop extras driver v0.94 Harald Welte
  2008-07-04  9:10   ` Matthew Garrett
@ 2008-09-22 22:38   ` Len Brown
  2008-09-23 15:46     ` Harald Welte
  1 sibling, 1 reply; 28+ messages in thread
From: Len Brown @ 2008-09-22 22:38 UTC (permalink / raw)
  To: Harald Welte; +Cc: linux-acpi, Hiroshi Miura

Harald,
Thanks for picking up this driver -- I'm sure its users will be pleased
to get it included in the kernel.

I've got a couple of nitpicks I'd like you to fix
before I can merge it...

Please move the driver source to drivers/misc,
where the other laptop drivers live.
drivers/acpi/ is not for reverse-engineered platform drivers.

Also, please consider changing the driver's name.
toshiba_acpi is the oldtest, but least maintained driver of all
the laptop platform drivers, so it isn't a good example.

A better example is drivers/misc/*-laptop.c

If this works on a broad range of panasonic, the panasonic-laptop
panasonic make sense, else if it is more limited, then a more
specific name would be better.  Unclear what pcc stands for...

Also, you can delete this line:

-#include <linux/proc_fs.h>

and this

-/* definitions for /proc/ interface */

since you no longer have a /proc/interface.

thanks,
-Len

ps. I'll keep your previous version in my test tree until
you send along a new version, which hopefully we can merge into .28.



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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-09-22 22:38   ` Len Brown
@ 2008-09-23 15:46     ` Harald Welte
  2008-09-24  8:09       ` Len Brown
  2008-09-25  4:45       ` SPS 三浦 広志(IT人材戦略)
  0 siblings, 2 replies; 28+ messages in thread
From: Harald Welte @ 2008-09-23 15:46 UTC (permalink / raw)
  To: Len Brown; +Cc: linux-acpi, Hiroshi Miura

Hi Len,

On Mon, Sep 22, 2008 at 06:38:24PM -0400, Len Brown wrote:
> Thanks for picking up this driver -- I'm sure its users will be pleased
> to get it included in the kernel.

Hi Len, thanks for your encouraging feedback.

> Please move the driver source to drivers/misc,
> where the other laptop drivers live.
> drivers/acpi/ is not for reverse-engineered platform drivers.

ok, done.

> Also, please consider changing the driver's name.
> toshiba_acpi is the oldtest, but least maintained driver of all
> the laptop platform drivers, so it isn't a good example.
> 
> A better example is drivers/misc/*-laptop.c

ok.

> If this works on a broad range of panasonic, the panasonic-laptop
> panasonic make sense, else if it is more limited, then a more
> specific name would be better.  Unclear what pcc stands for...

I don't really know the various panasonic products, but the 

> Also, you can delete this line:
> -#include <linux/proc_fs.h>
> and this
> -/* definitions for /proc/ interface */

done.

> ps. I'll keep your previous version in my test tree until
> you send along a new version, which hopefully we can merge into .28.

Please see the latest version of my patch below.  It fixes all the issues that
you mentioned.  I've tested it on my system, seems to work just as fine as
before:

>From de7d0c48f36c1192bd03a8a781955057f6ddcac6 Mon Sep 17 00:00:00 2001
From: Harald Welte <laforge@gnumonks.org>
Date: Tue, 23 Sep 2008 17:41:08 +0200
Subject: [PATCH] ACPI: Add Panasonic Laptop ACPI extras driver

This is a driver for ACPI extras such as hotkeys and backlight
brightness control on various Panasonic "Let's Note" series laptop
computers.

It exports the backlight via the backlight class device API,
and the hotkeys as input event device.  Some more esoteric
items like number of installed batteries are exported via sysfs
device attributes.

Hotkey events also generate old-style ACPI enents through
/proc/acpi/event to interoperate with current versions of acpid.

Signed-off-by: Harald Welte <laforge@gnumonks.org>
Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Acked-by: Matthew Garrett <mjg@redhat.com>
---
 MAINTAINERS                     |    5 +
 drivers/misc/Kconfig            |   11 +
 drivers/misc/Makefile           |    1 +
 drivers/misc/panasonic-laptop.c |  767 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 784 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/panasonic-laptop.c

diff --git a/MAINTAINERS b/MAINTAINERS
index cad81a2..fa35793 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3148,6 +3148,11 @@ M:	olof@lixom.net
 L:	i2c@lm-sensors.org
 S:	Maintained
 
+PANASONIC LAPTOP ACPI EXTRAS DRIVER
+P:	Harald Welte
+M:	laforge@gnumonks.org
+S:	Maintained
+
 PARALLEL PORT SUPPORT
 L:	linux-parport@lists.infradead.org (subscribers-only)
 S:	Orphan
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index a726f3b..4ed8357 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -245,6 +245,17 @@ config MSI_LAPTOP
 
 	  If you have an MSI S270 laptop, say Y or M here.
 
+config PANASONIC_LAPTOP
+	tristate "Panasonic Laptop Extras"
+	depends on X86 && INPUT
+        depends on BACKLIGHT_CLASS_DEVICE
+	---help---
+	  This driver adds support for access to backlight control and hotkeys
+	  on Panasonic Let's Note laptops.
+
+	  If you have a Panasonic Let's note laptop (such as the R1(N variant),
+	  R2, R3, R5, T2, W2 and Y2 series), say Y.
+
 config COMPAL_LAPTOP
 	tristate "Compal Laptop Extras"
 	depends on X86
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index c6c13f6..909e246 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_SGI_IOC4)		+= ioc4.o
 obj-$(CONFIG_SONY_LAPTOP)	+= sony-laptop.o
 obj-$(CONFIG_THINKPAD_ACPI)	+= thinkpad_acpi.o
 obj-$(CONFIG_FUJITSU_LAPTOP)	+= fujitsu-laptop.o
+obj-$(CONFIG_PANASONIC_LAPTOP)	+= panasonic-laptop.o
 obj-$(CONFIG_EEPROM_93CX6)	+= eeprom_93cx6.o
 obj-$(CONFIG_INTEL_MENLOW)	+= intel_menlow.o
 obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
diff --git a/drivers/misc/panasonic-laptop.c b/drivers/misc/panasonic-laptop.c
new file mode 100644
index 0000000..9aecda5
--- /dev/null
+++ b/drivers/misc/panasonic-laptop.c
@@ -0,0 +1,767 @@
+/*
+ *  Panasonic HotKey and LCD brightness control driver
+ *  (C) 2004 Hiroshi Miura <miura@da-cha.org>
+ *  (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/
+ *  (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>
+ *  (C) 2004 David Bronaugh <dbronaugh>
+ *  (C) 2006-2008 Harald Welte <laforge@gnumonks.org>
+ *
+ *  derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  publicshed by the Free Software Foundation.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ *---------------------------------------------------------------------------
+ *
+ * ChangeLog:
+ *	Sep.23, 2008	Harald Welte <laforge@gnumonks.org>
+ *		-v0.95	rename driver from drivers/acpi/pcc_acpi.c to
+ *			drivers/misc/panasonic-laptop.c
+ *
+ * 	Jul.04, 2008	Harald Welte <laforge@gnumonks.org>
+ * 		-v0.94	replace /proc interface with device attributes
+ * 			support {set,get}keycode on th input device
+ *
+ *      Jun.27, 2008	Harald Welte <laforge@gnumonks.org>
+ *      	-v0.92	merge with 2.6.26-rc6 input API changes
+ *      		remove broken <= 2.6.15 kernel support
+ *      		resolve all compiler warnings
+ *      		various coding style fixes (checkpatch.pl)
+ *      		add support for backlight api
+ *      		major code restructuring
+ *
+ * 	Dac.28, 2007	Harald Welte <laforge@gnumonks.org>
+ * 		-v0.91	merge with 2.6.24-rc6 ACPI changes
+ *
+ * 	Nov.04, 2006	Hiroshi Miura <miura@da-cha.org>
+ * 		-v0.9	remove warning about section reference.
+ * 			remove acpi_os_free
+ * 			add /proc/acpi/pcc/brightness interface for HAL access
+ * 			merge dbronaugh's enhancement
+ * 			Aug.17, 2004 David Bronaugh (dbronaugh)
+ *  				- Added screen brightness setting interface
+ *				  Thanks to FreeBSD crew (acpi_panasonic.c)
+ * 				  for the ideas I needed to accomplish it
+ *
+ *	May.29, 2006	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.8.4 follow to change keyinput structure
+ *			thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,
+ *			Jacob Bower <jacob.bower@ic.ac.uk> and
+ *			Hiroshi Yokota for providing solutions.
+ *
+ *	Oct.02, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.8.2	merge code of YOKOTA Hiroshi
+ *					<yokota@netlab.is.tsukuba.ac.jp>.
+ *			Add sticky key mode interface.
+ *			Refactoring acpi_pcc_generate_keyinput().
+ *
+ *	Sep.15, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.8	Generate key input event on input subsystem.
+ *			This is based on yet another driver written by
+ *							Ryuta Nakanishi.
+ *
+ *	Sep.10, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.7	Change proc interface functions using seq_file
+ *			facility as same as other ACPI drivers.
+ *
+ *	Aug.28, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.6.4 Fix a silly error with status checking
+ *
+ *	Aug.25, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		-v0.6.3 replace read_acpi_int by standard function
+ *							acpi_evaluate_integer
+ *			some clean up and make smart copyright notice.
+ *			fix return value of pcc_acpi_get_key()
+ *			fix checking return value of acpi_bus_register_driver()
+ *
+ *      Aug.22, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              -v0.6.2 Add check on ACPI data (num_sifr)
+ *                      Coding style cleanups, better error messages/handling
+ *			Fixed an off-by-one error in memory allocation
+ *
+ *      Aug.21, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              -v0.6.1 Fix a silly error with status checking
+ *
+ *      Aug.20, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              - v0.6  Correct brightness controls to reflect reality
+ *                      based on information gleaned by Hiroshi Miura
+ *                      and discussions with Hiroshi Miura
+ *
+ *	Aug.10, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.5  support LCD brightness control
+ *			based on the disclosed information by MEI.
+ *
+ *	Jul.25, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.4  first post version
+ *		        add function to retrive SIFR
+ *
+ *	Jul.24, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.3  get proper status of hotkey
+ *
+ *      Jul.22, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.2  add HotKey handler
+ *
+ *      Jul.17, 2004	Hiroshi Miura <miura@da-cha.org>
+ *		- v0.1  start from toshiba_acpi driver written by John Belmonte
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/backlight.h>
+#include <linux/ctype.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/input.h>
+
+
+#ifndef ACPI_HOTKEY_COMPONENT
+#define ACPI_HOTKEY_COMPONENT	0x10000000
+#endif
+
+#define _COMPONENT		ACPI_HOTKEY_COMPONENT
+
+MODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte");
+MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");
+MODULE_LICENSE("GPL");
+
+#define LOGPREFIX "pcc_acpi: "
+
+/* Define ACPI PATHs */
+/* Lets note hotkeys */
+#define METHOD_HKEY_QUERY	"HINF"
+#define METHOD_HKEY_SQTY	"SQTY"
+#define METHOD_HKEY_SINF	"SINF"
+#define METHOD_HKEY_SSET	"SSET"
+#define HKEY_NOTIFY		 0x80
+
+#define ACPI_PCC_DRIVER_NAME	"Panasonic Laptop Support"
+#define ACPI_PCC_DEVICE_NAME	"Hotkey"
+#define ACPI_PCC_CLASS		"pcc"
+
+#define ACPI_PCC_INPUT_PHYS	"panasonic/hkey0"
+
+/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
+   ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
+*/
+enum SINF_BITS { SINF_NUM_BATTERIES = 0,
+		 SINF_LCD_TYPE,
+		 SINF_AC_MAX_BRIGHT,
+		 SINF_AC_MIN_BRIGHT,
+		 SINF_AC_CUR_BRIGHT,
+		 SINF_DC_MAX_BRIGHT,
+		 SINF_DC_MIN_BRIGHT,
+		 SINF_DC_CUR_BRIGHT,
+		 SINF_MUTE,
+		 SINF_RESERVED,
+		 SINF_ENV_STATE,
+		 SINF_STICKY_KEY = 0x80,
+	};
+/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
+
+static int acpi_pcc_hotkey_add(struct acpi_device *device);
+static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type);
+static int acpi_pcc_hotkey_resume(struct acpi_device *device);
+
+static const struct acpi_device_id pcc_device_ids[] = {
+	{ "MAT0012", 0},
+	{ "MAT0013", 0},
+	{ "MAT0018", 0},
+	{ "MAT0019", 0},
+	{ "", 0},
+};
+
+static struct acpi_driver acpi_pcc_driver = {
+	.name =		ACPI_PCC_DRIVER_NAME,
+	.class =	ACPI_PCC_CLASS,
+	.ids =		pcc_device_ids,
+	.ops =		{
+				.add =		acpi_pcc_hotkey_add,
+				.remove =	acpi_pcc_hotkey_remove,
+				.resume =       acpi_pcc_hotkey_resume,
+			},
+};
+
+#define KEYMAP_SIZE		11
+static const int initial_keymap[KEYMAP_SIZE] = {
+	/*  0 */ KEY_RESERVED,
+	/*  1 */ KEY_BRIGHTNESSDOWN,
+	/*  2 */ KEY_BRIGHTNESSUP,
+	/*  3 */ KEY_DISPLAYTOGGLE,
+	/*  4 */ KEY_MUTE,
+	/*  5 */ KEY_VOLUMEDOWN,
+	/*  6 */ KEY_VOLUMEUP,
+	/*  7 */ KEY_SLEEP,
+	/*  8 */ KEY_PROG1, /* Change CPU boost */
+	/*  9 */ KEY_BATTERY,
+	/* 10 */ KEY_SUSPEND,
+};
+
+struct pcc_acpi {
+	acpi_handle		handle;
+	unsigned long		num_sifr;
+	int			sticky_mode;
+	u32 			*sinf;
+	struct acpi_device	*device;
+	struct input_dev	*input_dev;
+	struct backlight_device	*backlight;
+	int			keymap[KEYMAP_SIZE];
+};
+
+struct pcc_keyinput {
+	struct acpi_hotkey      *hotkey;
+};
+
+/* method access functions */
+static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
+{
+	union acpi_object in_objs[] = {
+		{ .integer.type  = ACPI_TYPE_INTEGER,
+		  .integer.value = func, },
+		{ .integer.type  = ACPI_TYPE_INTEGER,
+		  .integer.value = val, },
+	};
+	struct acpi_object_list params = {
+		.count   = ARRAY_SIZE(in_objs),
+		.pointer = in_objs,
+	};
+	acpi_status status = AE_OK;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_write_sset");
+
+	status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET,
+				      &params, NULL);
+
+	return status == AE_OK;
+}
+
+static inline int acpi_pcc_get_sqty(struct acpi_device *device)
+{
+	unsigned long s;
+	acpi_status status;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_get_sqty");
+
+	status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,
+				       NULL, &s);
+	if (ACPI_SUCCESS(status))
+		return s;
+	else {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "evaluation error HKEY.SQTY\n"));
+		return -EINVAL;
+	}
+}
+
+static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
+{
+	acpi_status status;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *hkey = NULL;
+	int i;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_retrieve_biosdata");
+
+	status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, 0,
+				      &buffer);
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "evaluation error HKEY.SINF\n"));
+		return 0;
+	}
+
+	hkey = buffer.pointer;
+	if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
+		goto end;
+	}
+
+	if (pcc->num_sifr < hkey->package.count) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				 "SQTY reports bad SINF length\n"));
+		status = AE_ERROR;
+		goto end;
+	}
+
+	for (i = 0; i < hkey->package.count; i++) {
+		union acpi_object *element = &(hkey->package.elements[i]);
+		if (likely(element->type == ACPI_TYPE_INTEGER)) {
+			sinf[i] = element->integer.value;
+		} else
+			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+					 "Invalid HKEY.SINF data\n"));
+	}
+	sinf[hkey->package.count] = -1;
+
+end:
+	kfree(buffer.pointer);
+	return status == AE_OK;
+}
+
+/* backlight API interface functions */
+
+/* This driver currently treats AC and DC brightness identical,
+ * since we don't need to invent an interface to the core ACPI
+ * logic to receive events in case a power supply is plugged in
+ * or removed */
+
+static int bl_get(struct backlight_device *bd)
+{
+	struct pcc_acpi *pcc = bl_get_data(bd);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return pcc->sinf[SINF_AC_CUR_BRIGHT];
+}
+
+static int bl_set_status(struct backlight_device *bd)
+{
+	struct pcc_acpi *pcc = bl_get_data(bd);
+	int bright = bd->props.brightness;
+	int rc;
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
+		bright = pcc->sinf[SINF_AC_MIN_BRIGHT];
+
+	if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT])
+		bright = pcc->sinf[SINF_DC_MIN_BRIGHT];
+
+	if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] ||
+	    bright > pcc->sinf[SINF_AC_MAX_BRIGHT])
+		return -EINVAL;
+
+	rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright);
+	if (rc < 0)
+		return rc;
+
+	return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright);
+}
+
+static struct backlight_ops pcc_backlight_ops = {
+	.get_brightness	= bl_get,
+	.update_status	= bl_set_status,
+};
+
+
+/* sysfs user interface functions */
+
+static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return sprintf(buf, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
+}
+
+static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return sprintf(buf, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
+}
+
+static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return sprintf(buf, "%u\n", pcc->sinf[SINF_MUTE]);
+}
+
+static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+		return -EIO;
+
+	return sprintf(buf, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
+}
+
+static ssize_t set_sticky(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	struct acpi_device *acpi = to_acpi_device(dev);
+	struct pcc_acpi *pcc = acpi_driver_data(acpi);
+	int val;
+
+	if (count && sscanf(buf, "%i", &val) == 1 &&
+	    (val == 0 || val == 1)) {
+		acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val);
+		pcc->sticky_mode = val;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(numbatt, S_IRUGO, show_numbatt, NULL);
+static DEVICE_ATTR(lcdtype, S_IRUGO, show_lcdtype, NULL);
+static DEVICE_ATTR(mute, S_IRUGO, show_mute, NULL);
+static DEVICE_ATTR(sticky_key, S_IRUGO | S_IWUSR, show_sticky, set_sticky);
+
+static struct attribute *pcc_sysfs_entries[] = {
+	&dev_attr_numbatt.attr,
+	&dev_attr_lcdtype.attr,
+	&dev_attr_mute.attr,
+	&dev_attr_sticky_key.attr,
+	NULL,
+};
+
+static struct attribute_group pcc_attr_group = {
+	.name	= NULL,		/* put in device directory */
+	.attrs	= pcc_sysfs_entries,
+};
+
+
+/* hotkey input device driver */
+
+static int pcc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+{
+	struct pcc_acpi *pcc = input_get_drvdata(dev);
+
+	if (scancode >= ARRAY_SIZE(pcc->keymap))
+		return -EINVAL;
+
+	*keycode = pcc->keymap[scancode];
+
+	return 0;
+}
+
+static int keymap_get_by_keycode(struct pcc_acpi *pcc, int keycode)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) {
+		if (pcc->keymap[i] == keycode)
+			return i+1;
+	}
+
+	return 0;
+}
+
+static int pcc_setkeycode(struct input_dev *dev, int scancode, int keycode)
+{
+	struct pcc_acpi *pcc = input_get_drvdata(dev);
+	int oldkeycode;
+
+	if (scancode >= ARRAY_SIZE(pcc->keymap))
+		return -EINVAL;
+
+	if (keycode < 0 || keycode > KEY_MAX)
+		return -EINVAL;
+
+	oldkeycode = pcc->keymap[scancode];
+	pcc->keymap[scancode] = keycode;
+
+	set_bit(keycode, dev->keybit);
+
+	if (!keymap_get_by_keycode(pcc, oldkeycode))
+		clear_bit(oldkeycode, dev->keybit);
+
+	return 0;
+}
+
+static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
+{
+	struct input_dev *hotk_input_dev = pcc->input_dev;
+	int rc;
+	int key_code, hkey_num;
+	unsigned long result;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_generate_keyinput");
+
+	rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
+				   NULL, &result);
+	if (!ACPI_SUCCESS(rc)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				 "error getting hotkey status\n"));
+		return;
+	}
+
+	acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
+
+	hkey_num = result & 0xf;
+
+	if (hkey_num < 0 || hkey_num > ARRAY_SIZE(pcc->keymap)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "hotkey number out of range: %d\n",
+				  hkey_num));
+		return;
+	}
+
+	key_code = pcc->keymap[hkey_num];
+
+	if (key_code != KEY_RESERVED) {
+		int pushed = (result & 0x80) ? TRUE : FALSE;
+
+		input_report_key(hotk_input_dev, key_code, pushed);
+		input_sync(hotk_input_dev);
+	}
+
+	return;
+}
+
+static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct pcc_acpi *pcc = (struct pcc_acpi *) data;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_notify");
+
+	switch (event) {
+	case HKEY_NOTIFY:
+		acpi_pcc_generate_keyinput(pcc);
+		break;
+	default:
+		/* nothing to do */
+		break;
+	}
+}
+
+static int acpi_pcc_init_input(struct pcc_acpi *pcc)
+{
+	int i, rc;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_init_input");
+
+	pcc->input_dev = input_allocate_device();
+	if (!pcc->input_dev) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Couldn't allocate input device for hotkey"));
+		return -ENOMEM;
+	}
+
+	pcc->input_dev->evbit[0] = BIT(EV_KEY);
+
+	pcc->input_dev->name = ACPI_PCC_DRIVER_NAME;
+	pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS;
+	pcc->input_dev->id.bustype = BUS_HOST;
+	pcc->input_dev->id.vendor = 0x0001;
+	pcc->input_dev->id.product = 0x0001;
+	pcc->input_dev->id.version = 0x0100;
+	pcc->input_dev->getkeycode = pcc_getkeycode;
+	pcc->input_dev->setkeycode = pcc_setkeycode;
+
+	/* load initial keymap */
+	memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap));
+
+	for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++)
+		__set_bit(pcc->keymap[i], pcc->input_dev->keybit);
+	__clear_bit(KEY_RESERVED, pcc->input_dev->keybit);
+
+	input_set_drvdata(pcc->input_dev, pcc);
+
+	rc = input_register_device(pcc->input_dev);
+	if (rc < 0)
+		input_free_device(pcc->input_dev);
+
+	return rc;
+}
+
+/* kernel module interface */
+
+static int acpi_pcc_hotkey_resume(struct acpi_device *device)
+{
+	struct pcc_acpi *pcc = acpi_driver_data(device);
+	acpi_status status = AE_OK;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_resume");
+
+	if (device == NULL || pcc == NULL)
+		return -EINVAL;
+
+	ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
+			  pcc->sticky_mode));
+
+	status = acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
+
+	return status == AE_OK ? 0 : -EINVAL;
+}
+
+static int acpi_pcc_hotkey_add(struct acpi_device *device)
+{
+	acpi_status status;
+	struct pcc_acpi *pcc;
+	int num_sifr, result;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_add");
+
+	if (!device)
+		return -EINVAL;
+
+	num_sifr = acpi_pcc_get_sqty(device);
+
+	if (num_sifr > 255) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large"));
+		return -ENODEV;
+	}
+
+	pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL);
+	if (!pcc) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Couldn't allocate mem for pcc"));
+		return -ENOMEM;
+	}
+
+	pcc->sinf = kzalloc(sizeof(u32) * (num_sifr + 1), GFP_KERNEL);
+	if (!pcc->sinf) {
+		result = -ENOMEM;
+		goto out_hotkey;
+	}
+
+	pcc->device = device;
+	pcc->handle = device->handle;
+	pcc->num_sifr = num_sifr;
+	acpi_driver_data(device) = pcc;
+	strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
+	strcpy(acpi_device_class(device), ACPI_PCC_CLASS);
+
+	result = acpi_pcc_init_input(pcc);
+	if (result) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error installing keyinput handler\n"));
+		goto out_sinf;
+	}
+
+	/* initialize hotkey input device */
+	status = acpi_install_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
+					     acpi_pcc_hotkey_notify, pcc);
+
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error installing notify handler\n"));
+		result = -ENODEV;
+		goto out_input;
+	}
+
+	/* initialize backlight */
+	pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
+						   &pcc_backlight_ops);
+	if (IS_ERR(pcc->backlight))
+		goto out_notify;
+
+	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				 "Couldn't retrieve BIOS data\n"));
+		goto out_backlight;
+	}
+
+	/* read the initial brightness setting from the hardware */
+	pcc->backlight->props.max_brightness =
+					pcc->sinf[SINF_AC_MAX_BRIGHT];
+	pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
+
+	/* read the initial sticky key mode from the hardware */
+	pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY];
+
+	/* add sysfs attributes */
+	result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
+	if (result)
+		goto out_backlight;
+
+	return 0;
+
+out_backlight:
+	backlight_device_unregister(pcc->backlight);
+out_notify:
+	acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
+				   acpi_pcc_hotkey_notify);
+out_input:
+	input_unregister_device(pcc->input_dev);
+	/* no need to input_free_device() since core input API refcount and
+	 * free()s the device */
+out_sinf:
+	kfree(pcc->sinf);
+out_hotkey:
+	kfree(pcc);
+
+	return result;
+}
+
+static int __init acpi_pcc_init(void)
+{
+	int result = 0;
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_init");
+
+	if (acpi_disabled)
+		return -ENODEV;
+
+	result = acpi_bus_register_driver(&acpi_pcc_driver);
+	if (result < 0) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error registering hotkey driver\n"));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
+{
+	struct pcc_acpi *pcc = acpi_driver_data(device);
+
+	ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_remove");
+
+	if (!device || !pcc)
+		return -EINVAL;
+
+	sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);
+
+	backlight_device_unregister(pcc->backlight);
+
+	acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
+				   acpi_pcc_hotkey_notify);
+
+	input_unregister_device(pcc->input_dev);
+	/* no need to input_free_device() since core input API refcount and
+	 * free()s the device */
+
+	kfree(pcc->sinf);
+	kfree(pcc);
+
+	return 0;
+}
+
+static void __exit acpi_pcc_exit(void)
+{
+	ACPI_FUNCTION_TRACE("acpi_pcc_exit");
+
+	acpi_bus_unregister_driver(&acpi_pcc_driver);
+}
+
+module_init(acpi_pcc_init);
+module_exit(acpi_pcc_exit);
-- 
1.5.6.5

-- 
- Harald Welte <laforge@gnumonks.org>           http://laforge.gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
                                                  (ETSI EN 300 175-7 Ch. A6)

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-09-23 15:46     ` Harald Welte
@ 2008-09-24  8:09       ` Len Brown
  2008-09-25  4:45       ` SPS 三浦 広志(IT人材戦略)
  1 sibling, 0 replies; 28+ messages in thread
From: Len Brown @ 2008-09-24  8:09 UTC (permalink / raw)
  To: Harald Welte; +Cc: linux-acpi, Hiroshi Miura

panasonic-laptop applied to acpi-test.

thanks,
-len



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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-09-23 15:46     ` Harald Welte
  2008-09-24  8:09       ` Len Brown
@ 2008-09-25  4:45       ` SPS 三浦 広志(IT人材戦略)
  2008-09-25 16:44         ` Len Brown
  1 sibling, 1 reply; 28+ messages in thread
From: SPS 三浦 広志(IT人材戦略) @ 2008-09-25  4:45 UTC (permalink / raw)
  To: Harald Welte; +Cc: Len Brown, linux-acpi, Hiroshi Miura

Hi Harald,

Thank you for your help and I'm sorry for my inactiveness...

2008-09-23 17:46 +0200  Harald Welte wrote:
> Hi Len,
> 
> On Mon, Sep 22, 2008 at 06:38:24PM -0400, Len Brown wrote:
> > Thanks for picking up this driver -- I'm sure its users will be pleased
> > to get it included in the kernel.
> 
> Hi Len, thanks for your encouraging feedback.

> 
> > If this works on a broad range of panasonic, the panasonic-laptop
> > panasonic make sense, else if it is more limited, then a more
> > specific name would be better.  Unclear what pcc stands for...
> 
> I don't really know the various panasonic products, but the 

pcc stands for Panasonic Computer Company, which is exist several years
ago. This driver works on a broad range of panasonic laptops,
such as Let's note series and Tough book series.

I had discussed bios writers in the Panasonic 2 yrs ago,
they said they keeps backword compatibility for their products, at least
after CF-R1/T1/W1/Y1 toughbook/let's note series.

They will adopt new acpi specification when microsoft os adopt it.
That's why recent model, such as CF-W6/W7, can not control lcd
brightness through this driver but can through generic video 
driver.

I originaly write driver in reverse engineering basis,  but at that
time, bios writer provided me some specification.



Hiroshi Miura
miura@da-cha.org
miurahr@nttdata.co.jp
-- 
        Global IT Innovator

__--------~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                NTT DATA Group

Hiroshi  Miura
HRD Manager for IT architect and specialist, 
Planning Section, System Platform Sector


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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-09-25  4:45       ` SPS 三浦 広志(IT人材戦略)
@ 2008-09-25 16:44         ` Len Brown
  2008-09-26  2:17           ` Hiroshi Miura
  0 siblings, 1 reply; 28+ messages in thread
From: Len Brown @ 2008-09-25 16:44 UTC (permalink / raw)
  To: SPS 三浦 広志(IT人材戦略)
  Cc: Harald Welte, linux-acpi, Hiroshi Miura

[-- Attachment #1: Type: TEXT/PLAIN, Size: 1843 bytes --]



On Thu, 25 Sep 2008, SPS 三浦 広志(IT人材戦略) wrote:

> Hi Harald,
> 
> Thank you for your help and I'm sorry for my inactiveness...
> 
> 2008-09-23 17:46 +0200  Harald Welte wrote:
> > Hi Len,
> > 
> > On Mon, Sep 22, 2008 at 06:38:24PM -0400, Len Brown wrote:
> > > Thanks for picking up this driver -- I'm sure its users will be pleased
> > > to get it included in the kernel.
> > 
> > Hi Len, thanks for your encouraging feedback.
> 
> > > If this works on a broad range of panasonic, the panasonic-laptop
> > > panasonic make sense, else if it is more limited, then a more
> > > specific name would be better.  Unclear what pcc stands for...
> > 
> > I don't really know the various panasonic products, but the 
> 
> pcc stands for Panasonic Computer Company, which is exist several years
> ago. This driver works on a broad range of panasonic laptops,
> such as Let's note series and Tough book series.
> 
> I had discussed bios writers in the Panasonic 2 yrs ago,
> they said they keeps backword compatibility for their products, at least
> after CF-R1/T1/W1/Y1 toughbook/let's note series.
> 
> They will adopt new acpi specification when microsoft os adopt it.
> That's why recent model, such as CF-W6/W7, can not control lcd
> brightness through this driver but can through generic video 
> driver.
> 
> I originaly write driver in reverse engineering basis,  but at that
> time, bios writer provided me some specification.
> 
> Hiroshi Miura
> miura@da-cha.org
> miurahr@nttdata.co.jp

Thanks Hiroshi, for the background.

I'm glad that Panasonic is willing to share enough info with
the Linux community so that we are not lost in trying to support
users of their products.  As Harald has signed up to maintain
this driver in-tree, perharps you can share your contacts
at Panasonic with Harald?

cheers,
-Len

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

* Re: [PATCH] Panasonic Let's Note laptop extras driver v0.94
  2008-09-25 16:44         ` Len Brown
@ 2008-09-26  2:17           ` Hiroshi Miura
  0 siblings, 0 replies; 28+ messages in thread
From: Hiroshi Miura @ 2008-09-26  2:17 UTC (permalink / raw)
  To: Len Brown; +Cc: Harald Welte, linux-acpi, Hiroshi Miura

Hi,

2008/9/25 Len Brown <lenb@kernel.org>:
> I'm glad that Panasonic is willing to share enough info with
> the Linux community so that we are not lost in trying to support
> users of their products.  As Harald has signed up to maintain
> this driver in-tree, perharps you can share your contacts
> at Panasonic with Harald?

Yes, of coarse, because recently I don't communicate with them,
so I need to update my contacts.


Hiroshi

-- 
HIroshi Miura
NTT DATA Corp. and IPA OSS center
(株)NTTデータ /(独)情報処理推進機構
三浦広志

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

end of thread, other threads:[~2008-09-26  2:17 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-07-02  9:00 [PATCH] Panasonic Let's Note laptop extras driver Harald Welte
2008-07-02 10:19 ` Matthew Garrett
2008-07-02 13:03   ` Harald Welte
2008-07-02 13:17     ` Matthew Garrett
2008-07-03 16:52   ` Harald Welte
2008-07-03 17:08     ` Matthew Garrett
2008-07-04  3:14       ` Henrique de Moraes Holschuh
2008-07-04  4:43         ` Harald Welte
2008-07-04 11:55           ` Henrique de Moraes Holschuh
2008-07-03 17:35 ` [PATCH] Panasonic Let's Note laptop extras driver v0.93 Harald Welte
2008-07-03 19:23   ` Matthew Garrett
2008-07-04  1:31     ` Harald Welte
2008-07-04  3:44 ` [PATCH] Panasonic Let's Note laptop extras driver v0.94 Harald Welte
2008-07-04  9:10   ` Matthew Garrett
2008-07-04 12:07     ` Henrique de Moraes Holschuh
2008-07-04 12:11       ` Matthew Garrett
2008-07-04 12:22         ` Henrique de Moraes Holschuh
2008-07-04 12:33           ` Matthew Garrett
2008-07-04 20:18             ` Henrique de Moraes Holschuh
2008-07-12 10:36     ` Harald Welte
2008-07-12 10:56       ` Matthew Garrett
2008-09-22 22:38   ` Len Brown
2008-09-23 15:46     ` Harald Welte
2008-09-24  8:09       ` Len Brown
2008-09-25  4:45       ` SPS 三浦 広志(IT人材戦略)
2008-09-25 16:44         ` Len Brown
2008-09-26  2:17           ` Hiroshi Miura
  -- strict thread matches above, loose matches on Subject: below --
2008-07-12 11:53 Kyler Laird

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).