All of lore.kernel.org
 help / color / mirror / Atom feed
* RFC: Wacom Bluetooth HID driver, first pass
@ 2009-03-13 23:59 Bastien Nocera
  2009-03-16 10:55   ` Bastien Nocera
  2009-03-16 23:55   ` Bastien Nocera
  0 siblings, 2 replies; 20+ messages in thread
From: Bastien Nocera @ 2009-03-13 23:59 UTC (permalink / raw)
  To: linux-input; +Cc: mjg, Peter Hutterer, zap

[-- Attachment #1: Type: text/plain, Size: 793 bytes --]

Heya,

This is a first pass at a driver for the Wacom Graphire Bluetooth
tablet, based on work by Andrew Zabolotny[1]. It's mildly tested (eg. it
works in my very few tests).

I was looking for guidance for the code itself, possibly making it
easier to merge in the input/tablet/wacom* drivers into it in the
future, as well as some explanations as to how I'm supposed to reset
tools, etc. so the user-space drivers (the X.org linuxwacom driver)
doesn't need special-casing for the device.

Note that it requires a user-space activation to switch to Mode 2, using
bluetoothd (as the Bluetooth HID doesn't support hid_output_raw_event).
Patch at:
http://cvs.fedoraproject.org/viewvc/rpms/bluez/devel/bluez-activate-wacom-mode2.patch?view=markup

Cheers

[1]: http://cs.ozerki.net/zap/wacom-bt/

[-- Attachment #2: linux-bt-wacom-draft.patch --]
[-- Type: text/x-patch, Size: 9126 bytes --]

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index e85c8fe..f3244e3 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,13 @@ config THRUSTMASTER_FF
 	  Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
 	  a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel.
 
+config HID_WACOM
+	tristate "Wacom Bluetooth devices support" if EMBEDDED
+	depends on BT_HIDP
+	default !EMBEDDED
+	---help---
+	Support for Wacom Graphire Bluetooth tablet.
+
 config ZEROPLUS_FF
 	tristate "Zeroplus based game controller support"
 	depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fbd021f..410b302 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_GREENASIA_FF)	+= hid-gaff.o
 obj-$(CONFIG_THRUSTMASTER_FF)	+= hid-tmff.o
 obj-$(CONFIG_HID_TOPSEED)	+= hid-topseed.o
 obj-$(CONFIG_ZEROPLUS_FF)	+= hid-zpff.o
+obj-$(CONFIG_HID_WACOM)		+= hid-wacom.o
 
 obj-$(CONFIG_USB_HID)		+= usbhid/
 obj-$(CONFIG_USB_MOUSE)		+= usbhid/
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 1cc9674..d134f5d 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1305,6 +1305,7 @@ static const struct hid_device_id hid_blacklist[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
 
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 8851197..e8f96c2 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -400,6 +400,7 @@
 #define USB_DEVICE_ID_VERNIER_LCSPEC	0x0006
 
 #define USB_VENDOR_ID_WACOM		0x056a
+#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH	0x81
 
 #define USB_VENDOR_ID_WISEGROUP		0x0925
 #define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
--- /dev/null	2009-03-13 17:21:47.514016387 +0000
+++ linux-2.6/drivers/hid/hid-wacom.c	2009-03-13 23:39:05.000000000 +0000
@@ -0,0 +1,268 @@
+/*
+ *  Bluetooth Wacom Tablet support
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
+ *  Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
+ *  Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
+ */
+
+/*
+ * 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 the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+struct wacom_data {
+	__u16 tool;
+	unsigned char butstate;
+};
+
+static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
+		u8 *raw_data, int size)
+{
+	struct wacom_data *wdata = hid_get_drvdata(hdev);
+	struct hid_input *hidinput;
+	struct input_dev *input;
+	unsigned char *data = (unsigned char *) raw_data;
+	int tool, x, y, rw, need_sync;
+
+	if (!(hdev->claimed & HID_CLAIMED_INPUT))
+		return 0;
+
+	tool = 0;
+	need_sync = 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;
+
+	/* 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;
+		}
+
+		/* 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->absmax[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_sync(input);
+			input_report_key(input, wdata->tool, 0);
+			input_sync(input);
+		}
+		if ((wdata->tool = tool)) {
+			input_report_key(input, tool, 1);
+			need_sync = 1;
+		}
+	}
+
+	if (tool) {
+		input_report_abs(input, ABS_X, x);
+		input_report_abs(input, ABS_Y, y);
+		need_sync = 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);
+			/* 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;
+			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, data[1] & 0x04);
+			break;
+		}
+	}
+
+	if (need_sync) {
+		input_sync(input);
+		need_sync = 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_0, rw & 0x02);
+		input_report_key(input, BTN_1, rw & 0x01);
+		need_sync = 1;
+	}
+
+	if (need_sync) {
+		input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
+		input_sync(input);
+	}
+
+	return 1;
+}
+
+static int wacom_probe(struct hid_device *hdev,
+		const struct hid_device_id *id)
+{
+	struct hid_input *hidinput;
+	struct input_dev *input;
+	struct wacom_data *wdata;
+	int ret;
+
+	wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
+	if (wdata == NULL) {
+		dev_err(&hdev->dev, "can't alloc wacom descriptor\n");
+		return -ENOMEM;
+	}
+
+	hid_set_drvdata(hdev, wdata);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
+	input = hidinput->input;
+
+	/* Basics */
+	input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
+	input->absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) |
+		BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
+	input->relbit[0] |= BIT(REL_WHEEL);
+	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);
+
+	/* Pad */
+	input->evbit[0] |= BIT(EV_MSC);
+	input->mscbit[0] |= BIT(MSC_SERIAL);
+
+	/* Distance, rubber and mouse */
+	input->absbit[0] |= BIT(ABS_DISTANCE);
+	set_bit(BTN_TOOL_RUBBER, input->keybit);
+	set_bit(BTN_TOOL_MOUSE, input->keybit);
+
+	input->absmax[ABS_PRESSURE] = 511;
+	input->absmax[ABS_DISTANCE] = 32;
+
+	input->absmax[ABS_X] = 16704;
+	input->absmax[ABS_Y] = 12064;
+	input->absfuzz[ABS_X] = 4;
+	input->absfuzz[ABS_Y] = 4;
+
+	return 0;
+err_free:
+	kfree(wdata);
+	return ret;
+}
+
+static void wacom_remove(struct hid_device *hdev)
+{
+	hid_hw_stop(hdev);
+	kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id wacom_devices[] = {
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
+
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, wacom_devices);
+
+static struct hid_driver wacom_driver = {
+	.name = "wacom",
+	.id_table = wacom_devices,
+	.probe = wacom_probe,
+	.remove = wacom_remove,
+	.raw_event = wacom_raw_event,
+};
+
+static int wacom_init(void)
+{
+	int ret;
+
+	ret = hid_register_driver(&wacom_driver);
+	if (ret)
+		printk(KERN_ERR "can't register wacom driver\n");
+	printk(KERN_ERR "wacom driver registered\n");
+	return ret;
+}
+
+static void wacom_exit(void)
+{
+	hid_unregister_driver(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(wacom);

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

* Re: RFC: Wacom Bluetooth HID driver, first pass
@ 2009-03-16 10:55   ` Bastien Nocera
  0 siblings, 0 replies; 20+ messages in thread
From: Bastien Nocera @ 2009-03-16 10:55 UTC (permalink / raw)
  To: linux-input, BlueZ development; +Cc: mjg, Peter Hutterer, zap

On Fri, 2009-03-13 at 23:59 +0000, Bastien Nocera wrote:
> Heya,
> 
> This is a first pass at a driver for the Wacom Graphire Bluetooth
> tablet, based on work by Andrew Zabolotny[1]. It's mildly tested (eg. it
> works in my very few tests).
> 
> I was looking for guidance for the code itself, possibly making it
> easier to merge in the input/tablet/wacom* drivers into it in the
> future, as well as some explanations as to how I'm supposed to reset
> tools, etc. so the user-space drivers (the X.org linuxwacom driver)
> doesn't need special-casing for the device.
> 
> Note that it requires a user-space activation to switch to Mode 2, using
> bluetoothd (as the Bluetooth HID doesn't support hid_output_raw_event).
> Patch at:
> http://cvs.fedoraproject.org/viewvc/rpms/bluez/devel/bluez-activate-wacom-mode2.patch?view=markup

A couple of notes:
- the wheel action is reversed, it's a simple fix, done locally
- hidp and the Bluetooth sub-system says it can't probe the device with
error "-14" when hid-wacom.ko isn't already loaded
- I'm getting oopses in hci_conn_del() when the device goes away (eg. I
turn it off).

I'd appreciate any help making this just a tad more stable.

Any comments about the code itself?

Cheers

PS: Original mail at
http://thread.gmane.org/gmane.linux.kernel.input/7078 for the Bluetooth
hackers


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

* Re: RFC: Wacom Bluetooth HID driver, first pass
@ 2009-03-16 10:55   ` Bastien Nocera
  0 siblings, 0 replies; 20+ messages in thread
From: Bastien Nocera @ 2009-03-16 10:55 UTC (permalink / raw)
  To: linux-input, BlueZ development
  Cc: mjg, Peter Hutterer, zap-ez6wqqBRKvoox3rIn2DAYQ

On Fri, 2009-03-13 at 23:59 +0000, Bastien Nocera wrote:
> Heya,
> 
> This is a first pass at a driver for the Wacom Graphire Bluetooth
> tablet, based on work by Andrew Zabolotny[1]. It's mildly tested (eg. it
> works in my very few tests).
> 
> I was looking for guidance for the code itself, possibly making it
> easier to merge in the input/tablet/wacom* drivers into it in the
> future, as well as some explanations as to how I'm supposed to reset
> tools, etc. so the user-space drivers (the X.org linuxwacom driver)
> doesn't need special-casing for the device.
> 
> Note that it requires a user-space activation to switch to Mode 2, using
> bluetoothd (as the Bluetooth HID doesn't support hid_output_raw_event).
> Patch at:
> http://cvs.fedoraproject.org/viewvc/rpms/bluez/devel/bluez-activate-wacom-mode2.patch?view=markup

A couple of notes:
- the wheel action is reversed, it's a simple fix, done locally
- hidp and the Bluetooth sub-system says it can't probe the device with
error "-14" when hid-wacom.ko isn't already loaded
- I'm getting oopses in hci_conn_del() when the device goes away (eg. I
turn it off).

I'd appreciate any help making this just a tad more stable.

Any comments about the code itself?

Cheers

PS: Original mail at
http://thread.gmane.org/gmane.linux.kernel.input/7078 for the Bluetooth
hackers

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

* Re: RFC: Wacom Bluetooth HID driver, first pass
@ 2009-03-16 14:00     ` Marcel Holtmann
  0 siblings, 0 replies; 20+ messages in thread
From: Marcel Holtmann @ 2009-03-16 14:00 UTC (permalink / raw)
  To: Bastien Nocera; +Cc: linux-input, BlueZ development, mjg, Peter Hutterer, zap

Hi Bastien,

>> This is a first pass at a driver for the Wacom Graphire Bluetooth
>> tablet, based on work by Andrew Zabolotny[1]. It's mildly tested  
>> (eg. it
>> works in my very few tests).
>>
>> I was looking for guidance for the code itself, possibly making it
>> easier to merge in the input/tablet/wacom* drivers into it in the
>> future, as well as some explanations as to how I'm supposed to reset
>> tools, etc. so the user-space drivers (the X.org linuxwacom driver)
>> doesn't need special-casing for the device.
>>
>> Note that it requires a user-space activation to switch to Mode 2,  
>> using
>> bluetoothd (as the Bluetooth HID doesn't support  
>> hid_output_raw_event).
>> Patch at:
>> http://cvs.fedoraproject.org/viewvc/rpms/bluez/devel/bluez-activate-wacom-mode2.patch?view=markup
>
> A couple of notes:
> - the wheel action is reversed, it's a simple fix, done locally
> - hidp and the Bluetooth sub-system says it can't probe the device  
> with
> error "-14" when hid-wacom.ko isn't already loaded

what was the reason for not forcing HID to load the generic driver? I  
lost track on how HID is suppose to handle these. You could also  
introduce a phony export like we do with L2CAP to ensure it is loaded  
even if userspace isn't ready to handle this dependency.

> - I'm getting oopses in hci_conn_del() when the device goes away  
> (eg. I
> turn it off).

Try to run a net-next-2.6 or bluetooth-next-2.6 kernel. Kyle should  
have merged these patches into the latest rawhide kernel.

> I'd appreciate any help making this just a tad more stable.
>
> Any comments about the code itself?

The main parsing function looks highly complicated and could either  
use more comments or break into two.

	if ((wdata->tool = tool)) {
		input_report_key(input, tool, 1);
		need_sync = 1;
	}

Don't do the assignment in the if clause test. Did you run  
checkpatch.pl on it?

Regards

Marcel


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

* Re: RFC: Wacom Bluetooth HID driver, first pass
@ 2009-03-16 14:00     ` Marcel Holtmann
  0 siblings, 0 replies; 20+ messages in thread
From: Marcel Holtmann @ 2009-03-16 14:00 UTC (permalink / raw)
  To: Bastien Nocera
  Cc: linux-input, BlueZ development, mjg, Peter Hutterer,
	zap-ez6wqqBRKvoox3rIn2DAYQ

Hi Bastien,

>> This is a first pass at a driver for the Wacom Graphire Bluetooth
>> tablet, based on work by Andrew Zabolotny[1]. It's mildly tested  
>> (eg. it
>> works in my very few tests).
>>
>> I was looking for guidance for the code itself, possibly making it
>> easier to merge in the input/tablet/wacom* drivers into it in the
>> future, as well as some explanations as to how I'm supposed to reset
>> tools, etc. so the user-space drivers (the X.org linuxwacom driver)
>> doesn't need special-casing for the device.
>>
>> Note that it requires a user-space activation to switch to Mode 2,  
>> using
>> bluetoothd (as the Bluetooth HID doesn't support  
>> hid_output_raw_event).
>> Patch at:
>> http://cvs.fedoraproject.org/viewvc/rpms/bluez/devel/bluez-activate-wacom-mode2.patch?view=markup
>
> A couple of notes:
> - the wheel action is reversed, it's a simple fix, done locally
> - hidp and the Bluetooth sub-system says it can't probe the device  
> with
> error "-14" when hid-wacom.ko isn't already loaded

what was the reason for not forcing HID to load the generic driver? I  
lost track on how HID is suppose to handle these. You could also  
introduce a phony export like we do with L2CAP to ensure it is loaded  
even if userspace isn't ready to handle this dependency.

> - I'm getting oopses in hci_conn_del() when the device goes away  
> (eg. I
> turn it off).

Try to run a net-next-2.6 or bluetooth-next-2.6 kernel. Kyle should  
have merged these patches into the latest rawhide kernel.

> I'd appreciate any help making this just a tad more stable.
>
> Any comments about the code itself?

The main parsing function looks highly complicated and could either  
use more comments or break into two.

	if ((wdata->tool = tool)) {
		input_report_key(input, tool, 1);
		need_sync = 1;
	}

Don't do the assignment in the if clause test. Did you run  
checkpatch.pl on it?

Regards

Marcel

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

* Re: RFC: Wacom Bluetooth HID driver, first pass
@ 2009-03-16 14:29       ` Bastien Nocera
  0 siblings, 0 replies; 20+ messages in thread
From: Bastien Nocera @ 2009-03-16 14:29 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-input, BlueZ development, mjg, Peter Hutterer, zap

On Mon, 2009-03-16 at 15:00 +0100, Marcel Holtmann wrote:
> Hi Bastien,
> 
> >> This is a first pass at a driver for the Wacom Graphire Bluetooth
> >> tablet, based on work by Andrew Zabolotny[1]. It's mildly tested  
> >> (eg. it
> >> works in my very few tests).
> >>
> >> I was looking for guidance for the code itself, possibly making it
> >> easier to merge in the input/tablet/wacom* drivers into it in the
> >> future, as well as some explanations as to how I'm supposed to reset
> >> tools, etc. so the user-space drivers (the X.org linuxwacom driver)
> >> doesn't need special-casing for the device.
> >>
> >> Note that it requires a user-space activation to switch to Mode 2,  
> >> using
> >> bluetoothd (as the Bluetooth HID doesn't support  
> >> hid_output_raw_event).
> >> Patch at:
> >> http://cvs.fedoraproject.org/viewvc/rpms/bluez/devel/bluez-activate-wacom-mode2.patch?view=markup
> >
> > A couple of notes:
> > - the wheel action is reversed, it's a simple fix, done locally
> > - hidp and the Bluetooth sub-system says it can't probe the device  
> > with
> > error "-14" when hid-wacom.ko isn't already loaded
> 
> what was the reason for not forcing HID to load the generic driver?

What generic driver? If you're talking about the default Wacom driver,
it isn't an HID driver, and requires a USB device.

>  I  
> lost track on how HID is suppose to handle these. You could also  
> introduce a phony export like we do with L2CAP to ensure it is loaded  
> even if userspace isn't ready to handle this dependency.

Well, the problem is that the driver _does_ load, but I'm not certain
about the sequence of events between the Bluetooth sub-system, the
user-space enabling (see the linked patch to bluetoothd), and the HID
sub-system.

> > - I'm getting oopses in hci_conn_del() when the device goes away  
> > (eg. I
> > turn it off).
> 
> Try to run a net-next-2.6 or bluetooth-next-2.6 kernel. Kyle should  
> have merged these patches into the latest rawhide kernel.

I was hoping this would be fixed in the latest linus-2.6 tree, guess
not.

> > I'd appreciate any help making this just a tad more stable.
> >
> > Any comments about the code itself?
> 
> The main parsing function looks highly complicated and could either  
> use more comments or break into two.

The parsing is adapted from the code Andrew wrote, so I don't have any
great insights into it. Right it's in "it works well enough to do
things" mode, clean-ups and bug fixes will probably follow when my
machine stops crashing when using that driver :)

> 	if ((wdata->tool = tool)) {
> 		input_report_key(input, tool, 1);
> 		need_sync = 1;
> 	}
> 
> Don't do the assignment in the if clause test.

As I mentioned, copy/paste code this is.

>  Did you run  
> checkpatch.pl on it?

Nope, I didn't, but will do.

Cheers

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

* Re: RFC: Wacom Bluetooth HID driver, first pass
@ 2009-03-16 14:29       ` Bastien Nocera
  0 siblings, 0 replies; 20+ messages in thread
From: Bastien Nocera @ 2009-03-16 14:29 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: linux-input, BlueZ development, mjg, Peter Hutterer,
	zap-ez6wqqBRKvoox3rIn2DAYQ

On Mon, 2009-03-16 at 15:00 +0100, Marcel Holtmann wrote:
> Hi Bastien,
> 
> >> This is a first pass at a driver for the Wacom Graphire Bluetooth
> >> tablet, based on work by Andrew Zabolotny[1]. It's mildly tested  
> >> (eg. it
> >> works in my very few tests).
> >>
> >> I was looking for guidance for the code itself, possibly making it
> >> easier to merge in the input/tablet/wacom* drivers into it in the
> >> future, as well as some explanations as to how I'm supposed to reset
> >> tools, etc. so the user-space drivers (the X.org linuxwacom driver)
> >> doesn't need special-casing for the device.
> >>
> >> Note that it requires a user-space activation to switch to Mode 2,  
> >> using
> >> bluetoothd (as the Bluetooth HID doesn't support  
> >> hid_output_raw_event).
> >> Patch at:
> >> http://cvs.fedoraproject.org/viewvc/rpms/bluez/devel/bluez-activate-wacom-mode2.patch?view=markup
> >
> > A couple of notes:
> > - the wheel action is reversed, it's a simple fix, done locally
> > - hidp and the Bluetooth sub-system says it can't probe the device  
> > with
> > error "-14" when hid-wacom.ko isn't already loaded
> 
> what was the reason for not forcing HID to load the generic driver?

What generic driver? If you're talking about the default Wacom driver,
it isn't an HID driver, and requires a USB device.

>  I  
> lost track on how HID is suppose to handle these. You could also  
> introduce a phony export like we do with L2CAP to ensure it is loaded  
> even if userspace isn't ready to handle this dependency.

Well, the problem is that the driver _does_ load, but I'm not certain
about the sequence of events between the Bluetooth sub-system, the
user-space enabling (see the linked patch to bluetoothd), and the HID
sub-system.

> > - I'm getting oopses in hci_conn_del() when the device goes away  
> > (eg. I
> > turn it off).
> 
> Try to run a net-next-2.6 or bluetooth-next-2.6 kernel. Kyle should  
> have merged these patches into the latest rawhide kernel.

I was hoping this would be fixed in the latest linus-2.6 tree, guess
not.

> > I'd appreciate any help making this just a tad more stable.
> >
> > Any comments about the code itself?
> 
> The main parsing function looks highly complicated and could either  
> use more comments or break into two.

The parsing is adapted from the code Andrew wrote, so I don't have any
great insights into it. Right it's in "it works well enough to do
things" mode, clean-ups and bug fixes will probably follow when my
machine stops crashing when using that driver :)

> 	if ((wdata->tool = tool)) {
> 		input_report_key(input, tool, 1);
> 		need_sync = 1;
> 	}
> 
> Don't do the assignment in the if clause test.

As I mentioned, copy/paste code this is.

>  Did you run  
> checkpatch.pl on it?

Nope, I didn't, but will do.

Cheers

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

* [PATCH] Wacom Graphire Bluetooth driver
@ 2009-03-16 23:55   ` Bastien Nocera
  0 siblings, 0 replies; 20+ messages in thread
From: Bastien Nocera @ 2009-03-16 23:55 UTC (permalink / raw)
  To: linux-input; +Cc: mjg, Peter Hutterer, zap, BlueZ development

>>From 3495e3be72d1df6132d5f5d978d2def3e4d57fc3 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Mon, 16 Mar 2009 23:36:54 +0000
Subject: [PATCH] Wacom Graphire Bluetooth driver

Signed-off-by: Bastien Nocera <hadess@hadess.net>

Based on the work by Andrew Zabolotny, an HID driver for the Bluetooth
Wacom tablet. This is required as it uses a slightly different
protocols from what's currently support by the drivers/input/wacom*
driver, and those only support USB.

A user-space patch is required to activate mode 2 of the Wacom tablet,
as hidp does not support hid_output_raw_report.
---
 drivers/hid/Kconfig     |    7 ++
 drivers/hid/Makefile    |    1 +
 drivers/hid/hid-core.c  |    1 +
 drivers/hid/hid-ids.h   |    1 +
 drivers/hid/hid-wacom.c |  272 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 282 insertions(+), 0 deletions(-)
 create mode 100644 drivers/hid/hid-wacom.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index e85c8fe..f3244e3 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,13 @@ config THRUSTMASTER_FF
 	  Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
 	  a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel.
 
+config HID_WACOM
+	tristate "Wacom Bluetooth devices support" if EMBEDDED
+	depends on BT_HIDP
+	default !EMBEDDED
+	---help---
+	Support for Wacom Graphire Bluetooth tablet.
+
 config ZEROPLUS_FF
 	tristate "Zeroplus based game controller support"
 	depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fbd021f..410b302 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_GREENASIA_FF)	+= hid-gaff.o
 obj-$(CONFIG_THRUSTMASTER_FF)	+= hid-tmff.o
 obj-$(CONFIG_HID_TOPSEED)	+= hid-topseed.o
 obj-$(CONFIG_ZEROPLUS_FF)	+= hid-zpff.o
+obj-$(CONFIG_HID_WACOM)		+= hid-wacom.o
 
 obj-$(CONFIG_USB_HID)		+= usbhid/
 obj-$(CONFIG_USB_MOUSE)		+= usbhid/
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 1cc9674..d134f5d 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1305,6 +1305,7 @@ static const struct hid_device_id hid_blacklist[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
 
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 8851197..e8f96c2 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -400,6 +400,7 @@
 #define USB_DEVICE_ID_VERNIER_LCSPEC	0x0006
 
 #define USB_VENDOR_ID_WACOM		0x056a
+#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH	0x81
 
 #define USB_VENDOR_ID_WISEGROUP		0x0925
 #define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
new file mode 100644
index 0000000..8f9783f
--- /dev/null
+++ b/drivers/hid/hid-wacom.c
@@ -0,0 +1,272 @@
+/*
+ *  Bluetooth Wacom Tablet support
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
+ *  Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
+ *  Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
+ */
+
+/*
+ * 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 the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+struct wacom_data {
+	__u16 tool;
+	unsigned char butstate;
+};
+
+static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
+		u8 *raw_data, int size)
+{
+	struct wacom_data *wdata = hid_get_drvdata(hdev);
+	struct hid_input *hidinput;
+	struct input_dev *input;
+	unsigned char *data = (unsigned char *) raw_data;
+	int tool, x, y, rw, need_sync;
+
+	if (!(hdev->claimed & HID_CLAIMED_INPUT))
+		return 0;
+
+	tool = 0;
+	need_sync = 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;
+
+	/* 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;
+		}
+
+		/* 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->absmax[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_sync(input);
+			input_report_key(input, wdata->tool, 0);
+			input_sync(input);
+		}
+		wdata->tool = tool;
+		if (tool) {
+			input_report_key(input, tool, 1);
+			need_sync = 1;
+		}
+	}
+
+	if (tool) {
+		input_report_abs(input, ABS_X, x);
+		input_report_abs(input, ABS_Y, y);
+		need_sync = 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);
+			/* 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, data[1] & 0x04);
+			break;
+		}
+	}
+
+	if (need_sync) {
+		input_sync(input);
+		need_sync = 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_0, rw & 0x02);
+		input_report_key(input, BTN_1, rw & 0x01);
+		need_sync = 1;
+	}
+
+	if (need_sync) {
+		input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
+		input_sync(input);
+	}
+
+	return 1;
+}
+
+static int wacom_probe(struct hid_device *hdev,
+		const struct hid_device_id *id)
+{
+	struct hid_input *hidinput;
+	struct input_dev *input;
+	struct wacom_data *wdata;
+	int ret;
+
+	wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
+	if (wdata == NULL) {
+		dev_err(&hdev->dev, "can't alloc wacom descriptor\n");
+		return -ENOMEM;
+	}
+
+	hid_set_drvdata(hdev, wdata);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
+	input = hidinput->input;
+
+	/* Basics */
+	input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
+	input->absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) |
+		BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
+	input->relbit[0] |= BIT(REL_WHEEL);
+	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);
+
+	/* Pad */
+	input->evbit[0] |= BIT(EV_MSC);
+	input->mscbit[0] |= BIT(MSC_SERIAL);
+
+	/* Distance, rubber and mouse */
+	input->absbit[0] |= BIT(ABS_DISTANCE);
+	set_bit(BTN_TOOL_RUBBER, input->keybit);
+	set_bit(BTN_TOOL_MOUSE, input->keybit);
+
+	input->absmax[ABS_PRESSURE] = 511;
+	input->absmax[ABS_DISTANCE] = 32;
+
+	input->absmax[ABS_X] = 16704;
+	input->absmax[ABS_Y] = 12064;
+	input->absfuzz[ABS_X] = 4;
+	input->absfuzz[ABS_Y] = 4;
+
+	return 0;
+err_free:
+	kfree(wdata);
+	return ret;
+}
+
+static void wacom_remove(struct hid_device *hdev)
+{
+	hid_hw_stop(hdev);
+	kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id wacom_devices[] = {
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
+
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, wacom_devices);
+
+static struct hid_driver wacom_driver = {
+	.name = "wacom",
+	.id_table = wacom_devices,
+	.probe = wacom_probe,
+	.remove = wacom_remove,
+	.raw_event = wacom_raw_event,
+};
+
+static int wacom_init(void)
+{
+	int ret;
+
+	ret = hid_register_driver(&wacom_driver);
+	if (ret)
+		printk(KERN_ERR "can't register wacom driver\n");
+	printk(KERN_ERR "wacom driver registered\n");
+	return ret;
+}
+
+static void wacom_exit(void)
+{
+	hid_unregister_driver(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(wacom);
-- 
1.6.2



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

* [PATCH] Wacom Graphire Bluetooth driver
@ 2009-03-16 23:55   ` Bastien Nocera
  0 siblings, 0 replies; 20+ messages in thread
From: Bastien Nocera @ 2009-03-16 23:55 UTC (permalink / raw)
  To: linux-input
  Cc: mjg, Peter Hutterer, zap-ez6wqqBRKvoox3rIn2DAYQ, BlueZ development

>From 3495e3be72d1df6132d5f5d978d2def3e4d57fc3 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess-0MeiytkfxGOsTnJN9+BGXg@public.gmane.org>
Date: Mon, 16 Mar 2009 23:36:54 +0000
Subject: [PATCH] Wacom Graphire Bluetooth driver

Signed-off-by: Bastien Nocera <hadess-0MeiytkfxGOsTnJN9+BGXg@public.gmane.org>

Based on the work by Andrew Zabolotny, an HID driver for the Bluetooth
Wacom tablet. This is required as it uses a slightly different
protocols from what's currently support by the drivers/input/wacom*
driver, and those only support USB.

A user-space patch is required to activate mode 2 of the Wacom tablet,
as hidp does not support hid_output_raw_report.
---
 drivers/hid/Kconfig     |    7 ++
 drivers/hid/Makefile    |    1 +
 drivers/hid/hid-core.c  |    1 +
 drivers/hid/hid-ids.h   |    1 +
 drivers/hid/hid-wacom.c |  272 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 282 insertions(+), 0 deletions(-)
 create mode 100644 drivers/hid/hid-wacom.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index e85c8fe..f3244e3 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,13 @@ config THRUSTMASTER_FF
 	  Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
 	  a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel.
 
+config HID_WACOM
+	tristate "Wacom Bluetooth devices support" if EMBEDDED
+	depends on BT_HIDP
+	default !EMBEDDED
+	---help---
+	Support for Wacom Graphire Bluetooth tablet.
+
 config ZEROPLUS_FF
 	tristate "Zeroplus based game controller support"
 	depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fbd021f..410b302 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_GREENASIA_FF)	+= hid-gaff.o
 obj-$(CONFIG_THRUSTMASTER_FF)	+= hid-tmff.o
 obj-$(CONFIG_HID_TOPSEED)	+= hid-topseed.o
 obj-$(CONFIG_ZEROPLUS_FF)	+= hid-zpff.o
