2007-09-03 08:58:42

by Stephen Hemminger

[permalink] [raw]
Subject: Re: [PATCH] fujtisu application panel driver

On Mon, 2 Jul 2007 15:16:30 -0400
"Dmitry Torokhov" <[email protected]> wrote:

> Hi Stephen,
>
> On 7/2/07, Stephen Hemminger <[email protected]> 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.


2007-09-07 12:02:49

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH] fujtisu application panel driver

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

2007-09-12 11:38:28

by Stephen Hemminger

[permalink] [raw]
Subject: [PATCH] Fujitsu application panel driver (rev3)

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 <[email protected]>

---
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 <[email protected]>
+ * Copyright (C) 2001-2003 Jochen Eisinger <[email protected]>
+ *
+ * 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 <[email protected]>");
+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:*");

2007-09-14 05:31:18

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH] Fujitsu application panel driver (rev3)

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 <[email protected]>

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 <[email protected]>
Signed-off-by: Dmitry Torokhov <[email protected]>
---
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 <[email protected]>
+ * Copyright (C) 2001-2003 Jochen Eisinger <[email protected]>
+ *
+ * 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 <[email protected]>");
+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:*");

2007-09-16 19:55:08

by Stephen Hemminger

[permalink] [raw]
Subject: Re: [PATCH] Fujitsu application panel driver (rev3)

On Fri, 14 Sep 2007 01:30:58 -0400
Dmitry Torokhov <[email protected]> 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.

2007-09-16 20:23:17

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH] Fujitsu application panel driver (rev3)

Hi Stephen,

On Sunday 16 September 2007 15:55, Stephen Hemminger wrote:
> On Fri, 14 Sep 2007 01:30:58 -0400
> Dmitry Torokhov <[email protected]> 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

2007-10-23 19:56:40

by Stephen Hemminger

[permalink] [raw]
Subject: [PATCH] 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). 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 <[email protected]>

---

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 <[email protected]>
+ * Copyright (C) 2001-2003 Jochen Eisinger <[email protected]>
+ *
+ * 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 <[email protected]>");
+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:*");

2007-10-24 23:03:53

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH] Fujitsu application panel driver

On Tue, 23 Oct 2007 12:55:55 -0700
Stephen Hemminger <[email protected]> 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]);
> +}
>
> ...
>

2007-10-25 20:19:50

by Stephen Hemminger

[permalink] [raw]
Subject: [PATCH] apanel: space savings.

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

Signed-off-by: Stephen Hemminger <[email protected]>

--- 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;
}

2007-10-28 05:30:29

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH] Fujitsu application panel driver

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

2007-11-18 22:37:21

by Robert Gerlach

[permalink] [raw]
Subject: Re: [PATCH] Fujitsu application panel driver

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

2007-11-19 04:45:56

by Stephen Hemminger

[permalink] [raw]
Subject: Re: [PATCH] Fujitsu application panel driver

On Sun, 18 Nov 2007 23:36:58 +0100
Robert Gerlach <[email protected]> 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 <[email protected]>

2007-11-19 12:50:50

by Robert Gerlach

[permalink] [raw]
Subject: Re: [PATCH] Fujitsu application panel driver

Am Montag 19 November 2007 05:43:07 schrieb Stephen Hemminger:
> On Sun, 18 Nov 2007 23:36:58 +0100
>
> Robert Gerlach <[email protected]> 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.

2007-11-19 17:27:17

by Stephen Hemminger

[permalink] [raw]
Subject: Re: [PATCH] Fujitsu application panel driver

On Mon, 19 Nov 2007 13:50:30 +0100
Robert Gerlach <[email protected]> wrote:

> Am Montag 19 November 2007 05:43:07 schrieb Stephen Hemminger:
> > On Sun, 18 Nov 2007 23:36:58 +0100
> >
> > Robert Gerlach <[email protected]> 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 <[email protected]>

2007-11-19 20:37:30

by Robert Gerlach

[permalink] [raw]
Subject: Re: [PATCH] Fujitsu application panel driver

Am Montag 19 November 2007 18:26:32 schrieben Sie:
> On Mon, 19 Nov 2007 13:50:30 +0100
>
> Robert Gerlach <[email protected]> wrote:
> > Am Montag 19 November 2007 05:43:07 schrieb Stephen Hemminger:
> > > On Sun, 18 Nov 2007 23:36:58 +0100
> > >
> > > Robert Gerlach <[email protected]> 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

2007-11-19 23:21:13

by Stephen Hemminger

[permalink] [raw]
Subject: Re: [PATCH] Fujitsu application panel driver

On Mon, 19 Nov 2007 13:50:30 +0100
Robert Gerlach <[email protected]> wrote:

> Am Montag 19 November 2007 05:43:07 schrieb Stephen Hemminger:
> > On Sun, 18 Nov 2007 23:36:58 +0100
> >
> > Robert Gerlach <[email protected]> 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 <[email protected]>

2007-12-12 14:10:45

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH] Fujitsu application panel driver

Hi Stephen,

On Oct 23, 2007 2:55 PM, Stephen Hemminger
<[email protected]> 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

2007-12-12 22:22:45

by Stephen Hemminger

[permalink] [raw]
Subject: [PATCH] Fujitsu application panel led value

Use proper encoding for LED values.

Signed-off-by: Stephen Hemminger <[email protected]>

---
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);
}

2007-12-19 09:05:04

by Robert Gerlach

[permalink] [raw]
Subject: Re: [PATCH] Fujitsu application panel driver

Am Dienstag 20 November 2007 00:19:18 schrieb Stephen Hemminger:
> On Mon, 19 Nov 2007 13:50:30 +0100
>
> Robert Gerlach <[email protected]> wrote:
> > Am Montag 19 November 2007 05:43:07 schrieb Stephen Hemminger:
> > > On Sun, 18 Nov 2007 23:36:58 +0100
> > >
> > > Robert Gerlach <[email protected]> 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