Re: Add INPUT support to toshiba_acpi

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.

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

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux