linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* toshiba_acpi.c: Full TOS1900 device support
@ 2010-10-26 19:14 Azael Avalos
  2010-10-26 19:20 ` Matthew Garrett
  0 siblings, 1 reply; 8+ messages in thread
From: Azael Avalos @ 2010-10-26 19:14 UTC (permalink / raw)
  To: platform-driver-x86; +Cc: Matthew Garrett, linux-kernel

Hi,

This is a long patch that adds full hotkey support to TOS1900 laptop devices,
converting toshiba_acpi to use sparse keymap in the process and also bumping
its version to 0.20.

Please review the code, and when all the needed changes are done and/or
corrections are made, I'll submit the code using all the appropriate procedures.


Saludos
Azael Avalos


--- toshiba_acpi.c	2010-10-26 12:54:00.868913701 -0600
+++ toshiba_new/toshiba_acpi.c	2010-10-26 12:50:04.164913701 -0600
@@ -35,7 +35,7 @@
  *
  */

-#define TOSHIBA_ACPI_VERSION	"0.19"
+#define TOSHIBA_ACPI_VERSION	"0.20"
 #define PROC_INTERFACE_VERSION	1

 #include <linux/kernel.h>
@@ -48,8 +48,11 @@
 #include <linux/platform_device.h>
 #include <linux/rfkill.h>
 #include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
 #include <linux/leds.h>
 #include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/workqueue.h>

 #include <asm/uaccess.h>

@@ -65,11 +68,23 @@
 #define MY_INFO KERN_INFO MY_LOGPREFIX

 /* Toshiba ACPI method paths */
-#define METHOD_LCD_BRIGHTNESS	"\\_SB_.PCI0.VGA_.LCD_._BCM"
 #define TOSH_INTERFACE_1	"\\_SB_.VALD"
 #define TOSH_INTERFACE_2	"\\_SB_.VALZ"
 #define METHOD_VIDEO_OUT	"\\_SB_.VALX.DSSX"
+#define TOSH_VIDEO_INTERFACE_1	"\\_SB.PCI0.PEGP.VGA.LCD"
+#define TOSH_VIDEO_INTERFACE_2	"\\_SB.PCI0.GFX0.LCD"
 #define GHCI_METHOD		".GHCI"
+#define SPFC_METHOD		".SPFC"
+#define EVENTS_METHOD		"INFO"
+#define EC_INTERFACE_1		"\\_SB.PCI0.LPC.EC0"
+#define EC_INTERFACE_2		"\\_SB.PCI0.LPC0.EC0"
+#define EC_INTERFACE_3		"\\_SB.PCI0.LPCB.EC0"
+#define NOTIFY_METHOD		"NTFY"
+
+#define TOSHIBA_FN_SCAN         0x6e	/* Fn key scancode */
+
+/* copied from drivers/input/serio/i8042-io.h */
+#define I8042_KBD_PHYS_DESC "isa0060/serio0"

 /* Toshiba HCI interface definitions
  *
@@ -108,6 +123,7 @@
 #define HCI_VIDEO_OUT_LCD		0x1
 #define HCI_VIDEO_OUT_CRT		0x2
 #define HCI_VIDEO_OUT_TV		0x4
+#define HCI_VIDEO_OUT_DVI		0x8
 #define HCI_WIRELESS_KILL_SWITCH	0x01
 #define HCI_WIRELESS_BT_PRESENT		0x0f
 #define HCI_WIRELESS_BT_ATTACH		0x40
@@ -121,36 +137,45 @@
 };
 MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);

-struct key_entry {
-	char type;
-	u16 code;
-	u16 keycode;
+static struct key_entry toshiba_acpi_keymap[]  = {
+	{KE_KEY, 0x100, { KEY_FN } },
+	{KE_KEY, 0x101, { KEY_MUTE } },
+	{KE_KEY, 0x102, { KEY_ZOOMOUT } },
+	{KE_KEY, 0x103, { KEY_ZOOMIN } },
+	{KE_KEY, 0x139, { KEY_ZOOMRESET } },
+	{KE_KEY, 0x13b, { KEY_COFFEE } },
+	{KE_KEY, 0x13c, { KEY_BATTERY } },
+	{KE_KEY, 0x13d, { KEY_SLEEP } },
+	{KE_KEY, 0x13e, { KEY_SUSPEND } },
+	{KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } },
+	{KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
+	{KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
+	{KE_KEY, 0x142, { KEY_WLAN } },
+	{KE_KEY, 0x143, { KEY_PROG1 } },
+	{KE_KEY, 0x17f, { KEY_FN } },
+	{KE_KEY, 0xb05, { KEY_PROG2 } },
+	{KE_KEY, 0xb06, { KEY_WWW } },
+	{KE_KEY, 0xb07, { KEY_MAIL } },
+	{KE_KEY, 0xb30, { KEY_STOP } },
+	{KE_KEY, 0xb31, { KEY_PREVIOUSSONG } },
+	{KE_KEY, 0xb32, { KEY_NEXTSONG } },
+	{KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
+	{KE_KEY, 0xb5a, { KEY_MEDIA } },
+	{KE_END, 0},
 };

-enum {KE_KEY, KE_END};
-
-static struct key_entry toshiba_acpi_keymap[]  = {
-	{KE_KEY, 0x101, KEY_MUTE},
-	{KE_KEY, 0x102, KEY_ZOOMOUT},
-	{KE_KEY, 0x103, KEY_ZOOMIN},
-	{KE_KEY, 0x13b, KEY_COFFEE},
-	{KE_KEY, 0x13c, KEY_BATTERY},
-	{KE_KEY, 0x13d, KEY_SLEEP},
-	{KE_KEY, 0x13e, KEY_SUSPEND},
-	{KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE},
-	{KE_KEY, 0x140, KEY_BRIGHTNESSDOWN},
-	{KE_KEY, 0x141, KEY_BRIGHTNESSUP},
-	{KE_KEY, 0x142, KEY_WLAN},
-	{KE_KEY, 0x143, KEY_PROG1},
-	{KE_KEY, 0xb05, KEY_PROG2},
-	{KE_KEY, 0xb06, KEY_WWW},
-	{KE_KEY, 0xb07, KEY_MAIL},
-	{KE_KEY, 0xb30, KEY_STOP},
-	{KE_KEY, 0xb31, KEY_PREVIOUSSONG},
-	{KE_KEY, 0xb32, KEY_NEXTSONG},
-	{KE_KEY, 0xb33, KEY_PLAYPAUSE},
-	{KE_KEY, 0xb5a, KEY_MEDIA},
-	{KE_END, 0, 0},
+static const struct input_device_id toshiba_acpi_ids[] = {
+	{
+		.bustype = BUS_I8042,
+		.vendor = 0x0001,
+		.product = 0x1,
+		.evbit = { BIT(EV_KEY) },
+		.flags = INPUT_DEVICE_ID_MATCH_BUS |
+			 INPUT_DEVICE_ID_MATCH_VENDOR |
+			 INPUT_DEVICE_ID_MATCH_PRODUCT |
+			 INPUT_DEVICE_ID_MATCH_EVBIT,
+	},
+	{ },
 };

 /* utility
@@ -163,7 +188,6 @@

 /* acpi interface wrappers
  */
-
 static int is_valid_acpi_path(const char *methodName)
 {
 	acpi_handle handle;
@@ -173,6 +197,45 @@
 	return !ACPI_FAILURE(status);
 }

+static acpi_status execute_acpi_method(acpi_handle handle, char *method,
+				       const int *param, int *result)
+{
+	struct acpi_object_list args_list;
+	struct acpi_buffer buff;
+	union acpi_object in_obj, out_obj;
+	acpi_status status;
+
+	if (param) {
+		args_list.count = 1;
+		args_list.pointer = &in_obj;
+		in_obj.type = ACPI_TYPE_INTEGER;
+		in_obj.integer.value = *param;
+	} else
+		args_list.count = 0;
+
+	buff.length = sizeof(out_obj);
+	buff.pointer = &out_obj;
+
+	status = acpi_evaluate_object(handle, method, &args_list, &buff);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_ERR
+		       "ACPI method (%s) execution failed\n", method);
+		return -EINVAL;
+	}
+
+	if (!result)
+		return status;
+
+	if (out_obj.type != ACPI_TYPE_INTEGER) {
+		printk(MY_ERR
+		       "ACPI method result is not a number\n");
+		return -EINVAL;
+	}
+
+	*result = out_obj.integer.value;
+	return status;
+}
+
 static int write_acpi_int(const char *methodName, int val)
 {
 	struct acpi_object_list params;
@@ -289,8 +352,12 @@
 	struct platform_device *p_dev;
 	struct rfkill *bt_rfk;
 	struct input_dev *hotkey_dev;
+	struct work_struct hotkey_work;
 	int illumination_installed;
 	acpi_handle handle;
+	acpi_handle ec_handle;
+	acpi_handle video_handle;
+	bool spfc;

 	const char *bt_name;

@@ -366,7 +433,7 @@
 	acpi_status status;
 	enum led_brightness result;

-	/*Â First request : initialize communication. */
+	/* First request : initialize communication. */
 	in[0] = 0xf100;
 	status = hci_raw(in, out);
 	if (ACPI_FAILURE(status)) {
@@ -504,14 +571,23 @@

 static int get_lcd(struct backlight_device *bd)
 {
+	acpi_status status;
+	acpi_integer val;
 	u32 hci_result;
 	u32 value;

-	hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);
-	if (hci_result == HCI_SUCCESS) {
-		return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
-	} else
-		return -EFAULT;
+	if (toshiba_acpi.spfc && toshiba_acpi.video_handle) {
+		status = acpi_evaluate_integer(toshiba_acpi.video_handle,
+					       "_BQC", NULL, &val);
+		if (ACPI_SUCCESS(status))
+			return val;
+	} else {
+		hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);
+		if (hci_result == HCI_SUCCESS)
+			return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
+	}
+
+	return -EFAULT;
 }

 static int lcd_proc_show(struct seq_file *m, void *v)
@@ -536,14 +612,29 @@

 static int set_lcd(int value)
 {
+	struct acpi_object_list param;
+	union acpi_object in_obj;
+	acpi_status status;
 	u32 hci_result;

-	value = value << HCI_LCD_BRIGHTNESS_SHIFT;
-	hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result);
-	if (hci_result != HCI_SUCCESS)
-		return -EFAULT;
+	if (toshiba_acpi.spfc && toshiba_acpi.video_handle) {
+		param.count = 1;
+		param.pointer = &in_obj;
+		in_obj.type = ACPI_TYPE_INTEGER;
+		in_obj.integer.value = value;

-	return 0;
+		status = acpi_evaluate_integer(toshiba_acpi.video_handle,
+					       "_BCM", &param, NULL);
+		if (ACPI_FAILURE(status))
+			return -EFAULT;
+	} else {
+		value = value << HCI_LCD_BRIGHTNESS_SHIFT;
+		hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result);
+		if (hci_result != HCI_SUCCESS)
+			return -EFAULT;
+	}
+
+	return 0;
 }

 static int set_lcd_status(struct backlight_device *bd)
@@ -852,89 +943,147 @@
         .update_status  = set_lcd_status,
 };

-static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code)
+static void toshiba_input_event(struct input_handle *handle, unsigned int type,
+			     unsigned int code, int value)
 {
-	struct key_entry *key;
-
-	for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
-		if (code == key->code)
-			return key;
-
-	return NULL;
+	if (type == EV_MSC && code == MSC_SCAN && value == TOSHIBA_FN_SCAN)
+		schedule_work(&((struct toshiba_acpi_dev *)handle->private)->hotkey_work);
 }

-static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code)
+static int toshiba_input_connect(struct input_handler *handler,
+				 struct input_dev *dev,
+				 const struct input_device_id *id)
 {
-	struct key_entry *key;
+	struct input_handle *handle;
+	int error;

-	for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
-		if (code == key->keycode && key->type == KE_KEY)
-			return key;
+	if (!strcmp(dev->phys, I8042_KBD_PHYS_DESC))
+		return -ENODEV;

-	return NULL;
+	if (dev->grab)
+		printk(MY_ERR
+		       "Input device id grabbed by %s, Fn hotkeys will not work\n",
+		       dev->grab->name);
+
+	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+	if (!handle)
+		return -ENOMEM;
+
+	handle->dev = dev;
+	handle->handler = handler;
+	handle->name = "toshiba_acpi";
+	handle->private = handler->private;
+
+	error = input_register_handle(handle);
+	if (error) {
+		printk(MY_ERR
+		       "Failed to register input handle\n");
+		goto free_handle;
+	}
+
+	error = input_open_device(handle);
+	if (error) {
+		printk(MY_ERR
+		       "Failed to open input device handle\n");
+		goto unregister_handle;
+	}
+
+	return 0;
+
+unregister_handle:
+	input_unregister_handle(handle);
+free_handle:
+	kfree(handle);
+
+	return error;
 }

-static int toshiba_acpi_getkeycode(struct input_dev *dev,
-				   unsigned int scancode, unsigned int *keycode)
+static void toshiba_input_disconnect(struct input_handle * handle)
 {
-	struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode);
+	input_close_device(handle);
+	input_unregister_handle(handle);
+	kfree(handle);
+}

-	if (key && key->type == KE_KEY) {
-		*keycode = key->keycode;
-		return 0;
+static struct input_handler toshiba_input_handler = {
+	.name = "toshiba_acpi",
+	.event = toshiba_input_event,
+	.connect = toshiba_input_connect,
+	.disconnect = toshiba_input_disconnect,
+	.id_table = toshiba_acpi_ids,
+};
+
+static acpi_status get_spfc_events(int *hotkey)
+{
+	acpi_status status;
+
+	/* Some models (Satellite X205, A135, NB600, among others) need
+	 * to call the NTFY method first in order to get the hotkey events.
+	 * But not all (TOS1900) models implement this.
+	 */
+	if (toshiba_acpi.ec_handle != NULL) {
+		status = execute_acpi_method(toshiba_acpi.ec_handle,
+			  NOTIFY_METHOD, NULL, NULL);
+		if (ACPI_FAILURE(status)) {
+			printk(MY_ERR "ACPI method (%s) execution failed\n",
+				  NOTIFY_METHOD);
+			return -EIO;
+		}
 	}

-	return -EINVAL;
+	/* Now we can poll the INFO method to get last pressed hotkey */
+	status = execute_acpi_method(toshiba_acpi.handle,
+			  EVENTS_METHOD, NULL, hotkey);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_ERR "ACPI method (%s) execution failed\n",
+			  EVENTS_METHOD);
+		return -EIO;
+	}
+
+	return status;
 }

-static int toshiba_acpi_setkeycode(struct input_dev *dev,
-				   unsigned int scancode, unsigned int keycode)
+static void handle_spfc_hotkeys(struct work_struct *work)
 {
-	struct key_entry *key;
-	unsigned int old_keycode;
+	acpi_status status;
+	struct input_dev *input_dev;
+	int hotkey;

-	key = toshiba_acpi_get_entry_by_scancode(scancode);
-	if (key && key->type == KE_KEY) {
-		old_keycode = key->keycode;
-		key->keycode = keycode;
-		set_bit(keycode, dev->keybit);
-		if (!toshiba_acpi_get_entry_by_keycode(old_keycode))
-			clear_bit(old_keycode, dev->keybit);
-		return 0;
+	status = get_spfc_events(&hotkey);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_ERR "Failed to get hotkey event\n");
+		return;
 	}

-	return -EINVAL;
+	/* We only care about a key press */
+	if ((hotkey == 0x100) || (hotkey & 0x80))
+		return;
+
+	input_dev = container_of(work, struct toshiba_acpi_dev,
+				 hotkey_work)->hotkey_dev;
+	if (!sparse_keymap_report_event(input_dev,
+				  hotkey, 1, true))
+		printk(MY_INFO "Unknown key %x\n", hotkey);
 }

 static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
 {
 	u32 hci_result, value;
-	struct key_entry *key;

 	if (event != 0x80)
 		return;
 	do {
 		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
 		if (hci_result == HCI_SUCCESS) {
-			if (value == 0x100)
-				continue;
 			/* act on key press; ignore key release */
-			if (value & 0x80)
+			if ((value == 0x100) || (value & 0x80))
 				continue;

-			key = toshiba_acpi_get_entry_by_scancode
-				(value);
-			if (!key) {
+			if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev,
+					     value, 1, true)) {
 				printk(MY_INFO "Unknown key %x\n",
 				       value);
-				continue;
 			}
-			input_report_key(toshiba_acpi.hotkey_dev,
-					 key->keycode, 1);
-			input_sync(toshiba_acpi.hotkey_dev);
-			input_report_key(toshiba_acpi.hotkey_dev,
-					 key->keycode, 0);
-			input_sync(toshiba_acpi.hotkey_dev);
 		} else if (hci_result == HCI_NOT_SUPPORTED) {
 			/* This is a workaround for an unresolved issue on
 			 * some machines where system events sporadically
@@ -943,33 +1092,67 @@
 			printk(MY_NOTICE "Re-enabled hotkeys\n");
 		}
 	} while (hci_result != HCI_EMPTY);
+	
 }

-static int toshiba_acpi_setup_keyboard(char *device)
+static int set_ec_handle(void)
 {
 	acpi_status status;
-	acpi_handle handle;
-	int result;
-	const struct key_entry *key;
+	char *ec_device;

-	status = acpi_get_handle(NULL, device, &handle);
+	if (is_valid_acpi_path(EC_INTERFACE_1 "." NOTIFY_METHOD))
+		ec_device = EC_INTERFACE_1;
+	else if (is_valid_acpi_path(EC_INTERFACE_2 "." NOTIFY_METHOD))
+		ec_device = EC_INTERFACE_2;
+	else if (is_valid_acpi_path(EC_INTERFACE_3 "." NOTIFY_METHOD))
+		ec_device = EC_INTERFACE_3;
+	else {
+		printk(MY_NOTICE "No known EC interface found.\n");
+		return -ENODEV;
+	}
+
+	status = acpi_get_handle(NULL, ec_device, &toshiba_acpi.ec_handle);
 	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Unable to get notification device\n");
+		printk(MY_INFO "Unable to get EC device handle\n");
 		return -ENODEV;
 	}
+	printk(MY_INFO "    EC device handle: %s\n", ec_device);

-	toshiba_acpi.handle = handle;
+	return 0;
+}

-	status = acpi_evaluate_object(handle, "ENAB", NULL, NULL);
+static int set_spfc_video(void)
+{
+	acpi_status status;
+	char *video_device;
+
+	if (is_valid_acpi_path(TOSH_VIDEO_INTERFACE_1))
+		video_device = TOSH_VIDEO_INTERFACE_1;
+	else if (is_valid_acpi_path(TOSH_VIDEO_INTERFACE_2))
+		video_device = TOSH_VIDEO_INTERFACE_2;
+	else {
+		printk(MY_NOTICE "No known SPFC video interface found.\n");
+		return -ENODEV;
+	}
+
+	status = acpi_get_handle(NULL, video_device, &toshiba_acpi.video_handle);
 	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Unable to enable hotkeys\n");
+		printk(MY_INFO "Unable to get SPFC video device handle\n");
 		return -ENODEV;
 	}
+	printk(MY_INFO "    Video device handle: %s\n", video_device);

-	status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY,
-					      toshiba_acpi_notify, NULL);
+	return 0;
+}
+
+static int __init toshiba_acpi_setup_keyboard(char *device)
+{
+	acpi_status status;
+	int error;
+
+	status = acpi_get_handle(NULL, device, &toshiba_acpi.handle);
 	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Unable to install hotkey notification\n");
+		printk(MY_INFO "Unable to get notification device\n");
 		return -ENODEV;
 	}

@@ -982,27 +1165,78 @@
 	toshiba_acpi.hotkey_dev->name = "Toshiba input device";
 	toshiba_acpi.hotkey_dev->phys = device;
 	toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
-	toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode;
-	toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode;
+	toshiba_acpi.hotkey_dev->id.vendor = PCI_VENDOR_ID_TOSHIBA;

-	for (key = toshiba_acpi_keymap; key->type != KE_END; key++) {
-		set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit);
-		set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit);
-	}
+	error = sparse_keymap_setup(toshiba_acpi.hotkey_dev,
+			  toshiba_acpi_keymap, NULL);
+	if (error)
+		goto err_free_dev;

-	result = input_register_device(toshiba_acpi.hotkey_dev);
-	if (result) {
-		printk(MY_INFO "Unable to register input device\n");
-		return result;
+	if (toshiba_acpi.spfc) {
+		status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL);
+		if (ACPI_FAILURE(status)) {
+			printk(MY_INFO "Unable to enable hotkeys\n");
+			error = -ENODEV;
+			goto err_free_keymap;
+		}
+
+		error = input_register_device(toshiba_acpi.hotkey_dev);
+		if (error) {
+			printk(MY_ERR "Unable to register input device\n");
+			goto err_free_dev;
+		}
+	
+		INIT_WORK(&toshiba_acpi.hotkey_work, handle_spfc_hotkeys);
+
+		toshiba_input_handler.private = &toshiba_acpi;
+
+		error = input_register_handler(&toshiba_input_handler);
+		if (error) {
+			printk(MY_ERR "Failed to register input device handler\n");
+			goto err_free_keymap;
+		}
+	} else {
+		status = acpi_install_notify_handler(toshiba_acpi.handle,
+				  ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL);
+		if (ACPI_FAILURE(status)) {
+			printk(MY_INFO "Unable to install hotkey notification\n");
+			error = -ENODEV;
+			goto err_free_keymap;
+		}
+
+		error = input_register_device(toshiba_acpi.hotkey_dev);
+		if (error) {
+			printk(MY_INFO "Unable to register input device\n");
+			goto err_remove_notify;
+		}
 	}

 	return 0;
+
+err_remove_notify:
+	acpi_remove_notify_handler(toshiba_acpi.handle,
+		  ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
+err_free_keymap:
+	sparse_keymap_free(toshiba_acpi.hotkey_dev);
+err_free_dev:
+	input_free_device(toshiba_acpi.hotkey_dev);
+	toshiba_acpi.hotkey_dev = NULL;
+	return error;
 }

 static void toshiba_acpi_exit(void)
 {
-	if (toshiba_acpi.hotkey_dev)
+	if (toshiba_acpi.hotkey_dev) {
+		if (toshiba_acpi.spfc) {
+			flush_scheduled_work();
+			input_unregister_handler(&toshiba_input_handler);
+		} else {
+			acpi_remove_notify_handler(toshiba_acpi.handle,
+				  ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
+		}
+		sparse_keymap_free(toshiba_acpi.hotkey_dev);
 		input_unregister_device(toshiba_acpi.hotkey_dev);
+	}

 	if (toshiba_acpi.bt_rfk) {
 		rfkill_unregister(toshiba_acpi.bt_rfk);
@@ -1017,13 +1251,11 @@
 	if (toshiba_proc_dir)
 		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);

-	acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY,
-				   toshiba_acpi_notify);
-
 	if (toshiba_acpi.illumination_installed)
 		led_classdev_unregister(&toshiba_led);

-	platform_device_unregister(toshiba_acpi.p_dev);
+	if (toshiba_acpi.p_dev)
+		platform_device_unregister(toshiba_acpi.p_dev);

 	return;
 }
@@ -1041,10 +1273,17 @@
 	/* simple device detection: look for HCI method */
 	if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) {
 		method_hci = TOSH_INTERFACE_1 GHCI_METHOD;
+		toshiba_acpi.spfc = false;
 		if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1))
 			printk(MY_INFO "Unable to activate hotkeys\n");
 	} else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) {
 		method_hci = TOSH_INTERFACE_2 GHCI_METHOD;
+		toshiba_acpi.spfc = false;
+		if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2))
+			printk(MY_INFO "Unable to activate hotkeys\n");
+	} else if (is_valid_acpi_path(TOSH_INTERFACE_2 SPFC_METHOD)) {
+		method_hci = TOSH_INTERFACE_2 SPFC_METHOD;
+		toshiba_acpi.spfc = true;
 		if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2))
 			printk(MY_INFO "Unable to activate hotkeys\n");
 	} else
@@ -1072,6 +1311,26 @@
 	/* enable event fifo */
 	hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);

+	/* TOS1900 models hotkey activation */
+	if (toshiba_acpi.spfc) {
+		/* Some TOS1900 models don't implement the ENAB method,
+		 * and hotkeys activation goes by calling HCI_HOTKEY_EVENT
+		 */
+		hci_write1(HCI_HOTKEY_EVENT, 1, &hci_result);
+		/* The EC handle must be set for models without
+		 * the ENAB method so they can access the NOTIFY method,
+		 * and thus, access the INFO method...
+		 */
+		if (set_ec_handle())
+			toshiba_acpi.ec_handle = NULL;
+		/* Now we set the video handle for known TOS1900 devices */
+		if (set_spfc_video())
+			toshiba_acpi.video_handle = NULL;
+	} else {
+		toshiba_acpi.ec_handle = NULL;
+		toshiba_acpi.video_handle = NULL;
+	}
+
 	toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
 	if (!toshiba_proc_dir) {
 		toshiba_acpi_exit();
@@ -1082,11 +1341,11 @@

 	props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
 	toshiba_backlight_device = backlight_device_register("toshiba",
-							     &toshiba_acpi.p_dev->dev,
-							     NULL,
-							     &toshiba_backlight_data,
-							     &props);
-        if (IS_ERR(toshiba_backlight_device)) {
+						     &toshiba_acpi.p_dev->dev,
+						     NULL,
+						     &toshiba_backlight_data,
+						     &props);
+	if (IS_ERR(toshiba_backlight_device)) {
 		ret = PTR_ERR(toshiba_backlight_device);

 		printk(KERN_ERR "Could not register toshiba backlight device\n");


-- 
-- El mundo apesta y vosotros apestais tambien --

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

* Re: toshiba_acpi.c: Full TOS1900 device support
  2010-10-26 19:14 toshiba_acpi.c: Full TOS1900 device support Azael Avalos
@ 2010-10-26 19:20 ` Matthew Garrett
  2010-10-27 19:24   ` Azael Avalos
  0 siblings, 1 reply; 8+ messages in thread
From: Matthew Garrett @ 2010-10-26 19:20 UTC (permalink / raw)
  To: Azael Avalos; +Cc: platform-driver-x86, linux-kernel

On Tue, Oct 26, 2010 at 01:14:25PM -0600, Azael Avalos wrote:
> 
> -	hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);
> -	if (hci_result == HCI_SUCCESS) {
> -		return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
> -	} else
> -		return -EFAULT;
> +	if (toshiba_acpi.spfc && toshiba_acpi.video_handle) {
> +		status = acpi_evaluate_integer(toshiba_acpi.video_handle,
> +					       "_BQC", NULL, &val);
> +		if (ACPI_SUCCESS(status))
> +			return val;

This doesn't look right. Shouldn't _BQC indicate that it's an ACPI spec 
video device, and thus driven by the ACPI video driver?

> +static void toshiba_input_event(struct input_handle *handle, unsigned int type,
> +			     unsigned int code, int value)
>  {
> -	struct key_entry *key;
> -
> -	for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
> -		if (code == key->code)
> -			return key;
> -
> -	return NULL;
> +	if (type == EV_MSC && code == MSC_SCAN && value == TOSHIBA_FN_SCAN)
> +		schedule_work(&((struct toshiba_acpi_dev *)handle->private)->hotkey_work);
>  }

Ugh. Use i8042_install_filter instead, and just look at the raw i8042 
stream. That'll avoid the ugly strncmp for the device names in order to 
find the right device.

Otherwise things seem reasonably good - it's a fair rewrite, so I'll 
want to read over things a bit more.

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

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

* Re: toshiba_acpi.c: Full TOS1900 device support
  2010-10-26 19:20 ` Matthew Garrett
