LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* Add INPUT support to toshiba_acpi
@ 2007-05-31 12:36 Richard Hughes
  2007-05-31 12:53 ` Bastien Nocera
  2007-06-23 14:56 ` Rolf Eike Beer
  0 siblings, 2 replies; 23+ messages in thread
From: Richard Hughes @ 2007-05-31 12:36 UTC (permalink / raw)
  To: John Belmonte; +Cc: linux-kernel, linux-acpi, Bastien Nocera

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

Attached patch adds a kernel thread to do polling on Toshiba hardware.

Toshiba hardware is a little oddball, and does not provide ACPI events
on some key presses, typically Fn hotkey buttons. The key interface is
now polled, and events now matched to a list of toshiba specific
scancodes, and are squirted to userspace using the INPUT subsystem.

This means that toshiba laptops buttons "just work" without any
userspace daemon (using uinput) such as fnfx or bodges such as using a
userspace hal addon. Doing the polling in kernel is more efficient, and
makes stuff just work out of the box. You can assign the keys using
standard X keymaps, or using tools such as gnome-keybinding-properties.

This is similar to other patches sent for the thinkpad_acpi driver, and
is part of the "Unf*ck my keyboard" initiative to make multimedia keys
just work.

Changes from the first patch involve switching to a workqueue for the
polling, not breaking the spaces in "hotkeys_via_input" and also masking
out the fn key button up.

 toshiba_acpi.c |  228 +++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 206 insertions(+), 22 deletions(-)

Signed-off-by: Richard Hughes <richard@hughsie.com>


