linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] fujtisu application panel driver
@ 2007-07-02 18:17 Stephen Hemminger
  2007-07-02 19:16 ` Dmitry Torokhov
  2007-07-05  1:29 ` [PATCH] fujtisu application panel driver Jason Uhlenkott
  0 siblings, 2 replies; 23+ messages in thread
From: Stephen Hemminger @ 2007-07-02 18:17 UTC (permalink / raw)
  To: Dmitry Torokhov, Len Brown, Andrew Morton; +Cc: linux-input, linux-kernel

This driver supports the application buttons on some Fujitsu Lifebook laptops.
These buttons are read via the SMBus, for more details see:
	http://apanel.sourceforge.net/tech.php
The buttons are handled as by the regular input system.
Two models are detected now, but other Fujitsu laptop's have
keys that may work similarly.

It is based on the earlier apanel driver done by Jochen Eisenger, but
with many changes.  The original driver used ioctl's and a separate
user space program; this version hooks into the input subsystem so
that the normal Gnome/KDE shortcuts work without any userspace
changes.

Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>

---
 drivers/input/misc/Kconfig  |   12 +
 drivers/input/misc/Makefile |    1 
 drivers/input/misc/apanel.c |  448 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 461 insertions(+)

--- a/drivers/input/misc/Kconfig	2007-06-27 16:50:36.000000000 -0400
+++ b/drivers/input/misc/Kconfig	2007-06-30 10:04:19.000000000 -0400
@@ -40,6 +40,19 @@ config INPUT_M68K_BEEP
 	tristate "M68k Beeper support"
 	depends on M68K
 
+config INPUT_APANEL
+	tristate "Fujitsu Lifebook Application Panel buttons"
+	depends on X86
+	select I2C_I801
+	select INPUT_POLLDEV
+	help
+	 Say Y here for support of the Application Panel buttons, used on
+	 Fujitsu Lifebook. These are attached to the mainboard through
+	 an SMBus interface managed by the I2C Intel ICH (i801) driver.
+
+	 To compile this driver as a module, choose M here: the module will
+	 be called apanel.
+
 config INPUT_IXP4XX_BEEPER
 	tristate "IXP4XX Beeper support"
 	depends on ARCH_IXP4XX
--- a/drivers/input/misc/Makefile	2007-06-27 16:50:36.000000000 -0400
+++ b/drivers/input/misc/Makefile	2007-06-30 10:01:57.000000000 -0400
@@ -18,3 +18,4 @@ obj-$(CONFIG_INPUT_POWERMATE)		+= powerm
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
+obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/input/misc/apanel.c	2007-07-01 14:32:29.000000000 -0400
@@ -0,0 +1,394 @@
+/*
+ *   SMBus client for the Fujitsu Lifebook Application Panel
+ *
+ *  Copyright (C) 2007 Stephen Hemminger <shemminger@osdl.org>
+ *  Copyright (C) 2001-2003 Jochen Eisinger <jochen@penguin-breeder.org>
+ *
+ * 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 published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/input-polldev.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+struct keymap {
+	u32 mask;
+	u8 keycode;
+};
+
+static struct keymap app_media_keys[] = {
+	{ 0x1, 	 KEY_MAIL },
+	{ 0x2,	 KEY_WWW },
+	{ 0x4,	 KEY_PROG2 },
+	{ 0x8,	 KEY_PROG1 },
+	{ 0x100, KEY_FORWARD },
+	{ 0x200, KEY_REWIND },
+	{ 0x400, KEY_STOPCD },
+	{ 0x800, KEY_PLAYPAUSE },
+	{ 0 }
+};
+
+static struct keymap four_keys[] = {
+	{ 0x1, 	KEY_PROG4 },
+	{ 0x2,	KEY_PROG3 },
+	{ 0x4,	KEY_PROG2 },
+	{ 0x8,	KEY_PROG1 },
+	{ 0 }
+};
+
+static const struct keymap *keymap;
+
+static int __init dmi_matched(struct dmi_system_id *dmi)
+{
+	keymap = dmi->driver_data;
+	return 1;
+}
+
+/* List of systems known to work */
+static struct dmi_system_id apanel_dmi_table[] __initdata = {
+	{
+		.callback = dmi_matched,
+		.ident = "Lifebook S",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"),
+		},
+		.driver_data = app_media_keys,
+	},
+	{
+		.callback = dmi_matched,
+		.ident = "Lifebook B6210",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "Lifebook B6210"),
+		},
+		.driver_data = four_keys,
+	},
+	{ }
+};
+
+enum apanel_devid {
+	APANEL_DEV_NONE	 = 0,
+	APANEL_DEV_APPBTN= 1,
+	APANEL_DEV_CDBTN = 2,
+	APANEL_DEV_LCD	 = 3,
+	APANEL_DEV_LED	 = 4,
+
+	APANEL_DEV_MAX,
+};
+
+enum apanel_chip {
+	CHIP_NONE   = 0,
+	CHIP_OZ992C = 1,
+	CHIP_OZ163T = 2,
+	CHIP_OZ711M3= 4,
+};
+static enum apanel_chip device_chip[APANEL_DEV_MAX];
+
+static const char *device_names[APANEL_DEV_MAX] = {
+	[APANEL_DEV_APPBTN] = "Application Buttons",
+	[APANEL_DEV_LCD]    = "LCD",
+	[APANEL_DEV_LED]    = "LED",
+	[APANEL_DEV_CDBTN]  = "CD Buttons",
+};
+
+struct apanel {
+	struct input_polled_dev *ipdev;
+	struct i2c_client client;
+	struct work_struct led_work;
+};
+
+MODULE_AUTHOR("Stephen Hemminger <shemminger@osdl.org>");
+MODULE_DESCRIPTION("Fujitsu Lifebook Application Panel driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.3");
+
+static int force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Load even if computer is not in database");
+
+static int apanel_probe(struct i2c_adapter *, int, int);
+
+/* for now, we only support one address */
+static unsigned short normal_i2c[] = {0, I2C_CLIENT_END};
+static unsigned short ignore = I2C_CLIENT_END;
+static struct i2c_client_address_data addr_data = {
+	.normal_i2c	= normal_i2c,
+	.probe		= &ignore,
+	.ignore		= &ignore,
+};
+
+/* Poll for key changes every 100ms
+ *
+ * Read Application keys via SMI
+ *  A (0x4), B (0x8), Internet (0x2), Email (0x1).
+ */
+static void report_key(struct input_dev *input,unsigned keycode)
+{
+	input_report_key(input, keycode, 1);
+	input_sync(input);
+	input_report_key(input, keycode, 0);
+	input_sync(input);
+}
+
+static void apanel_poll(struct input_polled_dev *ipdev)
+{
+	struct apanel *ap = ipdev->private;
+	u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+	const struct keymap *key;
+	s32 data;
+
+	data = i2c_smbus_read_word_data(&ap->client, cmd);
+	if (data < 0) {
+		if (printk_ratelimit())
+			printk(KERN_DEBUG "apanel: smbus read error %d\n", data);
+		return;
+	}
+
+	/* write back to clear latch */
+	i2c_smbus_write_word_data(&ap->client, cmd, 0);
+
+	if (!data)
+		return;
+
+	dev_dbg(&ap->client.dev, "poll data=%#x\n", data);
+	for (key = keymap; key->mask; ++key)
+		if (data & key->mask)
+			report_key(ap->ipdev->input, key->keycode);
+}
+
+/* Track state changes of LED
+ * There are actually four LED's (A, B, Internet, E-Mail),
+ * unsure about how to access others and what LED_xxx value would be useful.
+ */
+static void apanel_led(struct work_struct *work)
+{
+	struct apanel *ap = container_of(work, struct apanel, led_work);
+
+	i2c_smbus_write_word_data(&ap->client, 0x10,
+				  test_bit(LED_MAIL, ap->ipdev->input->led)
+				  ? 0x8000 : 0);
+}
+
+/* Callback from input layer when state change happens.
+ * Used to handle LED control.
+ */
+static int apanel_event(struct input_dev *dev, unsigned int type,
+			unsigned int code, int value)
+{
+	struct apanel *ap = dev->private;
+
+	if (device_chip[APANEL_DEV_LED] == CHIP_NONE || type != EV_LED)
+		return -1;
+
+	schedule_work(&ap->led_work);
+	return 0;
+}
+
+static int apanel_detach_client(struct i2c_client *client)
+{
+	struct apanel *ap = i2c_get_clientdata(client);
+
+	input_unregister_polled_device(ap->ipdev);
+	i2c_detach_client(&ap->client);
+	kfree(ap);
+	return 0;
+}
+
+/* Function is invoked for every i2c adapter. */
+static int apanel_attach_adapter(struct i2c_adapter *adap)
+{
+	/* Our device is connected only to i801 on laptop */
+	if (adap->id != I2C_HW_SMBUS_I801)
+		return -ENODEV;
+
+	return i2c_probe(adap, &addr_data, apanel_probe);
+}
+
+static struct i2c_driver apanel_driver = {
+	.driver = {
+		.name = "apanel",
+	},
+	.attach_adapter = &apanel_attach_adapter,
+	.detach_client  = &apanel_detach_client,
+};
+
+/*
+  basically this function should probe the i2c client, but we know that it has
+  to be the one we're looking for.
+ */
+static int apanel_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+	struct apanel *ap;
+	const struct keymap *key;
+	struct input_polled_dev *ipdev;
+	u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+	int err = -ENOMEM;
+
+	ap = kzalloc(sizeof(*ap), GFP_KERNEL);
+	if (!ap)
+		goto out0;
+
+	dev_dbg(&ap->client.dev, "probe adapter %p addr %d kind %d\n",
+		 adap, addr, kind);
+
+	ipdev = input_allocate_polled_device();
+	if (!ipdev)
+		goto out1;
+
+	ap->ipdev = ipdev;
+	ap->client.adapter = adap;
+	ap->client.addr = addr;
+	strncpy(ap->client.name, "apanel", I2C_NAME_SIZE);
+	ap->client.driver = &apanel_driver;
+
+	INIT_WORK(&ap->led_work, apanel_led);
+	i2c_set_clientdata(&ap->client, ap);
+
+	i2c_smbus_write_word_data(&ap->client, cmd, 0);
+
+	ipdev->private = ap;
+	ipdev->poll = apanel_poll;
+	ipdev->poll_interval = 500;
+
+	ipdev->input->name = "Lifebook Panel buttons";
+	ipdev->input->phys = "apanel/input0";
+	ipdev->input->id.bustype = BUS_HOST;
+	ipdev->input->cdev.dev = &ap->client.dev;
+	ipdev->input->private = ap;
+	ipdev->input->evbit[LONG(EV_KEY)] = BIT(EV_KEY);
+
+	for (key = keymap; key->mask; ++key)
+		set_bit(key->keycode, ipdev->input->keybit);
+
+	if (device_chip[APANEL_DEV_LED] != CHIP_NONE) {
+		ipdev->input->event = apanel_event;
+		ipdev->input->evbit[0] |= BIT(EV_LED);
+		set_bit(LED_MAIL, ipdev->input->ledbit);
+	}
+
+	err = i2c_attach_client(&ap->client);
+	if (err)
+		goto out2;
+
+	err = input_register_polled_device(ipdev);
+	if (err)
+		goto out3;
+
+	return 0;
+out3:
+	i2c_detach_client(&ap->client);
+out2:
+	input_free_polled_device(ipdev);
+out1:
+	kfree(ap);
+out0:
+	return err;
+}
+
+/* Scan the system ROM for the signature "FJKEYINF" */
+static __init void __iomem *bios_signature(void)
+{
+	void __iomem *bios;
+	ssize_t offset;
+	const unsigned char signature[] = "FJKEYINF";
+
+	bios = ioremap(0xF0000, 0x10000); /* Can't fail */
+
+	for (offset = 0; offset < 0x10000; offset += 0x10) {
+		if (check_signature(bios + offset, signature,
+				    sizeof(signature)-1))
+			return bios + offset;
+	}
+
+	printk(KERN_ERR  "apanel: Fujitsu BIOS signature '%s' not found...\n",
+	       signature);
+	iounmap(bios);
+	return NULL;
+}
+
+static int __init apanel_init(void)
+{
+	void __iomem *bios;
+	u8 devno;
+	int found = 0;
+
+	if (!dmi_check_system(apanel_dmi_table)) {
+		printk(KERN_WARNING "apanel: DMI information does not match\n");
+		if (!force)
+			return -ENODEV;
+	}
+	BUG_ON(!keymap);
+
+	bios = bios_signature();
+	if (!bios)
+		return -ENODEV;
+
+	bios += 8;
+
+	/* just use the first address */
+	normal_i2c[0] = readb(bios+3) >> 1;
+
+	for ( ; (devno = readb(bios)) & 0x7f; bios += 4) {
+		unsigned char method, slave, chip;
+
+		method = readb(bios + 1);
+		chip = readb(bios + 2);
+		slave = readb(bios + 3) >> 1;
+
+		if (slave != normal_i2c[0]) {
+			printk(KERN_ERR "apanel: only one SMBus slave "
+				 "address supported, skiping device...\n");
+			continue;
+		}
+
+		/* translate alternative device numbers */
+		switch (devno) {
+		case 6:
+			devno = APANEL_DEV_APPBTN;
+			break;
+		case 7:
+			devno = APANEL_DEV_LED;
+			break;
+		}
+
+		if (devno >= APANEL_DEV_MAX)
+			printk(KERN_WARNING "apanel: unknown device %d found\n",
+			       devno);
+		else if (device_chip[devno] != CHIP_NONE)
+			printk(KERN_ERR "apanel: duplicate entry for %s\n",
+			       device_names[devno]);
+
+		else if (method != 1 && method != 2 && method != 4) {
+			printk(KERN_ERR "apanel: unknown  method %u for %s\n",
+			       method, device_names[devno]);
+		} else {
+			pr_debug("apanel: %s found, chip=%d\n",
+				 device_names[devno], chip);
+
+			device_chip[devno] = (enum apanel_chip) chip;
+			++found;
+		}
+	}
+	iounmap(bios);
+
+	if (found == 0) {
+		printk(KERN_ERR "apanel: no input devices reported by BIOS\n");
+		return -EIO;
+	}
+
+	return i2c_add_driver(&apanel_driver);
+}
+module_init(apanel_init);
+
+static void __exit apanel_cleanup(void)
+{
+	i2c_del_driver(&apanel_driver);
+}
+module_exit(apanel_cleanup);

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