@ 2010-10-27 19:24   ` Azael Avalos
  2010-10-27 19:29     ` Matthew Garrett
  0 siblings, 1 reply; 8+ messages in thread
From: Azael Avalos @ 2010-10-27 19:24 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: platform-driver-x86, linux-kernel

On Tue, Oct 26, 2010 at 1:20 PM, Matthew Garrett <mjg59@srcf.ucam.org> wrote:
> On Tue, Oct 26, 2010 at 01:14:25PM -0600, Azael Avalos wrote:
>>
>> -     hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);
>> -     if (hci_result == HCI_SUCCESS) {
>> -             return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
>> -     } else
>> -             return -EFAULT;
>> +     if (toshiba_acpi.spfc && toshiba_acpi.video_handle) {
>> +             status = acpi_evaluate_integer(toshiba_acpi.video_handle,
>> +                                            "_BQC", NULL, &val);
>> +             if (ACPI_SUCCESS(status))
>> +                     return val;
>
> This doesn't look right. Shouldn't _BQC indicate that it's an ACPI spec
> video device, and thus driven by the ACPI video driver?
>

Yes, actually the ACPI video driver does a better job of detecting the
video device,
so perhaps something like the following would be better IMHO for at least the
backlight:


	if (toshiba_acpi.spfc || !acpi_video_backlight_support()) {
		props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
		toshiba_backlight_device = backlight_device_register("toshiba",
						    &toshiba_acpi.p_dev->dev,
						    NULL,
						    &toshiba_backlight_data,
						    &props);
		if (IS_ERR(toshiba_backlight_device)) {
			ret = PTR_ERR(toshiba_backlight_device);

			printk(KERN_ERR
			       "Could not register toshiba backlight device\n");
			toshiba_backlight_device = NULL;
			toshiba_acpi_exit();
			return ret;
		}
	}


Is there an ACPI function that let's us query the display brightness?
Because TOS1900 devices lack the HCI method to query the LCD status,
and thus the proc interface will be useless, that is, if a userland (or any
other) application is using it.



>> +static void toshiba_input_event(struct input_handle *handle, unsigned int type,
>> +                          unsigned int code, int value)
>>  {
>> -     struct key_entry *key;
>> -
>> -     for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
>> -             if (code == key->code)
>> -                     return key;
>> -
>> -     return NULL;
>> +     if (type == EV_MSC && code == MSC_SCAN && value == TOSHIBA_FN_SCAN)
>> +             schedule_work(&((struct toshiba_acpi_dev *)handle->private)->hotkey_work);
>>  }
>
> Ugh. Use i8042_install_filter instead, and just look at the raw i8042
> stream. That'll avoid the ugly strncmp for the device names in order to
> find the right device.
>

Can you point me to some documents (if any) or even pieces of code
that I can look at.

I've tried using the i8042_install_filter function, but I haven't had
any luck so far,
or perhaps I'm using it in wrong, due to my limited kernel knowledge.


Saludos
Azael


-- 
-- El mundo apesta y vosotros apestais tambien --

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

* Re: toshiba_acpi.c: Full TOS1900 device support
  2010-10-27 19:24   ` Azael Avalos
@ 2010-10-27 19:29     ` Matthew Garrett
  2010-11-12  0:10       ` Azael Avalos
  0 siblings, 1 reply; 8+ messages in thread
From: Matthew Garrett @ 2010-10-27 19:29 UTC (permalink / raw)
  To: Azael Avalos; +Cc: platform-driver-x86, linux-kernel

On Wed, Oct 27, 2010 at 01:24:15PM -0600, Azael Avalos wrote:
> Yes, actually the ACPI video driver does a better job of detecting the
> video device,
> so perhaps something like the following would be better IMHO for at least the
> backlight:

Yeah, that looks better.

> Is there an ACPI function that let's us query the display brightness?
> Because TOS1900 devices lack the HCI method to query the LCD status,
> and thus the proc interface will be useless, that is, if a userland (or any
> other) application is using it.

Ignore the proc interface. Everyone should be using sysfs now. _BQC is 
the standardised ACPI function for this, but it's not always 
implemented.

> > Ugh. Use i8042_install_filter instead, and just look at the raw i8042
> > stream. That'll avoid the ugly strncmp for the device names in order to
> > find the right device.
> >
> 
> Can you point me to some documents (if any) or even pieces of code
> that I can look at.
> 
> I've tried using the i8042_install_filter function, but I haven't had
> any luck so far,
> or perhaps I'm using it in wrong, due to my limited kernel knowledge.

Take a look at the dell-laptop driver in drivers/platform/x86.

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

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

* Re: toshiba_acpi.c: Full TOS1900 device support
  2010-10-27 19:29     ` Matthew Garrett
@ 2010-11-12  0:10       ` Azael Avalos
  2010-11-24 19:17         ` Matthew Garrett
  0 siblings, 1 reply; 8+ messages in thread
From: Azael Avalos @ 2010-11-12  0:10 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: platform-driver-x86, linux-kernel

Ok, take two, here goes the patch again for review, took me a bit to
figure it out what to do with the raw data from i8042_install_filter, but
here it is:


--- toshiba_acpi.c	2010-10-26 12:54:00.868913701 -0600
+++ toshiba_new/toshiba_acpi.c	2010-11-11 17:09:55.652960623 -0700
@@ -35,7 +35,7 @@
  *
  */

-#define TOSHIBA_ACPI_VERSION	"0.19"
+#define TOSHIBA_ACPI_VERSION	"0.20"
 #define PROC_INTERFACE_VERSION	1

 #include <linux/kernel.h>
@@ -48,8 +48,12 @@
 #include <linux/platform_device.h>
 #include <linux/rfkill.h>
 #include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
 #include <linux/leds.h>
 #include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/workqueue.h>
+#include <linux/i8042.h>

 #include <asm/uaccess.h>

@@ -65,11 +69,18 @@
 #define MY_INFO KERN_INFO MY_LOGPREFIX

 /* Toshiba ACPI method paths */
-#define METHOD_LCD_BRIGHTNESS	"\\_SB_.PCI0.VGA_.LCD_._BCM"
 #define TOSH_INTERFACE_1	"\\_SB_.VALD"
 #define TOSH_INTERFACE_2	"\\_SB_.VALZ"
 #define METHOD_VIDEO_OUT	"\\_SB_.VALX.DSSX"
 #define GHCI_METHOD		".GHCI"
+#define SPFC_METHOD		".SPFC"
+#define EVENTS_METHOD		"INFO"
+#define EC_INTERFACE_1		"\\_SB.PCI0.LPC.EC0"
+#define EC_INTERFACE_2		"\\_SB.PCI0.LPC0.EC0"
+#define EC_INTERFACE_3		"\\_SB.PCI0.LPCB.EC0"
+#define NOTIFY_METHOD		"NTFY"
+
+#define TOSHIBA_FN_SCAN         0x6e	/* Fn key scancode */

 /* Toshiba HCI interface definitions
  *
@@ -108,6 +119,7 @@
 #define HCI_VIDEO_OUT_LCD		0x1
 #define HCI_VIDEO_OUT_CRT		0x2
 #define HCI_VIDEO_OUT_TV		0x4
+#define HCI_VIDEO_OUT_DVI		0x8
 #define HCI_WIRELESS_KILL_SWITCH	0x01
 #define HCI_WIRELESS_BT_PRESENT		0x0f
 #define HCI_WIRELESS_BT_ATTACH		0x40
@@ -121,36 +133,31 @@
 };
 MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);

-struct key_entry {
-	char type;
-	u16 code;
-	u16 keycode;
-};
-
-enum {KE_KEY, KE_END};
-
 static struct key_entry toshiba_acpi_keymap[]  = {
-	{KE_KEY, 0x101, KEY_MUTE},
-	{KE_KEY, 0x102, KEY_ZOOMOUT},
-	{KE_KEY, 0x103, KEY_ZOOMIN},
-	{KE_KEY, 0x13b, KEY_COFFEE},
-	{KE_KEY, 0x13c, KEY_BATTERY},
-	{KE_KEY, 0x13d, KEY_SLEEP},
-	{KE_KEY, 0x13e, KEY_SUSPEND},
-	{KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE},
-	{KE_KEY, 0x140, KEY_BRIGHTNESSDOWN},
-	{KE_KEY, 0x141, KEY_BRIGHTNESSUP},
-	{KE_KEY, 0x142, KEY_WLAN},
-	{KE_KEY, 0x143, KEY_PROG1},
-	{KE_KEY, 0xb05, KEY_PROG2},
-	{KE_KEY, 0xb06, KEY_WWW},
-	{KE_KEY, 0xb07, KEY_MAIL},
-	{KE_KEY, 0xb30, KEY_STOP},
-	{KE_KEY, 0xb31, KEY_PREVIOUSSONG},
-	{KE_KEY, 0xb32, KEY_NEXTSONG},
-	{KE_KEY, 0xb33, KEY_PLAYPAUSE},
-	{KE_KEY, 0xb5a, KEY_MEDIA},
-	{KE_END, 0, 0},
+	{KE_KEY, 0x100, { KEY_FN } },
+	{KE_KEY, 0x101, { KEY_MUTE } },
+	{KE_KEY, 0x102, { KEY_ZOOMOUT } },
+	{KE_KEY, 0x103, { KEY_ZOOMIN } },
+	{KE_KEY, 0x139, { KEY_ZOOMRESET } },
+	{KE_KEY, 0x13b, { KEY_COFFEE } },
+	{KE_KEY, 0x13c, { KEY_BATTERY } },
+	{KE_KEY, 0x13d, { KEY_SLEEP } },
+	{KE_KEY, 0x13e, { KEY_SUSPEND } },
+	{KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } },
+	{KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
+	{KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
+	{KE_KEY, 0x142, { KEY_WLAN } },
+	{KE_KEY, 0x143, { KEY_PROG1 } },
+	{KE_KEY, 0x17f, { KEY_FN } },
+	{KE_KEY, 0xb05, { KEY_PROG2 } },
+	{KE_KEY, 0xb06, { KEY_WWW } },
+	{KE_KEY, 0xb07, { KEY_MAIL } },
+	{KE_KEY, 0xb30, { KEY_STOP } },
+	{KE_KEY, 0xb31, { KEY_PREVIOUSSONG } },
+	{KE_KEY, 0xb32, { KEY_NEXTSONG } },
+	{KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
+	{KE_KEY, 0xb5a, { KEY_MEDIA } },
+	{KE_END, 0},
 };

 /* utility
@@ -163,7 +170,6 @@

 /* acpi interface wrappers
  */
-
 static int is_valid_acpi_path(const char *methodName)
 {
 	acpi_handle handle;
@@ -173,6 +179,45 @@
 	return !ACPI_FAILURE(status);
 }

+static acpi_status execute_acpi_method(acpi_handle handle, char *method,
+				       const int *param, int *result)
+{
+	struct acpi_object_list args_list;
+	struct acpi_buffer buff;
+	union acpi_object in_obj, out_obj;
+	acpi_status status;
+
+	if (param) {
+		args_list.count = 1;
+		args_list.pointer = &in_obj;
+		in_obj.type = ACPI_TYPE_INTEGER;
+		in_obj.integer.value = *param;
+	} else
+		args_list.count = 0;
+
+	buff.length = sizeof(out_obj);
+	buff.pointer = &out_obj;
+
+	status = acpi_evaluate_object(handle, method, &args_list, &buff);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_ERR
+		       "ACPI method (%s) execution failed\n", method);
+		return -EINVAL;
+	}
+
+	if (!result)
+		return status;
+
+	if (out_obj.type != ACPI_TYPE_INTEGER) {
+		printk(MY_ERR
+		       "ACPI method result is not a number\n");
+		return -EINVAL;
+	}
+
+	*result = out_obj.integer.value;
+	return status;
+}
+
 static int write_acpi_int(const char *methodName, int val)
 {
 	struct acpi_object_list params;
@@ -289,8 +334,11 @@
 	struct platform_device *p_dev;
 	struct rfkill *bt_rfk;
 	struct input_dev *hotkey_dev;
+	struct work_struct hotkey_work;
 	int illumination_installed;
 	acpi_handle handle;
+	acpi_handle ec_handle;
+	bool spfc;

 	const char *bt_name;

@@ -366,7 +414,7 @@
 	acpi_status status;
 	enum led_brightness result;

-	/*Â First request : initialize communication. */
+	/* First request : initialize communication. */
 	in[0] = 0xf100;
 	status = hci_raw(in, out);
 	if (ACPI_FAILURE(status)) {
@@ -508,10 +556,10 @@
 	u32 value;

 	hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);
-	if (hci_result == HCI_SUCCESS) {
+	if (hci_result == HCI_SUCCESS)
 		return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
-	} else
-		return -EFAULT;
+
+	return -EFAULT;
 }

 static int lcd_proc_show(struct seq_file *m, void *v)