+obj-$(CONFIG_HID_WACOM)		+= hid-wacom.o
 
 obj-$(CONFIG_USB_HID)		+= usbhid/
 obj-$(CONFIG_USB_MOUSE)		+= usbhid/
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 1cc9674..d134f5d 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1305,6 +1305,7 @@ static const struct hid_device_id hid_blacklist[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
 
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 8851197..e8f96c2 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -400,6 +400,7 @@
 #define USB_DEVICE_ID_VERNIER_LCSPEC	0x0006
 
 #define USB_VENDOR_ID_WACOM		0x056a
+#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH	0x81
 
 #define USB_VENDOR_ID_WISEGROUP		0x0925
 #define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
new file mode 100644
index 0000000..8f9783f
--- /dev/null
+++ b/drivers/hid/hid-wacom.c
@@ -0,0 +1,272 @@
+/*
+ *  Bluetooth Wacom Tablet support
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech-AlSwsSmVLrQ@public.gmane.org>
+ *  Copyright (c) 2005 Michael Haboustak <mike--vDbLwGUA7lNWk0Htik3J/w@public.gmane.org> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby <jirislaby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *  Copyright (c) 2006 Andrew Zabolotny <zap-ez6wqqBRKvoox3rIn2DAYQ@public.gmane.org>
+ *  Copyright (c) 2009 Bastien Nocera <hadess-0MeiytkfxGOsTnJN9+BGXg@public.gmane.org>
+ */
+
+/*
+ * 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 the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+struct wacom_data {
+	__u16 tool;
+	unsigned char butstate;
+};
+
+static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
+		u8 *raw_data, int size)
+{
+	struct wacom_data *wdata = hid_get_drvdata(hdev);
+	struct hid_input *hidinput;
+	struct input_dev *input;
+	unsigned char *data = (unsigned char *) raw_data;
+	int tool, x, y, rw, need_sync;
+
+	if (!(hdev->claimed & HID_CLAIMED_INPUT))
+		return 0;
+
+	tool = 0;
+	need_sync = 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;
+
+	/* 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;
+		}
+
+		/* 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->absmax[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_sync(input);
+			input_report_key(input, wdata->tool, 0);
+			input_sync(input);
+		}
+		wdata->tool = tool;
+		if (tool) {
+			input_report_key(input, tool, 1);
+			need_sync = 1;
+		}
+	}
+
+	if (tool) {
+		input_report_abs(input, ABS_X, x);
+		input_report_abs(input, ABS_Y, y);
+		need_sync = 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);
+			/* 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, data[1] & 0x04);
+			break;
+		}
+	}
+
+	if (need_sync) {
+		input_sync(input);
+		need_sync = 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_0, rw & 0x02);
+		input_report_key(input, BTN_1, rw & 0x01);
+		need_sync = 1;
+	}
+
+	if (need_sync) {
+		input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
+		input_sync(input);
+	}
+
+	return 1;
+}
+
+static int wacom_probe(struct hid_device *hdev,
+		const struct hid_device_id *id)
+{
+	struct hid_input *hidinput;
+	struct input_dev *input;
+	struct wacom_data *wdata;
+	int ret;
+
+	wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
+	if (wdata == NULL) {
+		dev_err(&hdev->dev, "can't alloc wacom descriptor\n");
+		return -ENOMEM;
+	}
+
+	hid_set_drvdata(hdev, wdata);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
+	input = hidinput->input;
+
+	/* Basics */
+	input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
+	input->absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) |
+		BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
+	input->relbit[0] |= BIT(REL_WHEEL);
+	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);
+
+	/* Pad */
+	input->evbit[0] |= BIT(EV_MSC);
+	input->mscbit[0] |= BIT(MSC_SERIAL);
+
+	/* Distance, rubber and mouse */
+	input->absbit[0] |= BIT(ABS_DISTANCE);
+	set_bit(BTN_TOOL_RUBBER, input->keybit);
+	set_bit(BTN_TOOL_MOUSE, input->keybit);
+
+	input->absmax[ABS_PRESSURE] = 511;
+	input->absmax[ABS_DISTANCE] = 32;
+
+	input->absmax[ABS_X] = 16704;
+	input->absmax[ABS_Y] = 12064;
+	input->absfuzz[ABS_X] = 4;
+	input->absfuzz[ABS_Y] = 4;
+
+	return 0;
+err_free:
+	kfree(wdata);
+	return ret;
+}
+
+static void wacom_remove(struct hid_device *hdev)
+{
+	hid_hw_stop(hdev);
+	kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id wacom_devices[] = {
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
+
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, wacom_devices);
+
+static struct hid_driver wacom_driver = {
+	.name = "wacom",
+	.id_table = wacom_devices,
+	.probe = wacom_probe,
+	.remove = wacom_remove,
+	.raw_event = wacom_raw_event,
+};
+
+static int wacom_init(void)
+{
+	int ret;
+
+	ret = hid_register_driver(&wacom_driver);
+	if (ret)
+		printk(KERN_ERR "can't register wacom driver\n");
+	printk(KERN_ERR "wacom driver registered\n");
+	return ret;
+}
+
+static void wacom_exit(void)
+{
+	hid_unregister_driver(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(wacom);
-- 
1.6.2

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

* Re: RFC: Wacom Bluetooth HID driver, first pass
  2009-03-16 14:29       ` Bastien Nocera
  (?)
@ 2009-03-16 23:58       ` Bastien Nocera
  -1 siblings, 0 replies; 20+ messages in thread
From: Bastien Nocera @ 2009-03-16 23:58 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-input, BlueZ development, mjg, Peter Hutterer, zap

On Mon, 2009-03-16 at 14:29 +0000, Bastien Nocera wrote:
> On Mon, 2009-03-16 at 15:00 +0100, Marcel Holtmann wrote:
<snip>
> > > A couple of notes:
> > > - the wheel action is reversed, it's a simple fix, done locally
> > > - hidp and the Bluetooth sub-system says it can't probe the device  
> > > with
> > > error "-14" when hid-wacom.ko isn't already loaded

Bluetooth: HIDP (Human Interface Emulation) ver 1.2 
wacom 0005:056A:0081.0001: parse failed
wacom: probe of 0005:056A:0081.0001 failed with error -14 
wacom driver registered

> > what was the reason for not forcing HID to load the generic driver?
> 
> What generic driver? If you're talking about the default Wacom driver,
> it isn't an HID driver, and requires a USB device.
> 
> >  I  
> > lost track on how HID is suppose to handle these. You could also  
> > introduce a phony export like we do with L2CAP to ensure it is loaded  
> > even if userspace isn't ready to handle this dependency.
> 
> Well, the problem is that the driver _does_ load, but I'm not certain
> about the sequence of events between the Bluetooth sub-system, the
> user-space enabling (see the linked patch to bluetoothd), and the HID
> sub-system.

Turning the device off and on again works though. No idea what's
happening.

> > > - I'm getting oopses in hci_conn_del() when the device goes away  
> > > (eg. I
> > > turn it off).
> > 
> > Try to run a net-next-2.6 or bluetooth-next-2.6 kernel. Kyle should  
> > have merged these patches into the latest rawhide kernel.
> 
> I was hoping this would be fixed in the latest linus-2.6 tree, guess
> not.

No more crashes. Great stuff.

I sent a fixed up patch to the list earlier.

Cheers

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

* Re: [PATCH] Wacom Graphire Bluetooth driver (updated with a few fixes)
  2009-03-16 23:55   ` Bastien Nocera
@ 2009-03-18  0:11     ` Bastien Nocera
  -1 siblings, 0 replies; 20+ messages in thread
From: Bastien Nocera @ 2009-03-18  0:11 UTC (permalink / raw)
  To: linux-input; +Cc: mjg, Peter Hutterer, zap, BlueZ development

>>From 99074a7cf9622837af84d330e07bdb46307c254f Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Mon, 16 Mar 2009 23:36:54 +0000
Subject: [PATCH] Wacom Graphire Bluetooth driver

Signed-off-by: Bastien Nocera <hadess@hadess.net>

Based on the work by Andrew Zabolotny, an HID driver for the Bluetooth
Wacom tablet. This is required as it uses a slightly different
protocols from what's currently support by the drivers/input/wacom*
driver, and those only support USB.

A user-space patch is required to activate mode 2 of the Wacom tablet,
as hidp does not support hid_output_raw_report.
---
 drivers/hid/Kconfig     |    7 ++
 drivers/hid/Makefile    |    1 +
 drivers/hid/hid-core.c  |    1 +
 drivers/hid/hid-ids.h   |    1 +
 drivers/hid/hid-wacom.c |  260 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 270 insertions(+), 0 deletions(-)
 create mode 100644 drivers/hid/hid-wacom.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index e85c8fe..f3244e3 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,13 @@ config THRUSTMASTER_FF
 	  Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
 	  a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel.
 
+config HID_WACOM
+	tristate "Wacom Bluetooth devices support" if EMBEDDED
+	depends on BT_HIDP
+	default !EMBEDDED
+	---help---
+	Support for Wacom Graphire Bluetooth tablet.
+
 config ZEROPLUS_FF
 	tristate "Zeroplus based game controller support"
 	depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fbd021f..410b302 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_GREENASIA_FF)	+= hid-gaff.o
 obj-$(CONFIG_THRUSTMASTER_FF)	+= hid-tmff.o
 obj-$(CONFIG_HID_TOPSEED)	+= hid-topseed.o
 obj-$(CONFIG_ZEROPLUS_FF)	+= hid-zpff.o
+obj-$(CONFIG_HID_WACOM)		+= hid-wacom.o
 
 obj-$(CONFIG_USB_HID)		+= usbhid/
 obj-$(CONFIG_USB_MOUSE)		+= usbhid/
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 1cc9674..d134f5d 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1305,6 +1305,7 @@ static const struct hid_device_id hid_blacklist[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
 
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 8851197..e8f96c2 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -400,6 +400,7 @@
 #define USB_DEVICE_ID_VERNIER_LCSPEC	0x0006
 
 #define USB_VENDOR_ID_WACOM		0x056a
+#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH	0x81
 
 #define USB_VENDOR_ID_WISEGROUP		0x0925
 #define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
new file mode 100644
index 0000000..67c8c3a
--- /dev/null
+++ b/drivers/hid/hid-wacom.c
@@ -0,0 +1,260 @@
+/*
+ *  Bluetooth Wacom Tablet support
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
+ *  Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
+ *  Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
+ */
+
+/*
+ * 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 the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+struct wacom_data {
+	__u16 tool;
+	unsigned char butstate;
+};
+
+static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
+		u8 *raw_data, int size)
+{
+	struct wacom_data *wdata = hid_get_drvdata(hdev);
+	struct hid_input *hidinput;
+	struct input_dev *input;
+	unsigned char *data = (unsigned char *) raw_data;
+	int 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;
+
+	/* 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;
+		}
+
+		/* 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->absmax[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);
+	}
+
+	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;
+		}
+
+		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_0, rw & 0x02);
+		input_report_key(input, BTN_1, rw & 0x01);
+		input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
+		input_sync(input);
+	}
+
+	return 1;
+}
+
+static int wacom_probe(struct hid_device *hdev,
+		const struct hid_device_id *id)
+{
+	struct hid_input *hidinput;
+	struct input_dev *input;
+	struct wacom_data *wdata;
+	int ret;
+
+	wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
+	if (wdata == NULL) {
+		dev_err(&hdev->dev, "can't alloc wacom descriptor\n");
+		return -ENOMEM;
+	}
+
+	hid_set_drvdata(hdev, wdata);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
+	input = hidinput->input;
+
+	/* Basics */
+	input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
+	input->absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) |
+		BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
+	input->relbit[0] |= BIT(REL_WHEEL);
+	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);
+
+	/* Pad */
+	input->evbit[0] |= BIT(EV_MSC);
+	input->mscbit[0] |= BIT(MSC_SERIAL);
+
+	/* Distance, rubber and mouse */
+	input->absbit[0] |= BIT(ABS_DISTANCE);
+	set_bit(BTN_TOOL_RUBBER, input->keybit);
+	set_bit(BTN_TOOL_MOUSE, input->keybit);
+
+	input->absmax[ABS_PRESSURE] = 511;
+	input->absmax[ABS_DISTANCE] = 32;
+
+	input->absmax[ABS_X] = 16704;
+	input->absmax[ABS_Y] = 12064;
+	input->absfuzz[ABS_X] = 4;
+	input->absfuzz[ABS_Y] = 4;
+
+	return 0;
+err_free:
+	kfree(wdata);
+	return ret;
+}
+
+static void wacom_remove(struct hid_device *hdev)
+{
+	hid_hw_stop(hdev);
+	kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id wacom_devices[] = {
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
+
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, wacom_devices);
+
+static struct hid_driver wacom_driver = {
+	.name = "wacom",
+	.id_table = wacom_devices,
+	.probe = wacom_probe,
+	.remove = wacom_remove,
+	.raw_event = wacom_raw_event,
+};
+
+static int wacom_init(void)
+{
+	int ret;
+
+	ret = hid_register_driver(&wacom_driver);
+	if (ret)
+		printk(KERN_ERR "can't register wacom driver\n");
+	printk(KERN_ERR "wacom driver registered\n");
+	return ret;
+}
+
+static void wacom_exit(void)
+{
+	hid_unregister_driver(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(wacom);
-- 
1.6.2



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

* Re: [PATCH] Wacom Graphire Bluetooth driver (updated with a few fixes)
@ 2009-03-18  0:11     ` Bastien Nocera
  0 siblings, 0 replies; 20+ messages in thread
From: Bastien Nocera @ 2009-03-18  0:11 UTC (permalink / raw)
  To: linux-input; +Cc: mjg, Peter Hutterer, zap, BlueZ development

>From 99074a7cf9622837af84d330e07bdb46307c254f Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Mon, 16 Mar 2009 23:36:54 +0000
Subject: [PATCH] Wacom Graphire Bluetooth driver

Signed-off-by: Bastien Nocera <hadess@hadess.net>

Based on the work by Andrew Zabolotny, an HID driver for the Bluetooth
Wacom tablet. This is required as it uses a slightly different
protocols from what's currently support by the drivers/input/wacom*
driver, and those only support USB.

A user-space patch is required to activate mode 2 of the Wacom tablet,
as hidp does not support hid_output_raw_report.
---
 drivers/hid/Kconfig     |    7 ++
 drivers/hid/Makefile    |    1 +
 drivers/hid/hid-core.c  |    1 +
 drivers/hid/hid-ids.h   |    1 +
 drivers/hid/hid-wacom.c |  260 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 270 insertions(+), 0 deletions(-)
 create mode 100644 drivers/hid/hid-wacom.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index e85c8fe..f3244e3 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,13 @@ config THRUSTMASTER_FF
 	  Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
 	  a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel.
 
+config HID_WACOM
+	tristate "Wacom Bluetooth devices support" if EMBEDDED
+	depends on BT_HIDP
+	default !EMBEDDED
+	---help---
+	Support for Wacom Graphire Bluetooth tablet.
+
 config ZEROPLUS_FF
 	tristate "Zeroplus based game controller support"
 	depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fbd021f..410b302 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_GREENASIA_FF)	+= hid-gaff.o
 obj-$(CONFIG_THRUSTMASTER_FF)	+= hid-tmff.o
 obj-$(CONFIG_HID_TOPSEED)	+= hid-topseed.o
 obj-$(CONFIG_ZEROPLUS_FF)	+= hid-zpff.o
+obj-$(CONFIG_HID_WACOM)		+= hid-wacom.o
 
 obj-$(CONFIG_USB_HID)		+= usbhid/
 obj-$(CONFIG_USB_MOUSE)		+= usbhid/
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 1cc9674..d134f5d 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1305,6 +1305,7 @@ static const struct hid_device_id hid_blacklist[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
 
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 8851197..e8f96c2 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -400,6 +400,7 @@
 #define USB_DEVICE_ID_VERNIER_LCSPEC	0x0006
 
 #define USB_VENDOR_ID_WACOM		0x056a
+#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH	0x81
 
 #define USB_VENDOR_ID_WISEGROUP		0x0925
 #define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
new file mode 100644
index 0000000..67c8c3a
--- /dev/null
+++ b/drivers/hid/hid-wacom.c
@@ -0,0 +1,260 @@
+/*
+ *  Bluetooth Wacom Tablet support
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
+ *  Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
+ *  Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
+ */
+
+/*
+ * 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 the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+struct wacom_data {
+	__u16 tool;
+	unsigned char butstate;
+};
+
+static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
+		u8 *raw_data, int size)
+{
+	struct wacom_data *wdata = hid_get_drvdata(hdev);
+	struct hid_input *hidinput;
+	struct input_dev *input;
+	unsigned char *data = (unsigned char *) raw_data;
+	int 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;
+
+	/* 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;
+		}
+
+		/* 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->absmax[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);
+	}
+
+	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;
+		}
+
+		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_0, rw & 0x02);
+		input_report_key(input, BTN_1, rw & 0x01);
+		input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
+		input_sync(input);
+	}
+
+	return 1;
+}
+
+static int wacom_probe(struct hid_device *hdev,
+		const struct hid_device_id *id)
+{
+	struct hid_input *hidinput;
+	struct input_dev *input;
+	struct wacom_data *wdata;
+	int ret;
+
+	wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
+	if (wdata == NULL) {
+		dev_err(&hdev->dev, "can't alloc wacom descriptor\n");
+		return -ENOMEM;
+	}
+
+	hid_set_drvdata(hdev, wdata);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
+	input = hidinput->input;
+
+	/* Basics */
+	input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
+	input->absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) |
+		BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
+	input->relbit[0] |= BIT(REL_WHEEL);
+	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);
+
+	/* Pad */
+	input->evbit[0] |= BIT(EV_MSC);
+	input->mscbit[0] |= BIT(MSC_SERIAL);
+
+	/* Distance, rubber and mouse */
+	input->absbit[0] |= BIT(ABS_DISTANCE);
+	set_bit(BTN_TOOL_RUBBER, input->keybit);
+	set_bit(BTN_TOOL_MOUSE, input->keybit);
+
+	input->absmax[ABS_PRESSURE] = 511;
+	input->absmax[ABS_DISTANCE] = 32;
+
+	input->absmax[ABS_X] = 16704;
+	input->absmax[ABS_Y] = 12064;
+	input->absfuzz[ABS_X] = 4;
+	input->absfuzz[ABS_Y] = 4;
+
+	return 0;
+err_free:
+	kfree(wdata);
+	return ret;
+}
+
+static void wacom_remove(struct hid_device *hdev)
+{
+	hid_hw_stop(hdev);
+	kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id wacom_devices[] = {
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
+
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, wacom_devices);
+
+static struct hid_driver wacom_driver = {
+	.name = "wacom",
+	.id_table = wacom_devices,
+	.probe = wacom_probe,
+	.remove = wacom_remove,
+	.raw_event = wacom_raw_event,
+};
+
+static int wacom_init(void)
+{
+	int ret;
+
+	ret = hid_register_driver(&wacom_driver);
+	if (ret)
+		printk(KERN_ERR "can't register wacom driver\n");
+	printk(KERN_ERR "wacom driver registered\n");
+	return ret;
+}
+
+static void wacom_exit(void)
+{
+	hid_unregister_driver(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(wacom);
-- 
1.6.2



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

* Re: [PATCH] Wacom Graphire Bluetooth driver (updated with a few fixes)
  2009-03-18  0:11     ` Bastien Nocera
  (?)
@ 2009-03-18 21:28     ` Andrew Zabolotny
  2009-03-18 22:47       ` Bastien Nocera
  -1 siblings, 1 reply; 20+ messages in thread
From: Andrew Zabolotny @ 2009-03-18 21:28 UTC (permalink / raw)
  To: Bastien Nocera; +Cc: linux-input, mjg, Peter Hutterer, BlueZ development

[-- Attachment #1: Type: text/plain, Size: 715 bytes --]

From Wed, 18 Mar 2009 00:11:52 +0000
Bastien Nocera <hadess@hadess.net> wrote:

> Based on the work by Andrew Zabolotny, an HID driver for the Bluetooth
> Wacom tablet.
Great to see my old work was finally useful :) I tried in the past to
contact the bluetooth stack author to submit my patch, but got no answer
and gave up.

Also I like the fact that you have dropped my wicked out-of-active-area
pseudo-buttons support. Such kind of things should be implemented, if
needed, in the x11 driver (but the driver would need a way to report
out-of-active area stylus presses).

What's that "mode 2 patch" you're talking about? Should it be applied
to hidd, and is it available somewhere?

-- 
Andrew

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

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

* Re: [PATCH] Wacom Graphire Bluetooth driver (updated with a few fixes)
  2009-03-18 21:28     ` Andrew Zabolotny
@ 2009-03-18 22:47       ` Bastien Nocera
  2009-03-28 19:35         ` Andrew Zabolotny
  0 siblings, 1 reply; 20+ messages in thread
From: Bastien Nocera @ 2009-03-18 22:47 UTC (permalink / raw)
  To: Andrew Zabolotny; +Cc: linux-input, mjg, Peter Hutterer, BlueZ development

On Thu, 2009-03-19 at 00:28 +0300, Andrew Zabolotny wrote:
> From Wed, 18 Mar 2009 00:11:52 +0000
> Bastien Nocera <hadess@hadess.net> wrote:
> 
> > Based on the work by Andrew Zabolotny, an HID driver for the Bluetooth
> > Wacom tablet.
> Great to see my old work was finally useful :) I tried in the past to
> contact the bluetooth stack author to submit my patch, but got no answer
> and gave up.

I think that the main problem was because, at that time, there was no
such thing as the HID sub-system to avoid having to reimplement things
twice for Bluetooth and USB devices.

And adding support for a specific device in the hidp driver isn't the
best way to do this. Nowadays the driver for joysticks and keyboards are
the same whether Bluetooth or USB.

> Also I like the fact that you have dropped my wicked out-of-active-area
> pseudo-buttons support. Such kind of things should be implemented, if
> needed, in the x11 driver (but the driver would need a way to report
> out-of-active area stylus presses).

I removed it because the other drivers didn't implement it. If it were
to be implemented, the USB drivers would need to support it as well, so
that the interface is agreed upon.

Better start with something small, and get it merged, then discuss
extensions.

> What's that "mode 2 patch" you're talking about? Should it be applied
> to hidd, and is it available somewhere?

It's at:
http://cvs.fedoraproject.org/viewvc/rpms/bluez/devel/bluez-activate-wacom-mode2.patch?view=markup

And it's a patch to input plugin for bluetoothd, not hidd. hidd is the
BlueZ 3.x daemon, we've been at BlueZ 4.x for a while.

The bluetoothd patch is already in Fedora rawhide, and the kernel patch
will get there once we've released Fedora 11 Beta.

Cheers


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

* Re: [PATCH] Wacom Graphire Bluetooth driver (updated with a few fixes)
  2009-03-18 22:47       ` Bastien Nocera
@ 2009-03-28 19:35         ` Andrew Zabolotny
  0 siblings, 0 replies; 20+ messages in thread
From: Andrew Zabolotny @ 2009-03-28 19:35 UTC (permalink / raw)
  To: Bastien Nocera, linux-input

[-- Attachment #1: Type: text/plain, Size: 1328 bytes --]

From Wed, 18 Mar 2009 22:47:28 +0000
Bastien Nocera <hadess@hadess.net> wrote:

> I think that the main problem was because, at that time, there was no
> such thing as the HID sub-system to avoid having to reimplement things
> twice for Bluetooth and USB devices.
Yes, that was the main problem and I hoped to discuss it with the
bluetooth stack developers... The solution, as I was seeing it, is to
make the bluetooth stack implement a "bluetooth bus": this would allow
to emit udev events when devices are connected/disconnected, thus using
the normal udev module autoloading stuff.

However, I see that the bluetooth stuff chose another path, by using a
user-space daemon. I'm very far from bluetooth developement to
understand why this choice was made, however I'm pretty sure there were
serious arguments in favour of that.

> And it's a patch to input plugin for bluetoothd, not hidd. hidd is the
> BlueZ 3.x daemon, we've been at BlueZ 4.x for a while.
Aha, it's the stuff for low-level tablet protocol initialization. Why
separating it from the driver itself? It's kind of tricky having
hardware initialization in a user-space daemon, and working with
hardware in the driver itself. Isn't there an analogue of the
hidp_send_ctrl_message() call for the new hid driver architecture?

-- 
Andrew

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

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

* Re: [PATCH] Wacom Graphire Bluetooth driver
@ 2009-03-31 22:35     ` Bastien Nocera
  0 siblings, 0 replies; 20+ messages in thread
From: Bastien Nocera @ 2009-03-31 22:35 UTC (permalink / raw)
  To: linux-input; +Cc: mjg, Peter Hutterer, zap, BlueZ development

On Mon, 2009-03-16 at 23:55 +0000, Bastien Nocera wrote:
> >From 3495e3be72d1df6132d5f5d978d2def3e4d57fc3 Mon Sep 17 00:00:00 2001
> From: Bastien Nocera <hadess@hadess.net>
> Date: Mon, 16 Mar 2009 23:36:54 +0000
> Subject: [PATCH] Wacom Graphire Bluetooth driver

Any news on the patch? Matthew kindly added it to the Fedora 11 kernel:
http://cvs.fedoraproject.org/viewvc/rpms/kernel/F-11/linux-2.6-input-wacom-bluetooth.patch?view=log

Cheers


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

* Re: [PATCH] Wacom Graphire Bluetooth driver
@ 2009-03-31 22:35     ` Bastien Nocera
  0 siblings, 0 replies; 20+ messages in thread
From: Bastien Nocera @ 2009-03-31 22:35 UTC (permalink / raw)
  To: linux-input
  Cc: mjg, Peter Hutterer, zap-ez6wqqBRKvoox3rIn2DAYQ, BlueZ development

On Mon, 2009-03-16 at 23:55 +0000, Bastien Nocera wrote:
> >From 3495e3be72d1df6132d5f5d978d2def3e4d57fc3 Mon Sep 17 00:00:00 2001
> From: Bastien Nocera <hadess-0MeiytkfxGOsTnJN9+BGXg@public.gmane.org>
> Date: Mon, 16 Mar 2009 23:36:54 +0000
> Subject: [PATCH] Wacom Graphire Bluetooth driver

Any news on the patch? Matthew kindly added it to the Fedora 11 kernel:
http://cvs.fedoraproject.org/viewvc/rpms/kernel/F-11/linux-2.6-input-wacom-bluetooth.patch?view=log

Cheers

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

* Re: [PATCH] Wacom Graphire Bluetooth driver (updated with a few fixes)
  2009-03-16 23:55   ` Bastien Nocera
                     ` (2 preceding siblings ...)
  (?)
@ 2009-05-06 15:14   ` Bastien Nocera
  2009-05-11 15:20     ` Jiri Kosina
  -1 siblings, 1 reply; 20+ messages in thread
From: Bastien Nocera @ 2009-05-06 15:14 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina

>From 99074a7cf9622837af84d330e07bdb46307c254f Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Mon, 16 Mar 2009 23:36:54 +0000
Subject: [PATCH] Wacom Graphire Bluetooth driver

Signed-off-by: Bastien Nocera <hadess@hadess.net>

Based on the work by Andrew Zabolotny, an HID driver for the Bluetooth
Wacom tablet. This is required as it uses a slightly different
protocols from what's currently support by the drivers/input/wacom*
driver, and those only support USB.

A user-space patch is required to activate mode 2 of the Wacom tablet,
as hidp does not support hid_output_raw_report.
---
 drivers/hid/Kconfig     |    7 ++
 drivers/hid/Makefile    |    1 +
 drivers/hid/hid-core.c  |    1 +
 drivers/hid/hid-ids.h   |    1 +
 drivers/hid/hid-wacom.c |  260 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 270 insertions(+), 0 deletions(-)
 create mode 100644 drivers/hid/hid-wacom.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index e85c8fe..f3244e3 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,13 @@ config THRUSTMASTER_FF
 	  Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
 	  a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel.
 
+config HID_WACOM
+	tristate "Wacom Bluetooth devices support" if EMBEDDED
+	depends on BT_HIDP
+	default !EMBEDDED
+	---help---
+	Support for Wacom Graphire Bluetooth tablet.
+
 config ZEROPLUS_FF
 	tristate "Zeroplus based game controller support"
 	depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fbd021f..410b302 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_GREENASIA_FF)	+= hid-gaff.o
 obj-$(CONFIG_THRUSTMASTER_FF)	+= hid-tmff.o
 obj-$(CONFIG_HID_TOPSEED)	+= hid-topseed.o
 obj-$(CONFIG_ZEROPLUS_FF)	+= hid-zpff.o
+obj-$(CONFIG_HID_WACOM)		+= hid-wacom.o
 
 obj-$(CONFIG_USB_HID)		+= usbhid/
 obj-$(CONFIG_USB_MOUSE)		+= usbhid/
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 1cc9674..d134f5d 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1305,6 +1305,7 @@ static const struct hid_device_id hid_blacklist[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
 
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 8851197..e8f96c2 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -400,6 +400,7 @@
 #define USB_DEVICE_ID_VERNIER_LCSPEC	0x0006
 
 #define USB_VENDOR_ID_WACOM		0x056a
+#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH	0x81
 
 #define USB_VENDOR_ID_WISEGROUP		0x0925
 #define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
new file mode 100644
index 0000000..67c8c3a
--- /dev/null
+++ b/drivers/hid/hid-wacom.c
@@ -0,0 +1,260 @@
+/*
+ *  Bluetooth Wacom Tablet support
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
+ *  Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
+ *  Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
+ */
+
+/*
+ * 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 the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+struct wacom_data {
+	__u16 tool;
+	unsigned char butstate;
+};
+
+static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
+		u8 *raw_data, int size)
+{
+	struct wacom_data *wdata = hid_get_drvdata(hdev);
+	struct hid_input *hidinput;
+	struct input_dev *input;
+	unsigned char *data = (unsigned char *) raw_data;
+	int 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;
+
+	/* 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;
+		}
+
+		/* 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->absmax[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);
+	}
+
+	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;
+		}
+
+		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_0, rw & 0x02);
+		input_report_key(input, BTN_1, rw & 0x01);
+		input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
+		input_sync(input);
+	}
+
+	return 1;
+}
+
+static int wacom_probe(struct hid_device *hdev,
+		const struct hid_device_id *id)
+{
+	struct hid_input *hidinput;
+	struct input_dev *input;
+	struct wacom_data *wdata;
+	int ret;
+
+	wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
+	if (wdata == NULL) {
+		dev_err(&hdev->dev, "can't alloc wacom descriptor\n");
+		return -ENOMEM;
+	}
+
+	hid_set_drvdata(hdev, wdata);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
+	input = hidinput->input;
+
+	/* Basics */
+	input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
+	input->absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) |
+		BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
+	input->relbit[0] |= BIT(REL_WHEEL);
+	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);
+
+	/* Pad */
+	input->evbit[0] |= BIT(EV_MSC);
+	input->mscbit[0] |= BIT(MSC_SERIAL);
+
+	/* Distance, rubber and mouse */
+	input->absbit[0] |= BIT(ABS_DISTANCE);
+	set_bit(BTN_TOOL_RUBBER, input->keybit);
+	set_bit(BTN_TOOL_MOUSE, input->keybit);
+
+	input->absmax[ABS_PRESSURE] = 511;
+	input->absmax[ABS_DISTANCE] = 32;
+
+	input->absmax[ABS_X] = 16704;
+	input->absmax[ABS_Y] = 12064;
+	input->absfuzz[ABS_X] = 4;
+	input->absfuzz[ABS_Y] = 4;
+
+	return 0;
+err_free:
+	kfree(wdata);
+	return ret;
+}
+
+static void wacom_remove(struct hid_device *hdev)
+{
+	hid_hw_stop(hdev);
+	kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id wacom_devices[] = {
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
+
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, wacom_devices);
+
+static struct hid_driver wacom_driver = {
+	.name = "wacom",
+	.id_table = wacom_devices,
+	.probe = wacom_probe,
+	.remove = wacom_remove,
+	.raw_event = wacom_raw_event,
+};
+
+static int wacom_init(void)
+{
+	int ret;
+
+	ret = hid_register_driver(&wacom_driver);
+	if (ret)
+		printk(KERN_ERR "can't register wacom driver\n");
+	printk(KERN_ERR "wacom driver registered\n");
+	return ret;
+}
+
+static void wacom_exit(void)
+{
+	hid_unregister_driver(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(wacom);
-- 
1.6.2



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

* Re: [PATCH] Wacom Graphire Bluetooth driver (updated with a few fixes)
  2009-05-06 15:14   ` [PATCH] Wacom Graphire Bluetooth driver (updated with a few fixes) Bastien Nocera
@ 2009-05-11 15:20     ` Jiri Kosina
  2009-05-11 16:12       ` Bastien Nocera
  0 siblings, 1 reply; 20+ messages in thread
From: Jiri Kosina @ 2009-05-11 15:20 UTC (permalink / raw)
  To: Bastien Nocera; +Cc: linux-input

On Wed, 6 May 2009, Bastien Nocera wrote:

> Signed-off-by: Bastien Nocera <hadess@hadess.net>
> 
> Based on the work by Andrew Zabolotny, an HID driver for the Bluetooth
> Wacom tablet. This is required as it uses a slightly different
> protocols from what's currently support by the drivers/input/wacom*
> driver, and those only support USB.

Hi Bastien,

thanks for the driver.

> +HID_COMPAT_LOAD_DRIVER(wacom);

We don't need this one anymore, the HID_COMPAT stuff has gone away 
completely already.

Otherwise, it looks good. So if you agree, I will remove the COMPAT stuff 
and queue it in my tree.

Thanks,

-- 
Jiri Kosina
SUSE Labs

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

* Re: [PATCH] Wacom Graphire Bluetooth driver (updated with a few fixes)
  2009-05-11 15:20     ` Jiri Kosina
@ 2009-05-11 16:12       ` Bastien Nocera
  0 siblings, 0 replies; 20+ messages in thread
From: Bastien Nocera @ 2009-05-11 16:12 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: linux-input

On Mon, 2009-05-11 at 17:20 +0200, Jiri Kosina wrote:
> On Wed, 6 May 2009, Bastien Nocera wrote:
> 
> > Signed-off-by: Bastien Nocera <hadess@hadess.net>
> > 
> > Based on the work by Andrew Zabolotny, an HID driver for the Bluetooth
> > Wacom tablet. This is required as it uses a slightly different
> > protocols from what's currently support by the drivers/input/wacom*
> > driver, and those only support USB.
> 
> Hi Bastien,
> 
> thanks for the driver.
> 
> > +HID_COMPAT_LOAD_DRIVER(wacom);
> 
> We don't need this one anymore, the HID_COMPAT stuff has gone away 
> completely already.
> 
> Otherwise, it looks good. So if you agree, I will remove the COMPAT stuff 
> and queue it in my tree.

Fine by me. Thanks!


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

end of thread, other threads:[~2009-05-11 16:13 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-03-13 23:59 RFC: Wacom Bluetooth HID driver, first pass Bastien Nocera
2009-03-16 10:55 ` Bastien Nocera
2009-03-16 10:55   ` Bastien Nocera
2009-03-16 14:00   ` Marcel Holtmann
2009-03-16 14:00     ` Marcel Holtmann
2009-03-16 14:29     ` Bastien Nocera
2009-03-16 14:29       ` Bastien Nocera
2009-03-16 23:58       ` Bastien Nocera
2009-03-16 23:55 ` [PATCH] Wacom Graphire Bluetooth driver Bastien Nocera
2009-03-16 23:55   ` Bastien Nocera
2009-03-18  0:11   ` [PATCH] Wacom Graphire Bluetooth driver (updated with a few fixes) Bastien Nocera
2009-03-18  0:11     ` Bastien Nocera
2009-03-18 21:28     ` Andrew Zabolotny
2009-03-18 22:47       ` Bastien Nocera
2009-03-28 19:35         ` Andrew Zabolotny
2009-03-31 22:35   ` [PATCH] Wacom Graphire Bluetooth driver Bastien Nocera
2009-03-31 22:35     ` Bastien Nocera
2009-05-06 15:14   ` [PATCH] Wacom Graphire Bluetooth driver (updated with a few fixes) Bastien Nocera
2009-05-11 15:20     ` Jiri Kosina
2009-05-11 16:12       ` Bastien Nocera

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.