[PATCH 2/3] UBUNTU: SAUCE: toshiba_acpi -- add acpi hotkey kernel thread
Andy Whitcroft
apw at canonical.com
Sat Jan 31 15:07:57 UTC 2009
OriginalAuthor: Daniel Silverstone
Bug: #269831
Add the acpi hotkey management thread. This is a direct forward port
of the Ubuntu specific parts of the toshiba_acpi modifications as
found in Intrepid.
Signed-off-by: Andy Whitcroft <apw at canonical.com>
---
drivers/acpi/toshiba_acpi.c | 201 +++++++++++++++++++++++++++++++++++++++----
1 files changed, 184 insertions(+), 17 deletions(-)
diff --git a/drivers/acpi/toshiba_acpi.c b/drivers/acpi/toshiba_acpi.c
index 1165bad..4f682fb 100644
--- a/drivers/acpi/toshiba_acpi.c
+++ b/drivers/acpi/toshiba_acpi.c
@@ -28,6 +28,7 @@
* engineering the Windows drivers
* Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
* Rob Miller - TV out and hotkeys help
+ * Daniel Silverstone - Punting of hotkeys via acpi using a thread
*
* PLEASE NOTE
*
@@ -48,7 +49,7 @@
*
*/
-#define TOSHIBA_ACPI_VERSION "0.19-dev"
+#define TOSHIBA_ACPI_VERSION "0.19-dev-acpikeys"
#define PROC_INTERFACE_VERSION 1
#include <linux/kernel.h>
@@ -64,6 +65,7 @@
#include <linux/platform_device.h>
#include <linux/rfkill.h>
#include <linux/input-polldev.h>
+#include <linux/freezer.h>
#include <asm/uaccess.h>
@@ -398,6 +400,11 @@ static struct backlight_device *toshiba_backlight_device;
static int force_fan;
static int last_key_event;
static int key_event_valid;
+static int hotkeys_over_acpi = 1;
+static int hotkeys_check_per_sec = 2;
+
+module_param(hotkeys_over_acpi, uint, 0400);
+module_param(hotkeys_check_per_sec, uint, 0400);
typedef struct _ProcItem {
const char *name;
@@ -625,27 +632,34 @@ 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_acpi) {
+ 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_acpi: %d\n", hotkeys_over_acpi);
end:
return p;
@@ -905,6 +919,133 @@ static struct backlight_ops toshiba_backlight_data = {
.update_status = set_lcd_status,
};
+static struct semaphore thread_sem;
+static int thread_should_die;
+
+static struct acpi_device *threaded_device = 0;
+
+static void thread_deliver_button_event(u32 value)
+{
+ if (!threaded_device) return;
+ if( value == 0x0100 ) {
+ /* Ignore FN on its own */
+ } else if( value & 0x80 ) {
+ acpi_bus_generate_proc_event( threaded_device, 1, value & ~0x80 );
+ } else {
+ acpi_bus_generate_proc_event( threaded_device, 0, value );
+ }
+}
+
+static int toshiba_acpi_thread(void *data)
+{
+ int dropped = 0;
+ u32 hci_result, value;
+
+ daemonize("ktoshkeyd");
+ set_user_nice(current, 4);
+ thread_should_die = 0;
+
+ up(&thread_sem);
+
+ do {
+ /* In case we get stuck; we can rmmod the module here */
+ if (thread_should_die)
+ 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);
+
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / hotkeys_check_per_sec);
+
+ if (thread_should_die)
+ break;
+
+ if (try_to_freeze())
+ continue;
+
+ do {
+ hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
+ if (hci_result == HCI_SUCCESS) {
+ thread_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);
+ }
+ set_user_nice(current, -20); /* Become nasty so we are cleaned up
+ * before the module exits making us oops */
+ up(&thread_sem);
+ return 0;
+}
+
+static int acpi_toshkeys_add (struct acpi_device *device)
+{
+ threaded_device = device;
+ strcpy(acpi_device_name(device), "Toshiba laptop hotkeys");
+ strcpy(acpi_device_class(device), "hkey");
+ return 0;
+}
+
+static int acpi_toshkeys_remove (struct acpi_device *device, int type)
+{
+ if (threaded_device == device)
+ threaded_device = 0;
+ return 0;
+}
+
+static const struct acpi_device_id acpi_toshkeys_ids[] = {
+ { "TOS6200", 0 },
+ { "TOS6207", 0 },
+ { "TOS6208", 0 },
+ {"", 0}
+};
+
+static struct acpi_driver acpi_threaded_toshkeys = {
+ .name = "Toshiba laptop hotkeys driver",
+ .class = "hkey",
+ .ids = acpi_toshkeys_ids,
+ .ops = {
+ .add = acpi_toshkeys_add,
+ .remove = acpi_toshkeys_remove,
+ },
+};
+
+static int __init init_threaded_acpi(void)
+{
+ acpi_status result = AE_OK;
+ result = acpi_bus_register_driver(&acpi_threaded_toshkeys);
+ if( result < 0 )
+ printk(MY_ERR "Registration of toshkeys acpi device failed\n");
+ return result;
+}
+
+static void kill_threaded_acpi(void)
+{
+ acpi_bus_unregister_driver(&acpi_threaded_toshkeys);
+}
+
static void toshiba_acpi_exit(void)
{
if (toshiba_acpi.poll_dev) {
@@ -918,6 +1059,12 @@ static void toshiba_acpi_exit(void)
if (toshiba_backlight_device)
backlight_device_unregister(toshiba_backlight_device);
+ if (hotkeys_over_acpi) {
+ thread_should_die = 1;
+ down(&thread_sem);
+ kill_threaded_acpi();
+ }
+
remove_device();
if (toshiba_proc_dir)
@@ -1002,6 +1149,26 @@ static int __init toshiba_acpi_init(void)
}
toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
+ if (hotkeys_over_acpi && ACPI_SUCCESS(status)) {
+ printk(MY_INFO "Toshiba hotkeys are sent as ACPI events\n");
+ if (hotkeys_check_per_sec < 1)
+ hotkeys_check_per_sec = 1;
+ if (hotkeys_check_per_sec > 10)
+ hotkeys_check_per_sec = 10;
+ printk(MY_INFO "ktoshkeyd will check %d time%s per second\n",
+ hotkeys_check_per_sec, hotkeys_check_per_sec==1?"":"s");
+ if (init_threaded_acpi() >= 0) {
+ init_MUTEX_LOCKED(&thread_sem);
+ kernel_thread(toshiba_acpi_thread, NULL, CLONE_KERNEL);
+ down(&thread_sem);
+ } else {
+ remove_device();
+ remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
+ status = AE_ERROR;
+ printk(MY_INFO "ktoshkeyd initialisation failed. Refusing to load module\n");
+ }
+ }
+
/* Register rfkill switch for Bluetooth */
if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) {
toshiba_acpi.rfk_dev = rfkill_allocate(&toshiba_acpi.p_dev->dev,
--
1.6.1.2.419.g0d87e
More information about the kernel-team
mailing list