@@ -543,7 +591,7 @@
 	if (hci_result != HCI_SUCCESS)
 		return -EFAULT;

-	return 0;
+	return 0;
 }

 static int set_lcd_status(struct backlight_device *bd)
@@ -852,89 +900,88 @@
         .update_status  = set_lcd_status,
 };

-static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code)
+static bool toshiba_i8042_filter(unsigned char data, unsigned char str,
+			     struct serio *port)
 {
-	struct key_entry *key;
+	if (str & 0x20)
+		return false;

-	for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
-		if (code == key->code)
-			return key;
+	if (unlikely(data == 0xe0))
+		return false;
+	else if ((data & 0x7f) == TOSHIBA_FN_SCAN)
+		schedule_work(&toshiba_acpi.hotkey_work);

-	return NULL;
+	return false;
 }

-static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code)
+static acpi_status get_spfc_events(int *hotkey)
 {
-	struct key_entry *key;
-
-	for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
-		if (code == key->keycode && key->type == KE_KEY)
-			return key;
-
-	return NULL;
-}
+	acpi_status status;

-static int toshiba_acpi_getkeycode(struct input_dev *dev,
-				   unsigned int scancode, unsigned int *keycode)
-{
-	struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode);
+	/* Some models (Satellite X205, A135, NB600, among others) need
+	 * to call the NTFY method first in order to get the hotkey events.
+	 * But not all (TOS1900) models implement this.
+	 */
+	if (toshiba_acpi.ec_handle != NULL) {
+		status = execute_acpi_method(toshiba_acpi.ec_handle,
+			  NOTIFY_METHOD, NULL, NULL);
+		if (ACPI_FAILURE(status)) {
+			printk(MY_ERR "ACPI method (%s) execution failed\n",
+				  NOTIFY_METHOD);
+			return -EIO;
+		}
+	}

-	if (key && key->type == KE_KEY) {
-		*keycode = key->keycode;
-		return 0;
+	/* Now we can poll the INFO method to get last pressed hotkey */
+	status = execute_acpi_method(toshiba_acpi.handle,
+			  EVENTS_METHOD, NULL, hotkey);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_ERR "ACPI method (%s) execution failed\n",
+			  EVENTS_METHOD);
+		return -EIO;
 	}

-	return -EINVAL;
+	return status;
 }

-static int toshiba_acpi_setkeycode(struct input_dev *dev,
-				   unsigned int scancode, unsigned int keycode)
+static void handle_spfc_hotkeys(struct work_struct *work)
 {
-	struct key_entry *key;
-	unsigned int old_keycode;
+	acpi_status status;
+	int hotkey;

-	key = toshiba_acpi_get_entry_by_scancode(scancode);
-	if (key && key->type == KE_KEY) {
-		old_keycode = key->keycode;
-		key->keycode = keycode;
-		set_bit(keycode, dev->keybit);
-		if (!toshiba_acpi_get_entry_by_keycode(old_keycode))
-			clear_bit(old_keycode, dev->keybit);
-		return 0;
+	status = get_spfc_events(&hotkey);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_ERR "Failed to get hotkey event\n");
+		return;
 	}

-	return -EINVAL;
+	/* We only care about a key press */
+	if ((hotkey == 0x100) || (hotkey & 0x80))
+		return;
+
+	if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev,
+				  hotkey, 1, true))
+		printk(MY_INFO "Unknown key %x\n", hotkey);
 }

 static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
 {
 	u32 hci_result, value;
-	struct key_entry *key;

 	if (event != 0x80)
 		return;
 	do {
 		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
 		if (hci_result == HCI_SUCCESS) {
-			if (value == 0x100)
-				continue;
 			/* act on key press; ignore key release */
-			if (value & 0x80)
+			if ((value == 0x100) || (value & 0x80))
 				continue;

-			key = toshiba_acpi_get_entry_by_scancode
-				(value);
-			if (!key) {
+			if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev,
+					     value, 1, true)) {
 				printk(MY_INFO "Unknown key %x\n",
 				       value);
-				continue;
 			}
-			input_report_key(toshiba_acpi.hotkey_dev,
-					 key->keycode, 1);
-			input_sync(toshiba_acpi.hotkey_dev);
-			input_report_key(toshiba_acpi.hotkey_dev,
-					 key->keycode, 0);
-			input_sync(toshiba_acpi.hotkey_dev);
 		} else if (hci_result == HCI_NOT_SUPPORTED) {
 			/* This is a workaround for an unresolved issue on
 			 * some machines where system events sporadically
@@ -943,33 +990,43 @@
 			printk(MY_NOTICE "Re-enabled hotkeys\n");
 		}
 	} while (hci_result != HCI_EMPTY);
+	
 }

-static int toshiba_acpi_setup_keyboard(char *device)
+static int set_ec_handle(void)
 {
 	acpi_status status;
-	acpi_handle handle;
-	int result;
-	const struct key_entry *key;
+	char *ec_device;

-	status = acpi_get_handle(NULL, device, &handle);
-	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Unable to get notification device\n");
+	if (is_valid_acpi_path(EC_INTERFACE_1 "." NOTIFY_METHOD))
+		ec_device = EC_INTERFACE_1;
+	else if (is_valid_acpi_path(EC_INTERFACE_2 "." NOTIFY_METHOD))
+		ec_device = EC_INTERFACE_2;
+	else if (is_valid_acpi_path(EC_INTERFACE_3 "." NOTIFY_METHOD))
+		ec_device = EC_INTERFACE_3;
+	else {
+		printk(MY_NOTICE "No known EC interface found.\n");
 		return -ENODEV;
 	}

-	toshiba_acpi.handle = handle;
-
-	status = acpi_evaluate_object(handle, "ENAB", NULL, NULL);
+	status = acpi_get_handle(NULL, ec_device, &toshiba_acpi.ec_handle);
 	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Unable to enable hotkeys\n");
+		printk(MY_INFO "Unable to get EC device handle\n");
 		return -ENODEV;
 	}
+	printk(MY_INFO "    EC device handle: %s\n", ec_device);

-	status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY,
-					      toshiba_acpi_notify, NULL);
+	return 0;
+}
+
+static int __init toshiba_acpi_setup_keyboard(char *device)
+{
+	acpi_status status;
+	int error;
+
+	status = acpi_get_handle(NULL, device, &toshiba_acpi.handle);
 	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Unable to install hotkey notification\n");
+		printk(MY_INFO "Unable to get notification device\n");
 		return -ENODEV;
 	}

@@ -982,27 +1039,96 @@
 	toshiba_acpi.hotkey_dev->name = "Toshiba input device";
 	toshiba_acpi.hotkey_dev->phys = device;
 	toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
-	toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode;
-	toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode;
+	toshiba_acpi.hotkey_dev->id.vendor = PCI_VENDOR_ID_TOSHIBA;

-	for (key = toshiba_acpi_keymap; key->type != KE_END; key++) {
-		set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit);
-		set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit);
+	error = sparse_keymap_setup(toshiba_acpi.hotkey_dev,
+			  toshiba_acpi_keymap, NULL);
+	if (error)
+		goto err_free_dev;
+
+	if (toshiba_acpi.spfc) {
+		status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL);
+		if (ACPI_FAILURE(status)) {
+			printk(MY_INFO "Unable to enable hotkeys\n");
+			error = -ENODEV;
+			goto err_free_keymap;
+		}
+
+		error = i8042_install_filter(toshiba_i8042_filter);
+		if (error) {
+			printk(MY_ERR "Unable to install key filter\n");
+			goto err_free_keymap;
+		}
+	
+		INIT_WORK(&toshiba_acpi.hotkey_work, handle_spfc_hotkeys);
+	} else {
+		status = acpi_install_notify_handler(toshiba_acpi.handle,
+				  ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL);
+		if (ACPI_FAILURE(status)) {
+			printk(MY_INFO "Unable to install hotkey notification\n");
+			error = -ENODEV;
+			goto err_free_keymap;
+		}
 	}

-	result = input_register_device(toshiba_acpi.hotkey_dev);
-	if (result) {
-		printk(MY_INFO "Unable to register input device\n");
-		return result;
+	error = input_register_device(toshiba_acpi.hotkey_dev);
+	if (error) {
+		printk(MY_ERR "Unable to register input device\n");
+		if (toshiba_acpi.spfc)
+			goto err_remove_filter;
+		else
+			goto err_free_keymap;
 	}

 	return 0;
+
+err_remove_filter:
+	i8042_remove_filter(toshiba_i8042_filter);
+err_free_keymap:
+	sparse_keymap_free(toshiba_acpi.hotkey_dev);
+err_free_dev:
+	input_free_device(toshiba_acpi.hotkey_dev);
+	toshiba_acpi.hotkey_dev = NULL;
+	return error;
+}
+
+static int set_device_handle(void)
+{
+	/* simple device detection: look for HCI method */
+	if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) {
+		method_hci = TOSH_INTERFACE_1 GHCI_METHOD;
+		toshiba_acpi.spfc = false;
+		if (!toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1))
+			return 0;
+	} else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) {
+		method_hci = TOSH_INTERFACE_2 GHCI_METHOD;
+		toshiba_acpi.spfc = false;
+		if (!toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2))
+			return 0;
+	} else if (is_valid_acpi_path(TOSH_INTERFACE_2 SPFC_METHOD)) {
+		method_hci = TOSH_INTERFACE_2 SPFC_METHOD;
+		toshiba_acpi.spfc = true;
+		if (!toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2))
+			return 0;
+	} else
+		printk(MY_INFO "No supported Toshiba interface found\n");
+
+	return -ENODEV;
 }

 static void toshiba_acpi_exit(void)
 {
-	if (toshiba_acpi.hotkey_dev)
+	if (toshiba_acpi.hotkey_dev) {
+		if (toshiba_acpi.spfc) {
+			flush_scheduled_work();
+			i8042_remove_filter(toshiba_i8042_filter);
+		} else {
+			acpi_remove_notify_handler(toshiba_acpi.handle,
+				  ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
+		}
+		sparse_keymap_free(toshiba_acpi.hotkey_dev);
 		input_unregister_device(toshiba_acpi.hotkey_dev);
+	}

 	if (toshiba_acpi.bt_rfk) {
 		rfkill_unregister(toshiba_acpi.bt_rfk);
@@ -1017,13 +1143,11 @@
 	if (toshiba_proc_dir)
 		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);

-	acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY,
-				   toshiba_acpi_notify);
-
 	if (toshiba_acpi.illumination_installed)
 		led_classdev_unregister(&toshiba_led);

-	platform_device_unregister(toshiba_acpi.p_dev);
+	if (toshiba_acpi.p_dev)
+		platform_device_unregister(toshiba_acpi.p_dev);

 	return;
 }
@@ -1038,16 +1162,7 @@
 	if (acpi_disabled)
 		return -ENODEV;

-	/* simple device detection: look for HCI method */
-	if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) {
-		method_hci = TOSH_INTERFACE_1 GHCI_METHOD;
-		if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1))
-			printk(MY_INFO "Unable to activate hotkeys\n");
-	} else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) {
-		method_hci = TOSH_INTERFACE_2 GHCI_METHOD;
-		if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2))
-			printk(MY_INFO "Unable to activate hotkeys\n");
-	} else
+	if (set_device_handle())
 		return -ENODEV;

 	printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n",
@@ -1072,6 +1187,21 @@
 	/* enable event fifo */
 	hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);

+	/* TOS1900 models hotkey activation */
+	if (toshiba_acpi.spfc) {
+		/* Some TOS1900 models don't implement the ENAB method,
+		 * and hotkeys activation goes by calling HCI_HOTKEY_EVENT
+		 */
+		hci_write1(HCI_HOTKEY_EVENT, 1, &hci_result);
+		/* The EC handle must be set for models without
+		 * the ENAB method so they can access the NOTIFY method,
+		 * and thus, access the INFO method...
+		 */
+		if (set_ec_handle())
+			toshiba_acpi.ec_handle = NULL;
+	} else
+		toshiba_acpi.ec_handle = NULL;
+
 	toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
 	if (!toshiba_proc_dir) {
 		toshiba_acpi_exit();
@@ -1080,19 +1210,24 @@
 		create_toshiba_proc_entries();
 	}

-	props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
-	toshiba_backlight_device = backlight_device_register("toshiba",
-							     &toshiba_acpi.p_dev->dev,
-							     NULL,
-							     &toshiba_backlight_data,
-							     &props);
-        if (IS_ERR(toshiba_backlight_device)) {
-		ret = PTR_ERR(toshiba_backlight_device);
-
-		printk(KERN_ERR "Could not register toshiba backlight device\n");
-		toshiba_backlight_device = NULL;
-		toshiba_acpi_exit();
-		return ret;
+	/* Only register backlight if the ACPI backlight is not
+	 * available or if a TOS1900 device is not detected
+	 */
+	if (!toshiba_acpi.spfc || !acpi_video_backlight_support()) {
+		memset(&props, 0, sizeof(struct backlight_properties));
+		props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
+		toshiba_backlight_device = backlight_device_register("toshiba",
+						    &toshiba_acpi.p_dev->dev,
+						    NULL,
+						    &toshiba_backlight_data,
+						    &props);
+		if (IS_ERR(toshiba_backlight_device)) {
+			printk(KERN_ERR
+			       "Could not register toshiba backlight device\n");
+			toshiba_backlight_device = NULL;
+			toshiba_acpi_exit();
+			return PTR_ERR(toshiba_backlight_device);
+		}
 	}

 	/* Register rfkill switch for Bluetooth */



-- 
-- El mundo apesta y vosotros apestais tambien --

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

* Re: toshiba_acpi.c: Full TOS1900 device support
  2010-11-12  0:10       ` Azael Avalos
@ 2010-11-24 19:17         ` Matthew Garrett
  2010-11-28  0:44           ` Azael Avalos
  0 siblings, 1 reply; 8+ messages in thread
From: Matthew Garrett @ 2010-11-24 19:17 UTC (permalink / raw)
  To: Azael Avalos; +Cc: platform-driver-x86, linux-kernel

Can you try this (entirely untested) patch and see if it works on your 
system? It's basically your code, but with some of the conditionals 
cleaned up when we already know that information.

diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 4276da7..bd4a30e 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -51,6 +51,9 @@
 #include <linux/input/sparse-keymap.h>
 #include <linux/leds.h>
 #include <linux/slab.h>
+#include <linux/sysdev.h>
+#include <linux/workqueue.h>
+#include <linux/i8042.h>
 
 #include <asm/uaccess.h>
 
@@ -67,10 +70,9 @@ MODULE_LICENSE("GPL");
 
 /* Toshiba ACPI method paths */
 #define METHOD_LCD_BRIGHTNESS	"\\_SB_.PCI0.VGA_.LCD_._BCM"
-#define TOSH_INTERFACE_1	"\\_SB_.VALD"
-#define TOSH_INTERFACE_2	"\\_SB_.VALZ"
 #define METHOD_VIDEO_OUT	"\\_SB_.VALX.DSSX"
-#define GHCI_METHOD		".GHCI"
+
+#define TOSHIBA_FN_SCAN		0x6e
 
 /* Toshiba HCI interface definitions
  *
@@ -114,10 +116,45 @@ MODULE_LICENSE("GPL");
 #define HCI_WIRELESS_BT_ATTACH		0x40
 #define HCI_WIRELESS_BT_POWER		0x80
 
+struct acpi_ec {
+        acpi_handle handle;
+        unsigned long gpe;
+        unsigned long command_addr;
+        unsigned long data_addr;
+        unsigned long global_lock;
+        unsigned long flags;
+        struct mutex lock;
+        wait_queue_head_t wait;
+        struct list_head list;
+        struct transaction *curr;
+        spinlock_t curr_lock;
+        struct sys_device sysdev;
+};
+
+extern struct acpi_ec *first_ec;
+
+struct toshiba_acpi_dev {
+	struct platform_device *p_dev;
+	struct acpi_device *acpi_dev;
+	struct rfkill *bt_rfk;
+	struct input_dev *hotkey_dev;
+	struct work_struct hotkey_work;
+	int illumination_installed;
+	acpi_handle handle;
+
+	const char *bt_name;
+
+	struct mutex mutex;
+};
+
+static struct toshiba_acpi_dev toshiba_acpi = {
+	.bt_name = "Toshiba Bluetooth",
+};
+
 static const struct acpi_device_id toshiba_device_ids[] = {
 	{"TOS6200", 0},
 	{"TOS6208", 0},
-	{"TOS1900", 0},
+	{"TOS1900", 1},
 	{"", 0},
 };
 MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
@@ -158,15 +195,6 @@ static __inline__ void _set_bit(u32 * word, u32 mask, int value)
 /* acpi interface wrappers
  */
 
-static int is_valid_acpi_path(const char *methodName)
-{
-	acpi_handle handle;
-	acpi_status status;
-
-	status = acpi_get_handle(NULL, (char *)methodName, &handle);
-	return !ACPI_FAILURE(status);
-}
-
 static int write_acpi_int(const char *methodName, int val)
 {
 	struct acpi_object_list params;
@@ -199,8 +227,6 @@ static int read_acpi_int(const char *methodName, int *pVal)
 }
 #endif
 
-static const char *method_hci /*= 0*/ ;
-
 /* Perform a raw HCI call.  Here we don't care about input or output buffer
  * format.
  */
@@ -223,8 +249,8 @@ static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
 	results.length = sizeof(out_objs);
 	results.pointer = out_objs;
 
-	status = acpi_evaluate_object(NULL, (char *)method_hci, &params,
-				      &results);
+	status = acpi_evaluate_object(toshiba_acpi.acpi_dev->handle, "GHCI",
+				      &params, &results);
 	if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
 		for (i = 0; i < out_objs->package.count; ++i) {
 			out[i] = out_objs->package.elements[i].integer.value;
@@ -279,18 +305,6 @@ static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result)
 	return status;
 }
 
-struct toshiba_acpi_dev {
-	struct platform_device *p_dev;
-	struct rfkill *bt_rfk;
-	struct input_dev *hotkey_dev;
-	int illumination_installed;
-	acpi_handle handle;
-
-	const char *bt_name;
-
-	struct mutex mutex;
-};
-
 /* Illumination support */
 static int toshiba_illumination_available(void)
 {
@@ -395,10 +409,6 @@ static struct led_classdev toshiba_led = {
 	.brightness_get = toshiba_illumination_get,
 };
 
-static struct toshiba_acpi_dev toshiba_acpi = {
-	.bt_name = "Toshiba Bluetooth",
-};
-
 /* Bluetooth rfkill handlers */
 
 static u32 hci_get_bt_present(bool *present)
@@ -846,26 +856,118 @@ static struct backlight_ops toshiba_backlight_data = {
         .update_status  = set_lcd_status,
 };
 
-static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
+static acpi_status execute_acpi_method(acpi_handle handle, char *method,
+				       const int *param, int *result)
+{
+	struct acpi_object_list args_list;
+	struct acpi_buffer buff;
+	union acpi_object in_obj, out_obj;
+	acpi_status status;
+
+	if (param) {
+		args_list.count = 1;
+		args_list.pointer = &in_obj;
+		in_obj.type = ACPI_TYPE_INTEGER;
+		in_obj.integer.value = *param;
+	} else {
+		args_list.count = 0;
+	}
+
+	buff.length = sizeof(out_obj);
+	buff.pointer = &out_obj;
+
+	status = acpi_evaluate_object(handle, method, &args_list, &buff);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_ERR
+		       "ACPI method (%s) execution failed\n", method);
+		return -EINVAL;
+	}
+
+	if (!result)
+		return status;
+
+	if (out_obj.type != ACPI_TYPE_INTEGER) {
+		printk(MY_ERR
+		       "ACPI method result is not a number\n");
+		return -EINVAL;
+	}
+
+	*result = out_obj.integer.value;
+	return status;
+}
+
+static bool toshiba_i8042_filter(unsigned char data, unsigned char str,
+				 struct serio *port)
+{
+	if (str & 0x20)
+		return false;
+
+	if (unlikely(data == 0xe0))
+		return false;
+
+	if ((data & 0x7f) == TOSHIBA_FN_SCAN)
+		schedule_work(&toshiba_acpi.hotkey_work);
+
+	return false;
+}
+
+static int toshiba_acpi_query_event(int *hotkey)
+{
+	acpi_status status;
+
+	if (!first_ec)
+		return -ENODEV;
+
+	status = execute_acpi_method(first_ec->handle, "NTFY", NULL, NULL);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_ERR "Unable to query EC\n");
+		return -EIO;
+	}
+
+	status = execute_acpi_method(first_ec->handle, "INFO", NULL, hotkey);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_ERR "Unable to request hotkey from EC\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void toshiba_report_input_event(int value)
+{
+	if (value == 0x100)
+		return;
+	/* act on key press; ignore key release */
+	if (value & 0x80)
+		return;
+
+	if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev, value, 1,
+					true)) {
+		printk(MY_INFO "Unknown key %x\n", value);
+	}
+}
+
+static void toshiba_acpi_work(struct work_struct *work)
+{
+	int hotkey;
+
+	if (toshiba_acpi_query_event(&hotkey))
+		printk(MY_ERR "Failed to get hotkey events\n");
+	else
+		toshiba_report_input_event(hotkey);
+}
+
+static void toshiba_acpi_notify(struct acpi_device *device, u32 event)
 {
 	u32 hci_result, value;
 
 	if (event != 0x80)
 		return;
+
 	do {
 		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
 		if (hci_result == HCI_SUCCESS) {
-			if (value == 0x100)
-				continue;
-			/* act on key press; ignore key release */
-			if (value & 0x80)
-				continue;
-
-			if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev,
-							value, 1, true)) {
-				printk(MY_INFO "Unknown key %x\n",
-				       value);
-			}
+			toshiba_report_input_event(value);
 		} else if (hci_result == HCI_NOT_SUPPORTED) {
 			/* This is a workaround for an unresolved issue on
 			 * some machines where system events sporadically
@@ -874,19 +976,15 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
 			printk(MY_NOTICE "Re-enabled hotkeys\n");
 		}
 	} while (hci_result != HCI_EMPTY);
+
+
 }
 
-static int __init toshiba_acpi_setup_keyboard(char *device)
+static int __init toshiba_acpi_setup_keyboard(int type)
 {
 	acpi_status status;
 	int error;
 
-	status = acpi_get_handle(NULL, device, &toshiba_acpi.handle);
-	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Unable to get notification device\n");
-		return -ENODEV;
-	}
-
 	toshiba_acpi.hotkey_dev = input_allocate_device();
 	if (!toshiba_acpi.hotkey_dev) {
 		printk(MY_INFO "Unable to register input device\n");
@@ -894,7 +992,7 @@ static int __init toshiba_acpi_setup_keyboard(char *device)
 	}
 
 	toshiba_acpi.hotkey_dev->name = "Toshiba input device";
-	toshiba_acpi.hotkey_dev->phys = device;
+	toshiba_acpi.hotkey_dev->phys = "acpi/toshiba";
 	toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
 
 	error = sparse_keymap_setup(toshiba_acpi.hotkey_dev,
@@ -902,33 +1000,35 @@ static int __init toshiba_acpi_setup_keyboard(char *device)
 	if (error)
 		goto err_free_dev;
 
-	status = acpi_install_notify_handler(toshiba_acpi.handle,
-				ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL);
-	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Unable to install hotkey notification\n");
-		error = -ENODEV;
-		goto err_free_keymap;
+	if (type == 1) {
+		error = i8042_install_filter(toshiba_i8042_filter);
+		if (error) {
+			printk(MY_ERR "Unable to install key filter\n");
+			goto err_free_keymap;
+		}
+
+		INIT_WORK(&toshiba_acpi.hotkey_work, toshiba_acpi_work);
 	}
 
-	status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL);
+	status = acpi_evaluate_object(toshiba_acpi.acpi_dev->handle, "ENAB",
+				      NULL, NULL);
 	if (ACPI_FAILURE(status)) {
 		printk(MY_INFO "Unable to enable hotkeys\n");
 		error = -ENODEV;
-		goto err_remove_notify;
+		goto err_free_keymap;
 	}
 
 	error = input_register_device(toshiba_acpi.hotkey_dev);
 	if (error) {
 		printk(MY_INFO "Unable to register input device\n");
-		goto err_remove_notify;
+		goto err_free_keymap;
 	}
 
 	return 0;
 
- err_remove_notify:
-	acpi_remove_notify_handler(toshiba_acpi.handle,
-				   ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
  err_free_keymap:
+	if (type == 1)
+		i8042_remove_filter(toshiba_i8042_filter);
 	sparse_keymap_free(toshiba_acpi.hotkey_dev);
  err_free_dev:
 	input_free_device(toshiba_acpi.hotkey_dev);
@@ -936,11 +1036,9 @@ static int __init toshiba_acpi_setup_keyboard(char *device)
 	return error;
 }
 
-static void toshiba_acpi_exit(void)
+static void toshiba_acpi_cleanup(void)
 {
 	if (toshiba_acpi.hotkey_dev) {
-		acpi_remove_notify_handler(toshiba_acpi.handle,
-				ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
 		sparse_keymap_free(toshiba_acpi.hotkey_dev);
 		input_unregister_device(toshiba_acpi.hotkey_dev);
 	}
@@ -966,54 +1064,44 @@ static void toshiba_acpi_exit(void)
 	return;
 }
 
-static int __init toshiba_acpi_init(void)
+static int toshiba_acpi_remove(struct acpi_device *device, int type)
+{
+	toshiba_acpi_cleanup();
+
+	return 0;
+}
+
+static int __devinit toshiba_acpi_add(struct acpi_device *device)
 {
 	u32 hci_result;
 	bool bt_present;
 	int ret = 0;
 	struct backlight_properties props;
 
-	if (acpi_disabled)
-		return -ENODEV;
-
-	/* simple device detection: look for HCI method */
-	if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) {
-		method_hci = TOSH_INTERFACE_1 GHCI_METHOD;
-		if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1))
-			printk(MY_INFO "Unable to activate hotkeys\n");
-	} else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) {
-		method_hci = TOSH_INTERFACE_2 GHCI_METHOD;
-		if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2))
-			printk(MY_INFO "Unable to activate hotkeys\n");
-	} else
-		return -ENODEV;
-
 	printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n",
 	       TOSHIBA_ACPI_VERSION);
-	printk(MY_INFO "    HCI method: %s\n", method_hci);
 
 	mutex_init(&toshiba_acpi.mutex);
 
-	toshiba_acpi.p_dev = platform_device_register_simple("toshiba_acpi",
-							      -1, NULL, 0);
-	if (IS_ERR(toshiba_acpi.p_dev)) {
-		ret = PTR_ERR(toshiba_acpi.p_dev);
-		printk(MY_ERR "unable to register platform device\n");
-		toshiba_acpi.p_dev = NULL;
-		toshiba_acpi_exit();
-		return ret;
-	}
-
 	force_fan = 0;
 	key_event_valid = 0;
 
+	toshiba_acpi.acpi_dev = device;
+
+	ret = toshiba_acpi_setup_keyboard(device->device_type);
+
+	if (ret) {
+		printk(MY_ERR "Unable to setup hotkeys\n");
+		return ret;
+	}
+
 	/* 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) {
-		toshiba_acpi_exit();
-		return -ENODEV;
+		ret = -ENODEV;
+		goto out;
 	} else {
 		create_toshiba_proc_entries();
 	}
@@ -1029,8 +1117,7 @@ static int __init toshiba_acpi_init(void)
 
 		printk(KERN_ERR "Could not register toshiba backlight device\n");
 		toshiba_backlight_device = NULL;
-		toshiba_acpi_exit();
-		return ret;
+		goto out;
 	}
 
 	/* Register rfkill switch for Bluetooth */
@@ -1042,16 +1129,15 @@ static int __init toshiba_acpi_init(void)
 						   &toshiba_acpi);
 		if (!toshiba_acpi.bt_rfk) {
 			printk(MY_ERR "unable to allocate rfkill device\n");
-			toshiba_acpi_exit();
-			return -ENOMEM;
+			ret = -ENOMEM;
+			goto out;
 		}
 
 		ret = rfkill_register(toshiba_acpi.bt_rfk);
 		if (ret) {
 			printk(MY_ERR "unable to register rfkill device\n");
 			rfkill_destroy(toshiba_acpi.bt_rfk);
-			toshiba_acpi_exit();
-			return ret;
+			goto out;
 		}
 	}
 
@@ -1062,7 +1148,49 @@ static int __init toshiba_acpi_init(void)
 			toshiba_acpi.illumination_installed = 1;
 	}
 
-	return 0;
+out:
+	if (ret)
+		toshiba_acpi_cleanup();
+	return ret;
+}
+
+static struct acpi_driver toshiba_acpi_driver = {
+	.name = "Toshiba ACPI driver",
+	.owner = THIS_MODULE,
+	.ids = toshiba_device_ids,
+	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
+	.ops = {
+		.add = toshiba_acpi_add,
+		.remove = toshiba_acpi_remove,
+		.notify = toshiba_acpi_notify,
+	}
+};
+
+static int __init toshiba_acpi_init(void)
+{
+	int ret;
+
+	toshiba_acpi.p_dev = platform_device_register_simple("toshiba_acpi",
+							      -1, NULL, 0);
+	if (IS_ERR(toshiba_acpi.p_dev)) {
+		printk(MY_ERR "unable to register platform device\n");
+		return PTR_ERR(toshiba_acpi.p_dev);
+	}
+
+	ret = acpi_bus_register_driver(&toshiba_acpi_driver);
+
+	if (ret) {
+		printk(MY_ERR "unable to register acpi driver\n");
+		platform_device_unregister(toshiba_acpi.p_dev);
+	}
+
+	return ret;
+}	
+
+static void __exit toshiba_acpi_exit(void)
+{
+	acpi_bus_unregister_driver(&toshiba_acpi_driver);
+	platform_device_unregister(toshiba_acpi.p_dev);
 }
 
 module_init(toshiba_acpi_init);

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

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

* Re: toshiba_acpi.c: Full TOS1900 device support
  2010-11-24 19:17         ` Matthew Garrett
@ 2010-11-28  0:44           ` Azael Avalos
  2010-12-06 17:41             ` Azael Avalos
  0 siblings, 1 reply; 8+ messages in thread
From: Azael Avalos @ 2010-11-28  0:44 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: platform-driver-x86, linux-kernel

Hi there,

Sorry for the late reply.

On Wed, Nov 24, 2010 at 12:17 PM, Matthew Garrett <mjg59@srcf.ucam.org> wrote:
> Can you try this (entirely untested) patch and see if it works on your
> system? It's basically your code, but with some of the conditionals
> cleaned up when we already know that information.

The patch works, however, I added a couple lines in order to make it work
on my laptop:

In toshiba_acpi_query_event:
       status = execute_acpi_method(toshiba_acpi.acpi_dev->handle, "INFO",
				    NULL, hotkey);

Here, the handle should be the ACPI device, not the EC

In toshiba_acpi_add:
	/* enable hotkey event */
	hci_write1(HCI_HOTKEY_EVENT, 1, &hci_result);

My model (and a couple others) the ENAB method is empty, and hotkey
activation is done via HCI_HOTKEY_EVENT


Another thing that I've noticed is that "device->device_type" is always zero
for me, I thought that it was supposed to be 1, due to  {"TOS1900", 1}.
So I changed the comparison value to 0 in order to get the hotkeys
reported.


Saludos
Azael


-- 
-- El mundo apesta y vosotros apestais tambien --

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

* Re: toshiba_acpi.c: Full TOS1900 device support
  2010-11-28  0:44           ` Azael Avalos
@ 2010-12-06 17:41             ` Azael Avalos
  0 siblings, 0 replies; 8+ messages in thread
From: Azael Avalos @ 2010-12-06 17:41 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: platform-driver-x86, linux-kernel

Hi Matthew,

Here's the v3 of the patch with your changes and adapted to devices
with SPFC method, also added the Toshiba Software Configuration
Interface (SCI) registers and functions (which Illumination uses).


--- toshiba_acpi.c	2010-12-06 10:32:17.197704744 -0700
+++ toshiba_acpi.c	2010-12-05 17:23:24.745438137 -0700
@@ -5,6 +5,7 @@
  *  Copyright (C) 2002-2004 John Belmonte
  *  Copyright (C) 2008 Philip Langdale
  *  Copyright (C) 2010 Pierre Ducroquet
+ *  Copyright (C) 2010 Azael Avalos
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -35,7 +36,7 @@
  *
  */

-#define TOSHIBA_ACPI_VERSION	"0.19"
+#define TOSHIBA_ACPI_VERSION	"0.20"
 #define PROC_INTERFACE_VERSION	1

 #include <linux/kernel.h>
@@ -48,8 +49,12 @@
 #include <linux/platform_device.h>
 #include <linux/rfkill.h>
 #include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
 #include <linux/leds.h>
 #include <linux/slab.h>
+#include <linux/sysdev.h>
+#include <linux/workqueue.h>
+#include <linux/i8042.h>

 #include <asm/uaccess.h>

@@ -65,11 +70,9 @@
 #define MY_INFO KERN_INFO MY_LOGPREFIX

 /* Toshiba ACPI method paths */
-#define METHOD_LCD_BRIGHTNESS	"\\_SB_.PCI0.VGA_.LCD_._BCM"
-#define TOSH_INTERFACE_1	"\\_SB_.VALD"
-#define TOSH_INTERFACE_2	"\\_SB_.VALZ"
 #define METHOD_VIDEO_OUT	"\\_SB_.VALX.DSSX"
-#define GHCI_METHOD		".GHCI"
+
+#define TOSHIBA_FN_SCAN                0x6e

 /* Toshiba HCI interface definitions
  *
@@ -83,23 +86,37 @@

 #define HCI_WORDS			6

-/* operations */
+/* HCI operations */
 #define HCI_SET				0xff00
 #define HCI_GET				0xfe00
+/* SCI operations */
+#define SCI_SUPPORT_CHECK		0xf000
+#define SCI_OPEN			0xf100
+#define SCI_CLOSE			0xf200
+#define SCI_SET				0xf400
+#define SCI_GET				0xf300

-/* return codes */
+/* HCI return codes */
 #define HCI_SUCCESS			0x0000
 #define HCI_FAILURE			0x1000
 #define HCI_NOT_SUPPORTED		0x8000
 #define HCI_EMPTY			0x8c00
+/* SCI return codes */
+#define SCI_NOT_SUPPORTED		HCI_NOT_SUPPORTED
+#define SCI_ALREADY_OPEN		0x8100
+#define SCI_NOT_OPENED			0x8200
+#define SCI_NOT_PRESENT			0x8600
+#define SCI_NOT_INSTALLED		0x8e00

-/* registers */
+/* HCI registers */
 #define HCI_FAN				0x0004
 #define HCI_SYSTEM_EVENT		0x0016
 #define HCI_VIDEO_OUT			0x001c
 #define HCI_HOTKEY_EVENT		0x001e
 #define HCI_LCD_BRIGHTNESS		0x002a
 #define HCI_WIRELESS			0x0056
+/* SCI registers */
+#define SCI_ILLUMINATION		0x014e

 /* field definitions */
 #define HCI_LCD_BRIGHTNESS_BITS		3
@@ -113,44 +130,77 @@
 #define HCI_WIRELESS_BT_ATTACH		0x40
 #define HCI_WIRELESS_BT_POWER		0x80

+struct acpi_ec {
+	acpi_handle handle;
+	unsigned long gpe;
+	unsigned long command_addr;
+	unsigned long data_addr;
+	unsigned long global_lock;
+	unsigned long flags;
+	struct mutex lock;
+	wait_queue_head_t wait;
+	struct list_head list;
+	struct transaction *curr;
+	spinlock_t curr_lock;
+	struct sys_device sysdev;
+};
+
+extern struct acpi_ec *first_ec;
+
+struct toshiba_acpi_dev {
+	struct platform_device *p_dev;
+	struct acpi_device *acpi_dev;
+	struct rfkill *bt_rfk;
+	struct input_dev *hotkey_dev;
+	struct work_struct hotkey_work;
+	int illumination_installed;
+	acpi_handle handle;
+
+	int hci_type;
+	char *hci_method;
+
+	const char *bt_name;
+
+	struct mutex mutex;
+};
+
+static struct toshiba_acpi_dev toshiba_acpi = {
+	.bt_name = "Toshiba Bluetooth",
+};
+
 static const struct acpi_device_id toshiba_device_ids[] = {
 	{"TOS6200", 0},
 	{"TOS6208", 0},
-	{"TOS1900", 0},
+	{"TOS1900", 1},
 	{"", 0},
 };
 MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);

-struct key_entry {
-	char type;
-	u16 code;
-	u16 keycode;
-};
-
-enum {KE_KEY, KE_END};
-
 static struct key_entry toshiba_acpi_keymap[]  = {
-	{KE_KEY, 0x101, KEY_MUTE},
-	{KE_KEY, 0x102, KEY_ZOOMOUT},
-	{KE_KEY, 0x103, KEY_ZOOMIN},
-	{KE_KEY, 0x13b, KEY_COFFEE},
-	{KE_KEY, 0x13c, KEY_BATTERY},
-	{KE_KEY, 0x13d, KEY_SLEEP},
-	{KE_KEY, 0x13e, KEY_SUSPEND},
-	{KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE},
-	{KE_KEY, 0x140, KEY_BRIGHTNESSDOWN},
-	{KE_KEY, 0x141, KEY_BRIGHTNESSUP},
-	{KE_KEY, 0x142, KEY_WLAN},
-	{KE_KEY, 0x143, KEY_PROG1},
-	{KE_KEY, 0xb05, KEY_PROG2},
-	{KE_KEY, 0xb06, KEY_WWW},
-	{KE_KEY, 0xb07, KEY_MAIL},
-	{KE_KEY, 0xb30, KEY_STOP},
-	{KE_KEY, 0xb31, KEY_PREVIOUSSONG},
-	{KE_KEY, 0xb32, KEY_NEXTSONG},
-	{KE_KEY, 0xb33, KEY_PLAYPAUSE},
-	{KE_KEY, 0xb5a, KEY_MEDIA},
-	{KE_END, 0, 0},
+	{KE_KEY, 0x100, { KEY_FN } },
+	{KE_KEY, 0x101, { KEY_MUTE } },
+	{KE_KEY, 0x102, { KEY_ZOOMOUT } },
+	{KE_KEY, 0x103, { KEY_ZOOMIN } },
+	{KE_KEY, 0x139, { KEY_ZOOMRESET } },
+	{KE_KEY, 0x13b, { KEY_COFFEE } },
+	{KE_KEY, 0x13c, { KEY_BATTERY } },
+	{KE_KEY, 0x13d, { KEY_SLEEP } },
+	{KE_KEY, 0x13e, { KEY_SUSPEND } },
+	{KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } },
+	{KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
+	{KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
+	{KE_KEY, 0x142, { KEY_WLAN } },
+	{KE_KEY, 0x143, { KEY_PROG1 } },
+	{KE_KEY, 0x17f, { KEY_FN } },
+	{KE_KEY, 0xb05, { KEY_PROG2 } },
+	{KE_KEY, 0xb06, { KEY_WWW } },
+	{KE_KEY, 0xb07, { KEY_MAIL } },
+	{KE_KEY, 0xb30, { KEY_STOP } },
+	{KE_KEY, 0xb31, { KEY_PREVIOUSSONG } },
+	{KE_KEY, 0xb32, { KEY_NEXTSONG } },
+	{KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
+	{KE_KEY, 0xb5a, { KEY_MEDIA } },
+	{KE_END, 0},
 };

 /* utility
@@ -164,15 +214,6 @@
 /* acpi interface wrappers
  */

-static int is_valid_acpi_path(const char *methodName)
-{
-	acpi_handle handle;
-	acpi_status status;
-
-	status = acpi_get_handle(NULL, (char *)methodName, &handle);
-	return !ACPI_FAILURE(status);
-}
-
 static int write_acpi_int(const char *methodName, int val)
 {
 	struct acpi_object_list params;
@@ -185,27 +226,8 @@
 	in_objs[0].integer.value = val;

 	status = acpi_evaluate_object(NULL, (char *)methodName, &params, NULL);
-	return (status == AE_OK);
-}
-
-#if 0
-static int read_acpi_int(const char *methodName, int *pVal)
-{
-	struct acpi_buffer results;
-	union acpi_object out_objs[1];
-	acpi_status status;
-
-	results.length = sizeof(out_objs);
-	results.pointer = out_objs;
-
-	status = acpi_evaluate_object(0, (char *)methodName, 0, &results);
-	*pVal = out_objs[0].integer.value;
-
-	return (status == AE_OK) && (out_objs[0].type == ACPI_TYPE_INTEGER);
+	return (ACPI_SUCCESS(status));
 }
-#endif
-
-static const char *method_hci /*= 0*/ ;

 /* Perform a raw HCI call.  Here we don't care about input or output buffer
  * format.
@@ -229,9 +251,10 @@
 	results.length = sizeof(out_objs);
 	results.pointer = out_objs;

-	status = acpi_evaluate_object(NULL, (char *)method_hci, &params,
-				      &results);
-	if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
+	status = acpi_evaluate_object(toshiba_acpi.acpi_dev->handle,
+				      toshiba_acpi.hci_method,
+				      &params, &results);
+	if ((ACPI_SUCCESS(status)) && (out_objs->package.count <= HCI_WORDS)) {
 		for (i = 0; i < out_objs->package.count; ++i) {
 			out[i] = out_objs->package.elements[i].integer.value;
 		}
@@ -270,7 +293,7 @@
 	u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 };
 	u32 out[HCI_WORDS];
 	acpi_status status = hci_raw(in, out);
-	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
+	*result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
 	return status;
 }

@@ -281,38 +304,82 @@
 	acpi_status status = hci_raw(in, out);
 	*out1 = out[2];
 	*out2 = out[3];
-	*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
+	*result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
 	return status;
 }

-struct toshiba_acpi_dev {
-	struct platform_device *p_dev;
-	struct rfkill *bt_rfk;
-	struct input_dev *hotkey_dev;
-	int illumination_installed;
-	acpi_handle handle;
+static int sci_present(void)
+{
+	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
+	u32 out[HCI_WORDS];

-	const char *bt_name;
+	/* Check system for SCI support */
+	in[0] = SCI_SUPPORT_CHECK;
+	hci_raw(in, out);
+	if (out[0] == SCI_NOT_PRESENT) {
+		printk(MY_INFO "Toshiba SCI not available\n");
+		return 0;
+	}

-	struct mutex mutex;
-};
+	return 1;
+}
+
+static int sci_open(void)
+{
+	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
+	u32 out[HCI_WORDS];
+
+	if (!sci_present())
+		return HCI_FAILURE;
+
+	in[0] = SCI_OPEN;
+	hci_raw(in, out);
+	if (out[0] == SCI_ALREADY_OPEN) {
+		printk(MY_NOTICE "Toshiba SCI already opened\n");
+		return HCI_SUCCESS;
+	}
+
+	return out[0];
+}
+
+static int sci_close(void)
+{
+	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
+	u32 out[HCI_WORDS];
+
+	if (!sci_present())
+		return HCI_FAILURE;
+
+	in[0] = SCI_CLOSE;
+	hci_raw(in, out);
+	if (out[0] == SCI_NOT_OPENED)
+		printk(MY_INFO "Toshiba SCI not opened\n");
+
+	return out[0];
+}

 /* Illumination support */
 static int toshiba_illumination_available(void)
 {
 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
 	u32 out[HCI_WORDS];
-	acpi_status status;
+	int ret;

-	in[0] = 0xf100;
-	status = hci_raw(in, out);
-	if (ACPI_FAILURE(status)) {
+	ret = sci_open();
+	if (ret == HCI_FAILURE) {
 		printk(MY_INFO "Illumination device not available\n");
 		return 0;
 	}
-	in[0] = 0xf400;
-	status = hci_raw(in, out);
-	return 1;
+
+	in[0] = SCI_GET;
+	in[1] = SCI_ILLUMINATION;
+	hci_raw(in, out);
+	if (out[0] == HCI_SUCCESS)
+		return 1;
+
+	printk(MY_INFO "Illumination device not available\n");
+
+	return 0;
 }

 static void toshiba_illumination_set(struct led_classdev *cdev,
@@ -320,77 +387,41 @@
 {
 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
 	u32 out[HCI_WORDS];
-	acpi_status status;

-	/* First request : initialize communication. */
-	in[0] = 0xf100;
-	status = hci_raw(in, out);
-	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Illumination device not available\n");
+	in[0] = SCI_SET;
+	in[1] = SCI_ILLUMINATION;
+	/* Switch the illumination on/off */
+	in[2] = (brightness) ? 1 : 0;
+	hci_raw(in, out);
+	if (out[0] == HCI_FAILURE) {
+		printk(MY_ERR "ACPI call for illumination failed.\n");
 		return;
-	}
-
-	if (brightness) {
-		/* Switch the illumination on */
-		in[0] = 0xf400;
-		in[1] = 0x14e;
-		in[2] = 1;
-		status = hci_raw(in, out);
-		if (ACPI_FAILURE(status)) {
-			printk(MY_INFO "ACPI call for illumination failed.\n");
-			return;
-		}
-	} else {
-		/* Switch the illumination off */
-		in[0] = 0xf400;
-		in[1] = 0x14e;
-		in[2] = 0;
-		status = hci_raw(in, out);
-		if (ACPI_FAILURE(status)) {
-			printk(MY_INFO "ACPI call for illumination failed.\n");
-			return;
-		}
-	}
+	} else if (out[0] == SCI_NOT_SUPPORTED)
+		printk(MY_INFO "Illumination not supported\n");

-	/* Last request : close communication. */
-	in[0] = 0xf200;
-	in[1] = 0;
-	in[2] = 0;
-	hci_raw(in, out);
+	return;
 }

 static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
 {
 	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
 	u32 out[HCI_WORDS];
-	acpi_status status;
 	enum led_brightness result;

-	/*Â First request : initialize communication. */
-	in[0] = 0xf100;
-	status = hci_raw(in, out);
-	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Illumination device not available\n");
-		return LED_OFF;
-	}
-
+	in[0] = SCI_GET;
+	in[1] = SCI_ILLUMINATION;
 	/* Check the illumination */
-	in[0] = 0xf300;
-	in[1] = 0x14e;
-	status = hci_raw(in, out);
-	if (ACPI_FAILURE(status)) {
+	hci_raw(in, out);
+	if (out[0] == HCI_FAILURE) {
 		printk(MY_INFO "ACPI call for illumination failed.\n");
 		return LED_OFF;
+	} else if (out[0] == SCI_NOT_SUPPORTED) {
+		printk(MY_INFO "Illumination not supported\n");
+		return LED_OFF;
 	}

 	result = out[2] ? LED_FULL : LED_OFF;

-	/* Last request : close communication. */
-	in[0] = 0xf200;
-	in[1] = 0;
-	in[2] = 0;
-	hci_raw(in, out);
-
 	return result;
 }

@@ -401,10 +432,6 @@
 	.brightness_get = toshiba_illumination_get,
 };