* Re: [PATCH] fujtisu application panel driver
  2007-07-02 18:17 [PATCH] fujtisu application panel driver Stephen Hemminger
@ 2007-07-02 19:16 ` Dmitry Torokhov
  2007-09-03  8:58   ` Stephen Hemminger
  2007-07-05  1:29 ` [PATCH] fujtisu application panel driver Jason Uhlenkott
  1 sibling, 1 reply; 23+ messages in thread
From: Dmitry Torokhov @ 2007-07-02 19:16 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: Len Brown, Andrew Morton, linux-input, linux-kernel

Hi Stephen,

On 7/2/07, Stephen Hemminger <shemminger@linux-foundation.org> wrote:
> This driver supports the application buttons on some Fujitsu Lifebook laptops.
> These buttons are read via the SMBus, for more details see:
>        http://apanel.sourceforge.net/tech.php
> The buttons are handled as by the regular input system.
> Two models are detected now, but other Fujitsu laptop's have
> keys that may work similarly.
>
> It is based on the earlier apanel driver done by Jochen Eisenger, but
> with many changes.  The original driver used ioctl's and a separate
> user space program; this version hooks into the input subsystem so
> that the normal Gnome/KDE shortcuts work without any userspace
> changes.
>

Thank you very much for updating the patch. I have a couple of requests though:

1. LEDs shoud use the generic led subsystem instead of input layer. I
do not have plans of adding any more LED_XXX constants and I think
that adding any LEDs not directly relating to keyboard state was a
mistake.

2. It would be nice if driver supported changing its keymaps now that
we allow overriding default getkeycode() and setkeycode().

3. Do not aaccess input_dev->private directly. input_set_drvdata() and
input+_getdrvdata shoudl be used.

> +static int apanel_event(struct input_dev *dev, unsigned int type,
> +                       unsigned int code, int value)
> +{
> +       struct apanel *ap = dev->private;

Also I don't think the above is correct. I think you need the following here:

   struct input_polled_dev *polldev = input_get_drvdata(dev);
   struct apanal *ap = polldev->private;

4:
> +       ipdev->input->cdev.dev = &ap->client.dev;

Please change to "ipdev->input->dev.parent = &ap->client.dev;"

5:
> +       ipdev->input->private = ap;

polledev uses input->private for its own purposed, you need to use
ipdev->private.

Thank you.

-- 
Dmitry

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

* Re: [PATCH] fujtisu application panel driver
  2007-07-02 18:17 [PATCH] fujtisu application panel driver Stephen Hemminger
  2007-07-02 19:16 ` Dmitry Torokhov
@ 2007-07-05  1:29 ` Jason Uhlenkott
  2007-09-03  9:00   ` Stephen Hemminger
  1 sibling, 1 reply; 23+ messages in thread
From: Jason Uhlenkott @ 2007-07-05  1:29 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Dmitry Torokhov, Len Brown, Andrew Morton, linux-input, linux-kernel

On Mon, Jul 02, 2007 at 11:17:24 -0700, Stephen Hemminger wrote:
> +static int __init apanel_init(void)
> +{
> +	void __iomem *bios;
> +	u8 devno;
> +	int found = 0;
> +
> +	if (!dmi_check_system(apanel_dmi_table)) {
> +		printk(KERN_WARNING "apanel: DMI information does not match\n");
> +		if (!force)
> +			return -ENODEV;
> +	}
> +	BUG_ON(!keymap);

If force=1 and there's no DMI match, we're guaranteed to pop this BUG_ON.


> +
> +			device_chip[devno] = (enum apanel_chip) chip;
> +			++found;
> +		}
> +	}
> +	iounmap(bios);

This isn't the address we got from ioremap (although it happens to
work, since iounmap internally masks it against PAGE_MASK).

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

* Re: [PATCH] fujtisu application panel driver
  2007-07-02 19:16 ` Dmitry Torokhov
@ 2007-09-03  8:58   ` Stephen Hemminger
  2007-09-07 12:02     ` Pavel Machek
  0 siblings, 1 reply; 23+ messages in thread
From: Stephen Hemminger @ 2007-09-03  8:58 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Len Brown, Andrew Morton, linux-input, linux-kernel

On Mon, 2 Jul 2007 15:16:30 -0400
"Dmitry Torokhov" <dmitry.torokhov@gmail.com> wrote:

> Hi Stephen,
> 
> On 7/2/07, Stephen Hemminger <shemminger@linux-foundation.org> wrote:
> > This driver supports the application buttons on some Fujitsu Lifebook laptops.
> > These buttons are read via the SMBus, for more details see:
> >        http://apanel.sourceforge.net/tech.php
> > The buttons are handled as by the regular input system.
> > Two models are detected now, but other Fujitsu laptop's have
> > keys that may work similarly.
> >
> > It is based on the earlier apanel driver done by Jochen Eisenger, but
> > with many changes.  The original driver used ioctl's and a separate
> > user space program; this version hooks into the input subsystem so
> > that the normal Gnome/KDE shortcuts work without any userspace
> > changes.
> >
> 
> Thank you very much for updating the patch. I have a couple of requests though:
> 
> 1. LEDs shoud use the generic led subsystem instead of input layer. I
> do not have plans of adding any more LED_XXX constants and I think
> that adding any LEDs not directly relating to keyboard state was a
> mistake.

No, way to much work for one stinking LED.
Also there is existing application support for legacy LED_MAIL through
input subsystem, and no application support for mail led through LED
subsystem.

> 2. It would be nice if driver supported changing its keymaps now that
> we allow overriding default getkeycode() and setkeycode().

No, this drivers mapping table is from bits to key codes, not scan codes to keycodes.
Each key is a bit in the scan code, so hitting A and B key generates
scancode of : 0xC = 0x4 | 0x8.
get/setkeycode is designed for keyboards where each key generates different
unique code.

> 3. Do not aaccess input_dev->private directly. input_set_drvdata() and
> input+_getdrvdata shoudl be used.

New version of  driver has to use input_polled_dev->private directly.

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

* Re: [PATCH] fujtisu application panel driver
  2007-07-05  1:29 ` [PATCH] fujtisu application panel driver Jason Uhlenkott
@ 2007-09-03  9:00   ` Stephen Hemminger
  0 siblings, 0 replies; 23+ messages in thread
From: Stephen Hemminger @ 2007-09-03  9:00 UTC (permalink / raw)
  To: Jason Uhlenkott
  Cc: Dmitry Torokhov, Len Brown, Andrew Morton, linux-input, linux-kernel

This driver supports the application buttons on some Fujitsu Lifebook laptops.
These buttons are read via the SMBus, for more details see:
	http://apanel.sourceforge.net/tech.php
The buttons are handled as by the regular input system.
Two models are detected now, but other Fujitsu laptop's have
keys that may work similarly.

It is based on the earlier apanel driver done by Jochen Eisenger, but
with many changes.  The original driver used ioctl's and a separate
user space program; this version hooks into the input subsystem so
that the normal Gnome/KDE shortcuts work without any userspace
changes.

This patch is against 2.6.23-rc5 and fixes the input_polled_dev and
mapping issues from earlier version.

Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>

---
 drivers/input/misc/Kconfig  |   14 +
 drivers/input/misc/Makefile |    1 
 drivers/input/misc/apanel.c |  403 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 418 insertions(+)

--- a/drivers/input/misc/Kconfig	2007-08-29 15:33:36.000000000 +0100
+++ b/drivers/input/misc/Kconfig	2007-09-03 07:56:33.000000000 +0100
@@ -40,6 +40,20 @@ config INPUT_M68K_BEEP
 	tristate "M68k Beeper support"
 	depends on M68K
 
+config INPUT_APANEL
+	tristate "Fujitsu Lifebook Application Panel buttons"
+	depends on X86
+	select I2C_I801
+	select INPUT_POLLDEV
+	select CHECK_SIGNATURE
+	help
+	 Say Y here for support of the Application Panel buttons, used on
+	 Fujitsu Lifebook. These are attached to the mainboard through
+	 an SMBus interface managed by the I2C Intel ICH (i801) driver.
+
+	 To compile this driver as a module, choose M here: the module will
+	 be called apanel.
+
 config INPUT_IXP4XX_BEEPER
 	tristate "IXP4XX Beeper support"
 	depends on ARCH_IXP4XX
--- a/drivers/input/misc/Makefile	2007-08-29 15:31:51.000000000 +0100
+++ b/drivers/input/misc/Makefile	2007-09-03 07:45:56.000000000 +0100
@@ -18,3 +18,4 @@ obj-$(CONFIG_INPUT_POWERMATE)		+= powerm
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
+obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/input/misc/apanel.c	2007-09-03 09:35:52.000000000 +0100
@@ -0,0 +1,403 @@
+/*
+ *  Fujitsu Lifebook Application Panel button drive
+ *
+ *  Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org>
+ *  Copyright (C) 2001-2003 Jochen Eisinger <jochen@penguin-breeder.org>
+ *
+ * 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 published by
+ * the Free Software Foundation.
+ *
+ * Many Fujitsu Lifebook have a small panel of buttons that are
+ * accessible via the i2c/smbus interface. This driver polls those
+ * buttons and generates input events.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/input-polldev.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+struct keymap {
+	u16 mask;
+	unsigned keycode;
+};
+
+static struct keymap app_media_keys[] = {
+	{ 0x1, 	 KEY_MAIL },
+	{ 0x2,	 KEY_WWW },
+	{ 0x4,	 KEY_PROG2 },
+	{ 0x8,	 KEY_PROG1 },
+	{ 0x100, KEY_FORWARD },
+	{ 0x200, KEY_REWIND },
+	{ 0x400, KEY_STOPCD },
+	{ 0x800, KEY_PLAYPAUSE },
+	{ 0 }
+};
+
+static struct keymap four_keys[] = {
+	{ 0x1, 	KEY_PROG4 },
+	{ 0x2,	KEY_PROG3 },
+	{ 0x4,	KEY_PROG2 },
+	{ 0x8,	KEY_PROG1 },
+	{ 0 }
+};
+
+static const struct keymap *keymap;
+
+static int __init dmi_matched(struct dmi_system_id *dmi)
+{
+	keymap = dmi->driver_data;
+	return 1;
+}
+
+/* List of systems known to work */
+static struct dmi_system_id apanel_dmi_table[] __initdata = {
+	{
+		.callback = dmi_matched,
+		.ident = "Lifebook S",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"),
+		},
+		.driver_data = app_media_keys,
+	},
+	{
+		.callback = dmi_matched,
+		.ident = "Lifebook B6210",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "Lifebook B6210"),
+		},
+		.driver_data = four_keys,
+	},
+	{ }
+};
+
+/* Magic constants in BIOS that tell about buttons */
+enum apanel_devid {
+	APANEL_DEV_NONE	 = 0,
+	APANEL_DEV_APPBTN= 1,
+	APANEL_DEV_CDBTN = 2,
+	APANEL_DEV_LCD	 = 3,
+	APANEL_DEV_LED	 = 4,
+	APANEL_DEV_MAX,
+};
+
+enum apanel_chip {
+	CHIP_NONE   = 0,
+	CHIP_OZ992C = 1,
+	CHIP_OZ163T = 2,
+	CHIP_OZ711M3= 4,
+};
+static enum apanel_chip device_chip[APANEL_DEV_MAX];
+
+/* names for APANEL_XXX */
+static const char *device_names[APANEL_DEV_MAX] = {
+	[APANEL_DEV_APPBTN] = "Application Buttons",
+	[APANEL_DEV_LCD]    = "LCD",
+	[APANEL_DEV_LED]    = "LED",
+	[APANEL_DEV_CDBTN]  = "CD Buttons",
+};
+
+struct apanel {
+	struct input_polled_dev *ipdev;
+	struct i2c_client client;
+	struct work_struct led_work;
+};
+
+MODULE_AUTHOR("Stephen Hemminger <shemminger@osdl.org>");
+MODULE_DESCRIPTION("Fujitsu Lifebook Application Panel driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+static int force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Load even if computer is not in database");
+
+static int apanel_probe(struct i2c_adapter *, int, int);
+
+/* for now, we only support one address */
+static unsigned short normal_i2c[] = {0, I2C_CLIENT_END};
+static unsigned short ignore = I2C_CLIENT_END;
+static struct i2c_client_address_data addr_data = {
+	.normal_i2c	= normal_i2c,
+	.probe		= &ignore,
+	.ignore		= &ignore,
+};
+
+/* Poll for key changes every 100ms
+ *
+ * Read Application keys via SMI
+ *  A (0x4), B (0x8), Internet (0x2), Email (0x1).
+ */
+static void report_key(struct input_dev *input, unsigned keycode)
+{
+	input_report_key(input, keycode, 1);
+	input_sync(input);
+	input_report_key(input, keycode, 0);
+	input_sync(input);
+}
+
+static void apanel_poll(struct input_polled_dev *ipdev)
+{
+	struct apanel *ap = ipdev->private;
+	u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+	const struct keymap *key;
+	s32 data;
+
+	data = i2c_smbus_read_word_data(&ap->client, cmd);
+	if (data < 0) {
+		if (printk_ratelimit())
+			printk(KERN_DEBUG "apanel: smbus read error %d\n", data);
+		return;
+	}
+
+	/* write back to clear latch */
+	i2c_smbus_write_word_data(&ap->client, cmd, 0);
+
+	if (!data)
+		return;
+
+	pr_debug("apanel: poll data=%#x\n", data);
+	for (key = keymap; key->mask; ++key)
+		if (data & key->mask)
+			report_key(ap->ipdev->input, key->keycode);
+}
+
+/* Track state changes of LED
+ * There are actually four LED's (A, B, Internet, E-Mail),
+ * unsure about how to access others and what LED_xxx value would be useful.
+ */
+static void apanel_led(struct work_struct *work)
+{
+	struct apanel *ap = container_of(work, struct apanel, led_work);
+
+	i2c_smbus_write_word_data(&ap->client, 0x10,
+				  test_bit(LED_MAIL, ap->ipdev->input->led)
+				  ? 0x8000 : 0);
+}
+
+/* Callback from input layer when state change happens.
+ * Used to handle LED control.
+ */
+static int apanel_event(struct input_dev *input, unsigned int type,
+			unsigned int code, int value)
+{
+	struct input_polled_dev *ipdev = input->private;
+	struct apanel *ap = ipdev->private;
+
+	if (device_chip[APANEL_DEV_LED] == CHIP_NONE || type != EV_LED)
+		return -1;
+
+	schedule_work(&ap->led_work);
+	return 0;
+}
+
+static int apanel_detach_client(struct i2c_client *client)
+{
+	struct apanel *ap = i2c_get_clientdata(client);
+
+	input_unregister_polled_device(ap->ipdev);
+	i2c_detach_client(&ap->client);
+	kfree(ap);
+	return 0;
+}
+
+/* Function is invoked for every i2c adapter. */
+static int apanel_attach_adapter(struct i2c_adapter *adap)
+{
+	pr_debug("apanel: attach adapter id=%d\n", adap->id);
+
+	/* Our device is connected only to i801 on laptop */
+	if (adap->id != I2C_HW_SMBUS_I801)
+		return -ENODEV;
+
+	return i2c_probe(adap, &addr_data, apanel_probe);
+}
+
+static struct i2c_driver apanel_driver = {
+	.driver = {
+		.name = "apanel",
+	},
+	.attach_adapter = &apanel_attach_adapter,
+	.detach_client  = &apanel_detach_client,
+};
+
+/*
+  basically this function should probe the i2c client, but we know that it has
+  to be the one we're looking for.
+ */
+static int apanel_probe(struct i2c_adapter *bus, int address, int kind)
+{
+	struct apanel *ap;
+	const struct keymap *key;
+	struct input_polled_dev *ipdev;
+	u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+	int err = -ENOMEM;
+
+	pr_debug("apanel: probe adapter %p addr %d kind %d\n",
+		 bus, address, kind);
+
+	ap = kzalloc(sizeof(*ap), GFP_KERNEL);
+	if (!ap)
+		goto out0;
+
+	ipdev = input_allocate_polled_device();
+	if (!ipdev)
+		goto out1;
+
+	ap->ipdev = ipdev;
+	ap->client.adapter = bus;
+	ap->client.addr = address;
+	ap->client.driver = &apanel_driver;
+	strlcpy(ap->client.name, "apanel", I2C_NAME_SIZE);
+	i2c_set_clientdata(&ap->client, ap);
+	INIT_WORK(&ap->led_work, apanel_led);
+
+	ipdev->poll = apanel_poll;
+	ipdev->poll_interval = 500;
+	ipdev->input->name = "Lifebook Panel buttons";
+	ipdev->input->phys = "apanel/input0";
+	ipdev->input->id.bustype = BUS_HOST;
+	ipdev->private = ap;
+
+	ipdev->input->evbit[LONG(EV_KEY)] = BIT(EV_KEY);
+	for (key = keymap; key->mask; ++key)
+		set_bit(key->keycode, ipdev->input->keybit);
+
+	if (device_chip[APANEL_DEV_LED] != CHIP_NONE) {
+		ipdev->input->event = apanel_event;
+		ipdev->input->evbit[0] |= BIT(EV_LED);
+		set_bit(LED_MAIL, ipdev->input->ledbit);
+	}
+
+	err = i2c_attach_client(&ap->client);
+	if (err)
+		goto out2;
+
+	ipdev->input->dev.parent = &ap->client.dev;
+	printk("ipdev=%p input=%p ap=%p\n", ipdev, ipdev->input, ap);
+
+	i2c_smbus_write_word_data(&ap->client, cmd, 0);
+
+	err = input_register_polled_device(ipdev);
+	if (err)
+		goto out3;
+
+	return 0;
+out3:
+	i2c_detach_client(&ap->client);
+out2:
+	input_free_polled_device(ipdev);
+out1:
+	kfree(ap);
+out0:
+	return err;
+}
+
+/* Scan the system ROM for the signature "FJKEYINF" */
+static __init const void __iomem *bios_signature(const void __iomem *bios)
+{
+	ssize_t offset;
+	const unsigned char signature[] = "FJKEYINF";
+
+	for (offset = 0; offset < 0x10000; offset += 0x10) {
+		if (check_signature(bios + offset, signature,
+				    sizeof(signature)-1))
+			return bios + offset;
+	}
+
+	printk(KERN_ERR  "apanel: Fujitsu BIOS signature '%s' not found...\n",
+	       signature);
+	return NULL;
+}
+
+static int __init apanel_init(void)
+{
+	void __iomem *bios;
+	const void __iomem *p;
+	u8 devno;
+	int found = 0;
+
+	if (!dmi_check_system(apanel_dmi_table)) {
+		printk(KERN_WARNING "apanel: DMI information does not match\n");
+		if (!force)
+			return -ENODEV;
+
+		/* default key map */
+		keymap = four_keys;
+	}
+
+	bios = ioremap(0xF0000, 0x10000); /* Can't fail */
+
+	p = bios_signature(bios);
+	if (!p) {
+		iounmap(bios);
+		return -ENODEV;
+	}
+
+	/* just use the first address */
+	p += 8;
+	normal_i2c[0] = readb(p+3) >> 1;
+
+	for ( ; (devno = readb(p)) & 0x7f; p += 4) {
+		unsigned char method, slave, chip;
+
+		method = readb(p + 1);
+		chip = readb(p + 2);
+		slave = readb(p + 3) >> 1;
+
+		if (slave != normal_i2c[0]) {
+			printk(KERN_ERR "apanel: only one SMBus slave "
+				 "address supported, skiping device...\n");
+			continue;
+		}
+
+		/* translate alternative device numbers */
+		switch (devno) {
+		case 6:
+			devno = APANEL_DEV_APPBTN;
+			break;
+		case 7:
+			devno = APANEL_DEV_LED;
+			break;
+		}
+
+		if (devno >= APANEL_DEV_MAX)
+			printk(KERN_WARNING "apanel: unknown device %d found\n",
+			       devno);
+		else if (device_chip[devno] != CHIP_NONE)
+			printk(KERN_ERR "apanel: duplicate entry for %s\n",
+			       device_names[devno]);
+
+		else if (method != 1 && method != 2 && method != 4) {
+			printk(KERN_ERR "apanel: unknown  method %u for %s\n",
+			       method, device_names[devno]);
+		} else {
+			pr_debug("apanel: %s found, chip=%d\n",
+				 device_names[devno], chip);
+
+			device_chip[devno] = (enum apanel_chip) chip;
+			++found;
+		}
+	}
+	iounmap(bios);
+
+	if (found == 0) {
+		printk(KERN_ERR "apanel: no input devices reported by BIOS\n");
+		return -EIO;
+	}
+
+	return i2c_add_driver(&apanel_driver);
+}
+module_init(apanel_init);
+
+static void __exit apanel_cleanup(void)
+{
+	i2c_del_driver(&apanel_driver);
+}
+module_exit(apanel_cleanup);

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

* Re: [PATCH] fujtisu application panel driver
  2007-09-03  8:58   ` Stephen Hemminger
