Please test & comment. I cannot convince git to block text in a
different way - if anyone knows how to do it please let me know.
-- 
Regards,
Przemo Firszt
>From 59a8e0ea626e8cceda69d066f3c0df185c7fa297 Mon Sep 17 00:00:00 2001
From: Przemo Firszt <prz...@firszt.eu>
Date: Sun, 28 Aug 2011 20:35:30 +0100
Subject: [PATCH] Initial driver for Wacom Intuos4 Wireless (Bluetooth)

This is very basic driver for Wacom Intuos4 Wireless tablet. It supports only
position, pressure and tip button of pen and eraser. More features will be
added in future releases.

Signed-off-by: Przemo Firszt <prz...@firszt.eu>
---
 drivers/hid/hid-core.c  |    1 +
 drivers/hid/hid-ids.h   |    1 +
 drivers/hid/hid-wacom.c |  359 +++++++++++++++++++++++++++++++----------------
 3 files changed, 242 insertions(+), 119 deletions(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 242353d..29bf4ae 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1502,6 +1502,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 7484e1b..b8ec5be 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -660,6 +660,7 @@
 
 #define USB_VENDOR_ID_WACOM		0x056a
 #define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH	0x81
+#define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH	0x00BD
 
 #define USB_VENDOR_ID_WALTOP				0x172f
 #define USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH	0x0032
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
index a3e465a..957bef6 100644
--- a/drivers/hid/hid-wacom.c
+++ b/drivers/hid/hid-wacom.c
@@ -9,6 +9,7 @@
  *  Copyright (c) 2008 Jiri Slaby <jirisl...@gmail.com>
  *  Copyright (c) 2006 Andrew Zabolotny <z...@homelink.ru>
  *  Copyright (c) 2009 Bastien Nocera <had...@hadess.net>
+ *  Copyright (c) 2011 Przemysław Firszt <prz...@firszt.eu>
  */
 
 /*
@@ -32,6 +33,7 @@
 
 struct wacom_data {
 	__u16 tool;
+	__u8 features;
 	unsigned char butstate;
 	unsigned char high_speed;
 #ifdef CONFIG_HID_WACOM_POWER_SUPPLY
@@ -107,6 +109,19 @@ static int wacom_ac_get_property(struct power_supply *psy,
 }
 #endif
 
+static void wacom_set_features(struct hid_device *hdev)
+{
+	int ret;
+	__u8 rep_data[2];
+
+	/*set high speed, tablet mode*/
+	rep_data[0] = 0x03;
+	rep_data[1] = 0x20;
+	ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
+				HID_FEATURE_REPORT);
+	return;
+}
+
 static void wacom_poke(struct hid_device *hdev, u8 speed)
 {
 	struct wacom_data *wdata = hid_get_drvdata(hdev);
@@ -177,6 +192,77 @@ static ssize_t wacom_store_speed(struct device *dev,
 static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
 		wacom_show_speed, wacom_store_speed);
 
+static void wacom_i4_parse_pen_report(struct wacom_data *wdata,
+			struct input_dev *input, unsigned char *data)
+{
+	__u16 x, y, pressure;
+	__u32 id;
+
+	switch (data[1]) {
+	case 0x80: /* Out of proximity report */
+		wdata->tool = 0;
+		input_report_key(input, BTN_TOUCH, 0);
+		input_report_abs(input, ABS_PRESSURE, 0);
+		input_report_key(input, wdata->tool, 0);
+		input_sync(input);
+		break;
+	case 0xC2: /* Tool report */
+		id = ((data[2] << 4) | (data[3] >> 4) |
+			((data[7] & 0x0f) << 20) |
+			((data[8] & 0xf0) << 12)) & 0xfffff;
+
+		switch (id) {
+		case 0x802:
+			wdata->tool = BTN_TOOL_PEN;
+			break;
+		case 0x80A:
+			wdata->tool = BTN_TOOL_RUBBER;
+			break;
+		}
+		break;
+	default: /* Position/pressure report */
+		x = data[2] << 9 | data[3] << 1 | ((data[9] & 0x02) >> 1);
+		y = data[4] << 9 | data[5] << 1 | (data[9] & 0x01);
+		pressure = (data[6] << 3) | ((data[7] & 0xC0) >> 5)
+			| (data[1] & 0x01);
+
+		if (pressure > 1)
+			input_report_key(input, BTN_TOUCH, 1);
+		else
+			input_report_key(input, BTN_TOUCH, 0);
+
+		input_report_key(input, wdata->tool, 1);
+		input_report_abs(input, ABS_X, x);
+		input_report_abs(input, ABS_Y, y);
+		input_report_abs(input, ABS_PRESSURE, pressure);
+		input_sync(input);
+		break;
+	}
+
+	return;
+}
+
+static void wacom_i4_parse_report(struct hid_device *hdev,
+			struct wacom_data *wdata,
+			struct input_dev *input, unsigned char *data)
+{
+	switch (data[0]) {
+	case 0x00: /* Empty report */
+		break;
+	case 0x02: /* Pen report */
+		wacom_i4_parse_pen_report(wdata, input, data);
+		break;
+	case 0x03: /* Features Report */
+		wdata->features = data[2];
+		break;
+	case 0x0C: /* Button report */
+		break;
+	default:
+		hid_err(hdev, "Unknown report: %d,%d\n", data[0], data[1]);
+		break;
+	}
+}
+
 static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
 		u8 *raw_data, int size)
 {
@@ -184,122 +270,147 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
 	struct hid_input *hidinput;
 	struct input_dev *input;
 	unsigned char *data = (unsigned char *) raw_data;
-	int tool, x, y, rw;
+	int i, tool, x, y, rw;
 
 	if (!(hdev->claimed & HID_CLAIMED_INPUT))
 		return 0;
 
-	tool = 0;
 	hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
 	input = hidinput->input;
 
-	/* Check if this is a tablet report */
-	if (data[0] != 0x03)
-		return 0;
+	switch (hdev->product) {
+	case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
+		tool = 0;
+		/* Check if this is a tablet report */
+		if (data[0] != 0x03)
+			return 0;
+
+		/* Get X & Y positions */
+		x = le16_to_cpu(*(__le16 *) &data[2]);
+		y = le16_to_cpu(*(__le16 *) &data[4]);
+
+		/* Get current tool identifier */
+		if (data[1] & 0x90) { /* If pen is in the in/active area */
+			switch ((data[1] >> 5) & 3) {
+			case 0:	/* Pen */
+				tool = BTN_TOOL_PEN;
+				break;
+
+			case 1: /* Rubber */
+				tool = BTN_TOOL_RUBBER;
+				break;
+
+			case 2: /* Mouse with wheel */
+			case 3: /* Mouse without wheel */
+				tool = BTN_TOOL_MOUSE;
+				break;
+			}
 
-	/* Get X & Y positions */
-	x = le16_to_cpu(*(__le16 *) &data[2]);
-	y = le16_to_cpu(*(__le16 *) &data[4]);
+			/* Reset tool if out of active tablet area */
+			if (!(data[1] & 0x10))
+				tool = 0;
+		}
 
-	/* Get current tool identifier */
-	if (data[1] & 0x90) { /* If pen is in the in/active area */
-		switch ((data[1] >> 5) & 3) {
-		case 0:	/* Pen */
-			tool = BTN_TOOL_PEN;
-			break;
+		/* If tool changed, notify input subsystem */
+		if (wdata->tool != tool) {
+			if (wdata->tool) {
+				/* Completely reset old tool state */
+				if (wdata->tool == BTN_TOOL_MOUSE) {
+					input_report_key(input, BTN_LEFT, 0);
+					input_report_key(input, BTN_RIGHT, 0);
+					input_report_key(input, BTN_MIDDLE, 0);
+					input_report_abs(input, ABS_DISTANCE,
+					input_abs_get_max(input, ABS_DISTANCE));
+				} else {
+					input_report_key(input, BTN_TOUCH, 0);
+					input_report_key(input, BTN_STYLUS, 0);
+					input_report_key(input, BTN_STYLUS2, 0);
+					input_report_abs(input, ABS_PRESSURE, 0);
+				}
+				input_report_key(input, wdata->tool, 0);
+				input_sync(input);
+			}
+			wdata->tool = tool;
+			if (tool)
+				input_report_key(input, tool, 1);
+		}
 
-		case 1: /* Rubber */
-			tool = BTN_TOOL_RUBBER;
-			break;
+		if (tool) {
+			input_report_abs(input, ABS_X, x);
+			input_report_abs(input, ABS_Y, y);
+
+			switch ((data[1] >> 5) & 3) {
+			case 2: /* Mouse with wheel */
+				input_report_key(input, BTN_MIDDLE, data[1] & 0x04);
+				rw = (data[6] & 0x01) ? -1 :
+					(data[6] & 0x02) ? 1 : 0;
+				input_report_rel(input, REL_WHEEL, rw);
+				/* fall through */
+
+			case 3: /* Mouse without wheel */
+				input_report_key(input, BTN_LEFT, data[1] & 0x01);
+				input_report_key(input, BTN_RIGHT, data[1] & 0x02);
+				/* Compute distance between mouse and tablet */
+				rw = 44 - (data[6] >> 2);
+				if (rw < 0)
+					rw = 0;
+				else if (rw > 31)
+					rw = 31;
+				input_report_abs(input, ABS_DISTANCE, rw);
+				break;
+
+			default:
+				input_report_abs(input, ABS_PRESSURE,
+					data[6] | (((__u16) (data[1] & 0x08)) << 5));
+				input_report_key(input, BTN_TOUCH, data[1] & 0x01);
+				input_report_key(input, BTN_STYLUS, data[1] & 0x02);
+				input_report_key(input, BTN_STYLUS2,
+					(tool == BTN_TOOL_PEN) &&
+					data[1] & 0x04);
+				break;
+			}
 
-		case 2: /* Mouse with wheel */
-		case 3: /* Mouse without wheel */
-			tool = BTN_TOOL_MOUSE;
-			break;
+			input_sync(input);
 		}
 
-		/* Reset tool if out of active tablet area */
-		if (!(data[1] & 0x10))
-			tool = 0;
-	}
-
-	/* If tool changed, notify input subsystem */
-	if (wdata->tool != tool) {
-		if (wdata->tool) {
-			/* Completely reset old tool state */
-			if (wdata->tool == BTN_TOOL_MOUSE) {
-				input_report_key(input, BTN_LEFT, 0);
-				input_report_key(input, BTN_RIGHT, 0);
-				input_report_key(input, BTN_MIDDLE, 0);
-				input_report_abs(input, ABS_DISTANCE,
-					input_abs_get_max(input, ABS_DISTANCE));
-			} else {
-				input_report_key(input, BTN_TOUCH, 0);
-				input_report_key(input, BTN_STYLUS, 0);
-				input_report_key(input, BTN_STYLUS2, 0);
-				input_report_abs(input, ABS_PRESSURE, 0);
-			}
-			input_report_key(input, wdata->tool, 0);
+		/* Report the state of the two buttons at the top of the tablet
+		 * as two extra fingerpad keys (buttons 4 & 5). */
+		rw = data[7] & 0x03;
+		if (rw != wdata->butstate) {
+			wdata->butstate = rw;
+			input_report_key(input, BTN_LEFT, rw & 0x02);
+			input_report_key(input, BTN_RIGHT, rw & 0x01);
+			input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
 			input_sync(input);
 		}
-		wdata->tool = tool;
-		if (tool)
-			input_report_key(input, tool, 1);
-	}
 
-	if (tool) {
-		input_report_abs(input, ABS_X, x);
-		input_report_abs(input, ABS_Y, y);
+#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
+		/* Store current battery capacity */
+		rw = (data[7] >> 2 & 0x07);
+		if (rw != wdata->battery_capacity)
+			wdata->battery_capacity = rw;
+#endif
+		break;
+	case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
+		i = 1;
 
-		switch ((data[1] >> 5) & 3) {
-		case 2: /* Mouse with wheel */
-			input_report_key(input, BTN_MIDDLE, data[1] & 0x04);
-			rw = (data[6] & 0x01) ? -1 :
-				(data[6] & 0x02) ? 1 : 0;
-			input_report_rel(input, REL_WHEEL, rw);
+		switch (data[0]) {
+		case 0x04:
+			wacom_i4_parse_report(hdev, wdata, input, data + i);
+			i += 10;
 			/* fall through */
-
-		case 3: /* Mouse without wheel */
-			input_report_key(input, BTN_LEFT, data[1] & 0x01);
-			input_report_key(input, BTN_RIGHT, data[1] & 0x02);
-			/* Compute distance between mouse and tablet */
-			rw = 44 - (data[6] >> 2);
-			if (rw < 0)
-				rw = 0;
-			else if (rw > 31)
-				rw = 31;
-			input_report_abs(input, ABS_DISTANCE, rw);
+		case 0x03:
+			wacom_i4_parse_report(hdev, wdata, input, data + i);
+			i += 10;
+			wacom_i4_parse_report(hdev, wdata, input, data + i);
 			break;
-
 		default:
-			input_report_abs(input, ABS_PRESSURE,
-					data[6] | (((__u16) (data[1] & 0x08)) << 5));
-			input_report_key(input, BTN_TOUCH, data[1] & 0x01);
-			input_report_key(input, BTN_STYLUS, data[1] & 0x02);
-			input_report_key(input, BTN_STYLUS2, (tool == BTN_TOOL_PEN) && data[1] & 0x04);
-			break;
+			hid_err(hdev, "Unknown report: %d,%d size:%d\n",
+					data[0], data[1], size);
+			return 0;
 		}
-
-		input_sync(input);
-	}
-
-	/* Report the state of the two buttons at the top of the tablet
-	 * as two extra fingerpad keys (buttons 4 & 5). */
-	rw = data[7] & 0x03;
-	if (rw != wdata->butstate) {
-		wdata->butstate = rw;
-		input_report_key(input, BTN_LEFT, rw & 0x02);
-		input_report_key(input, BTN_RIGHT, rw & 0x01);
-		input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
-		input_sync(input);
+		break;
 	}
-
-#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
-	/* Store current battery capacity */
-	rw = (data[7] >> 2 & 0x07);
-	if (rw != wdata->battery_capacity)
-		wdata->battery_capacity = rw;
-#endif
 	return 1;
 }
 
@@ -372,38 +483,49 @@ static int wacom_probe(struct hid_device *hdev,
 	hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
 	input = hidinput->input;
 
-	__set_bit(INPUT_PROP_POINTER, input->propbit);
+	__set_bit(BTN_TOOL_PEN, input->keybit);
+	__set_bit(BTN_TOOL_RUBBER, input->keybit);
+	__set_bit(BTN_TOUCH, input->keybit);
 
-	/* Basics */
 	input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
 
-	__set_bit(REL_WHEEL, input->relbit);
+	switch (hdev->product) {
+	case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
+		__set_bit(INPUT_PROP_POINTER, input->propbit);
 
-	__set_bit(BTN_TOOL_PEN, input->keybit);
-	__set_bit(BTN_TOUCH, input->keybit);
-	__set_bit(BTN_STYLUS, input->keybit);
-	__set_bit(BTN_STYLUS2, input->keybit);
-	__set_bit(BTN_LEFT, input->keybit);
-	__set_bit(BTN_RIGHT, input->keybit);
-	__set_bit(BTN_MIDDLE, input->keybit);
+		/* Basics */
+		__set_bit(REL_WHEEL, input->relbit);
+		__set_bit(BTN_STYLUS, input->keybit);
+		__set_bit(BTN_STYLUS2, input->keybit);
+		__set_bit(BTN_LEFT, input->keybit);
+		__set_bit(BTN_RIGHT, input->keybit);
+		__set_bit(BTN_MIDDLE, input->keybit);
 
-	/* Pad */
-	input->evbit[0] |= BIT(EV_MSC);
+		/* Pad */
+		input->evbit[0] |= BIT(EV_MSC);
 
-	__set_bit(MSC_SERIAL, input->mscbit);
+		__set_bit(MSC_SERIAL, input->mscbit);
 
-	__set_bit(BTN_0, input->keybit);
-	__set_bit(BTN_1, input->keybit);
-	__set_bit(BTN_TOOL_FINGER, input->keybit);
+		__set_bit(BTN_0, input->keybit);
+		__set_bit(BTN_1, input->keybit);
+		__set_bit(BTN_TOOL_FINGER, input->keybit);
 
-	/* Distance, rubber and mouse */
-	__set_bit(BTN_TOOL_RUBBER, input->keybit);
-	__set_bit(BTN_TOOL_MOUSE, input->keybit);
+		/* Distance, rubber and mouse */
+		__set_bit(BTN_TOOL_MOUSE, input->keybit);
 
-	input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
-	input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
-	input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
-	input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
+		input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
+		input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
+		input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
+		input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
+		break;
+	case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
+		wdata->features = 0;
+		input_set_abs_params(input, ABS_X, 0, 40640, 4, 0);
+		input_set_abs_params(input, ABS_Y, 0, 25400, 4, 0);
+		input_set_abs_params(input, ABS_PRESSURE, 0, 2047, 0, 0);
+		wacom_set_features(hdev);
+		break;
+	}
 
 	return 0;
 
@@ -436,7 +558,7 @@ static void wacom_remove(struct hid_device *hdev)
 
 static const struct hid_device_id wacom_devices[] = {
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
-
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
 	{ }
 };
 MODULE_DEVICE_TABLE(hid, wacom_devices);
@@ -467,4 +589,3 @@ static void __exit wacom_exit(void)
 module_init(wacom_init);
 module_exit(wacom_exit);
 MODULE_LICENSE("GPL");
-
-- 
1.7.6

------------------------------------------------------------------------------
All the data continuously generated in your IT infrastructure contains a
definitive record of customers, application performance, security
threats, fraudulent activity and more. Splunk takes this data and makes
sense of it. Business sense. IT sense. Common sense.
http://p.sf.net/sfu/splunk-d2d-oct
_______________________________________________
Linuxwacom-devel mailing list
Linuxwacom-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxwacom-devel

Reply via email to