-static struct toshiba_acpi_dev toshiba_acpi = {
-	.bt_name = "Toshiba Bluetooth",
-};
-
 /* Bluetooth rfkill handlers */

 static u32 hci_get_bt_present(bool *present)
@@ -848,93 +875,128 @@
 }

 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 struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code)
+static acpi_status execute_acpi_method(acpi_handle handle, char *method,
+                                      const int *param, int *result)
 {
-	struct key_entry *key;
+	struct acpi_object_list args_list;
+	struct acpi_buffer buff;
+	union acpi_object in_obj, out_obj;
+	acpi_status status;

-	for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
-		if (code == key->code)
-			return key;
+	if (param) {
+		args_list.count = 1;
+		args_list.pointer = &in_obj;
+		in_obj.type = ACPI_TYPE_INTEGER;
+		in_obj.integer.value = *param;
+	} else {
+		args_list.count = 0;
+	}

-	return NULL;
+	buff.length = sizeof(out_obj);
+	buff.pointer = &out_obj;
+
+	status = acpi_evaluate_object(handle, method, &args_list, &buff);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_ERR
+			"ACPI method (%s) execution failed\n", method);
+		return -EINVAL;
+	}
+
+	if (!result)
+		return status;
+
+	if (out_obj.type != ACPI_TYPE_INTEGER) {
+		printk(MY_ERR
+			"ACPI method result is not a number\n");
+		return -EINVAL;
+	}
+
+	*result = out_obj.integer.value;
+	return status;
 }

-static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code)
+static bool toshiba_i8042_filter(unsigned char data, unsigned char str,
+                                struct serio *port)
 {
-	struct key_entry *key;
+	if (str & 0x20)
+		return false;

-	for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
-		if (code == key->keycode && key->type == KE_KEY)
-			return key;
+	if (unlikely(data == 0xe0))
+		return false;

-	return NULL;
+	if ((data & 0x7f) == TOSHIBA_FN_SCAN) {
+		schedule_work(&toshiba_acpi.hotkey_work);
+		return true;
+	}
+
+	return false;
 }

-static int toshiba_acpi_getkeycode(struct input_dev *dev,
-				   unsigned int scancode, unsigned int *keycode)
+static int toshiba_acpi_query_event(int *hotkey)
 {
-	struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode);
+	acpi_status status;

-	if (key && key->type == KE_KEY) {
-		*keycode = key->keycode;
-		return 0;
+	if (!first_ec) {
+		printk(MY_ERR "No EC device available\n");
+		return -ENODEV;
 	}

-	return -EINVAL;
+	status = execute_acpi_method(first_ec->handle, "NTFY", NULL, NULL);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_ERR "Unable to query EC\n");
+		return -EIO;
+	}
+
+	status = execute_acpi_method(toshiba_acpi.acpi_dev->handle, "INFO",
+				    NULL, hotkey);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_ERR "Unable to request hotkey from EC\n");
+		return -EIO;
+	}
+
+	return 0;
 }