@ 2007-09-07 12:02     ` Pavel Machek
  2007-09-12 11:38       ` [PATCH] Fujitsu application panel driver (rev3) Stephen Hemminger
  0 siblings, 1 reply; 23+ messages in thread
From: Pavel Machek @ 2007-09-07 12:02 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Dmitry Torokhov, Len Brown, Andrew Morton, linux-input, linux-kernel

Hi!

> > Thank you very much for updating the patch. I have a couple of requests though:
> > 
> > 1. LEDs shoud use the generic led subsystem instead of input layer. I
> > do not have plans of adding any more LED_XXX constants and I think
> > that adding any LEDs not directly relating to keyboard state was a
> > mistake.
> 
> No, way to much work for one stinking LED.

'Lets do it wrong way because right way is too much work'?

Come on... doing let driver is 30 minutes and 100-or-so lines of code.
							Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* [PATCH] Fujitsu application panel driver (rev3)
  2007-09-07 12:02     ` Pavel Machek
@ 2007-09-12 11:38       ` Stephen Hemminger
  2007-09-14  5:30         ` Dmitry Torokhov
  0 siblings, 1 reply; 23+ messages in thread
From: Stephen Hemminger @ 2007-09-12 11:38 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Dmitry Torokhov, Len Brown, Andrew Morton, linux-input, linux-kernel

This driver supports the application buttons on some Fujitsu Lifebook laptops.
It is based on the earlier apanel driver done by Jochen Eisenger, but
with many changes.  The original driver used ioctl's and a separate
user space program;  see http://apanel.sourceforge.net/tech.php
This version hooks into the input subsystem so that the normal
Gnome/KDE shortcuts work without any userspace changes.

The Mail Led is handled via leds class device.

Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>

---
 drivers/input/misc/Kconfig  |   14 +
 drivers/input/misc/Makefile |    1 
 drivers/input/misc/apanel.c |  403 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 418 insertions(+)

--- a/drivers/input/misc/Kconfig	2007-09-12 09:51:24.000000000 +0200
+++ b/drivers/input/misc/Kconfig	2007-09-12 09:56:36.000000000 +0200
@@ -40,6 +40,20 @@ config INPUT_M68K_BEEP
 	tristate "M68k Beeper support"
 	depends on M68K
 
+config INPUT_APANEL
+	tristate "Fujitsu Lifebook Application Panel buttons"
+	depends on X86
+	select I2C_I801
+	select INPUT_POLLDEV
+	select CHECK_SIGNATURE
+	help
+	 Say Y here for support of the Application Panel buttons, used on
+	 Fujitsu Lifebook. These are attached to the mainboard through
+	 an SMBus interface managed by the I2C Intel ICH (i801) driver.
+
+	 To compile this driver as a module, choose M here: the module will
+	 be called apanel.
+
 config INPUT_IXP4XX_BEEPER
 	tristate "IXP4XX Beeper support"
 	depends on ARCH_IXP4XX
--- a/drivers/input/misc/Makefile	2007-09-12 09:51:24.000000000 +0200
+++ b/drivers/input/misc/Makefile	2007-09-12 09:56:36.000000000 +0200
@@ -18,3 +18,4 @@ obj-$(CONFIG_INPUT_POWERMATE)		+= powerm
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
+obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/input/misc/apanel.c	2007-09-12 12:53:04.000000000 +0200
@@ -0,0 +1,420 @@
+/*
+ *  Fujitsu Lifebook Application Panel button drive
+ *
+ *  Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org>
+ *  Copyright (C) 2001-2003 Jochen Eisinger <jochen@penguin-breeder.org>
+ *
+ * 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 published by
+ * the Free Software Foundation.
+ *
+ * Many Fujitsu Lifebook have a small panel of buttons that are
+ * accessible via the i2c/smbus interface. This driver polls those
+ * buttons and generates input events.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/platform_device.h>
+#include <linux/input-polldev.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+
+#define APANEL_NAME	"Fujitsu Application Panel"
+#define APANEL_VERSION	"1.1"
+#define APANEL		"apanel"
+
+#ifndef pr_warn
+#define pr_warn(fmt, arg...) \
+	printk(KERN_WARNING fmt, ##arg)
+#endif
+
+struct keymap {
+	u16 mask;
+	unsigned keycode;
+};
+
+static struct keymap app_media_keys[] = {
+	{ 0x1, 	 KEY_MAIL },
+	{ 0x2,	 KEY_WWW },
+	{ 0x4,	 KEY_PROG2 },
+	{ 0x8,	 KEY_PROG1 },
+	{ 0x100, KEY_FORWARD },
+	{ 0x200, KEY_REWIND },
+	{ 0x400, KEY_STOPCD },
+	{ 0x800, KEY_PLAYPAUSE },
+	{ 0 }
+};
+
+static struct keymap four_keys[] = {
+	{ 0x1, 	KEY_PROG4 },
+	{ 0x2,	KEY_PROG3 },
+	{ 0x4,	KEY_PROG2 },
+	{ 0x8,	KEY_PROG1 },
+	{ 0 }
+};
+
+static const struct keymap *keymap;
+
+static int __init dmi_matched(struct dmi_system_id *dmi)
+{
+	keymap = dmi->driver_data;
+	return 1;
+}
+
+/* List of systems known to work */
+static struct dmi_system_id  __initdata apanel_dmi_table[] = {
+	{
+		.callback = dmi_matched,
+		.ident = "Lifebook S",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"),
+		},
+		.driver_data = app_media_keys,
+	},
+	{
+		.callback = dmi_matched,
+		.ident = "Lifebook B6210",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "Lifebook B6210"),
+		},
+		.driver_data = four_keys,
+	},
+	{ }
+};
+
+/* Magic constants in BIOS that tell about buttons */
+enum apanel_devid {
+	APANEL_DEV_NONE	  = 0,
+	APANEL_DEV_APPBTN = 1,
+	APANEL_DEV_CDBTN  = 2,
+	APANEL_DEV_LCD	  = 3,
+	APANEL_DEV_LED	  = 4,
+	APANEL_DEV_MAX,
+};
+
+enum apanel_chip {
+	CHIP_NONE    = 0,
+	CHIP_OZ992C  = 1,
+	CHIP_OZ163T  = 2,
+	CHIP_OZ711M3 = 4,
+};
+static enum apanel_chip device_chip[APANEL_DEV_MAX];
+
+/* names for APANEL_XXX */
+static const char *device_names[APANEL_DEV_MAX] = {
+	[APANEL_DEV_APPBTN] = "Application Buttons",
+	[APANEL_DEV_LCD]    = "LCD",
+	[APANEL_DEV_LED]    = "LED",
+	[APANEL_DEV_CDBTN]  = "CD Buttons",
+};
+
+struct apanel {
+	struct input_polled_dev *ipdev;
+	struct i2c_client client;
+
+	u16    led_bits;
+	struct work_struct led_work;
+	struct led_classdev mail_led;
+};
+
+static int force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Load even if computer is not in database");
+
+static int apanel_probe(struct i2c_adapter *, int, int);
+
+/* for now, we only support one address */
+static unsigned short normal_i2c[] = {0, I2C_CLIENT_END};
+static unsigned short ignore = I2C_CLIENT_END;
+static struct i2c_client_address_data addr_data = {
+	.normal_i2c	= normal_i2c,
+	.probe		= &ignore,
+	.ignore		= &ignore,
+};
+
+/* Poll for key changes every 100ms
+ *
+ * Read Application keys via SMI
+ *  A (0x4), B (0x8), Internet (0x2), Email (0x1).
+ */
+static void report_key(struct input_dev *input, unsigned keycode)
+{
+	input_report_key(input, keycode, 1);
+	input_sync(input);
+	input_report_key(input, keycode, 0);
+	input_sync(input);
+}
+
+static void apanel_poll(struct input_polled_dev *ipdev)
+{
+	struct apanel *ap = ipdev->private;
+	u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+	const struct keymap *key;
+	s32 data;
+
+	data = i2c_smbus_read_word_data(&ap->client, cmd);
+	if (data < 0) {
+		if (printk_ratelimit())
+			pr_info(APANEL ": smbus read error %d\n", data);
+		return;
+	}
+
+	/* write back to clear latch */
+	i2c_smbus_write_word_data(&ap->client, cmd, 0);
+
+	if (!data)
+		return;
+
+	pr_debug(APANEL ": poll data=%#x\n", data);
+	for (key = keymap; key->mask; ++key)
+		if (data & key->mask)
+			report_key(ap->ipdev->input, key->keycode);
+}
+
+/* Track state changes of LED */
+static void led_update(struct work_struct *work)
+{
+	struct apanel *ap = container_of(work, struct apanel, led_work);
+
+	i2c_smbus_write_word_data(&ap->client, 0x10, ap->led_bits);
+}
+
+static void mail_led_set(struct led_classdev *led,
+			 enum led_brightness value)
+{
+	struct apanel *ap = container_of(led, struct apanel, mail_led);
+	if (value)
+		ap->led_bits |= 0x8000;
+	else
+		ap->led_bits &= ~0x8000;
+	ap->led_bits = value;
+	schedule_work(&ap->led_work);
+}
+
+static int apanel_detach_client(struct i2c_client *client)
+{
+	struct apanel *ap = i2c_get_clientdata(client);
+
+	if (device_chip[APANEL_DEV_LED] != CHIP_NONE)
+		led_classdev_unregister(&ap->mail_led);
+
+	input_unregister_polled_device(ap->ipdev);
+	i2c_detach_client(&ap->client);
+	kfree(ap);
+	return 0;
+}
+
+/* Function is invoked for every i2c adapter. */
+static int apanel_attach_adapter(struct i2c_adapter *adap)
+{
+	pr_debug(APANEL ": attach adapter id=%d\n", adap->id);
+
+	/* Our device is connected only to i801 on laptop */
+	if (adap->id != I2C_HW_SMBUS_I801)
+		return -ENODEV;
+
+	return i2c_probe(adap, &addr_data, apanel_probe);
+}
+
+static struct i2c_driver apanel_driver = {
+	.driver = {
+		.name = APANEL,
+	},
+	.attach_adapter = &apanel_attach_adapter,
+	.detach_client  = &apanel_detach_client,
+};
+
+/*
+  basically this function should probe the i2c client, but we know that it has
+  to be the one we're looking for.
+ */
+static int apanel_probe(struct i2c_adapter *bus, int address, int kind)
+{
+	struct apanel *ap;
+	const struct keymap *key;
+	struct input_polled_dev *ipdev;
+	u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+	int err = -ENOMEM;
+
+	pr_debug(APANEL ": probe adapter %p addr %d kind %d\n",
+		 bus, address, kind);
+
+	ap = kzalloc(sizeof(*ap), GFP_KERNEL);
+	if (!ap)
+		goto out0;
+
+	ipdev = input_allocate_polled_device();
+	if (!ipdev)
+		goto out1;
+
+	ap->ipdev = ipdev;
+	ap->client.adapter = bus;
+	ap->client.addr = address;
+	ap->client.driver = &apanel_driver;
+	strlcpy(ap->client.name, APANEL, I2C_NAME_SIZE);
+	i2c_set_clientdata(&ap->client, ap);
+
+	err = i2c_attach_client(&ap->client);
+	if (err)
+		goto out2;
+
+	err = i2c_smbus_write_word_data(&ap->client, cmd, 0);
+	if (err) {
+		pr_warn(APANEL ": smbus write error %d\n", err);
+		goto out3;
+	}
+
+	ipdev->poll = apanel_poll;
+	ipdev->poll_interval = 1000;	/* 1 sec (power save) */
+	ipdev->input->name = APANEL_NAME " buttons";
+	ipdev->input->phys = "apanel/input0";
+	ipdev->input->id.bustype = BUS_HOST;
+	ipdev->private = ap;
+	ipdev->input->dev.parent = &ap->client.dev;
+
+	ipdev->input->evbit[LONG(EV_KEY)] = BIT(EV_KEY);
+	for (key = keymap; key->mask; ++key)
+		set_bit(key->keycode, ipdev->input->keybit);
+
+	err = input_register_polled_device(ipdev);
+	if (err)
+		goto out3;
+
+	INIT_WORK(&ap->led_work, led_update);
+	if (device_chip[APANEL_DEV_LED] != CHIP_NONE) {
+		ap->mail_led.name = APANEL ":mail";
+		ap->mail_led.brightness_set = mail_led_set;
+
+		err = led_classdev_register(&ap->client.dev, &ap->mail_led);
+		if (err)
+			goto out4;
+	}
+
+	return 0;
+out4:
+	input_unregister_polled_device(ipdev);
+out3:
+	i2c_detach_client(&ap->client);
+out2:
+	input_free_polled_device(ipdev);
+out1:
+	kfree(ap);
+out0:
+	return err;
+}
+
+/* Scan the system ROM for the signature "FJKEYINF" */
+static __init const void __iomem *bios_signature(const void __iomem *bios)
+{
+	ssize_t offset;
+	const unsigned char signature[] = "FJKEYINF";
+
+	for (offset = 0; offset < 0x10000; offset += 0x10) {
+		if (check_signature(bios + offset, signature,
+				    sizeof(signature)-1))
+			return bios + offset;
+	}
+
+	pr_warn(APANEL ": Fujitsu BIOS signature '%s' not found...\n",
+	       signature);
+	return NULL;
+}
+
+static int __init apanel_init(void)
+{
+	void __iomem *bios;
+	const void __iomem *p;
+	u8 devno;
+	int found = 0;
+
+	if (!dmi_check_system(apanel_dmi_table)) {
+		pr_warn(APANEL ": DMI information does not match\n");
+		if (!force)
+			return -ENODEV;
+
+		/* default key map */
+		keymap = four_keys;
+	}
+
+	bios = ioremap(0xF0000, 0x10000); /* Can't fail */
+
+	p = bios_signature(bios);
+	if (!p) {
+		iounmap(bios);
+		return -ENODEV;
+	}
+
+	/* just use the first address */
+	p += 8;
+	normal_i2c[0] = readb(p+3) >> 1;
+
+	for ( ; (devno = readb(p)) & 0x7f; p += 4) {
+		unsigned char method, slave, chip;
+
+		method = readb(p + 1);
+		chip = readb(p + 2);
+		slave = readb(p + 3) >> 1;
+
+		if (slave != normal_i2c[0]) {
+			printk(KERN_ERR APANEL ": only one SMBus slave "
+				 "address supported, skiping device...\n");
+			continue;
+		}
+
+		/* translate alternative device numbers */
+		switch (devno) {
+		case 6:
+			devno = APANEL_DEV_APPBTN;
+			break;
+		case 7:
+			devno = APANEL_DEV_LED;
+			break;
+		}
+
+		if (devno >= APANEL_DEV_MAX)
+			pr_warn(APANEL ": unknown device %d found\n", devno);
+		else if (device_chip[devno] != CHIP_NONE)
+			printk(KERN_ERR APANEL ": duplicate entry for %s\n",
+			       device_names[devno]);
+
+		else if (method != 1 && method != 2 && method != 4) {
+			printk(KERN_ERR APANEL ": unknown  method %u for %s\n",
+			       method, device_names[devno]);
+		} else {
+			pr_debug(APANEL ": %s found, chip=%d\n",
+				 device_names[devno], chip);
+
+			device_chip[devno] = (enum apanel_chip) chip;
+			++found;
+		}
+	}
+	iounmap(bios);
+
+	if (found == 0) {
+		printk(KERN_ERR APANEL ": no input devices reported by BIOS\n");
+		return -EIO;
+	}
+
+	return i2c_add_driver(&apanel_driver);
+}
+module_init(apanel_init);
+
+static void __exit apanel_cleanup(void)
+{
+	i2c_del_driver(&apanel_driver);
+}
+module_exit(apanel_cleanup);
+
+MODULE_AUTHOR("Stephen Hemminger <shemminger@osdl.org>");
+MODULE_DESCRIPTION(APANEL_NAME " driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(APANEL_VERSION);
+
+MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBookSSeries:pvr*:rvnFUJITSU:*");
+MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBookB6210:pvr*:rvnFUJITSU:*");

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

* Re: [PATCH] Fujitsu application panel driver (rev3)
  2007-09-12 11:38       ` [PATCH] Fujitsu application panel driver (rev3) Stephen Hemminger
@ 2007-09-14  5:30         ` Dmitry Torokhov
  2007-09-16 19:55           ` Stephen Hemminger
  0 siblings, 1 reply; 23+ messages in thread
From: Dmitry Torokhov @ 2007-09-14  5:30 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Pavel Machek, Len Brown, Andrew Morton, linux-input, linux-kernel

Hi Stephen,

On Wednesday 12 September 2007 07:38, Stephen Hemminger wrote:
> This driver supports the application buttons on some Fujitsu Lifebook laptops.
> It is based on the earlier apanel driver done by Jochen Eisenger, but
> with many changes.  The original driver used ioctl's and a separate
> user space program;  see http://apanel.sourceforge.net/tech.php
> This version hooks into the input subsystem so that the normal
> Gnome/KDE shortcuts work without any userspace changes.
> 
> The Mail Led is handled via leds class device.
> 

Thank you very much for convering the led to led subsystem. I tried
implementing loadable keymap support in the driver, could you please
try the patch below and if it still works for you then I will apply
it to my tree.

Thank you again.

-- 
Dmitry

Subject: Fujitsu application panel driver (rev4)
From: Stephen Hemminger <shemminger@linux-foundation.org>

Input: add Fujitsu application panel driver

This driver supports the application buttons on some Fujitsu Lifebook
laptops. It is based on the earlier apanel driver done by Jochen Eisenger,
but with many changes. The original driver used ioctl's and a separate
user space program; see http://apanel.sourceforge.net/tech.php
This version hooks into the input subsystem so that the normal Gnome/KDE
shortcuts work without any userspace changes.

The Mail Led is handled via leds class device.

Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
 drivers/input/misc/Kconfig  |   14 +
 drivers/input/misc/Makefile |    1 
 drivers/input/misc/apanel.c |  527 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 542 insertions(+)

Index: work/drivers/input/misc/Kconfig
===================================================================
--- work.orig/drivers/input/misc/Kconfig
+++ work/drivers/input/misc/Kconfig
@@ -40,6 +40,20 @@ config INPUT_M68K_BEEP
 	tristate "M68k Beeper support"
 	depends on M68K
 
+config INPUT_APANEL
+	tristate "Fujitsu Lifebook Application Panel buttons"
+	depends on X86
+	select I2C_I801
+	select INPUT_POLLDEV
+	select CHECK_SIGNATURE
+	help
+	  Say Y here for support of the Application Panel buttons, used on
+	  Fujitsu Lifebook. These are attached to the mainboard through
+	  an SMBus interface managed by the I2C Intel ICH (i801) driver.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called apanel.
+
 config INPUT_IXP4XX_BEEPER
 	tristate "IXP4XX Beeper support"
 	depends on ARCH_IXP4XX
