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 @@ -42,6 +42,7 @@ #include #include #include +#include #include @@ -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