-static int toshiba_acpi_setkeycode(struct input_dev *dev,
-				   unsigned int scancode, unsigned int keycode)
+static void toshiba_report_input_event(int value)
 {
-	struct key_entry *key;
-	unsigned int old_keycode;
+	if (value == 0x100)
+               return;

-	key = toshiba_acpi_get_entry_by_scancode(scancode);
-	if (key && key->type == KE_KEY) {
-		old_keycode = key->keycode;
-		key->keycode = keycode;
-		set_bit(keycode, dev->keybit);
-		if (!toshiba_acpi_get_entry_by_keycode(old_keycode))
-			clear_bit(old_keycode, dev->keybit);
-		return 0;
+	/* act on key press; ignore key release */
+	if (value & 0x80)
+		return;
+
+	if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev, value, 1,
+					true)) {
+		printk(MY_INFO "Unknown key %x\n", value);
 	}
+}

-	return -EINVAL;
+static void toshiba_acpi_work(struct work_struct *work)
+{
+	int hotkey;
+
+	if (toshiba_acpi_query_event(&hotkey))
+		printk(MY_ERR "Failed to get hotkey events\n");
+	else
+		toshiba_report_input_event(hotkey);
 }

-static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
+static void toshiba_acpi_notify(struct acpi_device *device, u32 event)
 {
 	u32 hci_result, value;
-	struct key_entry *key;

-	if (event != 0x80)
+	if (event != 0x80 || toshiba_acpi.hci_type == 2)
 		return;
+
 	do {
 		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
 		if (hci_result == HCI_SUCCESS) {
-			if (value == 0x100)
-				continue;
-			/* act on key press; ignore key release */
-			if (value & 0x80)
-				continue;
-
-			key = toshiba_acpi_get_entry_by_scancode
-				(value);
-			if (!key) {
-				printk(MY_INFO "Unknown key %x\n",
-				       value);
-				continue;
-			}
-			input_report_key(toshiba_acpi.hotkey_dev,
-					 key->keycode, 1);
-			input_sync(toshiba_acpi.hotkey_dev);
-			input_report_key(toshiba_acpi.hotkey_dev,
-					 key->keycode, 0);
-			input_sync(toshiba_acpi.hotkey_dev);
+			toshiba_report_input_event(value);
 		} else if (hci_result == HCI_NOT_SUPPORTED) {
 			/* This is a workaround for an unresolved issue on
 			 * some machines where system events sporadically
@@ -945,64 +1007,94 @@
 	} while (hci_result != HCI_EMPTY);
 }

-static int toshiba_acpi_setup_keyboard(char *device)
+static int __init toshiba_acpi_setup_keyboard(void)
 {
 	acpi_status status;
-	acpi_handle handle;
-	int result;
-	const struct key_entry *key;
+	int error;

-	status = acpi_get_handle(NULL, device, &handle);
-	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Unable to get notification device\n");
-		return -ENODEV;
+	toshiba_acpi.hotkey_dev = input_allocate_device();
+	if (!toshiba_acpi.hotkey_dev) {
+		printk(MY_INFO "Unable to register input device\n");
+		return -ENOMEM;
 	}

-	toshiba_acpi.handle = handle;
+	toshiba_acpi.hotkey_dev->name = "Toshiba input device";
+	toshiba_acpi.hotkey_dev->phys = "acpi/toshiba";
+	toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;

-	status = acpi_evaluate_object(handle, "ENAB", NULL, NULL);
-	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Unable to enable hotkeys\n");
-		return -ENODEV;
+	error = sparse_keymap_setup(toshiba_acpi.hotkey_dev,
+			  toshiba_acpi_keymap, NULL);
+	if (error)
+		goto err_free_dev;
+
+	if (toshiba_acpi.hci_type == 2) {
+		error = i8042_install_filter(toshiba_i8042_filter);
+		if (error) {
+			printk(MY_ERR "Unable to install key filter\n");
+			goto err_free_keymap;
+		}
+
+		INIT_WORK(&toshiba_acpi.hotkey_work, toshiba_acpi_work);
 	}

-	status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY,
-					      toshiba_acpi_notify, NULL);
+	status = acpi_evaluate_object(toshiba_acpi.acpi_dev->handle, "ENAB",
+				      NULL, NULL);
 	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Unable to install hotkey notification\n");
-		return -ENODEV;
+		printk(MY_INFO "Unable to enable hotkeys\n");
+		error = -ENODEV;
+		goto err_free_keymap;
 	}

-	toshiba_acpi.hotkey_dev = input_allocate_device();
-	if (!toshiba_acpi.hotkey_dev) {
+	error = input_register_device(toshiba_acpi.hotkey_dev);
+	if (error) {
 		printk(MY_INFO "Unable to register input device\n");
-		return -ENOMEM;
+		 goto err_free_keymap;
 	}

-	toshiba_acpi.hotkey_dev->name = "Toshiba input device";
-	toshiba_acpi.hotkey_dev->phys = device;
-	toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
-	toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode;
-	toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode;
+	return 0;

-	for (key = toshiba_acpi_keymap; key->type != KE_END; key++) {
-		set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit);
-		set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit);
+err_free_keymap:
+	if (toshiba_acpi.hci_type == 2)
+		i8042_remove_filter(toshiba_i8042_filter);
+err_free_dev:
+	input_free_device(toshiba_acpi.hotkey_dev);
+	toshiba_acpi.hotkey_dev = NULL;
+	return error;
+}
+
+static int find_hci_method(void)
+{
+	acpi_status status;
+	acpi_handle handle;
+
+	status = acpi_get_handle(toshiba_acpi.acpi_dev->handle,
+				 "GHCI", &handle);
+	if (ACPI_SUCCESS(status)) {
+		toshiba_acpi.hci_type = 1;
+		toshiba_acpi.hci_method = "GHCI";
+		return 0;
 	}

-	result = input_register_device(toshiba_acpi.hotkey_dev);
-	if (result) {
-		printk(MY_INFO "Unable to register input device\n");
-		return result;
+	status = acpi_get_handle(toshiba_acpi.acpi_dev->handle,
+				 "SPFC", &handle);
+	if (ACPI_SUCCESS(status)) {
+		toshiba_acpi.hci_type = 2;
+		toshiba_acpi.hci_method = "SPFC";
+		return 0;
 	}

-	return 0;
+	toshiba_acpi.hci_type = 0;
+	toshiba_acpi.hci_method = NULL;
+
+	return 1;
 }

-static void toshiba_acpi_exit(void)
+static void toshiba_acpi_cleanup(void)
 {
-	if (toshiba_acpi.hotkey_dev)
+	if (toshiba_acpi.hotkey_dev) {
 		input_unregister_device(toshiba_acpi.hotkey_dev);
+		sparse_keymap_free(toshiba_acpi.hotkey_dev);
+	}

 	if (toshiba_acpi.bt_rfk) {
 		rfkill_unregister(toshiba_acpi.bt_rfk);
@@ -1017,69 +1109,67 @@
 	if (toshiba_proc_dir)
 		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);

-	acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY,
-				   toshiba_acpi_notify);
-
-	if (toshiba_acpi.illumination_installed)
+	if (toshiba_acpi.illumination_installed) {
 		led_classdev_unregister(&toshiba_led);
-
-	platform_device_unregister(toshiba_acpi.p_dev);
+		sci_close();
+	}

 	return;
 }

-static int __init toshiba_acpi_init(void)
+static int toshiba_acpi_remove(struct acpi_device *device, int type)
+{
+	if (toshiba_acpi.hci_type == 2)
+		i8042_remove_filter(toshiba_i8042_filter);
+
+	toshiba_acpi_cleanup();
+
+	return 0;
+}
+
+static int __devinit toshiba_acpi_add(struct acpi_device *device)
 {
 	u32 hci_result;
 	bool bt_present;
 	int ret = 0;
 	struct backlight_properties props;

-	if (acpi_disabled)
-		return -ENODEV;
-
-	/* simple device detection: look for HCI method */
-	if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) {
-		method_hci = TOSH_INTERFACE_1 GHCI_METHOD;
-		if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1))
-			printk(MY_INFO "Unable to activate hotkeys\n");
-	} else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) {
-		method_hci = TOSH_INTERFACE_2 GHCI_METHOD;
-		if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2))
-			printk(MY_INFO "Unable to activate hotkeys\n");
-	} else
-		return -ENODEV;
-
 	printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n",
 	       TOSHIBA_ACPI_VERSION);
-	printk(MY_INFO "    HCI method: %s\n", method_hci);

 	mutex_init(&toshiba_acpi.mutex);

-	toshiba_acpi.p_dev = platform_device_register_simple("toshiba_acpi",
-							      -1, NULL, 0);
-	if (IS_ERR(toshiba_acpi.p_dev)) {
-		ret = PTR_ERR(toshiba_acpi.p_dev);
-		printk(MY_ERR "unable to register platform device\n");
-		toshiba_acpi.p_dev = NULL;
-		toshiba_acpi_exit();
+	force_fan = 0;
+	key_event_valid = 0;
+
+	toshiba_acpi.acpi_dev = device;
+
+	ret = find_hci_method();
+	if (ret) {
+		printk(MY_INFO "HCI interface not found\n");
 		return ret;
 	}

-	force_fan = 0;
-	key_event_valid = 0;
+	ret = toshiba_acpi_setup_keyboard();
+	if (ret) {
+		printk(MY_ERR "Unable to setup hotkeys\n");
+		return ret;
+	}

 	/* enable event fifo */
 	hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
+	/* enable hotkey event */
+	hci_write1(HCI_HOTKEY_EVENT, 1, &hci_result);

 	toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
 	if (!toshiba_proc_dir) {
-		toshiba_acpi_exit();
-		return -ENODEV;
+		ret = -ENODEV;
+		goto out;
 	} else {
 		create_toshiba_proc_entries();
 	}

+	memset(&props, 0, sizeof(struct backlight_properties));
 	props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
 	toshiba_backlight_device = backlight_device_register("toshiba",
 							     &toshiba_acpi.p_dev->dev,
@@ -1091,8 +1181,7 @@

 		printk(KERN_ERR "Could not register toshiba backlight device\n");
 		toshiba_backlight_device = NULL;
-		toshiba_acpi_exit();
-		return ret;
+		goto out;
 	}

 	/* Register rfkill switch for Bluetooth */
@@ -1104,16 +1193,15 @@
 						   &toshiba_acpi);
 		if (!toshiba_acpi.bt_rfk) {
 			printk(MY_ERR "unable to allocate rfkill device\n");
-			toshiba_acpi_exit();
-			return -ENOMEM;
+			ret = -ENOMEM;
+			goto out;
 		}

 		ret = rfkill_register(toshiba_acpi.bt_rfk);
 		if (ret) {
 			printk(MY_ERR "unable to register rfkill device\n");
 			rfkill_destroy(toshiba_acpi.bt_rfk);
-			toshiba_acpi_exit();
-			return ret;
+			goto out;
 		}
 	}

@@ -1124,7 +1212,48 @@
 			toshiba_acpi.illumination_installed = 1;
 	}

-	return 0;
+out:
+	if (ret)
+		toshiba_acpi_cleanup();
+	return ret;
+}
+
+static struct acpi_driver toshiba_acpi_driver = {
+	.name = "Toshiba ACPI driver",
+	.owner = THIS_MODULE,
+	.ids = toshiba_device_ids,
+	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
+	.ops = {
+		.add = toshiba_acpi_add,
+		.remove = toshiba_acpi_remove,
+		.notify = toshiba_acpi_notify,
+	}
+};
+
+static int __init toshiba_acpi_init(void)
+{
+	int ret;
+
+	toshiba_acpi.p_dev = platform_device_register_simple("toshiba_acpi",
+							     -1, NULL, 0);
+	if (IS_ERR(toshiba_acpi.p_dev)) {
+		printk(MY_ERR "unable to register platform device\n");
+		return PTR_ERR(toshiba_acpi.p_dev);
+	}
+
+	ret = acpi_bus_register_driver(&toshiba_acpi_driver);
+	if (ret) {
+		printk(MY_ERR "unable to register acpi driver\n");
+		platform_device_unregister(toshiba_acpi.p_dev);
+	}
+
+	return ret;
+}
+
+static void __exit toshiba_acpi_exit(void)
+{
+	acpi_bus_unregister_driver(&toshiba_acpi_driver);
+	platform_device_unregister(toshiba_acpi.p_dev);
 }

 module_init(toshiba_acpi_init);



-- 
-- El mundo apesta y vosotros apestais tambien --

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

end of thread, other threads:[~2010-12-06 17:47 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-10-26 19:14 toshiba_acpi.c: Full TOS1900 device support Azael Avalos
2010-10-26 19:20 ` Matthew Garrett
2010-10-27 19:24   ` Azael Avalos
2010-10-27 19:29     ` Matthew Garrett
2010-11-12  0:10       ` Azael Avalos
2010-11-24 19:17         ` Matthew Garrett
2010-11-28  0:44           ` Azael Avalos
2010-12-06 17:41             ` Azael Avalos

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