Index: work/drivers/input/misc/Makefile
===================================================================
--- work.orig/drivers/input/misc/Makefile
+++ work/drivers/input/misc/Makefile
@@ -18,3 +18,4 @@ obj-$(CONFIG_INPUT_POWERMATE)		+= powerm
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
+obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
Index: work/drivers/input/misc/apanel.c
===================================================================
--- /dev/null
+++ work/drivers/input/misc/apanel.c
@@ -0,0 +1,527 @@
+/*
+ *  Fujitsu Lifebook Application Panel button drive
+ *
+ *  Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org>
+ *  Copyright (C) 2001-2003 Jochen Eisinger <jochen@penguin-breeder.org>
+ *
+ * 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 published by
+ * the Free Software Foundation.
+ *
+ * Many Fujitsu Lifebook have a small panel of buttons that are
+ * accessible via the i2c/smbus interface. This driver polls those
+ * buttons and generates input events.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/platform_device.h>
+#include <linux/input-polldev.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+
+#define APANEL_NAME	"Fujitsu Application Panel"
+#define APANEL_VERSION	"1.1"
+#define APANEL		"apanel"
+
+#ifndef pr_warn
+#define pr_warn(fmt, arg...) \
+	printk(KERN_WARNING fmt, ##arg)
+#endif
+
+struct keymap {
+	int bit;
+	unsigned int keycode;
+};
+
+static struct keymap app_media_keys[] __initdata = {
+	{ 0,	KEY_MAIL },
+	{ 1,	KEY_WWW },
+	{ 2,	KEY_PROG2 },
+	{ 3,	KEY_PROG1 },
+	{ 8,	KEY_FORWARD },
+	{ 9,	KEY_REWIND },
+	{ 10,	KEY_STOPCD },
+	{ 11,	KEY_PLAYPAUSE },
+	{ -1 }
+};
+
+static struct keymap four_keys[] __initdata = {
+	{ 0,	KEY_PROG4 },
+	{ 1,	KEY_PROG3 },
+	{ 2,	KEY_PROG2 },
+	{ 3,	KEY_PROG1 },
+	{ -1 }
+};
+
+static struct keymap *keymap;
+
+/* Copy the good keymap, as the original ones are free'd */
+static int __init apanel_copy_keymap(const struct keymap *src)
+{
+	const struct keymap *key;
+	unsigned int length = 1;
+
+	for (key = src; key->bit != -1; key++)
+		length++;
+
+	keymap = kmalloc(length * sizeof(struct keymap), GFP_KERNEL);
+	if (!keymap)
+		return -ENOMEM;
+
+	memcpy(keymap, src, length * sizeof(struct keymap));
+	return 0;
+}
+
+
+static int __init dmi_matched(struct dmi_system_id *dmi)
+{
+	apanel_copy_keymap(dmi->driver_data);
+	return 1;
+}
+
+/* List of systems known to work */
+static struct dmi_system_id  __initdata apanel_dmi_table[] = {
+	{
+		.callback = dmi_matched,
+		.ident = "Lifebook S",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"),
+		},
+		.driver_data = app_media_keys,
+	},
+	{
+		.callback = dmi_matched,
+		.ident = "Lifebook B6210",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "Lifebook B6210"),
+		},
+		.driver_data = four_keys,
+	},
+	{ }
+};
+
+/* Magic constants in BIOS that tell about buttons */
+enum apanel_devid {
+	APANEL_DEV_NONE		= 0,
+	APANEL_DEV_APPBTN	= 1,
+	APANEL_DEV_CDBTN	= 2,
+	APANEL_DEV_LCD		= 3,
+	APANEL_DEV_LED		= 4,
+	APANEL_DEV_MAX,
+};
+
+enum apanel_chip {
+	CHIP_NONE	= 0,
+	CHIP_OZ992C	= 1,
+	CHIP_OZ163T	= 2,
+	CHIP_OZ711M3	= 4,
+};
+static enum apanel_chip device_chip[APANEL_DEV_MAX];
+
+/* names for APANEL_XXX */
+static const char *device_names[APANEL_DEV_MAX] = {
+	[APANEL_DEV_APPBTN]	= "Application Buttons",
+	[APANEL_DEV_LCD]	= "LCD",
+	[APANEL_DEV_LED]	= "LED",
+	[APANEL_DEV_CDBTN]	= "CD Buttons",
+};
+
+struct apanel {
+	struct input_polled_dev *ipdev;
+	struct i2c_client client;
+
+	u16 led_bits;
+	struct work_struct led_work;
+	struct led_classdev mail_led;
+
+	struct keymap keymap[0];
+};
+
+static int force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Load even if computer is not in database");
+
+static int apanel_probe(struct i2c_adapter *, int, int);
+
+/* for now, we only support one address */
+static unsigned short normal_i2c[] = {0, I2C_CLIENT_END};
+static unsigned short ignore = I2C_CLIENT_END;
+static struct i2c_client_address_data addr_data = {
+	.normal_i2c	= normal_i2c,
+	.probe		= &ignore,
+	.ignore		= &ignore,
+};
+
+/* Poll for key changes every 100ms
+ *
+ * Read Application keys via SMI
+ *  A (0x4), B (0x8), Internet (0x2), Email (0x1).
+ */
+static void report_key(struct input_dev *input, const struct keymap *key)
+{
+	input_event(input, EV_MSC, MSC_SCAN, key->bit);
+	input_report_key(input, key->keycode, 1);
+	input_sync(input);
+
+	input_event(input, EV_MSC, MSC_SCAN, key->bit);
+	input_report_key(input, key->keycode, 0);
+	input_sync(input);
+}
+
+static void apanel_poll(struct input_polled_dev *ipdev)
+{
+	struct apanel *ap = ipdev->private;
+	struct input_dev *idev = ipdev->input;
+	u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+	const struct keymap *key;
+	s32 data;
+
+	data = i2c_smbus_read_word_data(&ap->client, cmd);
+	if (data < 0) {
+		if (printk_ratelimit())
+			pr_info(APANEL ": smbus read error %d\n", data);
+		return;
+	}
+
+	/* write back to clear latch */
+	i2c_smbus_write_word_data(&ap->client, cmd, 0);
+
+	if (!data)
+		return;
+
+	pr_debug(APANEL ": poll data=%#x\n", data);
+	for (key = keymap; key->bit != -1; ++key)
+		if (data & (1UL << key->bit))
+			report_key(idev, key);
+}
+
+static struct keymap *apanel_get_key_by_bit(int bit)
+{
+	struct keymap *key;
+
+	for (key = keymap; key->bit != -1; key++)
+		if (bit == key->bit)
+			return key;
+
+	return NULL;
+}
+
+static struct keymap *apanel_get_key_by_keycode(int keycode)
+{
+	struct keymap *key;
+
+	for (key = keymap; key->bit != -1; key++)
+		if (keycode == key->keycode)
+			return key;
+
+	return NULL;
+}
+
+static int apanel_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+{
+	const struct keymap *key = apanel_get_key_by_bit(scancode);
+
+	if (key) {
+		*keycode = key->keycode;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int apanel_setkeycode(struct input_dev *dev, int scancode, int keycode)
+{
+	struct keymap *key;
+	int old_keycode;
+
+	if (keycode < 0 || keycode > KEY_MAX)
+		return -EINVAL;
+
+	key = apanel_get_key_by_bit(scancode);
+	if (key) {
+		old_keycode = key->keycode;
+		key->keycode = keycode;
+		set_bit(keycode, dev->keybit);
+		if (!apanel_get_key_by_keycode(old_keycode))
+			clear_bit(old_keycode, dev->keybit);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+/* Track state changes of LED */
+static void led_update(struct work_struct *work)
+{
+	struct apanel *ap = container_of(work, struct apanel, led_work);
+
+	i2c_smbus_write_word_data(&ap->client, 0x10, ap->led_bits);
+}
+
+static void mail_led_set(struct led_classdev *led,
+			 enum led_brightness value)
+{
+	struct apanel *ap = container_of(led, struct apanel, mail_led);
+	if (value)
+		ap->led_bits |= 0x8000;
+	else
+		ap->led_bits &= ~0x8000;
+	ap->led_bits = value;
+	schedule_work(&ap->led_work);
+}
+
+static int apanel_detach_client(struct i2c_client *client)
+{
+	struct apanel *ap = i2c_get_clientdata(client);
+
+	if (device_chip[APANEL_DEV_LED] != CHIP_NONE)
+		led_classdev_unregister(&ap->mail_led);
+
+	input_unregister_polled_device(ap->ipdev);
+	i2c_detach_client(&ap->client);
+	input_free_polled_device(ap->ipdev);
+	kfree(ap);
+	return 0;
+}
+
+/* Function is invoked for every i2c adapter. */
+static int apanel_attach_adapter(struct i2c_adapter *adap)
+{
+	pr_debug(APANEL ": attach adapter id=%d\n", adap->id);
+
+	/* Our device is connected only to i801 on laptop */
+	if (adap->id != I2C_HW_SMBUS_I801)
+		return -ENODEV;
+
+	return i2c_probe(adap, &addr_data, apanel_probe);
+}
+
+static struct i2c_driver apanel_driver = {
+	.driver = {
+		.name = APANEL,
+	},
+	.attach_adapter = &apanel_attach_adapter,
+	.detach_client  = &apanel_detach_client,
+};
+
+/*
+  basically this function should probe the i2c client, but we know that it has
+  to be the one we're looking for.
+ */
+static int apanel_probe(struct i2c_adapter *bus, int address, int kind)
+{
+	struct apanel *ap;
+	const struct keymap *key;
+	struct input_polled_dev *ipdev;
+	struct input_dev *idev;
+	u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+	int err = -ENOMEM;
+
+	pr_debug(APANEL ": probe adapter %p addr %d kind %d\n",
+		 bus, address, kind);
+
+	ap = kzalloc(sizeof(*ap), GFP_KERNEL);
+	if (!ap)
+		goto out0;
+
+	ipdev = input_allocate_polled_device();
+	if (!ipdev)
+		goto out1;
+
+	ap->ipdev = ipdev;
+	ap->client.adapter = bus;
+	ap->client.addr = address;
+	ap->client.driver = &apanel_driver;
+	strlcpy(ap->client.name, APANEL, I2C_NAME_SIZE);
+	i2c_set_clientdata(&ap->client, ap);
+
+	err = i2c_attach_client(&ap->client);
+	if (err)
+		goto out2;
+
+	err = i2c_smbus_write_word_data(&ap->client, cmd, 0);
+	if (err) {
+		pr_warn(APANEL ": smbus write error %d\n", err);
+		goto out3;
+	}
+
+	ipdev->poll = apanel_poll;
+	ipdev->poll_interval = 1000;	/* 1 sec (power save) */
+	ipdev->private = ap;
+
+	idev = ipdev->input;
+	idev->name = APANEL_NAME " buttons";
+	idev->phys = "apanel/input0";
+	idev->id.bustype = BUS_HOST;
+	idev->dev.parent = &ap->client.dev;
+	idev->getkeycode = apanel_getkeycode;
+	idev->setkeycode = apanel_setkeycode;
+
+	__set_bit(EV_KEY, idev->evbit);
+	for (key = keymap; key->bit != -1; ++key)
+		__set_bit(key->keycode, idev->keybit);
+
+	input_set_capability(idev, EV_MSC, MSC_SCAN);
+
+	err = input_register_polled_device(ipdev);
+	if (err)
+		goto out3;
+
+	INIT_WORK(&ap->led_work, led_update);
+	if (device_chip[APANEL_DEV_LED] != CHIP_NONE) {
+		ap->mail_led.name = APANEL ":mail";
+		ap->mail_led.brightness_set = mail_led_set;
+
+		err = led_classdev_register(&ap->client.dev, &ap->mail_led);
+		if (err)
+			goto out4;
+	}
+
+	return 0;
+out4:
+	input_unregister_polled_device(ipdev);
+out3:
+	i2c_detach_client(&ap->client);
+out2:
+	input_free_polled_device(ipdev);
+out1:
+	kfree(ap);
+out0:
+	return err;
+}
+
+/* Scan the system ROM for the signature "FJKEYINF" */
+static __init const void __iomem *bios_signature(const void __iomem *bios)
+{
+	ssize_t offset;
+	const unsigned char signature[] = "FJKEYINF";
+
+	for (offset = 0; offset < 0x10000; offset += 0x10) {
+		if (check_signature(bios + offset, signature,
+				    sizeof(signature)-1))
+			return bios + offset;
+	}
+
+	pr_warn(APANEL ": Fujitsu BIOS signature '%s' not found...\n",
+	       signature);
+	return NULL;
+}
+
+static int __init apanel_init(void)
+{
+	void __iomem *bios;
+	const void __iomem *p;
+	u8 devno;
+	int error;
+	int found = 0;
+
+	if (!dmi_check_system(apanel_dmi_table)) {
+		pr_warn(APANEL ": DMI information does not match\n");
+		if (!force)
+			return -ENODEV;
+	}
+
+	if (!keymap) {
+		/* default key map */
+		error = apanel_copy_keymap(four_keys);
+		if (error) {
+			printk(KERN_ERR APANEL ": can't copy keymap, "
+				"error %d\n", error);
+			return error;
+		}
+	}
+
+	bios = ioremap(0xF0000, 0x10000); /* Can't fail */
+
+	p = bios_signature(bios);
+	if (!p) {
+		iounmap(bios);
+		error = -ENODEV;
+		goto fail;
+	}
+
+	/* just use the first address */
+	p += 8;
+	normal_i2c[0] = readb(p+3) >> 1;
+
+	for ( ; (devno = readb(p)) & 0x7f; p += 4) {
+		unsigned char method, slave, chip;
+
+		method = readb(p + 1);
+		chip = readb(p + 2);
+		slave = readb(p + 3) >> 1;
+
+		if (slave != normal_i2c[0]) {
+			printk(KERN_ERR APANEL ": only one SMBus slave "
+				 "address supported, skiping device...\n");
+			continue;
+		}
+
+		/* translate alternative device numbers */
+		switch (devno) {
+		case 6:
+			devno = APANEL_DEV_APPBTN;
+			break;
+		case 7:
+			devno = APANEL_DEV_LED;
+			break;
+		}
+
+		if (devno >= APANEL_DEV_MAX)
+			pr_warn(APANEL ": unknown device %d found\n", devno);
+		else if (device_chip[devno] != CHIP_NONE)
+			printk(KERN_ERR APANEL ": duplicate entry for %s\n",
+			       device_names[devno]);
+
+		else if (method != 1 && method != 2 && method != 4) {
+			printk(KERN_ERR APANEL ": unknown  method %u for %s\n",
+			       method, device_names[devno]);
+		} else {
+			pr_debug(APANEL ": %s found, chip=%d\n",
+				 device_names[devno], chip);
+
+			device_chip[devno] = (enum apanel_chip) chip;
+			++found;
+		}
+	}
+	iounmap(bios);
+
+	if (found == 0) {
+		printk(KERN_ERR APANEL ": no input devices reported by BIOS\n");
+		error = -EIO;
+		goto fail;
+	}
+
+	error = i2c_add_driver(&apanel_driver);
+	if (error)
+		goto fail;
+
+	return 0;
+
+ fail:
+	kfree(keymap);
+	return error;
+}
+
+module_init(apanel_init);
+
+static void __exit apanel_cleanup(void)
+{
+	i2c_del_driver(&apanel_driver);
+	kfree(keymap);
+}
+module_exit(apanel_cleanup);
+
+MODULE_AUTHOR("Stephen Hemminger <shemminger@osdl.org>");
+MODULE_DESCRIPTION(APANEL_NAME " driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(APANEL_VERSION);
+
+MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBookSSeries:pvr*:rvnFUJITSU:*");
+MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBookB6210:pvr*:rvnFUJITSU:*");

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

* Re: [PATCH] Fujitsu application panel driver (rev3)
  2007-09-14  5:30         ` Dmitry Torokhov
@ 2007-09-16 19:55           ` Stephen Hemminger
  2007-09-16 20:22             ` Dmitry Torokhov
  0 siblings, 1 reply; 23+ messages in thread
From: Stephen Hemminger @ 2007-09-16 19:55 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Pavel Machek, Len Brown, Andrew Morton, linux-input, linux-kernel

On Fri, 14 Sep 2007 01:30:58 -0400
Dmitry Torokhov <dtor@insightbb.com> wrote:

> Hi Stephen,
> 
> On Wednesday 12 September 2007 07:38, Stephen Hemminger wrote:
> > This driver supports the application buttons on some Fujitsu Lifebook laptops.
> > It is based on the earlier apanel driver done by Jochen Eisenger, but
> > with many changes.  The original driver used ioctl's and a separate
> > user space program;  see http://apanel.sourceforge.net/tech.php
> > This version hooks into the input subsystem so that the normal
> > Gnome/KDE shortcuts work without any userspace changes.
> > 
> > The Mail Led is handled via leds class device.
> > 
> 
> Thank you very much for convering the led to led subsystem. I tried
> implementing loadable keymap support in the driver, could you please
> try the patch below and if it still works for you then I will apply
> it to my tree.
> 

Sorry, you are raising the bar for new drivers, higher than existing code.
It is really bad (second system syndrome), if maintainers keep wanting
new code to to more than existing drivers.

Please take driver AS IS. Go ahead and add loadable key map support,
but fix all the other drivers in the tree at the same time that use the existing
interface.


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

* Re: [PATCH] Fujitsu application panel driver (rev3)
  2007-09-16 19:55           ` Stephen Hemminger
@ 2007-09-16 20:22             ` Dmitry Torokhov
  2007-10-23 19:55               ` [PATCH] Fujitsu application panel driver Stephen Hemminger
  0 siblings, 1 reply; 23+ messages in thread
From: Dmitry Torokhov @ 2007-09-16 20:22 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Pavel Machek, Len Brown, Andrew Morton, linux-input, linux-kernel

Hi Stephen,

On Sunday 16 September 2007 15:55, Stephen Hemminger wrote:
> On Fri, 14 Sep 2007 01:30:58 -0400
> Dmitry Torokhov <dtor@insightbb.com> wrote:
> 
> > Hi Stephen,
> > 
> > On Wednesday 12 September 2007 07:38, Stephen Hemminger wrote:
> > > This driver supports the application buttons on some Fujitsu Lifebook laptops.
> > > It is based on the earlier apanel driver done by Jochen Eisenger, but
> > > with many changes.  The original driver used ioctl's and a separate
> > > user space program;  see http://apanel.sourceforge.net/tech.php
> > > This version hooks into the input subsystem so that the normal
> > > Gnome/KDE shortcuts work without any userspace changes.
> > > 
> > > The Mail Led is handled via leds class device.
> > > 
> > 
> > Thank you very much for convering the led to led subsystem. I tried
> > implementing loadable keymap support in the driver, could you please
> > try the patch below and if it still works for you then I will apply
> > it to my tree.
> > 
> 
> Sorry, you are raising the bar for new drivers, higher than existing code.
> It is really bad (second system syndrome), if maintainers keep wanting
> new code to to more than existing drivers.
> 
> Please take driver AS IS. Go ahead and add loadable key map support,

I think I did that. I just asked you to run another test to make sure I did
not screw up. Please find the time to do that and the patch will be applied.
Unfortunately I do not own the hardware in question to do such test myself.

> but fix all the other drivers in the tree at the same time that use the existing
> interface.
> 

I will, time permitting. The support for doing loadable keymaps for drivers
with sparse scancodes (or their equivalent) was added not so long ago,
that's why there are a few drivers that don't implement loadable keymaps.
But still many others do.

-- 
Dmitry

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

* [PATCH] Fujitsu application panel driver
  2007-09-16 20:22             ` Dmitry Torokhov
@ 2007-10-23 19:55               ` Stephen Hemminger
  2007-10-24 23:03                 ` Andrew Morton
                                   ` (3 more replies)
  0 siblings, 4 replies; 23+ messages in thread
From: Stephen Hemminger @ 2007-10-23 19:55 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Pavel Machek, Len Brown, Andrew Morton, linux-input, linux-kernel

This driver supports the application buttons on some Fujitsu Lifebook
laptops. It is based on the earlier apanel driver done by Jochen
Eisenger, but with many changes.  The original driver used ioctl's and
a separate user space program (see http://apanel.sourceforge.net). This
driver hooks into the input subsystem so that the normal keys act as
expected without a daemon. In addition to buttons, the Mail Led is
handled via LEDs class device.

The driver now supports redefinable keymaps and no longer has to
have a DMI table for all Fujitsu laptops.

I thought about mixing this driver should be integrated into the Fujitsu laptop
extras driver that handles backlight, but rejected the idea because
it wasn't clear if all the Fujitsu laptops supported both.

Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>

---

 drivers/input/misc/Kconfig  |   14 +
 drivers/input/misc/Makefile |    1 
 drivers/input/misc/apanel.c |  407 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 422 insertions(+)

--- a/drivers/input/misc/Kconfig	2007-10-23 11:19:10.000000000 -0700
+++ b/drivers/input/misc/Kconfig	2007-10-23 11:19:48.000000000 -0700
@@ -40,6 +40,20 @@ config INPUT_M68K_BEEP
 	tristate "M68k Beeper support"
 	depends on M68K
 
+config INPUT_APANEL
+	tristate "Fujitsu Lifebook Application Panel buttons"
+	depends on X86
+	select I2C_I801
+	select INPUT_POLLDEV
+	select CHECK_SIGNATURE
+	help
+	 Say Y here for support of the Application Panel buttons, used on
+	 Fujitsu Lifebook. These are attached to the mainboard through
+	 an SMBus interface managed by the I2C Intel ICH (i801) driver.
+
+	 To compile this driver as a module, choose M here: the module will
+	 be called apanel.
+
 config INPUT_IXP4XX_BEEPER
 	tristate "IXP4XX Beeper support"
 	depends on ARCH_IXP4XX
--- a/drivers/input/misc/Makefile	2007-10-23 11:19:10.000000000 -0700
+++ b/drivers/input/misc/Makefile	2007-10-23 11:19:48.000000000 -0700
@@ -18,3 +18,4 @@ obj-$(CONFIG_INPUT_POWERMATE)		+= powerm
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
+obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/input/misc/apanel.c	2007-10-23 12:47:55.000000000 -0700
@@ -0,0 +1,407 @@
+/*
+ *  Fujitsu Lifebook Application Panel button drive
+ *
+ *  Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org>
+ *  Copyright (C) 2001-2003 Jochen Eisinger <jochen@penguin-breeder.org>
+ *
+ * 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 published by
+ * the Free Software Foundation.
+ *
+ * Many Fujitsu Lifebook laptops have a small panel of buttons that are
+ * accessible via the i2c/smbus interface. This driver polls those
+ * buttons and generates input events.
+ *
+ * For more details see:
+ * 	http://apanel.sourceforge.net/tech.php
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/input-polldev.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+
+#define APANEL_NAME	"Fujitsu Application Panel"
+#define APANEL_VERSION	"1.3"
+#define APANEL		"apanel"
+
+/* How often we poll keys - msecs */
+#define POLL_INTERVAL_DEFAULT	1000
+
+/* Magic constants in BIOS that tell about buttons */
+enum apanel_devid {
+	APANEL_DEV_NONE	  = 0,
+	APANEL_DEV_APPBTN = 1,
+	APANEL_DEV_CDBTN  = 2,
+	APANEL_DEV_LCD	  = 3,
+	APANEL_DEV_LED	  = 4,
+	APANEL_DEV_MAX,
+};
+
+enum apanel_chip {
+	CHIP_NONE    = 0,
+	CHIP_OZ992C  = 1,
+	CHIP_OZ163T  = 2,
+	CHIP_OZ711M3 = 4,
+};
+
+/* Result of BIOS snooping/probing -- what features are supported */
+static enum apanel_chip device_chip[APANEL_DEV_MAX];
+
+/* names for APANEL_XXX */
+static const char *device_names[APANEL_DEV_MAX] __initdata = {
+	[APANEL_DEV_APPBTN] = "Application Buttons",
+	[APANEL_DEV_LCD]    = "LCD",
+	[APANEL_DEV_LED]    = "LED",
+	[APANEL_DEV_CDBTN]  = "CD Buttons",
+};
+
+#define MAX_PANEL_KEYS	12
+
+struct apanel {
+	struct input_polled_dev *ipdev;
+	struct i2c_client client;
+	unsigned keymap[MAX_PANEL_KEYS];
+	u16    nkeys;
+	u16    led_bits;
+	struct work_struct led_work;
+	struct led_classdev mail_led;
+};
+
+
+static int apanel_probe(struct i2c_adapter *, int, int);
+
+/* for now, we only support one address */
+static unsigned short normal_i2c[] = {0, I2C_CLIENT_END};
+static unsigned short ignore = I2C_CLIENT_END;
+static struct i2c_client_address_data addr_data = {
+	.normal_i2c	= normal_i2c,
+	.probe		= &ignore,
+	.ignore		= &ignore,
+};
+
+static void report_key(struct input_dev *input, unsigned keycode)
+{
+	pr_debug(APANEL ": report key %#x\n", keycode);
+	input_report_key(input, keycode, 1);
+	input_sync(input);
+
+	input_report_key(input, keycode, 0);
+	input_sync(input);
+}
+
+/* Poll for key changes
+ *
+ * Read Application keys via SMI
+ *  A (0x4), B (0x8), Internet (0x2), Email (0x1).
+ *
+ * CD keys:
+ * Forward (0x100), Rewind (0x200), Stop (0x400), Pause (0x800)
+ */
+static void apanel_poll(struct input_polled_dev *ipdev)
+{
+	struct apanel *ap = ipdev->private;
+	struct input_dev *idev = ipdev->input;
+	u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+	s32 data;
+	int i;
+
+	data = i2c_smbus_read_word_data(&ap->client, cmd);
+	if (data < 0)
+		return;	/* ignore errors (due to ACPI??) */
+
+	/* write back to clear latch */
+	i2c_smbus_write_word_data(&ap->client, cmd, 0);
+
+	if (!data)
+		return;
+
+	dev_dbg(&ipdev->input->dev, APANEL ": data %#x\n", data);
+	for (i = 0; i < ipdev->input->keycodemax; i++)
+		if (1ul << i & data)
+			report_key(idev, ap->keymap[i]);
+}
+
+static int apanel_getkeycode(struct input_dev *idev, int scancode, int *keycode)
+{
+	struct apanel *ap = idev->private;
+
+	if (scancode < 0 || scancode >= MAX_PANEL_KEYS)
+		return -EINVAL;
+
+	*keycode = ap->keymap[scancode];
+	return 0;
+}
+
+static int apanel_setkeycode(struct input_dev *idev, int scancode, int keycode)
+{
+	struct apanel *ap = idev->private;
+
+	if (keycode < 0 || keycode > KEY_MAX)
+		return -EINVAL;
+
+	if (scancode < 0 || scancode >= MAX_PANEL_KEYS)
+		return -EINVAL;
+
+	clear_bit(ap->keymap[scancode], idev->keybit);
+	ap->keymap[scancode] = keycode;
+	set_bit(keycode, idev->keybit);
+	return 0;
+}
+
+/* Track state changes of LED */
+static void led_update(struct work_struct *work)
+{
+	struct apanel *ap = container_of(work, struct apanel, led_work);
+
+	i2c_smbus_write_word_data(&ap->client, 0x10, ap->led_bits);
+}
+
+static void mail_led_set(struct led_classdev *led,
+			 enum led_brightness value)
+{
+	struct apanel *ap = container_of(led, struct apanel, mail_led);
+	if (value)
+		ap->led_bits |= 0x8000;
+	else
+		ap->led_bits &= ~0x8000;
+	ap->led_bits = value;
+	schedule_work(&ap->led_work);
+}
+
+static int apanel_detach_client(struct i2c_client *client)
+{
+	struct apanel *ap = i2c_get_clientdata(client);
+
+	if (device_chip[APANEL_DEV_LED] != CHIP_NONE)
+		led_classdev_unregister(&ap->mail_led);
+
+	input_unregister_polled_device(ap->ipdev);
+	i2c_detach_client(&ap->client);
+
+	return 0;
+}
+
+/* Function is invoked for every i2c adapter. */
+static int apanel_attach_adapter(struct i2c_adapter *adap)
+{
+	dev_dbg(&adap->dev, APANEL ": attach adapter id=%d\n", adap->id);
+
+	/* Our device is connected only to i801 on laptop */
+	if (adap->id != I2C_HW_SMBUS_I801)
+		return -ENODEV;
+
+	return i2c_probe(adap, &addr_data, apanel_probe);
+}
+
+static struct i2c_driver apanel_driver = {
+	.driver = {
+		.name = APANEL,
+	},
+	.attach_adapter = &apanel_attach_adapter,
+	.detach_client  = &apanel_detach_client,
+};
+
+static struct apanel apanel = {
+	.client = {
+		.driver = &apanel_driver,
+		.name   = APANEL,
+	},
+	.keymap = {
+		[0] = KEY_MAIL,
+		[1] = KEY_WWW,
+		[2] = KEY_PROG2,
+		[3] = KEY_PROG1,
+
+		[8] = KEY_FORWARD,
+		[9] = KEY_REWIND,
+		[10] = KEY_STOPCD,
+		[11] = KEY_PLAYPAUSE,
+
+	},
+	.mail_led = {
+		.name = APANEL ":mail",
+		.brightness_set = mail_led_set,
+	},
+};
+
+/* NB: Only one panel on the i2c. */
+static int apanel_probe(struct i2c_adapter *bus, int address, int kind)
+{
+	struct apanel *ap;
+	struct input_polled_dev *ipdev;
+	struct input_dev *idev;
+	u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+	int i, err = -ENOMEM;
+
+	dev_dbg(&bus->dev, APANEL ": probe adapter %p addr %d kind %d\n",
+		bus, address, kind);
+
+	ap = &apanel;
+
+	ipdev = input_allocate_polled_device();
+	if (!ipdev)
+		goto out1;
+
+	ap->ipdev = ipdev;
+	ap->client.adapter = bus;
+	ap->client.addr = address;
+
+	i2c_set_clientdata(&ap->client, ap);
+
+	err = i2c_attach_client(&ap->client);
+	if (err)
+		goto out2;
+
+	err = i2c_smbus_write_word_data(&ap->client, cmd, 0);
+	if (err) {
+		dev_warn(&ap->client.dev, APANEL ": smbus write error %d\n",
+			 err);
+		goto out3;
+	}
+
+	ipdev->poll = apanel_poll;
+	ipdev->poll_interval = POLL_INTERVAL_DEFAULT;
+	ipdev->private = ap;
+
+	idev = ipdev->input;
+	idev->name = APANEL_NAME " buttons";
+	idev->phys = "apanel/input0";
+	idev->id.bustype = BUS_HOST;
+	idev->dev.parent = &ap->client.dev;
+	idev->getkeycode = apanel_getkeycode;
+	idev->setkeycode = apanel_setkeycode;
+
+	set_bit(EV_KEY, idev->evbit);
+
+	idev->keycodemax = (device_chip[APANEL_DEV_CDBTN] != CHIP_NONE) ? 12 : 4;
+
+	for (i = 0; i < idev->keycodemax; i++)
+		if (ap->keymap[i])
+			set_bit(ap->keymap[i], idev->keybit);
+
+	err = input_register_polled_device(ipdev);
+	if (err)
+		goto out3;
+
+	INIT_WORK(&ap->led_work, led_update);
+	if (device_chip[APANEL_DEV_LED] != CHIP_NONE) {
+		err = led_classdev_register(&ap->client.dev, &ap->mail_led);
+		if (err)
+			goto out4;
+	}
+
+	return 0;
+out4:
+	input_unregister_polled_device(ipdev);
+out3:
+	i2c_detach_client(&ap->client);
+out2:
+	input_free_polled_device(ipdev);
+out1:
+	return err;
+}
+
+/* Scan the system ROM for the signature "FJKEYINF" */
+static __init const void __iomem *bios_signature(const void __iomem *bios)
+{
+	ssize_t offset;
+	const unsigned char signature[] = "FJKEYINF";
+
+	for (offset = 0; offset < 0x10000; offset += 0x10) {
+		if (check_signature(bios + offset, signature,
+				    sizeof(signature)-1))
+			return bios + offset;
+	}
+	pr_notice(APANEL ": Fujitsu BIOS signature '%s' not found...\n",
+		  signature);
+	return NULL;
+}
+
+static int __init apanel_init(void)
+{
+	void __iomem *bios;
+	const void __iomem *p;
+	u8 devno;
+	int found = 0;
+
+	bios = ioremap(0xF0000, 0x10000); /* Can't fail */
+
+	p = bios_signature(bios);
+	if (!p) {
+		iounmap(bios);
+		return -ENODEV;
+	}
+
+	/* just use the first address */
+	p += 8;
+	normal_i2c[0] = readb(p+3) >> 1;
+
+	for ( ; (devno = readb(p)) & 0x7f; p += 4) {
+		unsigned char method, slave, chip;
+
+		method = readb(p + 1);
+		chip = readb(p + 2);
+		slave = readb(p + 3) >> 1;
+
+		if (slave != normal_i2c[0]) {
+			pr_notice(APANEL ": only one SMBus slave "
+				  "address supported, skiping device...\n");
+			continue;
+		}
+
+		/* translate alternative device numbers */
+		switch (devno) {
+		case 6:
+			devno = APANEL_DEV_APPBTN;
+			break;
+		case 7:
+			devno = APANEL_DEV_LED;
+			break;
+		}
+
+		if (devno >= APANEL_DEV_MAX)
+			pr_notice(APANEL ": unknown device %d found\n", devno);
+		else if (device_chip[devno] != CHIP_NONE)
+			pr_warning(APANEL ": duplicate entry for %s\n",
+				   device_names[devno]);
+
+		else if (method != 1 && method != 2 && method != 4) {
+			pr_notice(APANEL ": unknown  method %u for %s\n",
+				  method, device_names[devno]);
+		} else {
+			pr_debug(APANEL ": %s found.\n", device_names[devno]);
+
+			device_chip[devno] = (enum apanel_chip) chip;
+			++found;
+		}
+	}
+	iounmap(bios);
+
+	if (found == 0) {
+		pr_info(APANEL ": no input devices reported by BIOS\n");
+		return -EIO;
+	}
+
+	return i2c_add_driver(&apanel_driver);
+}
+module_init(apanel_init);
+
+static void __exit apanel_cleanup(void)
+{
+	i2c_del_driver(&apanel_driver);
+}
+module_exit(apanel_cleanup);
+
+MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>");
+MODULE_DESCRIPTION(APANEL_NAME " driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(APANEL_VERSION);
+
+MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
+MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");

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

* Re: [PATCH] Fujitsu application panel driver
  2007-10-23 19:55               ` [PATCH] Fujitsu application panel driver Stephen Hemminger
@ 2007-10-24 23:03                 ` Andrew Morton
  2007-10-25 20:19                   ` [PATCH] apanel: space savings Stephen Hemminger
  2007-10-28  5:30                 ` [PATCH] Fujitsu application panel driver Dmitry Torokhov
                                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 23+ messages in thread
From: Andrew Morton @ 2007-10-24 23:03 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: dtor, pavel, lenb, linux-input, linux-kernel

On Tue, 23 Oct 2007 12:55:55 -0700
Stephen Hemminger <shemminger@linux-foundation.org> wrote:

> This driver supports the application buttons on some Fujitsu Lifebook
> laptops. It is based on the earlier apanel driver done by Jochen
> Eisenger, but with many changes.  The original driver used ioctl's and
> a separate user space program (see http://apanel.sourceforge.net). This
> driver hooks into the input subsystem so that the normal keys act as
> expected without a daemon. In addition to buttons, the Mail Led is
> handled via LEDs class device.
> 
> The driver now supports redefinable keymaps and no longer has to
> have a DMI table for all Fujitsu laptops.
> 
> I thought about mixing this driver should be integrated into the Fujitsu laptop
> extras driver that handles backlight, but rejected the idea because
> it wasn't clear if all the Fujitsu laptops supported both.
> 
> ...
>
> +
> +/* Magic constants in BIOS that tell about buttons */
> +enum apanel_devid {
> +	APANEL_DEV_NONE	  = 0,
> +	APANEL_DEV_APPBTN = 1,
> +	APANEL_DEV_CDBTN  = 2,
> +	APANEL_DEV_LCD	  = 3,
> +	APANEL_DEV_LED	  = 4,
> +	APANEL_DEV_MAX,
> +};

APANEL_DEV_MAX == 5.

> +enum apanel_chip {
> +	CHIP_NONE    = 0,
> +	CHIP_OZ992C  = 1,
> +	CHIP_OZ163T  = 2,
> +	CHIP_OZ711M3 = 4,
> +};
> +
> +/* Result of BIOS snooping/probing -- what features are supported */
> +static enum apanel_chip device_chip[APANEL_DEV_MAX];
> +
> +/* names for APANEL_XXX */
> +static const char *device_names[APANEL_DEV_MAX] __initdata = {

We just wasted sizeof(char *)?

> +	[APANEL_DEV_APPBTN] = "Application Buttons",
> +	[APANEL_DEV_LCD]    = "LCD",
> +	[APANEL_DEV_LED]    = "LED",
> +	[APANEL_DEV_CDBTN]  = "CD Buttons",
> +};
> +
> +
> +/* Poll for key changes
> + *
> + * Read Application keys via SMI
> + *  A (0x4), B (0x8), Internet (0x2), Email (0x1).
> + *
> + * CD keys:
> + * Forward (0x100), Rewind (0x200), Stop (0x400), Pause (0x800)
> + */
> +static void apanel_poll(struct input_polled_dev *ipdev)
> +{
> +	struct apanel *ap = ipdev->private;
> +	struct input_dev *idev = ipdev->input;
> +	u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
> +	s32 data;
> +	int i;
> +
> +	data = i2c_smbus_read_word_data(&ap->client, cmd);
> +	if (data < 0)
> +		return;	/* ignore errors (due to ACPI??) */
> +
> +	/* write back to clear latch */
> +	i2c_smbus_write_word_data(&ap->client, cmd, 0);
> +
> +	if (!data)
> +		return;
> +
> +	dev_dbg(&ipdev->input->dev, APANEL ": data %#x\n", data);
> +	for (i = 0; i < ipdev->input->keycodemax; i++)
> +		if (1ul << i & data)

You made me google for the c precedence table.

> +			report_key(idev, ap->keymap[i]);
> +}
>
> ...
>


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

* [PATCH] apanel: space savings.
  2007-10-24 23:03                 ` Andrew Morton
@ 2007-10-25 20:19                   ` Stephen Hemminger
  0 siblings, 0 replies; 23+ messages in thread
From: Stephen Hemminger @ 2007-10-25 20:19 UTC (permalink / raw)
  To: Andrew Morton; +Cc: dtor, pavel, lenb, linux-input, linux-kernel

Eliminate the device_names they are only used for debug
messages. Add paren's for one expression.

Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>

--- a/drivers/input/misc/apanel.c	2007-10-25 09:20:23.000000000 -0700
+++ b/drivers/input/misc/apanel.c	2007-10-25 12:07:00.000000000 -0700
@@ -27,7 +27,7 @@
 #include <linux/leds.h>
 
 #define APANEL_NAME	"Fujitsu Application Panel"
-#define APANEL_VERSION	"1.3"
+#define APANEL_VERSION	"1.3.1"
 #define APANEL		"apanel"
 
 /* How often we poll keys - msecs */
@@ -40,6 +40,7 @@ enum apanel_devid {
 	APANEL_DEV_CDBTN  = 2,
 	APANEL_DEV_LCD	  = 3,
 	APANEL_DEV_LED	  = 4,
+
 	APANEL_DEV_MAX,
 };
 
@@ -53,14 +54,6 @@ enum apanel_chip {
 /* Result of BIOS snooping/probing -- what features are supported */
 static enum apanel_chip device_chip[APANEL_DEV_MAX];
 
-/* names for APANEL_XXX */
-static const char *device_names[APANEL_DEV_MAX] __initdata = {
-	[APANEL_DEV_APPBTN] = "Application Buttons",
-	[APANEL_DEV_LCD]    = "LCD",
-	[APANEL_DEV_LED]    = "LED",
-	[APANEL_DEV_CDBTN]  = "CD Buttons",
-};
-
 #define MAX_PANEL_KEYS	12
 
 struct apanel {
@@ -123,7 +116,7 @@ static void apanel_poll(struct input_pol
 
 	dev_dbg(&ipdev->input->dev, APANEL ": data %#x\n", data);
 	for (i = 0; i < ipdev->input->keycodemax; i++)
-		if (1ul << i & data)
+		if ((1u << i) & data)
 			report_key(idev, ap->keymap[i]);
 }
 
@@ -366,17 +359,14 @@ static int __init apanel_init(void)
 		}
 
 		if (devno >= APANEL_DEV_MAX)
-			pr_notice(APANEL ": unknown device %d found\n", devno);
+			pr_notice(APANEL ": unknown device %u found\n", devno);
 		else if (device_chip[devno] != CHIP_NONE)
-			pr_warning(APANEL ": duplicate entry for %s\n",
-				   device_names[devno]);
+			pr_warning(APANEL ": duplicate entry for devno %u\n", devno);
 
 		else if (method != 1 && method != 2 && method != 4) {
-			pr_notice(APANEL ": unknown  method %u for %s\n",
-				  method, device_names[devno]);
+			pr_notice(APANEL ": unknown method %u for devno %u\n",
+				  method, devno);
 		} else {
-			pr_debug(APANEL ": %s found.\n", device_names[devno]);
-
 			device_chip[devno] = (enum apanel_chip) chip;
 			++found;
 		}

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

* Re: [PATCH] Fujitsu application panel driver
  2007-10-23 19:55               ` [PATCH] Fujitsu application panel driver Stephen Hemminger
  2007-10-24 23:03                 ` Andrew Morton
@ 2007-10-28  5:30                 ` Dmitry Torokhov
  2007-11-18 22:36                 ` Robert Gerlach
  2007-12-12 14:10                 ` Dmitry Torokhov
  3 siblings, 0 replies; 23+ messages in thread
From: Dmitry Torokhov @ 2007-10-28  5:30 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Pavel Machek, Len Brown, Andrew Morton, linux-input, linux-kernel

Hi Stephen,

On Tuesday 23 October 2007 15:55, Stephen Hemminger wrote:
> +
> +static int apanel_setkeycode(struct input_dev *idev, int scancode, int keycode)
> +{
> +	struct apanel *ap = idev->private;
> +
> +	if (keycode < 0 || keycode > KEY_MAX)
> +		return -EINVAL;
> +
> +	if (scancode < 0 || scancode >= MAX_PANEL_KEYS)
> +		return -EINVAL;

scancode >= idev->keycodemax is prbably better here - we don't want to
allow setting keycode for unsupported buttons.

> +
> +	clear_bit(ap->keymap[scancode], idev->keybit);

This will not work if one has same code assigned to 2 buttons. Pretty
degenerate case, I know...

> +	ap->keymap[scancode] = keycode;
> +	set_bit(keycode, idev->keybit);
> +	return 0;
> +}

-- 
Dmitry

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

* Re: [PATCH] Fujitsu application panel driver
  2007-10-23 19:55               ` [PATCH] Fujitsu application panel driver Stephen Hemminger
  2007-10-24 23:03                 ` Andrew Morton
  2007-10-28  5:30                 ` [PATCH] Fujitsu application panel driver Dmitry Torokhov
@ 2007-11-18 22:36                 ` Robert Gerlach
  2007-11-19  4:43                   ` Stephen Hemminger
  2007-12-12 14:10                 ` Dmitry Torokhov
  3 siblings, 1 reply; 23+ messages in thread
From: Robert Gerlach @ 2007-11-18 22:36 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Dmitry Torokhov, Pavel Machek, Len Brown, Andrew Morton,
	linux-input, linux-kernel

Am Dienstag 23 Oktober 2007 21:55:55 schrieb Stephen Hemminger:
> +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
> +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");

Please make sure that this module is not autoload on Lifebook tablets (T 
series and P1[56]10). It works fine, but only supports a subset of features 
(4 of 7 buttons and no display orientation for my T4010).

Thanks,
  Robert

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

* Re: [PATCH] Fujitsu application panel driver
  2007-11-18 22:36                 ` Robert Gerlach
@ 2007-11-19  4:43                   ` Stephen Hemminger
  2007-11-19 12:50                     ` Robert Gerlach
  0 siblings, 1 reply; 23+ messages in thread
From: Stephen Hemminger @ 2007-11-19  4:43 UTC (permalink / raw)
  To: Robert Gerlach
  Cc: Dmitry Torokhov, Pavel Machek, Len Brown, Andrew Morton,
	linux-input, linux-kernel

On Sun, 18 Nov 2007 23:36:58 +0100
Robert Gerlach <khnz@gmx.de> wrote:

> Am Dienstag 23 Oktober 2007 21:55:55 schrieb Stephen Hemminger:
> > +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
> > +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");
> 
> Please make sure that this module is not autoload on Lifebook tablets (T 
> series and P1[56]10). It works fine, but only supports a subset of features 
> (4 of 7 buttons and no display orientation for my T4010).
> 
> Thanks,
>   Robert

I would rather get those tablets working (with help). Rather than trying
to enumerate all the possible DMI values of the full range of different laptops.


-- 
Stephen Hemminger <shemminger@linux-foundation.org>

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

* Re: [PATCH] Fujitsu application panel driver
  2007-11-19  4:43                   ` Stephen Hemminger
@ 2007-11-19 12:50                     ` Robert Gerlach
  2007-11-19 17:26                       ` Stephen Hemminger
  2007-11-19 23:19                       ` Stephen Hemminger
  0 siblings, 2 replies; 23+ messages in thread
From: Robert Gerlach @ 2007-11-19 12:50 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Dmitry Torokhov, Pavel Machek, Len Brown, Andrew Morton,
	linux-input, linux-kernel

Am Montag 19 November 2007 05:43:07 schrieb Stephen Hemminger:
> On Sun, 18 Nov 2007 23:36:58 +0100
>
> Robert Gerlach <khnz@gmx.de> wrote:
> > Am Dienstag 23 Oktober 2007 21:55:55 schrieb Stephen Hemminger:
> > > +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
> > > +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");
> >
> > Please make sure that this module is not autoload on Lifebook tablets (T
> > series and P1[56]10). It works fine, but only supports a subset of
> > features (4 of 7 buttons and no display orientation for my T4010).
>
> I would rather get those tablets working (with help). Rather than trying
> to enumerate all the possible DMI values of the full range of different
> laptops.

I've played with apanel some month ago. I've never found the keys and the 
switch over the I2C bus. So I was starting my own module (fsc_btns) to play 
with the IO range announced by ACPI. There I found it all. But my module 
doesn't work on all Lifebooks (S7110 for example; has no such ACPI device).

Do you think it is wise to merge apanel and fsc_btns? I think it's too 
different.

Robert


PS: I've no informations or specs from Fujitsu Siemens, so this can be totally 
wrong.


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

* Re: [PATCH] Fujitsu application panel driver
  2007-11-19 12:50                     ` Robert Gerlach
@ 2007-11-19 17:26                       ` Stephen Hemminger
  2007-11-19 20:37                         ` Robert Gerlach
  2007-11-19 23:19                       ` Stephen Hemminger
  1 sibling, 1 reply; 23+ messages in thread
From: Stephen Hemminger @ 2007-11-19 17:26 UTC (permalink / raw)
  To: Robert Gerlach
  Cc: Dmitry Torokhov, Pavel Machek, Len Brown, Andrew Morton,
	linux-input, linux-kernel

On Mon, 19 Nov 2007 13:50:30 +0100
Robert Gerlach <khnz@gmx.de> wrote:

> Am Montag 19 November 2007 05:43:07 schrieb Stephen Hemminger:
> > On Sun, 18 Nov 2007 23:36:58 +0100
> >
> > Robert Gerlach <khnz@gmx.de> wrote:
> > > Am Dienstag 23 Oktober 2007 21:55:55 schrieb Stephen Hemminger:
> > > > +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
> > > > +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");
> > >
> > > Please make sure that this module is not autoload on Lifebook tablets (T
> > > series and P1[56]10). It works fine, but only supports a subset of
> > > features (4 of 7 buttons and no display orientation for my T4010).
> >
> > I would rather get those tablets working (with help). Rather than trying
> > to enumerate all the possible DMI values of the full range of different
> > laptops.
> 
> I've played with apanel some month ago. I've never found the keys and the 
> switch over the I2C bus. So I was starting my own module (fsc_btns) to play 
> with the IO range announced by ACPI. There I found it all. But my module 
> doesn't work on all Lifebooks (S7110 for example; has no such ACPI device).

If the device doesn't have the buttons, then the probe routine won't find them
and it should just exit and unload itself.  Now it may be too noisy right now
and it might not detect properly, if so, that is where the bug is.


-- 
Stephen Hemminger <shemminger@linux-foundation.org>

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

* Re: [PATCH] Fujitsu application panel driver
  2007-11-19 17:26                       ` Stephen Hemminger
@ 2007-11-19 20:37                         ` Robert Gerlach
  0 siblings, 0 replies; 23+ messages in thread
From: Robert Gerlach @ 2007-11-19 20:37 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Dmitry Torokhov, Pavel Machek, Len Brown, Andrew Morton,
	linux-input, linux-kernel

Am Montag 19 November 2007 18:26:32 schrieben Sie:
> On Mon, 19 Nov 2007 13:50:30 +0100
>
> Robert Gerlach <khnz@gmx.de> wrote:
> > Am Montag 19 November 2007 05:43:07 schrieb Stephen Hemminger:
> > > On Sun, 18 Nov 2007 23:36:58 +0100
> > >
> > > Robert Gerlach <khnz@gmx.de> wrote:
> > > > Am Dienstag 23 Oktober 2007 21:55:55 schrieb Stephen Hemminger:
> > > > > +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
> > > > > +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");
> > > >
> > > > Please make sure that this module is not autoload on Lifebook tablets
> > > > (T series and P1[56]10). It works fine, but only supports a subset of
> > > > features (4 of 7 buttons and no display orientation for my T4010).
> > >
> > > I would rather get those tablets working (with help). Rather than
> > > trying to enumerate all the possible DMI values of the full range of
> > > different laptops.
> >
> > I've played with apanel some month ago. I've never found the keys and the
> > switch over the I2C bus. So I was starting my own module (fsc_btns) to
> > play with the IO range announced by ACPI. There I found it all. But my
> > module doesn't work on all Lifebooks (S7110 for example; has no such ACPI
> > device).
>
> If the device doesn't have the buttons, then the probe routine won't find
> them and it should just exit and unload itself.  Now it may be too noisy
> right now and it might not detect properly, if so, that is where the bug
> is.

Sorry, bad writing. What I want to say, apanel loads fine and works for the 4 
keys. Apanel and fsc_btns works side by side too and (in this case) keys 
reported twice, that's the bug.

For my T4010, apanel found 2 devices, devno 1 and 6 both with method=4 chip=4 
slave=25. The working keys are scrolldown (reported as prog2), scrollup 
(prog1), display rotation (www) and Fn (mail). If I find a way to make the 
other keys work, I will happily add it. Currently I have no idea.

By the way, input-kbd segv for apanel on my box. This fixed it for me:
--- orig/apanel.c	2007-11-19 20:41:58.000000000 +0100
+++ apanel.c	2007-11-19 20:44:54.000000000 +0100
@@ -274,8 +274,8 @@
 	idev->phys = "apanel/input0";
 	idev->id.bustype = BUS_HOST;
 	idev->dev.parent = &ap->client.dev;
-	idev->getkeycode = apanel_getkeycode;
-	idev->setkeycode = apanel_setkeycode;
+	idev->keycode = ap->keymap;
+	idev->keycodesize = sizeof(ap->keymap[0]);
 
 	set_bit(EV_KEY, idev->evbit);
 

  Robert

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

* Re: [PATCH] Fujitsu application panel driver
  2007-11-19 12:50                     ` Robert Gerlach
  2007-11-19 17:26                       ` Stephen Hemminger
@ 2007-11-19 23:19                       ` Stephen Hemminger
  2007-12-19  9:04                         ` Robert Gerlach
  1 sibling, 1 reply; 23+ messages in thread
From: Stephen Hemminger @ 2007-11-19 23:19 UTC (permalink / raw)
  To: Robert Gerlach
  Cc: Dmitry Torokhov, Pavel Machek, Len Brown, Andrew Morton,
	linux-input, linux-kernel

On Mon, 19 Nov 2007 13:50:30 +0100
Robert Gerlach <khnz@gmx.de> wrote:

> Am Montag 19 November 2007 05:43:07 schrieb Stephen Hemminger:
> > On Sun, 18 Nov 2007 23:36:58 +0100
> >
> > Robert Gerlach <khnz@gmx.de> wrote:
> > > Am Dienstag 23 Oktober 2007 21:55:55 schrieb Stephen Hemminger:
> > > > +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
> > > > +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");
> > >
> > > Please make sure that this module is not autoload on Lifebook tablets (T
> > > series and P1[56]10). It works fine, but only supports a subset of
> > > features (4 of 7 buttons and no display orientation for my T4010).
> >
> > I would rather get those tablets working (with help). Rather than trying
> > to enumerate all the possible DMI values of the full range of different
> > laptops.
> 
> I've played with apanel some month ago. I've never found the keys and the 
> switch over the I2C bus. So I was starting my own module (fsc_btns) to play 
> with the IO range announced by ACPI. There I found it all. But my module 
> doesn't work on all Lifebooks (S7110 for example; has no such ACPI device).
>

Are you sure the IO range isn't really the same as the I2C bus.

See: http://apanel.sourceforge.net/tech.php

-- 
Stephen Hemminger <shemminger@linux-foundation.org>

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

* Re: [PATCH] Fujitsu application panel driver
  2007-10-23 19:55               ` [PATCH] Fujitsu application panel driver Stephen Hemminger
                                   ` (2 preceding siblings ...)
  2007-11-18 22:36                 ` Robert Gerlach
@ 2007-12-12 14:10                 ` Dmitry Torokhov
  2007-12-12 22:21                   ` [PATCH] Fujitsu application panel led value Stephen Hemminger
  3 siblings, 1 reply; 23+ messages in thread
From: Dmitry Torokhov @ 2007-12-12 14:10 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Pavel Machek, Len Brown, Andrew Morton, linux-input, linux-kernel

Hi Stephen,

On Oct 23, 2007 2:55 PM, Stephen Hemminger
<shemminger@linux-foundation.org> wrote:
> +static void mail_led_set(struct led_classdev *led,
> +                        enum led_brightness value)
> +{
> +       struct apanel *ap = container_of(led, struct apanel, mail_led);
> +       if (value)
> +               ap->led_bits |= 0x8000;
> +       else
> +               ap->led_bits &= ~0x8000;
> +       ap->led_bits = value;
> +       schedule_work(&ap->led_work);
> +}

I was just about to apply the driver (and I folded in the other 4
patches you sent to me) but then I noticed the code above. It looks
like the conditional does not have any effect, ap->led_bits will be
overwritten with the raw value anyway. Please advise.

Thank you.

-- 
Dmitry

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

* [PATCH] Fujitsu application panel led value
  2007-12-12 14:10                 ` Dmitry Torokhov
@ 2007-12-12 22:21                   ` Stephen Hemminger
  0 siblings, 0 replies; 23+ messages in thread
From: Stephen Hemminger @ 2007-12-12 22:21 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Pavel Machek, Len Brown, Andrew Morton, linux-input, linux-kernel

Use proper encoding for LED values.

Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>

---
Note: earlier code worked because hardware only checks for zero.

--- a/drivers/input/misc/apanel.c	2007-12-12 13:07:03.000000000 -0700
+++ b/drivers/input/misc/apanel.c	2007-12-12 14:42:09.000000000 -0700
@@ -132,11 +132,11 @@ static void mail_led_set(struct led_clas
 			 enum led_brightness value)
 {
 	struct apanel *ap = container_of(led, struct apanel, mail_led);
-	if (value)
+
+	if (value != LED_OFF)
 		ap->led_bits |= 0x8000;
 	else
 		ap->led_bits &= ~0x8000;
-	ap->led_bits = value;
 	schedule_work(&ap->led_work);
 }
 

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

* Re: [PATCH] Fujitsu application panel driver
  2007-11-19 23:19                       ` Stephen Hemminger
@ 2007-12-19  9:04                         ` Robert Gerlach
  0 siblings, 0 replies; 23+ messages in thread
From: Robert Gerlach @ 2007-12-19  9:04 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Dmitry Torokhov, Pavel Machek, Len Brown, Andrew Morton,
	linux-input, linux-kernel

Am Dienstag 20 November 2007 00:19:18 schrieb Stephen Hemminger:
> On Mon, 19 Nov 2007 13:50:30 +0100
>
> Robert Gerlach <khnz@gmx.de> wrote:
> > Am Montag 19 November 2007 05:43:07 schrieb Stephen Hemminger:
> > > On Sun, 18 Nov 2007 23:36:58 +0100
> > >
> > > Robert Gerlach <khnz@gmx.de> wrote:
> > > > Am Dienstag 23 Oktober 2007 21:55:55 schrieb Stephen Hemminger:
> > > > > +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
> > > > > +MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");
> > > >
> > > > Please make sure that this module is not autoload on Lifebook tablets
> > > > (T series and P1[56]10). It works fine, but only supports a subset of
> > > > features (4 of 7 buttons and no display orientation for my T4010).
> > >
> > > I would rather get those tablets working (with help). Rather than
> > > trying to enumerate all the possible DMI values of the full range of
> > > different laptops.
> >
> > I've played with apanel some month ago. I've never found the keys and the
> > switch over the I2C bus. So I was starting my own module (fsc_btns) to
> > play with the IO range announced by ACPI. There I found it all. But my
> > module doesn't work on all Lifebooks (S7110 for example; has no such ACPI
> > device).
>
> Are you sure the IO range isn't really the same as the I2C bus.

No, I'm not sure. But, I was looking through the spec and played with my 
Lifebook, and again with no success.

> See: http://apanel.sourceforge.net/tech.php

Doesn't help ether.


  Robert

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

end of thread, other threads:[~2007-12-19  9:05 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-07-02 18:17 [PATCH] fujtisu application panel driver Stephen Hemminger
2007-07-02 19:16 ` Dmitry Torokhov
2007-09-03  8:58   ` Stephen Hemminger
2007-09-07 12:02     ` Pavel Machek
2007-09-12 11:38       ` [PATCH] Fujitsu application panel driver (rev3) Stephen Hemminger
2007-09-14  5:30         ` Dmitry Torokhov
2007-09-16 19:55           ` Stephen Hemminger
2007-09-16 20:22             ` Dmitry Torokhov
2007-10-23 19:55               ` [PATCH] Fujitsu application panel driver Stephen Hemminger
2007-10-24 23:03                 ` Andrew Morton
2007-10-25 20:19                   ` [PATCH] apanel: space savings Stephen Hemminger
2007-10-28  5:30                 ` [PATCH] Fujitsu application panel driver Dmitry Torokhov
2007-11-18 22:36                 ` Robert Gerlach
2007-11-19  4:43                   ` Stephen Hemminger
2007-11-19 12:50                     ` Robert Gerlach
2007-11-19 17:26                       ` Stephen Hemminger
2007-11-19 20:37                         ` Robert Gerlach
2007-11-19 23:19                       ` Stephen Hemminger
2007-12-19  9:04                         ` Robert Gerlach
2007-12-12 14:10                 ` Dmitry Torokhov
2007-12-12 22:21                   ` [PATCH] Fujitsu application panel led value Stephen Hemminger
2007-07-05  1:29 ` [PATCH] fujtisu application panel driver Jason Uhlenkott
2007-09-03  9:00   ` Stephen Hemminger

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).