[-- Attachment #2: toshiba_acpi_input_02.patch --]
[-- Type: text/x-patch, Size: 8920 bytes --]

--- origin/toshiba_acpi.c	2007-05-18 09:56:03.000000000 +0100
+++ toshiba_acpi.c	2007-05-31 13:24:02.000000000 +0100
@@ -27,13 +27,13 @@
  *		engineering the Windows drivers
  *	Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
  *	Rob Miller - TV out and hotkeys help
- *
+ *	Richard Hughes - emit INPUT events, based on a patch from Daniel Silverstone
  *
  *  TODO
  *
  */
 
-#define TOSHIBA_ACPI_VERSION	"0.18"
+#define TOSHIBA_ACPI_VERSION	"0.19"
 #define PROC_INTERFACE_VERSION	1
 
 #include <linux/kernel.h>
@@ -42,6 +42,7 @@
 #include <linux/types.h>
 #include <linux/proc_fs.h>
 #include <linux/backlight.h>
+#include <linux/input.h>
 
 #include <asm/uaccess.h>
 
@@ -213,9 +214,15 @@ static acpi_status hci_read1(u32 reg, u3
 
 static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
 static struct backlight_device *toshiba_backlight_device;
+static struct input_dev *toshiba_input;
 static int force_fan;
 static int last_key_event;
 static int key_event_valid;
+static int hotkeys_over_input = 1;
+static int hotkeys_check_per_sec = 2;
+
+module_param(hotkeys_over_input, uint, 0400);
+module_param(hotkeys_check_per_sec, uint, 0400);
 
 typedef struct _ProcItem {
 	const char *name;
@@ -443,27 +450,33 @@ static char *read_keys(char *p)
 	u32 hci_result;
 	u32 value;
 
-	if (!key_event_valid) {
-		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
-		if (hci_result == HCI_SUCCESS) {
-			key_event_valid = 1;
-			last_key_event = value;
-		} else if (hci_result == HCI_EMPTY) {
-			/* better luck next time */
-		} else if (hci_result == HCI_NOT_SUPPORTED) {
-			/* This is a workaround for an unresolved issue on
-			 * some machines where system events sporadically
-			 * become disabled. */
-			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
-			printk(MY_NOTICE "Re-enabled hotkeys\n");
-		} else {
-			printk(MY_ERR "Error reading hotkey status\n");
-			goto end;
+	if (!hotkeys_over_input) {
+		if (!key_event_valid) {
+			hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+			if (hci_result == HCI_SUCCESS) {
+				key_event_valid = 1;
+				last_key_event = value;
+			} else if (hci_result == HCI_EMPTY) {
+				/* better luck next time */
+			} else if (hci_result == HCI_NOT_SUPPORTED) {
+				/* This is a workaround for an unresolved issue on
+				 * some machines where system events sporadically
+				 * become disabled. */
+				hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+				printk(MY_NOTICE "Re-enabled hotkeys\n");
+			} else {
+				printk(MY_ERR "Error reading hotkey status\n");
+				goto end;
+			}
 		}
+	} else {
+		key_event_valid = 0;
+		last_key_event = 0;
 	}
 
 	p += sprintf(p, "hotkey_ready:            %d\n", key_event_valid);
 	p += sprintf(p, "hotkey:                  0x%04x\n", last_key_event);
+	p += sprintf(p, "hotkeys_via_input:       %d\n", hotkeys_over_input);
 
       end:
 	return p;
@@ -534,15 +547,121 @@ static acpi_status __exit remove_device(
 }
 
 static struct backlight_ops toshiba_backlight_data = {
-        .get_brightness = get_lcd,
-        .update_status  = set_lcd_status,
+	.get_brightness = get_lcd,
+	.update_status  = set_lcd_status,
 };
 
+static void toshiba_keys_fire(struct work_struct *work);
+static struct workqueue_struct *toshiba_keys_wq;
+static DECLARE_DELAYED_WORK(toshiba_keys_work, toshiba_keys_fire);
+
+static void toshiba_deliver_button_event(u32 value)
+{
+	int keycode = KEY_UNKNOWN;
+	int key_down = 1;
+
+	if (!toshiba_input)
+		return;
+
+	/* translate MSB to key up */
+	if (value & 0x80) {
+		value &= ~0x80;
+		key_down = 0;
+	}
+
+	/* ignore FN on its own */
+	if (value == 0x0100)
+		return;
+
+	/* translate keys to keycodes */
+	if (value == 0x101)
+		keycode = KEY_MUTE;
+	else if (value == 0x13b)
+		keycode = KEY_BREAK;
+	else if (value == 0x13c)
+		keycode = KEY_SEARCH;
+	else if (value == 0x13d)
+		keycode = KEY_SLEEP;
+	else if (value == 0x13e)
+		keycode = KEY_SUSPEND;
+	else if (value == 0x140)
+		keycode = KEY_BRIGHTNESSDOWN;
+	else if (value == 0x141)
+		keycode = KEY_BRIGHTNESSUP;
+	else if (value == 0x142)
+		keycode = KEY_WLAN;
+
+	if (keycode != KEY_UNKNOWN) {
+		printk(MY_INFO "mapped hkey %i to keycode %i [%i]\n", value, keycode, key_down);
+		input_report_key(toshiba_input, keycode, key_down);
+		input_sync(toshiba_input);
+	}
+}
+
+static void toshiba_keys_clear(void)
+{
+	int dropped = 0;
+	int clear_queue = 0;
+	u32 hci_result, value;
+
+	do {
+		/* We don't want to get stuck here; older toshibas such as the
+		 * A60 may load and then return junk during the hci_read */
+		if (clear_queue++ > 16)
+			break;
+
+		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+		if (hci_result == HCI_SUCCESS) {
+			dropped++;
+		} else if (hci_result == HCI_EMPTY) {
+			/* better luck next time */
+		} else if (hci_result == HCI_NOT_SUPPORTED) {
+			/* This is a workaround for an unresolved issue on
+			 * some machines where system events sporadically
+			 * become disabled. */
+			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+			printk(MY_NOTICE "Re-enabled hotkeys\n");
+		}
+	} while (hci_result != HCI_EMPTY);
+
+	printk(MY_INFO "Dropped %d keys from the queue on startup\n", dropped);
+}
+
+static void toshiba_keys_fire(struct work_struct *work)
+{
+	u32 hci_result, value;
+
+	do {
+		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+		if (hci_result == HCI_SUCCESS) {
+			/* we got a button event */
+			toshiba_deliver_button_event(value);
+		} else if (hci_result == HCI_EMPTY) {
+			/* better luck next time */
+		} else if (hci_result == HCI_NOT_SUPPORTED) {
+			/* This is a workaround for an
+			 * unresolved issue on some machines
+			 * where system events sporadically
+			 * become disabled. */
+			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+			printk(MY_NOTICE "Re-enabled hotkeys\n");
+		}
+	} while (hci_result == HCI_SUCCESS);
+
+	/* reschedule another check */
+	queue_delayed_work(toshiba_keys_wq, &toshiba_keys_work, HZ / hotkeys_check_per_sec);
+}
+
 static void __exit toshiba_acpi_exit(void)
 {
 	if (toshiba_backlight_device)
 		backlight_device_unregister(toshiba_backlight_device);
 
+	if (toshiba_keys_wq) {
+		cancel_delayed_work(&toshiba_keys_work);
+		destroy_workqueue(toshiba_keys_wq);
+	}
+
 	remove_device();
 
 	if (toshiba_proc_dir)
@@ -551,6 +670,45 @@ static void __exit toshiba_acpi_exit(voi
 	return;
 }
 
+static int __init toshiba_input_init(void)
+{
+	int error;
+
+	/* use INPUT for key events */
+	toshiba_input = input_allocate_device();
+	if (!toshiba_input) {
+		error = -ENOMEM;
+		goto err_no_mem;
+	}
+
+	toshiba_keys_clear();
+
+	/* create one 'keyboard' virtual input device for all the acpi events */
+	toshiba_input->name = "Toshiba Extra Buttons";
+	toshiba_input->phys = "toshiba/input0";
+	toshiba_input->id.bustype = BUS_HOST;
+	toshiba_input->id.product = 0x0001;
+	toshiba_input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_MUTE, toshiba_input->keybit);
+	set_bit(KEY_BREAK, toshiba_input->keybit); /* probably should be KEY_LOCK */
+	set_bit(KEY_SEARCH, toshiba_input->keybit);
+	set_bit(KEY_SUSPEND, toshiba_input->keybit);
+	set_bit(KEY_SLEEP, toshiba_input->keybit);
+	set_bit(KEY_BRIGHTNESSDOWN, toshiba_input->keybit);
+	set_bit(KEY_BRIGHTNESSUP, toshiba_input->keybit);
+	set_bit(KEY_WLAN, toshiba_input->keybit);
+
+	error = input_register_device(toshiba_input);
+	if (error)
+		goto err_free_input;
+	return 0;
+
+err_free_input:
+	input_free_device(toshiba_input);
+err_no_mem:
+	return error;
+}
+
 static int __init toshiba_acpi_init(void)
 {
 	acpi_status status = AE_OK;
@@ -590,12 +748,38 @@ static int __init toshiba_acpi_init(void
 	toshiba_backlight_device = backlight_device_register("toshiba",NULL,
 						NULL,
 						&toshiba_backlight_data);
-        if (IS_ERR(toshiba_backlight_device)) {
+	if (IS_ERR(toshiba_backlight_device)) {
 		printk(KERN_ERR "Could not register toshiba backlight device\n");
 		toshiba_backlight_device = NULL;
 		toshiba_acpi_exit();
 	}
-        toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
+	toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
+
+	/* we have to poll the device as we do not get events */
+	if (hotkeys_over_input && hotkeys_check_per_sec > 0) {
+
+		toshiba_input_init ();
+		/* just abort */
+		if (!toshiba_input) {
+			printk(KERN_ERR "could not allocate input device\n");
+			toshiba_acpi_exit();
+		}
+
+		/* setup workqueue */
+		toshiba_keys_wq = create_singlethread_workqueue("ktoshkeyd");
+		if (!toshiba_keys_wq) {
+			printk(KERN_ERR "failed to create workqueue\n");
+			toshiba_acpi_exit();
+		}
+
+		/* sanitise to something sane */
+		if (hotkeys_check_per_sec > 10)
+			hotkeys_check_per_sec = 10;
+		printk(KERN_INFO "ktoshkeyd checks per second : %d\n", hotkeys_check_per_sec);
+
+		/* start polling after delay */
+		queue_delayed_work(toshiba_keys_wq, &toshiba_keys_work, HZ / hotkeys_check_per_sec);
+	}
 
 	return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
 }

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

* Re: Add INPUT support to toshiba_acpi
  2007-05-31 12:36 Add INPUT support to toshiba_acpi Richard Hughes
@ 2007-05-31 12:53 ` Bastien Nocera
  2007-05-31 13:43   ` Richard Hughes
  2007-06-23 14:56 ` Rolf Eike Beer
  1 sibling, 1 reply; 23+ messages in thread
From: Bastien Nocera @ 2007-05-31 12:53 UTC (permalink / raw)
  To: Richard Hughes; +Cc: John Belmonte, linux-kernel, linux-acpi

On Thu, 2007-05-31 at 13:36 +0100, Richard Hughes wrote:
> Attached patch adds a kernel thread to do polling on Toshiba hardware.
> 
> Toshiba hardware is a little oddball, and does not provide ACPI events
> on some key presses, typically Fn hotkey buttons. The key interface is
> now polled, and events now matched to a list of toshiba specific
> scancodes, and are squirted to userspace using the INPUT subsystem.
> 
> This means that toshiba laptops buttons "just work" without any
> userspace daemon (using uinput) such as fnfx or bodges such as using a
> userspace hal addon. Doing the polling in kernel is more efficient, and
> makes stuff just work out of the box. You can assign the keys using
> standard X keymaps, or using tools such as gnome-keybinding-properties.
> 
> This is similar to other patches sent for the thinkpad_acpi driver, and
> is part of the "Unf*ck my keyboard" initiative to make multimedia keys
> just work.
> 
> Changes from the first patch involve switching to a workqueue for the
> polling, not breaking the spaces in "hotkeys_via_input" and also masking
> out the fn key button up.

A couple of things:
- use a switch statement in toshiba_deliver_button_event(), would look a
bit cleaner.
- make the magic number #defines
- not sure if it's possible, but you should disable the workqueue
altogether if nothing has the input device opened.

Hopefully we'll find a way to receive an interrupt, or some kind of
event when the keys are pressed in the future, and completely avoid the
polling. In the meantime, it should be minimised.

Cheers

-- 
Bastien Nocera <hadess@hadess.net> 


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

* Re: Add INPUT support to toshiba_acpi
  2007-05-31 12:53 ` Bastien Nocera
@ 2007-05-31 13:43   ` Richard Hughes
  2007-05-31 15:46     ` Richard Hughes
  0 siblings, 1 reply; 23+ messages in thread
From: Richard Hughes @ 2007-05-31 13:43 UTC (permalink / raw)
  To: Bastien Nocera; +Cc: John Belmonte, linux-kernel, linux-acpi

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

On Thu, 2007-05-31 at 13:53 +0100, Bastien Nocera wrote:
> On Thu, 2007-05-31 at 13:36 +0100, Richard Hughes wrote:
> > Attached patch adds a kernel thread to do polling on Toshiba hardware.
> > 
> > Toshiba hardware is a little oddball, and does not provide ACPI events
> > on some key presses, typically Fn hotkey buttons. The key interface is
> > now polled, and events now matched to a list of toshiba specific
> > scancodes, and are squirted to userspace using the INPUT subsystem.
> > 
> > This means that toshiba laptops buttons "just work" without any
> > userspace daemon (using uinput) such as fnfx or bodges such as using a
> > userspace hal addon. Doing the polling in kernel is more efficient, and
> > makes stuff just work out of the box. You can assign the keys using
> > standard X keymaps, or using tools such as gnome-keybinding-properties.
> > 
> > This is similar to other patches sent for the thinkpad_acpi driver, and
> > is part of the "Unf*ck my keyboard" initiative to make multimedia keys
> > just work.
> > 
> > Changes from the first patch involve switching to a workqueue for the
> > polling, not breaking the spaces in "hotkeys_via_input" and also masking
> > out the fn key button up.
> 
> A couple of things:
> - use a switch statement in toshiba_deliver_button_event(), would look a
> bit cleaner.

Agree, done.

> - make the magic number #defines

Agree, done.

> - not sure if it's possible, but you should disable the workqueue
> altogether if nothing has the input device opened.

Do we get an event when the [input]->users value changes? If not, we
could do 1Hz (users > 0) checking when the input device is unclaimed,
and then switch to 4Hz polling when the input device is open.

> Hopefully we'll find a way to receive an interrupt, or some kind of
> event when the keys are pressed in the future, and completely avoid the
> polling. In the meantime, it should be minimised.

Nope, impossible AFAICS. The hardware is just broken. Windows XP has an
toshiba supplied daemon that polls, so I think we have to just bite the
bullet.

New patch attached.

Richard.


[-- Attachment #2: toshiba_acpi_add_buttons_03.patch --]
[-- Type: text/x-patch, Size: 9600 bytes --]

diff --git a/drivers/acpi/toshiba_acpi.c b/drivers/acpi/toshiba_acpi.c
index 3906d47..cf4ab76 100644
--- a/drivers/acpi/toshiba_acpi.c
+++ b/drivers/acpi/toshiba_acpi.c
@@ -27,13 +27,13 @@
  *		engineering the Windows drivers
  *	Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
  *	Rob Miller - TV out and hotkeys help
- *
+ *	Richard Hughes - emit INPUT events, based on a patch from Daniel Silverstone
  *
  *  TODO
  *
  */
 
-#define TOSHIBA_ACPI_VERSION	"0.18"
+#define TOSHIBA_ACPI_VERSION	"0.19"
 #define PROC_INTERFACE_VERSION	1
 
 #include <linux/kernel.h>
@@ -42,6 +42,8 @@
 #include <linux/types.h>
 #include <linux/proc_fs.h>
 #include <linux/backlight.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
 
 #include <asm/uaccess.h>
 
@@ -99,6 +101,16 @@ MODULE_LICENSE("GPL");
 #define HCI_VIDEO_OUT_CRT		0x2
 #define HCI_VIDEO_OUT_TV		0x4
 
+/* key definitions */
+#define HCI_HKEY_MUTE			0x0101
+#define HCI_HKEY_BREAK			0x013b
+#define HCI_HKEY_SEARCH			0x013c
+#define HCI_HKEY_SUSPEND		0x013d
+#define HCI_HKEY_HIBERNATE		0x013e
+#define HCI_HKEY_BRIGHTNESSDOWN		0x0140
+#define HCI_HKEY_BRIGHTNESSUP		0x0141
+#define HCI_HKEY_WLAN			0x0142
+
 /* utility
  */
 
@@ -213,9 +225,15 @@ static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result)
 
 static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
 static struct backlight_device *toshiba_backlight_device;
+static struct input_dev *toshiba_input;
 static int force_fan;
 static int last_key_event;
 static int key_event_valid;
+static int hotkeys_over_input = 1;
+static int hotkeys_check_per_sec = 2;
+
+module_param(hotkeys_over_input, uint, 0400);
+module_param(hotkeys_check_per_sec, uint, 0400);
 
 typedef struct _ProcItem {
 	const char *name;
@@ -443,27 +461,33 @@ static char *read_keys(char *p)
 	u32 hci_result;
 	u32 value;
 
-	if (!key_event_valid) {
-		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
-		if (hci_result == HCI_SUCCESS) {
-			key_event_valid = 1;
-			last_key_event = value;
-		} else if (hci_result == HCI_EMPTY) {
-			/* better luck next time */
-		} else if (hci_result == HCI_NOT_SUPPORTED) {
-			/* This is a workaround for an unresolved issue on
-			 * some machines where system events sporadically
-			 * become disabled. */
-			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
-			printk(MY_NOTICE "Re-enabled hotkeys\n");
-		} else {
-			printk(MY_ERR "Error reading hotkey status\n");
-			goto end;
+	if (!hotkeys_over_input) {
+		if (!key_event_valid) {
+			hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+			if (hci_result == HCI_SUCCESS) {
+				key_event_valid = 1;
+				last_key_event = value;
+			} else if (hci_result == HCI_EMPTY) {
+				/* better luck next time */
+			} else if (hci_result == HCI_NOT_SUPPORTED) {
+				/* This is a workaround for an unresolved issue on
+				 * some machines where system events sporadically
+				 * become disabled. */
+				hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+				printk(MY_NOTICE "Re-enabled hotkeys\n");
+			} else {
+				printk(MY_ERR "Error reading hotkey status\n");
+				goto end;
+			}
 		}
+	} else {
+		key_event_valid = 0;
+		last_key_event = 0;
 	}
 
 	p += sprintf(p, "hotkey_ready:            %d\n", key_event_valid);
 	p += sprintf(p, "hotkey:                  0x%04x\n", last_key_event);
+	p += sprintf(p, "hotkeys_via_input:       %d\n", hotkeys_over_input);
 
       end:
 	return p;
@@ -534,15 +558,133 @@ static acpi_status __exit remove_device(void)
 }
 
 static struct backlight_ops toshiba_backlight_data = {
-        .get_brightness = get_lcd,
-        .update_status  = set_lcd_status,
+	.get_brightness = get_lcd,
+	.update_status  = set_lcd_status,
 };
 
+static void toshiba_keys_fire(struct work_struct *work);
+static struct workqueue_struct *toshiba_keys_wq;
+static DECLARE_DELAYED_WORK(toshiba_keys_work, toshiba_keys_fire);
+
+static void toshiba_deliver_button_event(u32 value)
+{
+	int keycode = KEY_UNKNOWN;
+	int key_down = 1;
+
+	if (!toshiba_input)
+		return;
+
+	/* translate MSB to key up */
+	if (value & 0x80) {
+		value &= ~0x80;
+		key_down = 0;
+	}
+
+	/* ignore FN on its own */
+	if (value == 0x0100)
+		return;
+
+	/* translate keys to keycodes */
+	switch (value) {
+	case HCI_HKEY_MUTE:
+		keycode = KEY_MUTE;
+		break;
+	case HCI_HKEY_BREAK:
+		keycode = KEY_BREAK;
+		break;
+	case HCI_HKEY_SEARCH:
+		keycode = KEY_SEARCH;
+		break;
+	case HCI_HKEY_SUSPEND:
+		keycode = KEY_SLEEP;
+		break;
+	case HCI_HKEY_HIBERNATE:
+		keycode = KEY_SUSPEND;
+		break;
+	case HCI_HKEY_BRIGHTNESSDOWN:
+		keycode = KEY_BRIGHTNESSDOWN;
+		break;
+	case HCI_HKEY_BRIGHTNESSUP:
+		keycode = KEY_BRIGHTNESSUP;
+		break;
+	case HCI_HKEY_WLAN:
+		keycode = KEY_WLAN;
+		break;
+	default:
+		keycode = KEY_UNKNOWN;
+	}
+
+	if (keycode != KEY_UNKNOWN) {
+		printk(MY_INFO "mapped hkey %i to keycode %i [%i]\n", value, keycode, key_down);
+		input_report_key(toshiba_input, keycode, key_down);
+		input_sync(toshiba_input);
+	}
+}
+
+static void toshiba_keys_clear(void)
+{
+	int dropped = 0;
+	int clear_queue = 0;
+	u32 hci_result, value;
+
+	do {
+		/* We don't want to get stuck here; older toshibas such as the
+		 * A60 may load and then return junk during the hci_read */
+		if (clear_queue++ > 16)
+			break;
+
+		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+		if (hci_result == HCI_SUCCESS) {
+			dropped++;
+		} else if (hci_result == HCI_EMPTY) {
+			/* better luck next time */
+		} else if (hci_result == HCI_NOT_SUPPORTED) {
+			/* This is a workaround for an unresolved issue on
+			 * some machines where system events sporadically
+			 * become disabled. */
+			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+			printk(MY_NOTICE "Re-enabled hotkeys\n");
+		}
+	} while (hci_result != HCI_EMPTY);
+
+	printk(MY_INFO "Dropped %d keys from the queue on startup\n", dropped);
+}
+
+static void toshiba_keys_fire(struct work_struct *work)
+{
+	u32 hci_result, value;
+
+	do {
+		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+		if (hci_result == HCI_SUCCESS) {
+			/* we got a button event */
+			toshiba_deliver_button_event(value);
+		} else if (hci_result == HCI_EMPTY) {
+			/* better luck next time */
+		} else if (hci_result == HCI_NOT_SUPPORTED) {
+			/* This is a workaround for an
+			 * unresolved issue on some machines
+			 * where system events sporadically
+			 * become disabled. */
+			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+			printk(MY_NOTICE "Re-enabled hotkeys\n");
+		}
+	} while (hci_result == HCI_SUCCESS);
+
+	/* reschedule another check */
+	queue_delayed_work(toshiba_keys_wq, &toshiba_keys_work, HZ / hotkeys_check_per_sec);
+}
+
 static void __exit toshiba_acpi_exit(void)
 {
 	if (toshiba_backlight_device)
 		backlight_device_unregister(toshiba_backlight_device);
 
+	if (toshiba_keys_wq) {
+		cancel_delayed_work(&toshiba_keys_work);
+		destroy_workqueue(toshiba_keys_wq);
+	}
+
 	remove_device();
 
 	if (toshiba_proc_dir)
@@ -551,6 +693,45 @@ static void __exit toshiba_acpi_exit(void)
 	return;
 }
 
+static int __init toshiba_input_init(void)
+{
+	int error;
+
+	/* use INPUT for key events */
+	toshiba_input = input_allocate_device();
+	if (!toshiba_input) {
+		error = -ENOMEM;
+		goto err_no_mem;
+	}
+
+	toshiba_keys_clear();
+
+	/* create one 'keyboard' virtual input device for all the acpi events */
+	toshiba_input->name = "Toshiba Extra Buttons";
+	toshiba_input->phys = "toshiba/input0";
+	toshiba_input->id.bustype = BUS_HOST;
+	toshiba_input->id.product = 0x0001;
+	toshiba_input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_MUTE, toshiba_input->keybit);
+	set_bit(KEY_BREAK, toshiba_input->keybit); /* probably should be KEY_LOCK */
+	set_bit(KEY_SEARCH, toshiba_input->keybit);
+	set_bit(KEY_SUSPEND, toshiba_input->keybit);
+	set_bit(KEY_SLEEP, toshiba_input->keybit);
+	set_bit(KEY_BRIGHTNESSDOWN, toshiba_input->keybit);
+	set_bit(KEY_BRIGHTNESSUP, toshiba_input->keybit);
+	set_bit(KEY_WLAN, toshiba_input->keybit);
+
+	error = input_register_device(toshiba_input);
+	if (error)
+		goto err_free_input;
+	return 0;
+
+err_free_input:
+	input_free_device(toshiba_input);
+err_no_mem:
+	return error;
+}
+
 static int __init toshiba_acpi_init(void)
 {
 	acpi_status status = AE_OK;
@@ -590,12 +771,38 @@ static int __init toshiba_acpi_init(void)
 	toshiba_backlight_device = backlight_device_register("toshiba",NULL,
 						NULL,
 						&toshiba_backlight_data);
-        if (IS_ERR(toshiba_backlight_device)) {
+	if (IS_ERR(toshiba_backlight_device)) {
 		printk(KERN_ERR "Could not register toshiba backlight device\n");
 		toshiba_backlight_device = NULL;
 		toshiba_acpi_exit();
 	}
-        toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
+	toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
+
+	/* we have to poll the device as we do not get events */
+	if (hotkeys_over_input && hotkeys_check_per_sec > 0) {
+
+		toshiba_input_init ();
+		/* just abort */
+		if (!toshiba_input) {
+			printk(KERN_ERR "could not allocate input device\n");
+			toshiba_acpi_exit();
+		}
+
+		/* setup workqueue */
+		toshiba_keys_wq = create_singlethread_workqueue("ktoshkeyd");
+		if (!toshiba_keys_wq) {
+			printk(KERN_ERR "failed to create workqueue\n");
+			toshiba_acpi_exit();
+		}
+
+		/* sanitise to something sane */
+		if (hotkeys_check_per_sec > 10)
+			hotkeys_check_per_sec = 10;
+		printk(KERN_INFO "ktoshkeyd checks per second : %d\n", hotkeys_check_per_sec);
+
+		/* start polling after delay */
+		queue_delayed_work(toshiba_keys_wq, &toshiba_keys_work, HZ / hotkeys_check_per_sec);
+	}
 
 	return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
 }

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

* Re: Add INPUT support to toshiba_acpi
  2007-05-31 13:43   ` Richard Hughes
@ 2007-05-31 15:46     ` Richard Hughes
  2007-05-31 16:46       ` Andreas Mohr
  2007-06-01 16:45       ` Dmitry Torokhov
  0 siblings, 2 replies; 23+ messages in thread
From: Richard Hughes @ 2007-05-31 15:46 UTC (permalink / raw)
  To: Bastien Nocera; +Cc: John Belmonte, linux-kernel, linux-acpi, Dmitry Torokhov

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

On Thu, 2007-05-31 at 14:44 +0100, Richard Hughes wrote:
> Nope, impossible AFAICS. The hardware is just broken. Windows XP has an
> toshiba supplied daemon that polls, so I think we have to just bite the
> bullet.

... adding in reply in different thread...

On Thu, 2007-05-31 at 10:37 -0400, Dmitry Torokhov wrote:
> Please use input-polldev to set up polled devices. It
> will create work queue for you and will make sure that polling is
> stopped when device is closed. 

Okay, I had never heard of this. I've done the suggested changes in the
attached patch. Please can you have a look and make sure I'm being sane.

> Also I don't think you want to use
> KEY_BREAK. What is the expected function of that key?

It's a "lock" key, I really want KEY_LOCK added to input.h, but that
might prove difficult. For now I've used KEY_CLEAR, yell if you think
there's a better one to substitute or if you guys want me to add get a
constant added to input.h.

Thanks for the review.

Richard.


[-- Attachment #2: toshiba_acpi_add_buttons_04.patch --]
[-- Type: text/x-patch, Size: 9558 bytes --]

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 139f41f..38835b6 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -222,6 +222,7 @@ config ACPI_TOSHIBA
 	tristate "Toshiba Laptop Extras"
 	depends on X86
 	select BACKLIGHT_CLASS_DEVICE
+	select INPUT_POLLDEV
 	---help---
 	  This driver adds support for access to certain system settings
 	  on "legacy free" Toshiba laptops.  These laptops can be recognized by
diff --git a/drivers/acpi/toshiba_acpi.c b/drivers/acpi/toshiba_acpi.c
index 3906d47..2b1a3d9 100644
--- a/drivers/acpi/toshiba_acpi.c
+++ b/drivers/acpi/toshiba_acpi.c
@@ -27,13 +27,13 @@
  *		engineering the Windows drivers
  *	Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
  *	Rob Miller - TV out and hotkeys help
- *
+ *	Richard Hughes - emit INPUT events for hotkeys
  *
  *  TODO
  *
  */
 
-#define TOSHIBA_ACPI_VERSION	"0.18"
+#define TOSHIBA_ACPI_VERSION	"0.19"
 #define PROC_INTERFACE_VERSION	1
 
 #include <linux/kernel.h>
@@ -42,6 +42,7 @@
 #include <linux/types.h>
 #include <linux/proc_fs.h>
 #include <linux/backlight.h>
+#include <linux/input-polldev.h>
 
 #include <asm/uaccess.h>
 
@@ -99,6 +100,16 @@ MODULE_LICENSE("GPL");
 #define HCI_VIDEO_OUT_CRT		0x2
 #define HCI_VIDEO_OUT_TV		0x4
 
+/* key definitions */
+#define HCI_HKEY_MUTE			0x0101
+#define HCI_HKEY_BREAK			0x013b
+#define HCI_HKEY_SEARCH			0x013c
+#define HCI_HKEY_SUSPEND		0x013d
+#define HCI_HKEY_HIBERNATE		0x013e
+#define HCI_HKEY_BRIGHTNESSDOWN		0x0140
+#define HCI_HKEY_BRIGHTNESSUP		0x0141
+#define HCI_HKEY_WLAN			0x0142
+
 /* utility
  */
 
@@ -213,9 +224,15 @@ static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result)
 
 static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
 static struct backlight_device *toshiba_backlight_device;
+static struct input_polled_dev *toshiba_poll_dev;
 static int force_fan;
 static int last_key_event;
 static int key_event_valid;
+static int hotkeys_over_input = 1;
+static int hotkeys_check_per_sec = 2;
+
+module_param(hotkeys_over_input, uint, 0400);
+module_param(hotkeys_check_per_sec, uint, 0400);
 
 typedef struct _ProcItem {
 	const char *name;
@@ -443,27 +460,33 @@ static char *read_keys(char *p)
 	u32 hci_result;
 	u32 value;
 
-	if (!key_event_valid) {
-		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
-		if (hci_result == HCI_SUCCESS) {
-			key_event_valid = 1;
-			last_key_event = value;
-		} else if (hci_result == HCI_EMPTY) {
-			/* better luck next time */
-		} else if (hci_result == HCI_NOT_SUPPORTED) {
-			/* This is a workaround for an unresolved issue on
-			 * some machines where system events sporadically
-			 * become disabled. */
-			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
-			printk(MY_NOTICE "Re-enabled hotkeys\n");
-		} else {
-			printk(MY_ERR "Error reading hotkey status\n");
-			goto end;
+	if (!hotkeys_over_input) {
+		if (!key_event_valid) {
+			hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+			if (hci_result == HCI_SUCCESS) {
+				key_event_valid = 1;
+				last_key_event = value;
+			} else if (hci_result == HCI_EMPTY) {
+				/* better luck next time */
+			} else if (hci_result == HCI_NOT_SUPPORTED) {
+				/* This is a workaround for an unresolved issue on
+				 * some machines where system events sporadically
+				 * become disabled. */
+				hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+				printk(MY_NOTICE "Re-enabled hotkeys\n");
+			} else {
+				printk(MY_ERR "Error reading hotkey status\n");
+				goto end;
+			}
 		}
+	} else {
+		key_event_valid = 0;
+		last_key_event = 0;
 	}
 
 	p += sprintf(p, "hotkey_ready:            %d\n", key_event_valid);
 	p += sprintf(p, "hotkey:                  0x%04x\n", last_key_event);
+	p += sprintf(p, "hotkeys_via_input:       %d\n", hotkeys_over_input);
 
       end:
 	return p;
@@ -534,15 +557,127 @@ static acpi_status __exit remove_device(void)
 }
 
 static struct backlight_ops toshiba_backlight_data = {
-        .get_brightness = get_lcd,
-        .update_status  = set_lcd_status,
+	.get_brightness = get_lcd,
+	.update_status  = set_lcd_status,
 };
 
+static void toshiba_deliver_button_event(struct input_dev *input, u32 value)
+{
+	int keycode = KEY_UNKNOWN;
+	int key_down = 1;
+
+	if (!input)
+		return;
+
+	/* translate MSB to key up */
+	if (value & 0x80) {
+		value &= ~0x80;
+		key_down = 0;
+	}
+
+	/* ignore FN on its own */
+	if (value == 0x0100)
+		return;
+
+	/* translate keys to keycodes */
+	switch (value) {
+	case HCI_HKEY_MUTE:
+		keycode = KEY_MUTE;
+		break;
+	case HCI_HKEY_BREAK:
+		keycode = KEY_BREAK;
+		break;
+	case HCI_HKEY_SEARCH:
+		keycode = KEY_SEARCH;
+		break;
+	case HCI_HKEY_SUSPEND:
+		keycode = KEY_SLEEP;
+		break;
+	case HCI_HKEY_HIBERNATE:
+		keycode = KEY_SUSPEND;
+		break;
+	case HCI_HKEY_BRIGHTNESSDOWN:
+		keycode = KEY_BRIGHTNESSDOWN;
+		break;
+	case HCI_HKEY_BRIGHTNESSUP:
+		keycode = KEY_BRIGHTNESSUP;
+		break;
+	case HCI_HKEY_WLAN:
+		keycode = KEY_WLAN;
+		break;
+	default:
+		keycode = KEY_UNKNOWN;
+	}
+
+	if (keycode != KEY_UNKNOWN) {
+		printk(MY_INFO "mapped hkey %i to keycode %i [%i]\n", value, keycode, key_down);
+		input_report_key(input, keycode, key_down);
+		input_sync(input);
+	}
+}
+
+static void toshiba_keys_clear(void)
+{
+	int dropped = 0;
+	int clear_queue = 0;
+	u32 hci_result, value;
+
+	do {
+		/* We don't want to get stuck here; older toshibas such as the
+		 * A60 may load and then return junk during the hci_read */
+		if (clear_queue++ > 16)
+			break;
+
+		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+		if (hci_result == HCI_SUCCESS) {
+			dropped++;
+		} else if (hci_result == HCI_EMPTY) {
+			/* better luck next time */
+		} else if (hci_result == HCI_NOT_SUPPORTED) {
+			/* This is a workaround for an unresolved issue on
+			 * some machines where system events sporadically
+			 * become disabled. */
+			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+			printk(MY_NOTICE "Re-enabled hotkeys\n");
+		}
+	} while (hci_result != HCI_EMPTY);
+
+	printk(MY_INFO "Dropped %d keys from the queue on startup\n", dropped);
+}
+
+static void handle_buttons(struct input_polled_dev *dev)
+{
+	u32 hci_result, value;
+	struct input_dev *input = dev->input;
+
+	do {
+		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+		if (hci_result == HCI_SUCCESS) {
+			/* we got a button event */
+			toshiba_deliver_button_event(input, value);
+		} else if (hci_result == HCI_EMPTY) {
+			/* better luck next time */
+		} else if (hci_result == HCI_NOT_SUPPORTED) {
+			/* This is a workaround for an
+			 * unresolved issue on some machines
+			 * where system events sporadically
+			 * become disabled. */
+			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+			printk(MY_NOTICE "Re-enabled hotkeys\n");
+		}
+	} while (hci_result == HCI_SUCCESS);
+}
+
 static void __exit toshiba_acpi_exit(void)
 {
 	if (toshiba_backlight_device)
 		backlight_device_unregister(toshiba_backlight_device);
 
+	if (toshiba_poll_dev) {
+		input_unregister_polled_device(toshiba_poll_dev);
+		input_free_polled_device(toshiba_poll_dev);
+	}
+
 	remove_device();
 
 	if (toshiba_proc_dir)
@@ -551,6 +686,49 @@ static void __exit toshiba_acpi_exit(void)
 	return;
 }
 
+static int __init toshiba_input_polldev_init(void)
+{
+	int error;
+	struct input_dev *input;
+
+	/* use INPUT for key events */
+	toshiba_poll_dev = input_allocate_polled_device();;
+	if (!toshiba_poll_dev) {
+		error = -ENOMEM;
+		goto err_no_mem;
+	}
+	toshiba_poll_dev->poll = handle_buttons;
+	toshiba_poll_dev->poll_interval = HZ / hotkeys_check_per_sec;
+	input = toshiba_poll_dev->input;
+
+	/* create one 'keyboard' virtual input device for all the acpi events */
+	input->name = "Toshiba Extra Buttons";
+	input->phys = "toshiba/input0";
+	input->id.bustype = BUS_HOST;
+	input->id.product = 0x0001;
+	input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_MUTE, input->keybit);
+	set_bit(KEY_BREAK, input->keybit); /* probably should be KEY_LOCK */
+	set_bit(KEY_SEARCH, input->keybit);
+	set_bit(KEY_SUSPEND, input->keybit);
+	set_bit(KEY_SLEEP, input->keybit);
+	set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
+	set_bit(KEY_BRIGHTNESSUP, input->keybit);
+	set_bit(KEY_WLAN, input->keybit);
+
+	toshiba_keys_clear();
+
+	error = input_register_polled_device(toshiba_poll_dev);
+	if (error)
+		goto err_free_input;
+	return 0;
+
+err_free_input:
+	input_free_polled_device(toshiba_poll_dev);
+err_no_mem:
+	return error;
+}
+
 static int __init toshiba_acpi_init(void)
 {
 	acpi_status status = AE_OK;
@@ -590,12 +768,28 @@ static int __init toshiba_acpi_init(void)
 	toshiba_backlight_device = backlight_device_register("toshiba",NULL,
 						NULL,
 						&toshiba_backlight_data);
-        if (IS_ERR(toshiba_backlight_device)) {
+	if (IS_ERR(toshiba_backlight_device)) {
 		printk(KERN_ERR "Could not register toshiba backlight device\n");
 		toshiba_backlight_device = NULL;
 		toshiba_acpi_exit();
 	}
-        toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
+	toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
+
+	/* we have to poll the device as we do not get events */
+	if (hotkeys_over_input && hotkeys_check_per_sec > 0) {
+
+		/* sanitise to something sane */
+		if (hotkeys_check_per_sec > 10)
+			hotkeys_check_per_sec = 10;
+		printk(KERN_INFO "ktoshkeyd checks per second : %d\n", hotkeys_check_per_sec);
+
+		toshiba_input_polldev_init ();
+		/* just abort */
+		if (!toshiba_poll_dev) {
+			printk(KERN_ERR "could not allocate input device\n");
+			toshiba_acpi_exit();
+		}
+	}
 
 	return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
 }

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

* Re: Add INPUT support to toshiba_acpi
  2007-05-31 15:46     ` Richard Hughes
@ 2007-05-31 16:46       ` Andreas Mohr
  2007-05-31 23:09         ` Richard Hughes
  2007-06-01 16:45       ` Dmitry Torokhov
  1 sibling, 1 reply; 23+ messages in thread
From: Andreas Mohr @ 2007-05-31 16:46 UTC (permalink / raw)
  To: Richard Hughes
  Cc: Bastien Nocera, John Belmonte, linux-kernel, linux-acpi, Dmitry Torokhov

Hi,

On Thu, May 31, 2007 at 04:46:56PM +0100, Richard Hughes wrote:

+	if (!hotkeys_over_input) {
+		if (!key_event_valid) {
+			hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+			if (hci_result == HCI_SUCCESS) {
+				key_event_valid = 1;
+				last_key_event = value;
+			} else if (hci_result == HCI_EMPTY) {
+				/* better luck next time */

HCI_EMPTY is *by far* the most frequent state to occur I think
(users won't press keys all the time), thus it's probably better(?)
for branch prediction to have this placed first, right?
Not that it matters too much instruction-wise, but still...

Apart from that I'm very happy to see progress on this front
(speaking as a "proud" owner of an old Toshiba notebook requiring
this stuff).

Oh, and maybe merge the sprintf()s into a single one to reduce code size.

And I'd definitely move the multiple identical "Re-enabled hotkeys" parts
into one single non-inlined(!) function for the same reason.
Not to mention that it's BUTT UGLY to have the *same* fat
multi-line comment duplicated bazillion times.

Thanks a lot!

Andreas Mohr

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

* Re: Add INPUT support to toshiba_acpi
  2007-05-31 16:46       ` Andreas Mohr
@ 2007-05-31 23:09         ` Richard Hughes
  0 siblings, 0 replies; 23+ messages in thread
From: Richard Hughes @ 2007-05-31 23:09 UTC (permalink / raw)
  To: Andreas Mohr
  Cc: Bastien Nocera, John Belmonte, linux-kernel, linux-acpi, Dmitry Torokhov

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

On Thu, 2007-05-31 at 18:46 +0200, Andreas Mohr wrote:
> HCI_EMPTY is *by far* the most frequent state to occur I think
> (users won't press keys all the time), thus it's probably better(?)
> for branch prediction to have this placed first, right?
> Not that it matters too much instruction-wise, but still...

Sure.

> Apart from that I'm very happy to see progress on this front
> (speaking as a "proud" owner of an old Toshiba notebook requiring
> this stuff).

Good - this stuff should just work :-)

> And I'd definitely move the multiple identical "Re-enabled hotkeys" parts
> into one single non-inlined(!) function for the same reason.
> Not to mention that it's BUTT UGLY to have the *same* fat
> multi-line comment duplicated bazillion times.

Agree. New patch (untested) attached.

Thanks for review,

Richard.


[-- Attachment #2: toshiba_acpi_add_buttons_05.patch --]
[-- Type: text/x-patch, Size: 9215 bytes --]

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 139f41f..38835b6 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -222,6 +222,7 @@ config ACPI_TOSHIBA
 	tristate "Toshiba Laptop Extras"
 	depends on X86
 	select BACKLIGHT_CLASS_DEVICE
+	select INPUT_POLLDEV
 	---help---
 	  This driver adds support for access to certain system settings
 	  on "legacy free" Toshiba laptops.  These laptops can be recognized by
diff --git a/drivers/acpi/toshiba_acpi.c b/drivers/acpi/toshiba_acpi.c
index 3906d47..f802609 100644
--- a/drivers/acpi/toshiba_acpi.c
+++ b/drivers/acpi/toshiba_acpi.c
@@ -27,13 +27,13 @@
  *		engineering the Windows drivers
  *	Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
  *	Rob Miller - TV out and hotkeys help
- *
+ *	Richard Hughes - emit INPUT events for hotkeys
  *
  *  TODO
  *
  */
 
-#define TOSHIBA_ACPI_VERSION	"0.18"
+#define TOSHIBA_ACPI_VERSION	"0.19"
 #define PROC_INTERFACE_VERSION	1
 
 #include <linux/kernel.h>
@@ -42,6 +42,7 @@
 #include <linux/types.h>
 #include <linux/proc_fs.h>
 #include <linux/backlight.h>
+#include <linux/input-polldev.h>
 
 #include <asm/uaccess.h>
 
@@ -99,6 +100,16 @@ MODULE_LICENSE("GPL");
 #define HCI_VIDEO_OUT_CRT		0x2
 #define HCI_VIDEO_OUT_TV		0x4
 
+/* key definitions */
+#define HCI_HKEY_MUTE			0x0101
+#define HCI_HKEY_LOCK			0x013b
+#define HCI_HKEY_SEARCH			0x013c
+#define HCI_HKEY_SUSPEND		0x013d
+#define HCI_HKEY_HIBERNATE		0x013e
+#define HCI_HKEY_BRIGHTNESSDOWN		0x0140
+#define HCI_HKEY_BRIGHTNESSUP		0x0141
+#define HCI_HKEY_WLAN			0x0142
+
 /* utility
  */
 
@@ -213,9 +224,15 @@ static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result)
 
 static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
 static struct backlight_device *toshiba_backlight_device;
+static struct input_polled_dev *toshiba_poll_dev;
 static int force_fan;
 static int last_key_event;
 static int key_event_valid;
+static int hotkeys_over_input = 1;
+static int hotkeys_check_per_sec = 2;
+
+module_param(hotkeys_over_input, uint, 0400);
+module_param(hotkeys_check_per_sec, uint, 0400);
 
 typedef struct _ProcItem {
 	const char *name;
@@ -438,34 +455,60 @@ static unsigned long write_fan(const char *buffer, unsigned long count)
 	return count;
 }
 
-static char *read_keys(char *p)
+/* returns zero if hci event is invalid */
+static u32 get_hci_system_event_value(void)
 {
 	u32 hci_result;
 	u32 value;
+	u32 retval = 0;
+
+	/* read a system event from the queue */
+	hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+
+	switch (hci_result) {
+	case HCI_EMPTY:
+		/* better luck next time */
+		break;
+	case HCI_SUCCESS:
+		/* value is the data in the queue */
+		retval = value;
+		break;
+	case HCI_NOT_SUPPORTED:
+		/* This is a workaround for an unresolved issue on
+		 * some machines where system events sporadically
+		 * become disabled. */
+		hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+		printk(MY_NOTICE "Re-enabled hotkeys\n");
+		break;
+	default:
+		/* there was an unknown error */
+		printk(MY_ERR "Error reading hotkey status\n");
+	}
+	return retval;
+}
 
-	if (!key_event_valid) {
-		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
-		if (hci_result == HCI_SUCCESS) {
-			key_event_valid = 1;
-			last_key_event = value;
-		} else if (hci_result == HCI_EMPTY) {
-			/* better luck next time */
-		} else if (hci_result == HCI_NOT_SUPPORTED) {
-			/* This is a workaround for an unresolved issue on
-			 * some machines where system events sporadically
-			 * become disabled. */
-			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
-			printk(MY_NOTICE "Re-enabled hotkeys\n");
-		} else {
-			printk(MY_ERR "Error reading hotkey status\n");
-			goto end;
+/* deprecate this interface... */
+static char *read_keys(char *p)
+{
+	u32 value;
+
+	if (hotkeys_over_input) {
+		key_event_valid = 0;
+		last_key_event = 0;
+	} else {
+		if (!key_event_valid) {
+			value = get_hci_system_event_value();
+			if (value != 0) {
+				key_event_valid = 1;
+				last_key_event = value;
+			}
 		}
 	}
 
 	p += sprintf(p, "hotkey_ready:            %d\n", key_event_valid);
 	p += sprintf(p, "hotkey:                  0x%04x\n", last_key_event);
+	p += sprintf(p, "hotkeys_via_input:       %d\n", hotkeys_over_input);
 
-      end:
 	return p;
 }
 
@@ -534,15 +577,103 @@ static acpi_status __exit remove_device(void)
 }
 
 static struct backlight_ops toshiba_backlight_data = {
-        .get_brightness = get_lcd,
-        .update_status  = set_lcd_status,
+	.get_brightness = get_lcd,
+	.update_status  = set_lcd_status,
 };
 
+static void toshiba_deliver_button_event(struct input_dev *input, u32 value)
+{
+	int keycode = KEY_UNKNOWN;
+	int key_down = 1;
+
+	if (!input)
+		return;
+
+	/* translate MSB to key up */
+	if (value & 0x80) {
+		value &= ~0x80;
+		key_down = 0;
+	}
+
+	/* ignore FN on its own */
+	if (value == 0x0100)
+		return;
+
+	/* translate keys to keycodes */
+	switch (value) {
+	case HCI_HKEY_MUTE:
+		keycode = KEY_MUTE;
+		break;
+	case HCI_HKEY_LOCK:
+		keycode = KEY_CLEAR;
+		break;
+	case HCI_HKEY_SEARCH:
+		keycode = KEY_SEARCH;
+		break;
+	case HCI_HKEY_SUSPEND:
+		keycode = KEY_SLEEP;
+		break;
+	case HCI_HKEY_HIBERNATE:
+		keycode = KEY_SUSPEND;
+		break;
+	case HCI_HKEY_BRIGHTNESSDOWN:
+		keycode = KEY_BRIGHTNESSDOWN;
+		break;
+	case HCI_HKEY_BRIGHTNESSUP:
+		keycode = KEY_BRIGHTNESSUP;
+		break;
+	case HCI_HKEY_WLAN:
+		keycode = KEY_WLAN;
+		break;
+	default:
+		keycode = KEY_UNKNOWN;
+	}
+
+	if (keycode != KEY_UNKNOWN) {
+		printk(MY_INFO "mapped hkey %i to keycode %i [%i]\n", value, keycode, key_down);
+		input_report_key(input, keycode, key_down);
+		input_sync(input);
+	}
+}
+
+/* the HCI buffer might have keys pressed some time ago */
+static void toshiba_keys_clear(void)
+{
+	int hci_queue = 0;
+	do {
+		/* we don't want to get stuck here */
+		if (hci_queue++ > 16)
+			break;
+	} while (get_hci_system_event_value());
+}
+
+static void handle_buttons(struct input_polled_dev *dev)
+{
+	u32 value;
+	int hci_queue = 0;
+	struct input_dev *input = dev->input;
+
+	do {
+		/* we don't want to get stuck here */
+		if (hci_queue++ > 16)
+			break;
+		/* try to get a button event */
+		value = get_hci_system_event_value();
+		if (value)
+			toshiba_deliver_button_event(input, value);
+	} while (value);
+}
+
 static void __exit toshiba_acpi_exit(void)
 {
 	if (toshiba_backlight_device)
 		backlight_device_unregister(toshiba_backlight_device);
 
+	if (toshiba_poll_dev) {
+		input_unregister_polled_device(toshiba_poll_dev);
+		input_free_polled_device(toshiba_poll_dev);
+	}
+
 	remove_device();
 
 	if (toshiba_proc_dir)
@@ -551,6 +682,49 @@ static void __exit toshiba_acpi_exit(void)
 	return;
 }
 
+static int __init toshiba_input_polldev_init(void)
+{
+	int error;
+	struct input_dev *input;
+
+	/* use INPUT for key events */
+	toshiba_poll_dev = input_allocate_polled_device();;
+	if (!toshiba_poll_dev) {
+		error = -ENOMEM;
+		goto err_no_mem;
+	}
+	toshiba_poll_dev->poll = handle_buttons;
+	toshiba_poll_dev->poll_interval = HZ / hotkeys_check_per_sec;
+	input = toshiba_poll_dev->input;
+
+	/* create one 'keyboard' virtual input device for all the acpi events */
+	input->name = "Toshiba Extra Buttons";
+	input->phys = "toshiba/input0";
+	input->id.bustype = BUS_HOST;
+	input->id.product = 0x0001;
+	input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_MUTE, input->keybit);
+	set_bit(KEY_BREAK, input->keybit); /* probably should be KEY_LOCK */
+	set_bit(KEY_SEARCH, input->keybit);
+	set_bit(KEY_SUSPEND, input->keybit);
+	set_bit(KEY_SLEEP, input->keybit);
+	set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
+	set_bit(KEY_BRIGHTNESSUP, input->keybit);
+	set_bit(KEY_WLAN, input->keybit);
+
+	toshiba_keys_clear();
+
+	error = input_register_polled_device(toshiba_poll_dev);
+	if (error)
+		goto err_free_input;
+	return 0;
+
+err_free_input:
+	input_free_polled_device(toshiba_poll_dev);
+err_no_mem:
+	return error;
+}
+
 static int __init toshiba_acpi_init(void)
 {
 	acpi_status status = AE_OK;
@@ -590,12 +764,28 @@ static int __init toshiba_acpi_init(void)
 	toshiba_backlight_device = backlight_device_register("toshiba",NULL,
 						NULL,
 						&toshiba_backlight_data);
-        if (IS_ERR(toshiba_backlight_device)) {
+	if (IS_ERR(toshiba_backlight_device)) {
 		printk(KERN_ERR "Could not register toshiba backlight device\n");
 		toshiba_backlight_device = NULL;
 		toshiba_acpi_exit();
 	}
-        toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
+	toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
+
+	/* we have to poll the device as we do not get events */
+	if (hotkeys_over_input && hotkeys_check_per_sec > 0) {
+
+		/* sanitise to something sane */
+		if (hotkeys_check_per_sec > 10)
+			hotkeys_check_per_sec = 10;
+		printk(KERN_INFO "ktoshkeyd checks per second : %d\n", hotkeys_check_per_sec);
+
+		toshiba_input_polldev_init ();
+		/* just abort */
+		if (!toshiba_poll_dev) {
+			printk(KERN_ERR "could not allocate input device\n");
+			toshiba_acpi_exit();
+		}
+	}
 
 	return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
 }
diff --git a/drivers/char/ChangeLog b/drivers/char/ChangeLog
diff --git a/drivers/net/LICENSE.SRC b/drivers/net/LICENSE.SRC

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

* Re: Add INPUT support to toshiba_acpi
  2007-05-31 15:46     ` Richard Hughes
  2007-05-31 16:46       ` Andreas Mohr
@ 2007-06-01 16:45       ` Dmitry Torokhov
  2007-06-02 12:50         ` Richard Hughes
  1 sibling, 1 reply; 23+ messages in thread
From: Dmitry Torokhov @ 2007-06-01 16:45 UTC (permalink / raw)
  To: Richard Hughes; +Cc: Bastien Nocera, John Belmonte, linux-kernel, linux-acpi

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

Hi Richard,

On 5/31/07, Richard Hughes <hughsient@gmail.com> wrote:
> On Thu, 2007-05-31 at 14:44 +0100, Richard Hughes wrote:
> > Nope, impossible AFAICS. The hardware is just broken. Windows XP has an
> > toshiba supplied daemon that polls, so I think we have to just bite the
> > bullet.
>
> ... adding in reply in different thread...
>
> On Thu, 2007-05-31 at 10:37 -0400, Dmitry Torokhov wrote:
> > Please use input-polldev to set up polled devices. It
> > will create work queue for you and will make sure that polling is
> > stopped when device is closed.
>
> Okay, I had never heard of this. I've done the suggested changes in the
> attached patch. Please can you have a look and make sure I'm being sane.
>

The patch was pretty good, I did not quite like the driver
registration code so I tried to clean it up. What do you think about
the attached patch (not tested due to the lack of hardware). If you
are OK with it please add your signed-off-by because version of the
patch I grabbed did not have it.

> > Also I don't think you want to use
> > KEY_BREAK. What is the expected function of that key?
>
> It's a "lock" key, I really want KEY_LOCK added to input.h, but that
> might prove difficult. For now I've used KEY_CLEAR, yell if you think
> there's a better one to substitute or if you guys want me to add get a
> constant added to input.h.

Iam still struggling with the purpose of the key. What would you
extect to happen when youser presses this key? Screen gets locked?
KEY_SCREENLOCK? KEY_SCREENSAVER?

-- 
Dmitry

[-- Attachment #2: toshiba_acpi_add_buttons.patch --]
[-- Type: text/plain, Size: 14181 bytes --]

From: Richard Hughes <hughsient@gmail.com>
Subject: toshiba_acpi: integrate with INPUT layer

Toshiba hardware is a little oddball, and does not provide ACPI events
on some key presses, typically Fn hotkey buttons. The key interface is
now polled, and events now matched to a list of toshiba specific
scancodes, and are squirted to userspace using the INPUT subsystem.

This means that toshiba laptops buttons "just work" without any
userspace daemon (using uinput) such as fnfx or bodges such as using a
userspace hal addon. Doing the polling in kernel is more efficient, and
makes stuff just work out of the box. You can assign the keys using
standard X keymaps, or using tools such as gnome-keybinding-properties.

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
 Documentation/kernel-parameters.txt |   11 +
 drivers/acpi/Kconfig                |    1 
 drivers/acpi/toshiba_acpi.c         |  333 ++++++++++++++++++++++++++++--------
 3 files changed, 278 insertions(+), 67 deletions(-)

Index: linux/drivers/acpi/Kconfig
===================================================================
--- linux.orig/drivers/acpi/Kconfig
+++ linux/drivers/acpi/Kconfig
@@ -222,6 +222,7 @@ config ACPI_TOSHIBA
 	tristate "Toshiba Laptop Extras"
 	depends on X86
 	select BACKLIGHT_CLASS_DEVICE
+	select INPUT_POLLDEV
 	---help---
 	  This driver adds support for access to certain system settings
 	  on "legacy free" Toshiba laptops.  These laptops can be recognized by
Index: linux/drivers/acpi/toshiba_acpi.c
===================================================================
--- linux.orig/drivers/acpi/toshiba_acpi.c
+++ linux/drivers/acpi/toshiba_acpi.c
@@ -27,13 +27,13 @@
  *		engineering the Windows drivers
  *	Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
  *	Rob Miller - TV out and hotkeys help
- *
+ *	Richard Hughes - emit INPUT events for hotkeys
  *
  *  TODO
  *
  */
 
-#define TOSHIBA_ACPI_VERSION	"0.18"
+#define TOSHIBA_ACPI_VERSION	"0.19"
 #define PROC_INTERFACE_VERSION	1
 
 #include <linux/kernel.h>
@@ -42,6 +42,7 @@
 #include <linux/types.h>
 #include <linux/proc_fs.h>
 #include <linux/backlight.h>
+#include <linux/input-polldev.h>
 
 #include <asm/uaccess.h>
 
@@ -55,6 +56,7 @@ MODULE_LICENSE("GPL");
 #define MY_ERR KERN_ERR MY_LOGPREFIX
 #define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
 #define MY_INFO KERN_INFO MY_LOGPREFIX
+#define MY_DEBUG KERN_DEBUG MY_LOGPREFIX
 
 /* Toshiba ACPI method paths */
 #define METHOD_LCD_BRIGHTNESS	"\\_SB_.PCI0.VGA_.LCD_._BCM"
@@ -99,6 +101,17 @@ MODULE_LICENSE("GPL");
 #define HCI_VIDEO_OUT_CRT		0x2
 #define HCI_VIDEO_OUT_TV		0x4
 
+/* key definitions */
+#define HCI_HKEY_FN			0x0100
+#define HCI_HKEY_MUTE			0x0101
+#define HCI_HKEY_BREAK			0x013b
+#define HCI_HKEY_SEARCH			0x013c
+#define HCI_HKEY_SUSPEND		0x013d
+#define HCI_HKEY_HIBERNATE		0x013e
+#define HCI_HKEY_BRIGHTNESSDOWN		0x0140
+#define HCI_HKEY_BRIGHTNESSUP		0x0141
+#define HCI_HKEY_WLAN			0x0142
+
 /* utility
  */
 
@@ -213,10 +226,21 @@ static acpi_status hci_read1(u32 reg, u3
 
 static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
 static struct backlight_device *toshiba_backlight_device;
+static struct input_polled_dev *toshiba_poll_dev;
 static int force_fan;
 static int last_key_event;
 static int key_event_valid;
 
+static int hotkeys_over_input = 1;
+module_param(hotkeys_over_input, bool, 0444);
+MODULE_PARM_DESC(hotkeys_over_input,
+		"Enable delivery of hotkey events via input layer.");
+
+static int hotkeys_poll_interval = 500;	/* msecs */
+module_param(hotkeys_poll_interval, uint, 0444);
+MODULE_PARM_DESC(hotkeys_poll_interval,
+		"How often to poll hotkey state (default is 500 msec)");
+
 typedef struct _ProcItem {
 	const char *name;
 	char *(*read_func) (char *);
@@ -438,32 +462,47 @@ static unsigned long write_fan(const cha
 	return count;
 }
 
+static u32 hci_poll_keys_once(u32 *value)
+{
+	u32 hci_result;
+
+	hci_read1(HCI_SYSTEM_EVENT, value, &hci_result);
+	if (hci_result == HCI_NOT_SUPPORTED) {
+		/* This is a workaround for an unresolved issue on
+		 * some machines where system events sporadically
+		 * become disabled. */
+		hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+		printk(MY_DEBUG "Re-enabled hotkeys\n");
+		hci_result = HCI_EMPTY;
+	}
+
+	return hci_result;
+}
+
 static char *read_keys(char *p)
 {
 	u32 hci_result;
 	u32 value;
 
-	if (!key_event_valid) {
-		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
-		if (hci_result == HCI_SUCCESS) {
-			key_event_valid = 1;
-			last_key_event = value;
-		} else if (hci_result == HCI_EMPTY) {
-			/* better luck next time */
-		} else if (hci_result == HCI_NOT_SUPPORTED) {
-			/* This is a workaround for an unresolved issue on
-			 * some machines where system events sporadically
-			 * become disabled. */
-			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
-			printk(MY_NOTICE "Re-enabled hotkeys\n");
-		} else {
-			printk(MY_ERR "Error reading hotkey status\n");
-			goto end;
+	if (!hotkeys_over_input) {
+		if (!key_event_valid) {
+			hci_result = hci_poll_keys_once(&value);
+			if (hci_result == HCI_SUCCESS) {
+				key_event_valid = 1;
+				last_key_event = value;
+			} else if (hci_result != HCI_EMPTY) {
+				printk(MY_ERR "Error reading hotkey status\n");
+				goto end;
+			}
 		}
+	} else {
+		key_event_valid = 0;
+		last_key_event = 0;
 	}
 
 	p += sprintf(p, "hotkey_ready:            %d\n", key_event_valid);
 	p += sprintf(p, "hotkey:                  0x%04x\n", last_key_event);
+	p += sprintf(p, "hotkeys_via_input:       %d\n", hotkeys_over_input);
 
       end:
 	return p;
@@ -501,60 +540,204 @@ static ProcItem proc_items[] = {
 	{"fan", read_fan, write_fan},
 	{"keys", read_keys, write_keys},
 	{"version", read_version, NULL},
-	{NULL}
 };
 
-static acpi_status __init add_device(void)
+static int __init add_proc_entries(void)
 {
 	struct proc_dir_entry *proc;
 	ProcItem *item;
+	int mode;
+	int i;
 
-	for (item = proc_items; item->name; ++item) {
-		proc = create_proc_read_entry(item->name,
-					      S_IFREG | S_IRUGO | S_IWUSR,
-					      toshiba_proc_dir,
-					      (read_proc_t *) dispatch_read,
-					      item);
-		if (proc)
-			proc->owner = THIS_MODULE;
-		if (proc && item->write_func)
+	toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
+	if (!toshiba_proc_dir) {
+		printk(MY_ERR "failed to create toshiba proc directory\n");
+		goto err_out;
+	}
+
+	toshiba_proc_dir->owner = THIS_MODULE;
+
+	for (i = 0; i < ARRAY_SIZE(proc_items); i++) {
+		item = &proc_items[i];
+
+		mode = S_IFREG | S_IRUGO;
+		if (item->write_func)
+			mode = S_IWUSR;
+
+		proc = create_proc_entry(item->name, mode, toshiba_proc_dir);
+		if (!proc) {
+			printk(MY_ERR "failed to create %s proc entry\n",
+				item->name);
+			goto err_remove_proc;
+		}
+
+		proc->owner = THIS_MODULE;
+		proc->data = item;
+		proc->read_proc = (read_proc_t *) dispatch_read;
+		if (item->write_func)
 			proc->write_proc = (write_proc_t *) dispatch_write;
 	}
 
-	return AE_OK;
+	return 0;
+
+ err_remove_proc:
+	while (-- i >= 0)
+		remove_proc_entry(proc_items[i].name, toshiba_proc_dir);
+	remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
+ err_out:
+	return -EBUSY; /* arbitrary */
 }
 
-static acpi_status __exit remove_device(void)
+static void __exit remove_proc_entries(void)
 {
-	ProcItem *item;
+	int i;
 
-	for (item = proc_items; item->name; ++item)
-		remove_proc_entry(item->name, toshiba_proc_dir);
-	return AE_OK;
+	for (i = 0; i < ARRAY_SIZE(proc_items); i++)
+		remove_proc_entry(proc_items[i].name, toshiba_proc_dir);
+
+	remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
 }
 
 static struct backlight_ops toshiba_backlight_data = {
-        .get_brightness = get_lcd,
-        .update_status  = set_lcd_status,
+	.get_brightness = get_lcd,
+	.update_status  = set_lcd_status,
 };
 
-static void __exit toshiba_acpi_exit(void)
+static void toshiba_deliver_button_event(struct input_dev *input, u32 value)
 {
-	if (toshiba_backlight_device)
-		backlight_device_unregister(toshiba_backlight_device);
+	int keycode;
+	int key_down;
 
-	remove_device();
+	/* translate MSB to key up */
+	key_down = !(value & 0x80);
+	value &= ~0x80;
+
+	/* translate keys to keycodes */
+	switch (value) {
+	case HCI_HKEY_FN:
+		keycode = KEY_RESERVED; /* ignore FN on its own */
+		return;
+	case HCI_HKEY_MUTE:
+		keycode = KEY_MUTE;
+		break;
+	case HCI_HKEY_BREAK:
+		keycode = KEY_BREAK;
+		break;
+	case HCI_HKEY_SEARCH:
+		keycode = KEY_SEARCH;
+		break;
+	case HCI_HKEY_SUSPEND:
+		keycode = KEY_SLEEP;
+		break;
+	case HCI_HKEY_HIBERNATE:
+		keycode = KEY_SUSPEND;
+		break;
+	case HCI_HKEY_BRIGHTNESSDOWN:
+		keycode = KEY_BRIGHTNESSDOWN;
+		break;
+	case HCI_HKEY_BRIGHTNESSUP:
+		keycode = KEY_BRIGHTNESSUP;
+		break;
+	case HCI_HKEY_WLAN:
+		keycode = KEY_WLAN;
+		break;
+	default:
+		keycode = KEY_UNKNOWN;
+		break;
+	}
+
+	if (keycode != KEY_RESERVED) {
+		input_report_key(input, keycode, key_down);
+		input_event(input, EV_MSC, MSC_SCAN, value);
+		input_sync(input);
+	}
+}
 
-	if (toshiba_proc_dir)
-		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
+static void toshiba_keys_flush(struct input_polled_dev *dev)
+{
+	int dropped = 0;
+	int clear_queue = 0;
+	u32 value;
+
+	/*
+	 * We don't want to get stuck here; older toshibas such as the
+	 * A60 may load and then return junk during the hci_read
+	 * so limit reads to 16 attempts.
+	 */
+	do {
+		if (hci_poll_keys_once(&value) != HCI_SUCCESS)
+			break;
+
+		dropped++;
+	} while (clear_queue++ < 16);
+
+	printk(MY_DEBUG "Dropped %d keys from the queue on startup\n",
+		dropped);
+}
+
+static void toshiba_keys_poll(struct input_polled_dev *dev)
+{
+	struct input_dev *input = dev->input;
+	u32 value;
+
+	while (hci_poll_keys_once(&value) == HCI_SUCCESS)
+		toshiba_deliver_button_event(input, value);
+}
+
+static int __init toshiba_input_polldev_init(void)
+{
+	int error;
+	struct input_dev *input;
+
+	/* use INPUT for key events */
+	toshiba_poll_dev = input_allocate_polled_device();;
+	if (!toshiba_poll_dev) {
+		printk(MY_ERR "could not allocate input device\n");
+		error = -ENOMEM;
+		goto err_no_mem;
+	}
+
+	toshiba_poll_dev->flush = toshiba_keys_flush;
+	toshiba_poll_dev->poll = toshiba_keys_poll;
+
+	/* sanitise polling to something sane */
+	if (hotkeys_poll_interval < 100)
+		hotkeys_poll_interval = 100;
+	toshiba_poll_dev->poll_interval = hotkeys_poll_interval;
+
+	/* create one 'keyboard' virtual input device for all the acpi events */
+	input = toshiba_poll_dev->input;
+	input->name = "Toshiba Extra Buttons";
+	input->phys = "toshiba/input0";
+	input->id.bustype = BUS_HOST;
+	input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_MUTE, input->keybit);
+	set_bit(KEY_BREAK, input->keybit); /* probably should be KEY_LOCK */
+	set_bit(KEY_SEARCH, input->keybit);
+	set_bit(KEY_SUSPEND, input->keybit);
+	set_bit(KEY_SLEEP, input->keybit);
+	set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
+	set_bit(KEY_BRIGHTNESSUP, input->keybit);
+	set_bit(KEY_WLAN, input->keybit);
+
+	error = input_register_polled_device(toshiba_poll_dev);
+	if (error) {
+		printk(MY_ERR "could not register input device\n");
+		goto err_free_input;
+	}
 
-	return;
+	return 0;
+
+ err_free_input:
+	input_free_polled_device(toshiba_poll_dev);
+ err_no_mem:
+	return error;
 }
 
 static int __init toshiba_acpi_init(void)
 {
-	acpi_status status = AE_OK;
 	u32 hci_result;
+	int error;
 
 	if (acpi_disabled)
 		return -ENODEV;
@@ -571,33 +754,49 @@ static int __init toshiba_acpi_init(void
 	       TOSHIBA_ACPI_VERSION);
 	printk(MY_INFO "    HCI method: %s\n", method_hci);
 
-	force_fan = 0;
-	key_event_valid = 0;
-
 	/* enable event fifo */
 	hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
 
-	toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
-	if (!toshiba_proc_dir) {
-		status = AE_ERROR;
-	} else {
-		toshiba_proc_dir->owner = THIS_MODULE;
-		status = add_device();
-		if (ACPI_FAILURE(status))
-			remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
-	}
-
-	toshiba_backlight_device = backlight_device_register("toshiba",NULL,
-						NULL,
-						&toshiba_backlight_data);
-        if (IS_ERR(toshiba_backlight_device)) {
-		printk(KERN_ERR "Could not register toshiba backlight device\n");
-		toshiba_backlight_device = NULL;
-		toshiba_acpi_exit();
+	error = add_proc_entries();
+	if (error)
+		goto err_out;
+
+	toshiba_backlight_device = backlight_device_register("toshiba", NULL,
+					NULL, &toshiba_backlight_data);
+	if (IS_ERR(toshiba_backlight_device)) {
+		printk(MY_ERR "Could not register toshiba backlight device\n");
+		goto err_remove_proc;
+	}
+	toshiba_backlight_device->props.max_brightness =
+		HCI_LCD_BRIGHTNESS_LEVELS - 1;
+
+	/* we have to poll the device as we do not get events */
+	if (hotkeys_over_input && hotkeys_poll_interval) {
+		error = toshiba_input_polldev_init();
+		if (error)
+			goto err_remove_backlight;
+	}
+
+	return 0;
+
+ err_remove_backlight:
+	backlight_device_unregister(toshiba_backlight_device);
+ err_remove_proc:
+	remove_proc_entries();
+ err_out:
+	return error;
+}
+
+static void __exit toshiba_acpi_exit(void)
+{
+	backlight_device_unregister(toshiba_backlight_device);
+
+	if (toshiba_poll_dev) {
+		input_unregister_polled_device(toshiba_poll_dev);
+		input_free_polled_device(toshiba_poll_dev);
 	}
-        toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
 
-	return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
+	remove_proc_entries();
 }
 
 module_init(toshiba_acpi_init);
Index: linux/Documentation/kernel-parameters.txt
===================================================================
--- linux.orig/Documentation/kernel-parameters.txt
+++ linux/Documentation/kernel-parameters.txt
@@ -1841,6 +1841,17 @@ and is between 256 and 4096 characters. 
 			See comment before function dc390_setup() in
 			drivers/scsi/tmscsim.c.
 
+	toshiba_acpi.hotkeys_over_input
+			[HW,ACPI]
+			Instructs the driver to deliver hotkey events
+			via input layer as opposed to /proc. Enabled
+			by default.
+	toshiba_acpi.hotkeys_poll_interval =
+			[HW,ACPI]
+			Determines how often the driver polls hotkey
+			state when hotkeys are delivered via input
+			layer. Default is 500 msecs.
+
 	tp720=		[HW,PS2]
 
 	trix=		[HW,OSS] MediaTrix AudioTrix Pro

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

* Re: Add INPUT support to toshiba_acpi
  2007-06-01 16:45       ` Dmitry Torokhov
@ 2007-06-02 12:50         ` Richard Hughes
  2007-06-02 14:23           ` Matthew Garrett
  0 siblings, 1 reply; 23+ messages in thread
From: Richard Hughes @ 2007-06-02 12:50 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Bastien Nocera, John Belmonte, linux-kernel, linux-acpi

On Fri, 2007-06-01 at 12:45 -0400, Dmitry Torokhov wrote:
> The patch was pretty good, I did not quite like the driver
> registration code so I tried to clean it up.

Cool, cheers.

> What do you think about
> the attached patch (not tested due to the lack of hardware). If you
> are OK with it please add your signed-off-by because version of the
> patch I grabbed did not have it.

Attached patch looks good - I can't test it until next week, but looks
logically correct to me. I would also like to add the oddball proc event
notification system to the removal-schedule document, but this can be a
fight for another day.

> > > Also I don't think you want to use
> > > KEY_BREAK. What is the expected function of that key?

To lock the screen. We probably want to replace HCI_HKEY_BREAK with
HCI_HKEY_LOCK, as the picture on the keys is a little padlock.

> > It's a "lock" key, I really want KEY_LOCK added to input.h, but that
> > might prove difficult. For now I've used KEY_CLEAR, yell if you think
> > there's a better one to substitute or if you guys want me to add get a
> > constant added to input.h.
> 
> Iam still struggling with the purpose of the key. What would you
> extect to happen when youser presses this key? Screen gets locked?
> KEY_SCREENLOCK? KEY_SCREENSAVER?

Either of these keys would be good to add.

So yes, patch looks good, cheers for the improvements.

Signed-off-by: Richard Hughes <richard@hughsie.com>

Thanks again,

Richard.



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

* Re: Add INPUT support to toshiba_acpi
  2007-06-02 12:50         ` Richard Hughes
@ 2007-06-02 14:23           ` Matthew Garrett
  2007-06-03  4:48             ` Dmitry Torokhov
  0 siblings, 1 reply; 23+ messages in thread
From: Matthew Garrett @ 2007-06-02 14:23 UTC (permalink / raw)
  To: Richard Hughes
  Cc: Dmitry Torokhov, Bastien Nocera, John Belmonte, linux-kernel, linux-acpi

On Sat, Jun 02, 2007 at 01:50:52PM +0100, Richard Hughes wrote:
> > KEY_SCREENLOCK? KEY_SCREENSAVER?
> 
> Either of these keys would be good to add.

We've been interpreting "KEY_COFFEE" as screenlock (as in "I've gone for 
a coffee break, lock the screen").

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

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

* Re: Add INPUT support to toshiba_acpi
  2007-06-02 14:23           ` Matthew Garrett
@ 2007-06-03  4:48             ` Dmitry Torokhov
  2007-06-07 16:58               ` Richard Hughes
  0 siblings, 1 reply; 23+ messages in thread
From: Dmitry Torokhov @ 2007-06-03  4:48 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Richard Hughes, Bastien Nocera, John Belmonte, linux-kernel, linux-acpi

On Saturday 02 June 2007 10:23, Matthew Garrett wrote:
> On Sat, Jun 02, 2007 at 01:50:52PM +0100, Richard Hughes wrote:
> > > KEY_SCREENLOCK? KEY_SCREENSAVER?
> > 
> > Either of these keys would be good to add.
> 
> We've been interpreting "KEY_COFFEE" as screenlock (as in "I've gone for 
> a coffee break, lock the screen").
> 

It looks like KEY_COFFE comes from 0x0c/0x19e - AL Terminal Lock/Screensaver
so your interpretation is indeed correct. I guess I better add an alias to
input.h

-- 
Dmitry

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

* Re: Add INPUT support to toshiba_acpi
  2007-06-03  4:48             ` Dmitry Torokhov
@ 2007-06-07 16:58               ` Richard Hughes
  2007-06-08 14:23                 ` Dmitry Torokhov
  2007-06-11 13:26                 ` Renato S. Yamane
  0 siblings, 2 replies; 23+ messages in thread
From: Richard Hughes @ 2007-06-07 16:58 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Matthew Garrett, Bastien Nocera, John Belmonte, linux-kernel, linux-acpi

On Sun, 2007-06-03 at 00:48 -0400, Dmitry Torokhov wrote:
> 
> 
> It looks like KEY_COFFE comes from 0x0c/0x19e - AL Terminal
> Lock/Screensaver
> so your interpretation is indeed correct. I guess I better add an
> alias to
> input.h

What's the status of this patch? Good for merging? Do you want me to
redo the current patch using input-polldev and the new setkeycode stuff?

Richard.


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

* Re: Add INPUT support to toshiba_acpi
  2007-06-07 16:58               ` Richard Hughes
@ 2007-06-08 14:23                 ` Dmitry Torokhov
  2007-06-08 14:30                   ` Richard Hughes
  2007-06-11 13:26                 ` Renato S. Yamane
  1 sibling, 1 reply; 23+ messages in thread
From: Dmitry Torokhov @ 2007-06-08 14:23 UTC (permalink / raw)
  To: Richard Hughes
  Cc: Matthew Garrett, Bastien Nocera, John Belmonte, linux-kernel, linux-acpi

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

On 6/7/07, Richard Hughes <hughsient@gmail.com> wrote:
> On Sun, 2007-06-03 at 00:48 -0400, Dmitry Torokhov wrote:
> >
> >
> > It looks like KEY_COFFE comes from 0x0c/0x19e - AL Terminal
> > Lock/Screensaver
> > so your interpretation is indeed correct. I guess I better add an
> > alias to
> > input.h
>
> What's the status of this patch? Good for merging?

Not my call - John is the official maintainer of the driver. However
from input layer POV it looks good.

> Do you want me to
> redo the current patch using input-polldev

The patch I send to you earlier already does this. I am attaching a
refreshed version that used KEY_COFFEE instead of KEY_BREAK.

> and the new setkeycode stuff?

And I have the patch doing this as well (attached). If you could
please give it a try I'd appreciate it.

-- 
Dmitry

[-- Attachment #2: toshiba_acpi_add_buttons.patch --]
[-- Type: text/plain, Size: 14199 bytes --]

From: Richard Hughes <hughsient@gmail.com>
Subject: toshiba_acpi: integrate with INPUT layer

Toshiba hardware is a little oddball, and does not provide ACPI events
on some key presses, typically Fn hotkey buttons. The key interface is
now polled, and events now matched to a list of toshiba specific
scancodes, and are squirted to userspace using the INPUT subsystem.

This means that toshiba laptops buttons "just work" without any
userspace daemon (using uinput) such as fnfx or bodges such as using a
userspace hal addon. Doing the polling in kernel is more efficient, and
makes stuff just work out of the box. You can assign the keys using
standard X keymaps, or using tools such as gnome-keybinding-properties.

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
 Documentation/kernel-parameters.txt |   11 +
 drivers/acpi/Kconfig                |    1 
 drivers/acpi/toshiba_acpi.c         |  333 ++++++++++++++++++++++++++++--------
 3 files changed, 278 insertions(+), 67 deletions(-)

Index: linux/drivers/acpi/Kconfig
===================================================================
--- linux.orig/drivers/acpi/Kconfig
+++ linux/drivers/acpi/Kconfig
@@ -222,6 +222,7 @@ config ACPI_TOSHIBA
 	tristate "Toshiba Laptop Extras"
 	depends on X86
 	select BACKLIGHT_CLASS_DEVICE
+	select INPUT_POLLDEV
 	---help---
 	  This driver adds support for access to certain system settings
 	  on "legacy free" Toshiba laptops.  These laptops can be recognized by
Index: linux/drivers/acpi/toshiba_acpi.c
===================================================================
--- linux.orig/drivers/acpi/toshiba_acpi.c
+++ linux/drivers/acpi/toshiba_acpi.c
@@ -27,13 +27,13 @@
  *		engineering the Windows drivers
  *	Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
  *	Rob Miller - TV out and hotkeys help
- *
+ *	Richard Hughes - emit INPUT events for hotkeys
  *
  *  TODO
  *
  */
 
-#define TOSHIBA_ACPI_VERSION	"0.18"
+#define TOSHIBA_ACPI_VERSION	"0.19"
 #define PROC_INTERFACE_VERSION	1
 
 #include <linux/kernel.h>
@@ -42,6 +42,7 @@
 #include <linux/types.h>
 #include <linux/proc_fs.h>
 #include <linux/backlight.h>
+#include <linux/input-polldev.h>
 
 #include <asm/uaccess.h>
 
@@ -55,6 +56,7 @@ MODULE_LICENSE("GPL");
 #define MY_ERR KERN_ERR MY_LOGPREFIX
 #define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
 #define MY_INFO KERN_INFO MY_LOGPREFIX
+#define MY_DEBUG KERN_DEBUG MY_LOGPREFIX
 
 /* Toshiba ACPI method paths */
 #define METHOD_LCD_BRIGHTNESS	"\\_SB_.PCI0.VGA_.LCD_._BCM"
@@ -99,6 +101,17 @@ MODULE_LICENSE("GPL");
 #define HCI_VIDEO_OUT_CRT		0x2
 #define HCI_VIDEO_OUT_TV		0x4
 
+/* key definitions */
+#define HCI_HKEY_FN			0x0100
+#define HCI_HKEY_MUTE			0x0101
+#define HCI_HKEY_BREAK			0x013b
+#define HCI_HKEY_SEARCH			0x013c
+#define HCI_HKEY_SUSPEND		0x013d
+#define HCI_HKEY_HIBERNATE		0x013e
+#define HCI_HKEY_BRIGHTNESSDOWN		0x0140
+#define HCI_HKEY_BRIGHTNESSUP		0x0141
+#define HCI_HKEY_WLAN			0x0142
+
 /* utility
  */
 
@@ -213,10 +226,21 @@ static acpi_status hci_read1(u32 reg, u3
 
 static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
 static struct backlight_device *toshiba_backlight_device;
+static struct input_polled_dev *toshiba_poll_dev;
 static int force_fan;
 static int last_key_event;
 static int key_event_valid;
 
+static int hotkeys_over_input = 1;
+module_param(hotkeys_over_input, bool, 0444);
+MODULE_PARM_DESC(hotkeys_over_input,
+		"Enable delivery of hotkey events via input layer.");
+
+static int hotkeys_poll_interval = 500;	/* msecs */
+module_param(hotkeys_poll_interval, uint, 0444);
+MODULE_PARM_DESC(hotkeys_poll_interval,
+		"How often to poll hotkey state (default is 500 msec)");
+
 typedef struct _ProcItem {
 	const char *name;
 	char *(*read_func) (char *);
@@ -438,32 +462,47 @@ static unsigned long write_fan(const cha
 	return count;
 }
 
+static u32 hci_poll_keys_once(u32 *value)
+{
+	u32 hci_result;
+
+	hci_read1(HCI_SYSTEM_EVENT, value, &hci_result);
+	if (hci_result == HCI_NOT_SUPPORTED) {
+		/* This is a workaround for an unresolved issue on
+		 * some machines where system events sporadically
+		 * become disabled. */
+		hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+		printk(MY_DEBUG "Re-enabled hotkeys\n");
+		hci_result = HCI_EMPTY;
+	}
+
+	return hci_result;
+}
+
 static char *read_keys(char *p)
 {
 	u32 hci_result;
 	u32 value;
 
-	if (!key_event_valid) {
-		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
-		if (hci_result == HCI_SUCCESS) {
-			key_event_valid = 1;
-			last_key_event = value;
-		} else if (hci_result == HCI_EMPTY) {
-			/* better luck next time */
-		} else if (hci_result == HCI_NOT_SUPPORTED) {
-			/* This is a workaround for an unresolved issue on
-			 * some machines where system events sporadically
-			 * become disabled. */
-			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
-			printk(MY_NOTICE "Re-enabled hotkeys\n");
-		} else {
-			printk(MY_ERR "Error reading hotkey status\n");
-			goto end;
+	if (!hotkeys_over_input) {
+		if (!key_event_valid) {
+			hci_result = hci_poll_keys_once(&value);
+			if (hci_result == HCI_SUCCESS) {
+				key_event_valid = 1;
+				last_key_event = value;
+			} else if (hci_result != HCI_EMPTY) {
+				printk(MY_ERR "Error reading hotkey status\n");
+				goto end;
+			}
 		}
+	} else {
+		key_event_valid = 0;
+		last_key_event = 0;
 	}
 
 	p += sprintf(p, "hotkey_ready:            %d\n", key_event_valid);
 	p += sprintf(p, "hotkey:                  0x%04x\n", last_key_event);
+	p += sprintf(p, "hotkeys_via_input:       %d\n", hotkeys_over_input);
 
       end:
 	return p;
@@ -501,60 +540,204 @@ static ProcItem proc_items[] = {
 	{"fan", read_fan, write_fan},
 	{"keys", read_keys, write_keys},
 	{"version", read_version, NULL},
-	{NULL}
 };
 
-static acpi_status __init add_device(void)
+static int __init add_proc_entries(void)
 {
 	struct proc_dir_entry *proc;
 	ProcItem *item;
+	int mode;
+	int i;
+
+	toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
+	if (!toshiba_proc_dir) {
+		printk(MY_ERR "failed to create toshiba proc directory\n");
+		goto err_out;
+	}
+
+	toshiba_proc_dir->owner = THIS_MODULE;
+
+	for (i = 0; i < ARRAY_SIZE(proc_items); i++) {
+		item = &proc_items[i];
 
-	for (item = proc_items; item->name; ++item) {
-		proc = create_proc_read_entry(item->name,
-					      S_IFREG | S_IRUGO | S_IWUSR,
-					      toshiba_proc_dir,
-					      (read_proc_t *) dispatch_read,
-					      item);
-		if (proc)
-			proc->owner = THIS_MODULE;
-		if (proc && item->write_func)
+		mode = S_IFREG | S_IRUGO;
+		if (item->write_func)
+			mode = S_IWUSR;
+
+		proc = create_proc_entry(item->name, mode, toshiba_proc_dir);
+		if (!proc) {
+			printk(MY_ERR "failed to create %s proc entry\n",
+				item->name);
+			goto err_remove_proc;
+		}
+
+		proc->owner = THIS_MODULE;
+		proc->data = item;
+		proc->read_proc = (read_proc_t *) dispatch_read;
+		if (item->write_func)
 			proc->write_proc = (write_proc_t *) dispatch_write;
 	}
 
-	return AE_OK;
+	return 0;
+
+ err_remove_proc:
+	while (-- i >= 0)
+		remove_proc_entry(proc_items[i].name, toshiba_proc_dir);
+	remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
+ err_out:
+	return -EBUSY; /* arbitrary */
 }
 
-static acpi_status __exit remove_device(void)
+static void __exit remove_proc_entries(void)
 {
-	ProcItem *item;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(proc_items); i++)
+		remove_proc_entry(proc_items[i].name, toshiba_proc_dir);
 
-	for (item = proc_items; item->name; ++item)
-		remove_proc_entry(item->name, toshiba_proc_dir);
-	return AE_OK;
+	remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
 }
 
 static struct backlight_ops toshiba_backlight_data = {
-        .get_brightness = get_lcd,
-        .update_status  = set_lcd_status,
+	.get_brightness = get_lcd,
+	.update_status  = set_lcd_status,
 };
 
-static void toshiba_acpi_exit(void)
+static void toshiba_deliver_button_event(struct input_dev *input, u32 value)
 {
-	if (toshiba_backlight_device)
-		backlight_device_unregister(toshiba_backlight_device);
+	int keycode;
+	int key_down;
 
-	remove_device();
+	/* translate MSB to key up */
+	key_down = !(value & 0x80);
+	value &= ~0x80;
+
+	/* translate keys to keycodes */
+	switch (value) {
+	case HCI_HKEY_FN:
+		keycode = KEY_RESERVED; /* ignore FN on its own */
+		return;
+	case HCI_HKEY_MUTE:
+		keycode = KEY_MUTE;
+		break;
+	case HCI_HKEY_BREAK:
+		keycode = KEY_COFFEE;	/* AKA KEY_SCREENLOCK */
+		break;
+	case HCI_HKEY_SEARCH:
+		keycode = KEY_SEARCH;
+		break;
+	case HCI_HKEY_SUSPEND:
+		keycode = KEY_SLEEP;
+		break;
+	case HCI_HKEY_HIBERNATE:
+		keycode = KEY_SUSPEND;
+		break;
+	case HCI_HKEY_BRIGHTNESSDOWN:
+		keycode = KEY_BRIGHTNESSDOWN;
+		break;
+	case HCI_HKEY_BRIGHTNESSUP:
+		keycode = KEY_BRIGHTNESSUP;
+		break;
+	case HCI_HKEY_WLAN:
+		keycode = KEY_WLAN;
+		break;
+	default:
+		keycode = KEY_UNKNOWN;
+		break;
+	}
+
+	if (keycode != KEY_RESERVED) {
+		input_report_key(input, keycode, key_down);
+		input_event(input, EV_MSC, MSC_SCAN, value);
+		input_sync(input);
+	}
+}
 
-	if (toshiba_proc_dir)
-		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
+static void toshiba_keys_flush(struct input_polled_dev *dev)
+{
+	int dropped = 0;
+	int clear_queue = 0;
+	u32 value;
+
+	/*
+	 * We don't want to get stuck here; older toshibas such as the
+	 * A60 may load and then return junk during the hci_read
+	 * so limit reads to 16 attempts.
+	 */
+	do {
+		if (hci_poll_keys_once(&value) != HCI_SUCCESS)
+			break;
+
+		dropped++;
+	} while (clear_queue++ < 16);
 
-	return;
+	printk(MY_DEBUG "Dropped %d keys from the queue on startup\n",
+		dropped);
+}
+
+static void toshiba_keys_poll(struct input_polled_dev *dev)
+{
+	struct input_dev *input = dev->input;
+	u32 value;
+
+	while (hci_poll_keys_once(&value) == HCI_SUCCESS)
+		toshiba_deliver_button_event(input, value);
+}
+
+static int __init toshiba_input_polldev_init(void)
+{
+	int error;
+	struct input_dev *input;
+
+	/* use INPUT for key events */
+	toshiba_poll_dev = input_allocate_polled_device();
+	if (!toshiba_poll_dev) {
+		printk(MY_ERR "could not allocate input device\n");
+		error = -ENOMEM;
+		goto err_no_mem;
+	}
+
+	toshiba_poll_dev->flush = toshiba_keys_flush;
+	toshiba_poll_dev->poll = toshiba_keys_poll;
+
+	/* sanitise polling to something sane */
+	if (hotkeys_poll_interval < 100)
+		hotkeys_poll_interval = 100;
+	toshiba_poll_dev->poll_interval = hotkeys_poll_interval;
+
+	/* create one 'keyboard' virtual input device for all the acpi events */
+	input = toshiba_poll_dev->input;
+	input->name = "Toshiba Extra Buttons";
+	input->phys = "toshiba/input0";
+	input->id.bustype = BUS_HOST;
+	input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_MUTE, input->keybit);
+	set_bit(KEY_BREAK, input->keybit); /* probably should be KEY_LOCK */
+	set_bit(KEY_SEARCH, input->keybit);
+	set_bit(KEY_SUSPEND, input->keybit);
+	set_bit(KEY_SLEEP, input->keybit);
+	set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
+	set_bit(KEY_BRIGHTNESSUP, input->keybit);
+	set_bit(KEY_WLAN, input->keybit);
+
+	error = input_register_polled_device(toshiba_poll_dev);
+	if (error) {
+		printk(MY_ERR "could not register input device\n");
+		goto err_free_input;
+	}
+
+	return 0;
+
+ err_free_input:
+	input_free_polled_device(toshiba_poll_dev);
+ err_no_mem:
+	return error;
 }
 
 static int __init toshiba_acpi_init(void)
 {
-	acpi_status status = AE_OK;
 	u32 hci_result;
+	int error;
 
 	if (acpi_disabled)
 		return -ENODEV;
@@ -571,33 +754,49 @@ static int __init toshiba_acpi_init(void
 	       TOSHIBA_ACPI_VERSION);
 	printk(MY_INFO "    HCI method: %s\n", method_hci);
 
-	force_fan = 0;
-	key_event_valid = 0;
-
 	/* enable event fifo */
 	hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
 
-	toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
-	if (!toshiba_proc_dir) {
-		status = AE_ERROR;
-	} else {
-		toshiba_proc_dir->owner = THIS_MODULE;
-		status = add_device();
-		if (ACPI_FAILURE(status))
-			remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
-	}
-
-	toshiba_backlight_device = backlight_device_register("toshiba",NULL,
-						NULL,
-						&toshiba_backlight_data);
-        if (IS_ERR(toshiba_backlight_device)) {
-		printk(KERN_ERR "Could not register toshiba backlight device\n");
-		toshiba_backlight_device = NULL;
-		toshiba_acpi_exit();
+	error = add_proc_entries();
+	if (error)
+		goto err_out;
+
+	toshiba_backlight_device = backlight_device_register("toshiba", NULL,
+					NULL, &toshiba_backlight_data);
+	if (IS_ERR(toshiba_backlight_device)) {
+		printk(MY_ERR "Could not register toshiba backlight device\n");
+		goto err_remove_proc;
+	}
+	toshiba_backlight_device->props.max_brightness =
+		HCI_LCD_BRIGHTNESS_LEVELS - 1;
+
+	/* we have to poll the device as we do not get events */
+	if (hotkeys_over_input && hotkeys_poll_interval) {
+		error = toshiba_input_polldev_init();
+		if (error)
+			goto err_remove_backlight;
+	}
+
+	return 0;
+
+ err_remove_backlight:
+	backlight_device_unregister(toshiba_backlight_device);
+ err_remove_proc:
+	remove_proc_entries();
+ err_out:
+	return error;
+}
+
+static void __exit toshiba_acpi_exit(void)
+{
+	backlight_device_unregister(toshiba_backlight_device);
+
+	if (toshiba_poll_dev) {
+		input_unregister_polled_device(toshiba_poll_dev);
+		input_free_polled_device(toshiba_poll_dev);
 	}
-        toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
 
-	return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
+	remove_proc_entries();
 }
 
 module_init(toshiba_acpi_init);
Index: linux/Documentation/kernel-parameters.txt
===================================================================
--- linux.orig/Documentation/kernel-parameters.txt
+++ linux/Documentation/kernel-parameters.txt
@@ -1875,6 +1875,17 @@ and is between 256 and 4096 characters. 
 			See comment before function dc390_setup() in
 			drivers/scsi/tmscsim.c.
 
+	toshiba_acpi.hotkeys_over_input
+			[HW,ACPI]
+			Instructs the driver to deliver hotkey events
+			via input layer as opposed to /proc. Enabled
+			by default.
+	toshiba_acpi.hotkeys_poll_interval =
+			[HW,ACPI]
+			Determines how often the driver polls hotkey
+			state when hotkeys are delivered via input
+			layer. Default is 500 msecs.
+
 	tp720=		[HW,PS2]
 
 	trix=		[HW,OSS] MediaTrix AudioTrix Pro

[-- Attachment #3: toshiba-acpi-setkeycodes.patch --]
[-- Type: text/plain, Size: 3905 bytes --]

Subject: toshiba_acpi: add support for altering keymap

Implement getkeycode and setkeycode methods of input device so that
users can adjust driver's keymap if they wish to do so.

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
 drivers/acpi/toshiba_acpi.c |  114 +++++++++++++++++++++++++++++---------------
 1 file changed, 77 insertions(+), 37 deletions(-)

Index: linux/drivers/acpi/toshiba_acpi.c
===================================================================
--- linux.orig/drivers/acpi/toshiba_acpi.c
+++ linux/drivers/acpi/toshiba_acpi.c
@@ -603,51 +603,88 @@ static struct backlight_ops toshiba_back
 	.update_status  = set_lcd_status,
 };
 
+static struct toshiba_key {
+	int code;
+	int keycode;
+} toshiba_keymap[] = {
+	{  HCI_HKEY_FN,			KEY_RESERVED }, /* ignore lone FN */
+	{  HCI_HKEY_MUTE,		KEY_MUTE },
+	{  HCI_HKEY_BREAK,		KEY_COFFEE }, /* AKA KEY_SCREENLOCK */
+	{  HCI_HKEY_SEARCH,		KEY_SEARCH },
+	{  HCI_HKEY_SUSPEND,		KEY_SLEEP },
+	{  HCI_HKEY_HIBERNATE,		KEY_SUSPEND },
+	{  HCI_HKEY_BRIGHTNESSDOWN,	KEY_BRIGHTNESSDOWN },
+	{  HCI_HKEY_BRIGHTNESSUP,	KEY_BRIGHTNESSUP },
+	{  HCI_HKEY_WLAN,		KEY_WLAN },
+};
+
+static struct toshiba_key *toshiba_get_key_by_scancode(int code)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(toshiba_keymap); i++)
+		if (code == toshiba_keymap[i].code)
+			return &toshiba_keymap[i];
+
+	return NULL;
+}
+
+static struct toshiba_key *toshiba_get_key_by_keycode(int keycode)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(toshiba_keymap); i++)
+		if (keycode == toshiba_keymap[i].keycode)
+			return &toshiba_keymap[i];
+
+	return NULL;
+}
+
+static int toshiba_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+{
+	const struct toshiba_key *key = toshiba_get_key_by_scancode(scancode);
+
+	if (key) {
+		*keycode = key->keycode;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int toshiba_setkeycode(struct input_dev *dev, int scancode, int keycode)
+{
+	struct toshiba_key *key;
+	int old_keycode;
+
+	if (keycode < 0 || keycode > KEY_MAX)
+		return -EINVAL;
+
+	key = toshiba_get_key_by_scancode(scancode);
+	if (key) {
+		old_keycode = key->keycode;
+		key->keycode = keycode;
+		set_bit(keycode, dev->keybit);
+		if (!toshiba_get_key_by_keycode(old_keycode))
+			clear_bit(old_keycode, dev->keybit);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
 static void toshiba_deliver_button_event(struct input_dev *input, u32 value)
 {
-	int keycode;
+	struct toshiba_key *key;
 	int key_down;
 
 	/* translate MSB to key up */
 	key_down = !(value & 0x80);
 	value &= ~0x80;
 
-	/* translate keys to keycodes */
-	switch (value) {
-	case HCI_HKEY_FN:
-		keycode = KEY_RESERVED; /* ignore FN on its own */
-		return;
-	case HCI_HKEY_MUTE:
-		keycode = KEY_MUTE;
-		break;
-	case HCI_HKEY_BREAK:
-		keycode = KEY_COFFEE;	/* AKA KEY_SCREENLOCK */
-		break;
-	case HCI_HKEY_SEARCH:
-		keycode = KEY_SEARCH;
-		break;
-	case HCI_HKEY_SUSPEND:
-		keycode = KEY_SLEEP;
-		break;
-	case HCI_HKEY_HIBERNATE:
-		keycode = KEY_SUSPEND;
-		break;
-	case HCI_HKEY_BRIGHTNESSDOWN:
-		keycode = KEY_BRIGHTNESSDOWN;
-		break;
-	case HCI_HKEY_BRIGHTNESSUP:
-		keycode = KEY_BRIGHTNESSUP;
-		break;
-	case HCI_HKEY_WLAN:
-		keycode = KEY_WLAN;
-		break;
-	default:
-		keycode = KEY_UNKNOWN;
-		break;
-	}
-
-	if (keycode != KEY_RESERVED) {
-		input_report_key(input, keycode, key_down);
+	key = toshiba_get_key_by_scancode(value);
+	if (key && key->keycode != KEY_RESERVED) {
+		input_report_key(input, key->keycode, key_down);
 		input_event(input, EV_MSC, MSC_SCAN, value);
 		input_sync(input);
 	}
@@ -720,6 +757,9 @@ static int __init toshiba_input_polldev_
 	set_bit(KEY_BRIGHTNESSUP, input->keybit);
 	set_bit(KEY_WLAN, input->keybit);
 
+	input->getkeycode = toshiba_getkeycode;
+	input->setkeycode = toshiba_setkeycode;
+
 	error = input_register_polled_device(toshiba_poll_dev);
 	if (error) {
 		printk(MY_ERR "could not register input device\n");

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

* Re: Add INPUT support to toshiba_acpi
  2007-06-08 14:23                 ` Dmitry Torokhov
@ 2007-06-08 14:30                   ` Richard Hughes
  0 siblings, 0 replies; 23+ messages in thread
From: Richard Hughes @ 2007-06-08 14:30 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Matthew Garrett, Bastien Nocera, John Belmonte, linux-kernel, linux-acpi

On Fri, 2007-06-08 at 10:23 -0400, Dmitry Torokhov wrote:
> On 6/7/07, Richard Hughes <hughsient@gmail.com> wrote:
> > On Sun, 2007-06-03 at 00:48 -0400, Dmitry Torokhov wrote:
> > >
> > >
> > > It looks like KEY_COFFE comes from 0x0c/0x19e - AL Terminal
> > > Lock/Screensaver
> > > so your interpretation is indeed correct. I guess I better add an
> > > alias to
> > > input.h
> >
> > What's the status of this patch? Good for merging?
> 
> Not my call - John is the official maintainer of the driver. However
> from input layer POV it looks good.

Good, thanks.

> > Do you want me to
> > redo the current patch using input-polldev
> 
> The patch I send to you earlier already does this. I am attaching a
> refreshed version that used KEY_COFFEE instead of KEY_BREAK.

Brilliant, these look great.

> > and the new setkeycode stuff?
> 
> And I have the patch doing this as well (attached). If you could
> please give it a try I'd appreciate it.

You beat me to it {hastily removes item from TODO...} :-)

I'll give it a try as soon as possible, thanks.

Richard.



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

* Re: Add INPUT support to toshiba_acpi
  2007-06-07 16:58               ` Richard Hughes
  2007-06-08 14:23                 ` Dmitry Torokhov
@ 2007-06-11 13:26                 ` Renato S. Yamane
  1 sibling, 0 replies; 23+ messages in thread
From: Renato S. Yamane @ 2007-06-11 13:26 UTC (permalink / raw)
  To: linux-kernel
  Cc: Richard Hughes, Dmitry Torokhov, Matthew Garrett, Bastien Nocera,
	John Belmonte, linux-acpi, Mathieu.Berard

Richard Hughes wrote:
> What's the status of this patch? Good for merging? Do you want me to
> redo the current patch using input-polldev and the new setkeycode stuff?

A detail:
I receive error message (no such device) when I try load toshiba_acpi on 
my Toshiba M45-S355 (BIOS is Toshiba and NOT Phoenix).

So, I can't change brightness of LCD.

To solve it, I use omnibook driver available in:
<http://omnibook.sourceforge.net>

Last SVN can get with:
svn export https://svn.sourceforge.net/svnroot/omnibook/omnibook/trunk

With omnibook driver I can change brightness writing in /proc/omnibook/lcd

Only one problem: multimedia keys don't is recognized by xev and I can't 
configure it with omnibook driver.

Best regards,
Renato S. Yamane

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

* Re: Add INPUT support to toshiba_acpi
  2007-05-31 12:36 Add INPUT support to toshiba_acpi Richard Hughes
  2007-05-31 12:53 ` Bastien Nocera
@ 2007-06-23 14:56 ` Rolf Eike Beer
  2007-06-25  9:52   ` Richard Hughes
  1 sibling, 1 reply; 23+ messages in thread
From: Rolf Eike Beer @ 2007-06-23 14:56 UTC (permalink / raw)
  To: Richard Hughes; +Cc: John Belmonte, linux-kernel, linux-acpi, Bastien Nocera

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

Richard Hughes wrote:
> Attached patch adds a kernel thread to do polling on Toshiba hardware.

Is there something similar available to support other Toshiba laptops? I own a 
A110-178 that is not supported by the driver but has a lot of keys that I 
can't use currently:

Fn:
-screen zoom (I actually don't care about this one)
-S2RAM
-S2Disk
-Mute*

Multimedia:
-Browser
-Video player
-Play/pause
-Stop
-Forward
-Reverse

*Mute: it's a bit special. If I use it I can an on screen display (mute/sound) 
but regardless of which setting is displayed the sound is still there.

None of the above keys generated a key event. Neither does "Brightness down", 
but it still works. "Brightness up" generates an event and works. Kpowersave 
tells me it can't do brightness switching in software (which works in 
WinXtraPain).

If you need an acpidump or I can do some testing just ask.

Eike

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

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

* Re: Add INPUT support to toshiba_acpi
  2007-06-23 14:56 ` Rolf Eike Beer
@ 2007-06-25  9:52   ` Richard Hughes
  2007-06-25 11:03     ` Rolf Eike Beer
  2007-06-26  5:03     ` Rolf Eike Beer
  0 siblings, 2 replies; 23+ messages in thread
From: Richard Hughes @ 2007-06-25  9:52 UTC (permalink / raw)
  To: Rolf Eike Beer; +Cc: John Belmonte, linux-kernel, linux-acpi, Bastien Nocera

On Sat, 2007-06-23 at 16:56 +0200, Rolf Eike Beer wrote:
> None of the above keys generated a key event. Neither does "Brightness down", 
> but it still works. "Brightness up" generates an event and works. Kpowersave 
> tells me it can't do brightness switching in software (which works in 
> WinXtraPain).

Do you know how windows does this? Do you have to load a special
system-try thing to make the keys work?

Richard.



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

* Re: Add INPUT support to toshiba_acpi
  2007-06-25  9:52   ` Richard Hughes
@ 2007-06-25 11:03     ` Rolf Eike Beer
  2007-06-26  5:03     ` Rolf Eike Beer
  1 sibling, 0 replies; 23+ messages in thread
From: Rolf Eike Beer @ 2007-06-25 11:03 UTC (permalink / raw)
  To: Richard Hughes; +Cc: John Belmonte, linux-kernel, linux-acpi, Bastien Nocera

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

Richard Hughes wrote:
> On Sat, 2007-06-23 at 16:56 +0200, Rolf Eike Beer wrote:
> > None of the above keys generated a key event. Neither does "Brightness
> > down", but it still works. "Brightness up" generates an event and works.
> > Kpowersave tells me it can't do brightness switching in software (which
> > works in WinXtraPain).
>
> Do you know how windows does this? Do you have to load a special
> system-try thing to make the keys work?

I'll have a look once I find time to boot Windows.

Eike

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

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

* Re: Add INPUT support to toshiba_acpi
  2007-06-25  9:52   ` Richard Hughes
  2007-06-25 11:03     ` Rolf Eike Beer
@ 2007-06-26  5:03     ` Rolf Eike Beer
  2007-06-26  8:56       ` Richard Hughes
  1 sibling, 1 reply; 23+ messages in thread
From: Rolf Eike Beer @ 2007-06-26  5:03 UTC (permalink / raw)
  To: Richard Hughes; +Cc: John Belmonte, linux-kernel, linux-acpi, Bastien Nocera

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

Richard Hughes wrote:
> On Sat, 2007-06-23 at 16:56 +0200, Rolf Eike Beer wrote:
> > None of the above keys generated a key event. Neither does "Brightness
> > down", but it still works. "Brightness up" generates an event and works.
> > Kpowersave tells me it can't do brightness switching in software (which
> > works in WinXtraPain).
>
> Do you know how windows does this? Do you have to load a special
> system-try thing to make the keys work?

Yes, I need a special process running for this. Would it be of some use if I 
take a look with IRPtracker on it or is that all you need to know?

Eike

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

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

* Re: Add INPUT support to toshiba_acpi
  2007-06-26  5:03     ` Rolf Eike Beer
@ 2007-06-26  8:56       ` Richard Hughes
  2007-06-28 11:27         ` Rolf Eike Beer
  0 siblings, 1 reply; 23+ messages in thread
From: Richard Hughes @ 2007-06-26  8:56 UTC (permalink / raw)
  To: Rolf Eike Beer; +Cc: John Belmonte, linux-kernel, linux-acpi, Bastien Nocera

On Tue, 2007-06-26 at 07:03 +0200, Rolf Eike Beer wrote:
> Richard Hughes wrote:
> > On Sat, 2007-06-23 at 16:56 +0200, Rolf Eike Beer wrote:
> > > None of the above keys generated a key event. Neither does
> "Brightness
> > > down", but it still works. "Brightness up" generates an event and
> works.
> > > Kpowersave tells me it can't do brightness switching in software
> (which
> > > works in WinXtraPain).
> Yes, I need a special process running for this. Would it be of some
> use if I take a look with IRPtracker on it or is that all you need to
> know?

Yes, although this is out of my area or expertise, sorry.

Richard.



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

* Re: Add INPUT support to toshiba_acpi
  2007-06-26  8:56       ` Richard Hughes
@ 2007-06-28 11:27         ` Rolf Eike Beer
  2007-06-28 12:22           ` Renato S. Yamane
  0 siblings, 1 reply; 23+ messages in thread
From: Rolf Eike Beer @ 2007-06-28 11:27 UTC (permalink / raw)
  To: Richard Hughes; +Cc: John Belmonte, linux-kernel, linux-acpi, Bastien Nocera

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

Richard Hughes wrote:
> On Tue, 2007-06-26 at 07:03 +0200, Rolf Eike Beer wrote:
>> Richard Hughes wrote:
>>> On Sat, 2007-06-23 at 16:56 +0200, Rolf Eike Beer wrote:
>>>> None of the above keys generated a key event. Neither does
>>>> "Brightness down", but it still works. "Brightness up" generates an event
>>>> and works.
>>
>>>> Kpowersave tells me it can't do brightness switching in software
>>>> (which works in WinXtraPain).
>>
>> Yes, I need a special process running for this. Would it be of some
>> use if I take a look with IRPtracker on it or is that all you need to
>> know?
>
> Yes, although this is out of my area or expertise, sorry.

I've looked a bit but can't find any driver interaction of those programs. Any 
further ideas welcome.

Eike

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

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

* Re: Add INPUT support to toshiba_acpi
  2007-06-28 11:27         ` Rolf Eike Beer
@ 2007-06-28 12:22           ` Renato S. Yamane
  2007-07-03  5:38             ` Rolf Eike Beer
  0 siblings, 1 reply; 23+ messages in thread
From: Renato S. Yamane @ 2007-06-28 12:22 UTC (permalink / raw)
  To: Rolf Eike Beer
  Cc: Richard Hughes, John Belmonte, linux-kernel, linux-acpi,
	Bastien Nocera, Mathieu.Berard

Rolf Eike Beer wrote:
> Richard Hughes wrote:
>> Yes, although this is out of my area or expertise, sorry.
> 
> I've looked a bit but can't find any driver interaction of those programs. Any 
> further ideas welcome.

Do you try omnibook driver?
svn export https://svn.sourceforge.net/svnroot/omnibook/omnibook/trunk

toshiba_acpi don't work on my Toshiba M45-S355 (Toshiba BIOS) and I try 
this module above and now I can change brightness writing in 
/proc/omnibook/lcd and kpowersave can change brightness too.

But, is impossible use multimedia keys (play, pause, browser, etc) and 
couple keys (fn-fx), because Fn key and Multimedia keys don't is recognized.

Regards,
Renato S. Yamane

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

* Re: Add INPUT support to toshiba_acpi
  2007-06-28 12:22           ` Renato S. Yamane
@ 2007-07-03  5:38             ` Rolf Eike Beer
  2007-07-03 12:59               ` Renato S. Yamane
  0 siblings, 1 reply; 23+ messages in thread
From: Rolf Eike Beer @ 2007-07-03  5:38 UTC (permalink / raw)
  To: Renato S. Yamane
  Cc: Richard Hughes, John Belmonte, linux-kernel, linux-acpi,
	Bastien Nocera, Mathieu.Berard

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

Renato S. Yamane wrote:
> Rolf Eike Beer wrote:
> > Richard Hughes wrote:
> >> Yes, although this is out of my area or expertise, sorry.
> >
> > I've looked a bit but can't find any driver interaction of those
> > programs. Any further ideas welcome.
>
> Do you try omnibook driver?
> svn export https://svn.sourceforge.net/svnroot/omnibook/omnibook/trunk
>
> toshiba_acpi don't work on my Toshiba M45-S355 (Toshiba BIOS) and I try
> this module above and now I can change brightness writing in
> /proc/omnibook/lcd and kpowersave can change brightness too.
>
> But, is impossible use multimedia keys (play, pause, browser, etc) and
> couple keys (fn-fx), because Fn key and Multimedia keys don't is
> recognized.

Oh wow! Even my Multimedia keys get recognized. Thanks very much for your 
pointer.

Eike

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

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

* Re: Add INPUT support to toshiba_acpi
  2007-07-03  5:38             ` Rolf Eike Beer
@ 2007-07-03 12:59               ` Renato S. Yamane
  0 siblings, 0 replies; 23+ messages in thread
From: Renato S. Yamane @ 2007-07-03 12:59 UTC (permalink / raw)
  To: linux-acpi
  Cc: Rolf Eike Beer, Renato S. Yamane, Richard Hughes, John Belmonte,
	linux-kernel, Bastien Nocera, Mathieu.Berard, mjg59, jaimeff

Rolf Eike Beer wrote:
> Renato S. Yamane wrote:
>> Rolf Eike Beer wrote:
>>> Richard Hughes wrote:
>>>>
>>>> Yes, although this is out of my area or expertise, sorry.
>>>
>>> I've looked a bit but can't find any driver interaction of those
>>> programs. Any further ideas welcome.
>>
>> Do you try omnibook driver?
>> svn export https://svn.sourceforge.net/svnroot/omnibook/omnibook/trunk
>>
>> toshiba_acpi don't work on my Toshiba M45-S355 (Toshiba BIOS) and I try
>> this module above and now I can change brightness writing in
>> /proc/omnibook/lcd and kpowersave can change brightness too.
>
> Oh wow! Even my Multimedia keys get recognized. Thanks very much for your 
> pointer.

I think that is a good idea more support to Toshiba Laptops, so 
omnibook+toshiba_acpi is a perfect couple, because some functions works 
with toshiba_acpi and others works with omnibook.

Jaime (in cc too) do a very useful module that work in some laptop 
models that not work on toshiba_acpi and omnibook driver.

So, I think that is a very good idea all work together to do a more 
powerful (and compatible) module in next kernel releases.

Regards,
Renato S. Yamane

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

end of thread, other threads:[~2007-07-03 12:59 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-05-31 12:36 Add INPUT support to toshiba_acpi Richard Hughes
2007-05-31 12:53 ` Bastien Nocera
2007-05-31 13:43   ` Richard Hughes
2007-05-31 15:46     ` Richard Hughes
2007-05-31 16:46       ` Andreas Mohr
2007-05-31 23:09         ` Richard Hughes
2007-06-01 16:45       ` Dmitry Torokhov
2007-06-02 12:50         ` Richard Hughes
2007-06-02 14:23           ` Matthew Garrett
2007-06-03  4:48             ` Dmitry Torokhov
2007-06-07 16:58               ` Richard Hughes
2007-06-08 14:23                 ` Dmitry Torokhov
2007-06-08 14:30                   ` Richard Hughes
2007-06-11 13:26                 ` Renato S. Yamane
2007-06-23 14:56 ` Rolf Eike Beer
2007-06-25  9:52   ` Richard Hughes
2007-06-25 11:03     ` Rolf Eike Beer
2007-06-26  5:03     ` Rolf Eike Beer
2007-06-26  8:56       ` Richard Hughes
2007-06-28 11:27         ` Rolf Eike Beer
2007-06-28 12:22           ` Renato S. Yamane
2007-07-03  5:38             ` Rolf Eike Beer
2007-07-03 12:59               ` Renato S. Yamane

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