Written November 2013 - January 2014
This document describes how I was able to read input events from a Wacom "Cintiq 24HD touch"
on Ubuntu Linux over a USB connection.
I wanted to read the input events programmatically from a single, custom, stand-alone C++ (or Java) program
that would display whatever visual feedback I wanted (custom cursors, ink trails, toolglasses, etc.)
I wanted to have access to as much raw input data as possible:
pen data (coordinates, pressure, tilt angles, button states) and touch data (finger coordinates),
even when both pen and fingers are being used simultaneously.
I did not attempt to install or use the most recent Wacom driver for linux because
(1) I am inexperienced with installing drivers and was afraid of screwing up my kernel configuration,
and (2) I assumed that the driver would not provide me with all the pen and touch data that I wanted.
Although I have not fully investigated the features of the latest Wacom driver for linux,
the quick glances I have given
http://sourceforge.net/apps/mediawiki/linuxwacom/index.php?title=FAQ#Which_devices_are_supported.3F
and
http://sourceforge.net/apps/mediawiki/linuxwacom/index.php?title=Device_IDs,
and the driver source code,
indicate to me that there is currently no support for Cintiq (multi)touch on linux.
At the time of writing, the "Cintiq 24HD touch" is the top-of-the-line device from Wacom, supporting simultaneous pen and multitouch input. It senses up to 10 finger positions. With the stylus, it senses tip position (x,y), azimuth and elevation angles, the on/off state of 2 barrel buttons, and distinguishes between the pen tip and eraser tip. When the pen (or eraser) tip is in contact with the screen, it senses pressure, and when the pen (or eraser) tip is hovering over the screen, the distance between the screen and tip is sensed up to a limit of roughly 1.5 cm (and a bit less than this near the edges of the screen).
If you're shopping for a less expensive device, note that not all touch-enabled Wacom devices support pen and touch simultaneously. See "Table 1" at http://www.wacomeng.com/touch/WacomFeelMulti-TouchFAQ.htm
My system:
Dell "Mobile Precision M4700" laptop, purchased in 2012
Running Ubuntu 12.10, 64 bit
kernel (reported by "uname -r"): 3.5.0-18-generic
Stuff already installed related to Wacom, that came with my distro:
> dpkg -l | fgrep -i wacom ii libwacom-common 0.6-0ubuntu1 all Wacom model feature query library (common files) ii libwacom2:amd64 0.6-0ubuntu1 amd64 Wacom model feature query library ii xserver-xorg-input-wacom 1:0.17.0-0ubuntu2 amd64 X.Org X server -- Wacom input driver
Wacom device: Cintiq 24HD touch, with a single physical USB cable, but seen as multiple USB (virtual) devices by the operating system.
The command
xsetwacom --listoutputs nothing on my system, even when the Cintiq is connected.
At first, I had hoped I would be able to read raw input events from the Cintiq simply by reading from some file of the form /dev/input/event*. For example, when I connect my USB mouse to my laptop, I notice that two new files
/dev/input/event15 /dev/input/mouse1become available, and running "cat" on either of these and then moving my mouse causes bytes to be echoed to the terminal. This also happens when I have a 3M multitouch screen connected. However, when I connect my Cintiq's USB cable, no new files appear under /dev/input/, which lead to my spending many hours investigating USB programming, and writing this document.
To get a list of your USB devices and some information on each, try the command "lsusb". On my Dell laptop, I see this output:
> lsusb Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub Bus 003 Device 003: ID 0409:005a NEC Corp. HighSpeed Hub # I see this when the Cintiq's USB cable is connected Bus 003 Device 002: ID 1532:0016 Razer USA, Ltd DeathAdder Mouse # I see this when my mouse is connected Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 003: ID 413c:8197 Dell Computer Corp. Bus 001 Device 004: ID 0c45:6449 Microdia Bus 003 Device 004: ID 056a:00f5 Wacom Co., Ltd # I see this when the Cintiq's USB cable is connected Bus 003 Device 005: ID 056a:00f6 Wacom Co., Ltd # I see this when the Cintiq's USB cable is connected Bus 003 Device 006: ID 056a:00f8 Wacom Co., Ltd # I see this when the Cintiq's USB cable is connected
If I do "lsusb -t", I see
> lsusb -t /: Bus 04.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M /: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 480M |__ Port 1: Dev 3, If 0, Class=hub, Driver=hub/4p, 480M # I see this when the Cintiq's USB cable is connected |__ Port 1: Dev 4, If 0, Class=HID, Driver=, 12M # I see this when the Cintiq's USB cable is connected |__ Port 2: Dev 5, If 0, Class=vend., Driver=, 12M # I see this when the Cintiq's USB cable is connected |__ Port 2: Dev 5, If 1, Class=HID, Driver=, 12M # I see this when the Cintiq's USB cable is connected |__ Port 3: Dev 6, If 0, Class=HID, Driver=, 12M # I see this when the Cintiq's USB cable is connected |__ Port 2: Dev 2, If 0, Class=HID, Driver=usbhid, 12M # I see this when my mouse is connected /: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=ehci_hcd/2p, 480M |__ Port 1: Dev 2, If 0, Class=hub, Driver=hub/8p, 480M /: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci_hcd/2p, 480M |__ Port 1: Dev 2, If 0, Class=hub, Driver=hub/6p, 480M |__ Port 4: Dev 3, If 0, Class=vend., Driver=btusb, 12M |__ Port 4: Dev 3, If 1, Class=vend., Driver=btusb, 12M |__ Port 4: Dev 3, If 2, Class=vend., Driver=, 12M |__ Port 4: Dev 3, If 3, Class=app., Driver=, 12M |__ Port 5: Dev 4, If 0, Class='bInterfaceClass 0x0e not yet handled', Driver=uvcvideo, 480M |__ Port 5: Dev 4, If 1, Class='bInterfaceClass 0x0e not yet handled', Driver=uvcvideo, 480M
I think the "12M" above refers to 12 megabits/second, or USB full speed, and the "480M" refers to 480 megabits/second, or USB high speed. ( http://www.usbmadesimple.co.uk/ums_1.htm ).
If I do "cat /sys/kernel/debug/usb/devices" as root, I see additional details about each USB device. The portions of the output pertaining to the Cintiq are:
> cat /sys/kernel/debug/usb/devices [...] T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 3 Spd=480 MxCh= 4 D: Ver= 2.00 Cls=09(hub ) Sub=00 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0409 ProdID=005a Rev= 1.00 C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub E: Ad=81(I) Atr=03(Int.) MxPS= 1 Ivl=256ms T: Bus=03 Lev=02 Prnt=03 Port=00 Cnt=01 Dev#= 4 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=056a ProdID=00f5 Rev= 0.01 S: Manufacturer=WACOM S: Product=Cintiq 24HDT Monitor Control S: SerialNumber=3DAN100024 C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr= 0mA I:* If#= 0 Alt= 0 #EPs= 2 Cls=03(HID ) Sub=00 Prot=00 Driver=(none) E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=1ms E: Ad=02(O) Atr=03(Int.) MxPS= 64 Ivl=1ms T: Bus=03 Lev=02 Prnt=03 Port=01 Cnt=02 Dev#= 5 Spd=12 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=056a ProdID=00f6 Rev= 1.06 S: Manufacturer=Wacom Co.,Ltd. S: Product=Cintiq 24HDT Touch C:* #Ifs= 2 Cfg#= 1 Atr=c0 MxPwr= 0mA I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) E: Ad=87(I) Atr=03(Int.) MxPS= 8 Ivl=3ms E: Ad=81(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=00 Prot=00 Driver=(none) E: Ad=83(I) Atr=03(Int.) MxPS= 64 Ivl=3ms T: Bus=03 Lev=02 Prnt=03 Port=02 Cnt=03 Dev#= 6 Spd=12 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=16 #Cfgs= 1 P: Vendor=056a ProdID=00f8 Rev= 2.07 S: Manufacturer=Tablet S: Product=Cintiq 24HDT Tablet C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA I:* If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=02 Driver=(none) E: Ad=82(I) Atr=03(Int.) MxPS= 10 Ivl=1ms [...]
My understanding of USB devices is that each device has one or more configurations, each of which has one or more interfaces, each of which has one or more endpoints. Each endpoint can either be read from or written to. In the above listing, I think that the initial letters D, P, S, C, I, E refer to device, product, string, configuration, interface, endpoint, respectively; #Ifs and #EPs refer to number of interfaces and number of endpoints, respectively; Cfg# and If# refer to configuration number and interface number, respectively; and notice that each endpoint has an address (Ad). It also seems that the interfaces are numbered 0, 1, etc., however configurations are numbered 1, 2, etc.
Notice above the mention of several "Int." endpoints and one "Bulk" endpoint. There are four ways of communicating with endpoints:
If I do "lsusb -v", I see a lot of detailed information about each USB device, and even more if I do this as root. The parts of the output pertaining to the Cintiq appear below. I suspect that the below information is retrieved by the operating system from the physical device when the USB cable is connected. Note that some of the information below may not be accurate (in examining source code for input-wacom, I found a comment in the file wacom_sys.c stating that "Interface Descriptor of wacom devices can be incomplete and inconsistent").
There's a way we can monitor traffic on a USB bus. As root, we do
modprobe usbmonNext, as an example, my mouse is on Bus 3, hence I could do (as root)
cat /sys/kernel/debug/usb/usbmon/3uand then move my mouse, and I should see bytes echoed to the terminal.
Before plugging in my Cintiq, I can do
cat /sys/kernel/debug/usb/usbmon/3uand then plug in the Cintiq, causing the following to appear on my terminal:
The above seems to show my laptop and the Cintiq "talking" to each other when the Cintiq is connected. I suspect the operating system retrieves descriptors from the Cintiq during this conversation, which can then be listed by the "lsusb -v" command.
As explained at https://www.kernel.org/doc/Documentation/usb/usbmon.txt , the first column in the above output is a "URB Tag" (I think URB = USB Request Block); the second column is a timestamp in microseconds in decimal; the third column is S for submission, C for callback, E for submission error; and the fourth column uses the following abbreviations:
Ci Co Control input and output Zi Zo Isochronous input and output Ii Io Interrupt input and output Bi Bo Bulk input and output
After plugging in the Cintiq, if I do "dmesg", I see information about the most recently connected USB devices:
(with blank lines added in the above output to make it easier to distinguish devices)> dmesg [...] [ 2375.274967] usb 3-1: new high-speed USB device number 3 using xhci_hcd [ 2375.291182] usb 3-1: New USB device found, idVendor=0409, idProduct=005a [ 2375.291191] usb 3-1: New USB device strings: Mfr=0, Product=0, SerialNumber=0 [ 2375.291934] hub 3-1:1.0: USB hub found [ 2375.292004] hub 3-1:1.0: 4 ports detected [ 2375.578799] usb 3-1.1: new full-speed USB device number 4 using xhci_hcd [ 2375.600165] usb 3-1.1: New USB device found, idVendor=056a, idProduct=00f5 [ 2375.600173] usb 3-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 [ 2375.600177] usb 3-1.1: Product: Cintiq 24HDT Monitor Control [ 2375.600181] usb 3-1.1: Manufacturer: WACOM [ 2375.600184] usb 3-1.1: SerialNumber: 3DAN100024 [ 2376.174389] usb 3-1.2: new full-speed USB device number 5 using xhci_hcd [ 2376.196806] usb 3-1.2: New USB device found, idVendor=056a, idProduct=00f6 [ 2376.196812] usb 3-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=0 [ 2376.196816] usb 3-1.2: Product: Cintiq 24HDT Touch [ 2376.196819] usb 3-1.2: Manufacturer: Wacom Co.,Ltd. [ 2376.197050] usb 3-1.2: ep 0x87 - rounding interval to 16 microframes, ep desc says 24 microframes [ 2376.197062] usb 3-1.2: ep 0x83 - rounding interval to 16 microframes, ep desc says 24 microframes [ 2377.965134] usb 3-1.3: new full-speed USB device number 6 using xhci_hcd [ 2377.984187] usb 3-1.3: New USB device found, idVendor=056a, idProduct=00f8 [ 2377.984195] usb 3-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0 [ 2377.984200] usb 3-1.3: Product: Cintiq 24HDT Tablet [ 2377.984203] usb 3-1.3: Manufacturer: Tablet
The below code prints out a list of the USB devices on your system. On my system, I need to run it as root for it to work. (However, I recommend compiling it as a regular user before running it as root -- it's generally good to avoid performing operations as root, to minimize the potential for accidental damage.)
[Show 90 lines of source code...]
On my system, the above code outputs this regarding the Cintiq:
[...] [device 2] idVendor: 1033 idProduct: 90 iManufacturer = 0 iProduct = 0 iSerialNumber = 0 [...] [device 10] idVendor: 1386 idProduct: 245 iManufacturer = 1 string = WACOM iProduct = 2 string = Cintiq 24HDT Monitor Control iSerialNumber = 3 string = 3DAN100024 [device 11] idVendor: 1386 idProduct: 246 iManufacturer = 1 string = Wacom Co.,Ltd. iProduct = 2 string = Cintiq 24HDT Touch iSerialNumber = 0 [device 12] idVendor: 1386 idProduct: 248 iManufacturer = 1 string = Tablet iProduct = 2 string = Cintiq 24HDT Tablet iSerialNumber = 0
The above code demonstrates how to translate the iManufacturer, iProduct, and iSerialNumber into human-readable strings. What about the idVendor and idProduct? On my system, known values for these with human-readable strings can be found in
/var/lib/usbutils/usb.idsAccording to the man page for lsusb, the above file contains "A list of all known USB ID's (vendors, products, classes, subclasses and protocols)." There is also the file
/usr/share/misc/usb.idswhich is a symlink to the first, on my system. I have read [ here ] that some systems store the list in
/usr/share/usb.idsand that the latest list is at http://www.linux-usb.org/usb.ids Notice that the command lsusb seems to access the list to print out the idVendor and idProduct as strings. If you want to also do this, you could look at the source code for lsusb to find out how it does this:
> which lsusb /usr/bin/lsusb > dpkg --search /usr/bin/lsusb usbutils: /usr/bin/lsusbSo, by obtaining the source code for the usbutils package, one could find how to do this programmatically.
The 3 devices: Vendor: 1386(decimal) == 056a(hex) == "Wacom Co., Ltd" 12 megabits/seconds (USB full speed) product id: 00f5(hex) == 245(decimal) Manufacturer = "WACOM" Product = "Cintiq 24HDT Monitor Control" SerialNumber = 3DAN100024 interface 0, class = 3 (HID = Human interface device) endpoint: address=0x81(IN) Interrupt MaxPacketSize=64 interval=1ms Of unknown use. endpoint: address=0x02(OUT) Interrupt MaxPacketSize=64 interval=1ms Of unknown use. product id: 00f6(hex) == 246(decimal) == "Cintiq 24HD touch (DTH-2400) touchscreen" Manufacturer = "Wacom Co.,Ltd." Product = "Cintiq 24HDT Touch" interface 0, class = 0xff (vendor-specific) endpoint: address=0x87(IN) Interrupt MaxPacketSize=8 interval=3ms Output from dmesg said end point 0x87 - rounding interval to 16 microframes, ep desc says 24 microframes This sends packets of 8 bytes each, when the fingers are touching the screen. I don't know what these packets are for; they always seem to contain the same bytes. endpoint: address=0x81(IN) Bulk MaxPacketSize=64 interval=0ms Of unknown use. Maybe this is for gaining access to the raw touch image data. interface 1, class = 3 (HID) endpoint: address=0x83(IN) Interrupt MaxPacketSize=64 interval=3ms Output from dmesg said end point 0x83 - rounding interval to 16 microframes, ep desc says 24 microframes This sends packets of 62 bytes each, containing touch data. Each packet can be imagined as being made up of 4 groups of 14 bytes, followed by 6 terminal bytes, for a total of 62. Each group of 14 corresponds to a finger, and the very last byte (byte 61) contains the number of fingers currently touching. Within each group of 14, byte 2 is the finger id, bytes 4 and 3 are x, and bytes 8 and 7 are y. (Bytes 11 and 13 seem to describe the shape of the finger tip blob, but I don't know how to interpret these bytes.) If there are more than 4 fingers touching, the additional fingers are reported in subsequent packets of 62. product id: 00f8(hex) == 248(decimal) == "Cintiq 24HD touch (DTH-2400) tablet" Manufacturer = "Tablet" Product = "Cintiq 24HDT Tablet" interface 0, class = 3 (HID) endpoint: address=0x82(IN) Interrupt MaxPacketSize=10 interval=1ms This sends packets of 10 bytes each, containing pen data. byte 1, bit 1: button 1 byte 1, bit 2: button 2 bytes 2 and 3: x // I compute x = ( byteArray[2] << 4 ) | ( byteArray[3] >> 4 ); bytes 4 and 5: y // I compute y = ( byteArray[4] << 4 ) | ( byteArray[5] >> 4 ); byte 6: pressure byte 7, bits 0-5: azimuth (angle around a vertical axis) byte 8, bits 0-6: elevation (angle around a horizontal axis) byte 9, bits 2-7: z (distance from screen) If z==0, it is a special packet that tells us which end of the stylus (pen tip or eraser) is in use. These special packets are only sent when the stylus comes into range or leaves the range of the screen. If z==0, pressure == 10, x == 2050, y == 2944, then the pen tip is coming into range. If z==0, pressure == 10, x == 2058, y == 2944, then the eraser is coming into range. If z==0, pressure == 0, x == 0, y == 0, then the stylus is leaving the screen's range. If z is not zero, then we have a "normal" packet. In my code, these special z==0 packets are not always received. I don't know if my code is simply not fast enough to read them, or if sometimes they are not sent. In designing my code, I have assumed that any packets could be dropped, and do my best to deal with this robustly. Because my code sometimes misses these packets, there's no guarantee that I can properly distinguish the eraser from the pen tip.
Finally, here is some C++ code to read in the multitouch and pen events and print them out to stdout: readCintiq24HDTouch.cpp
I also have a Java version available, written using usb4java. We have successfully run the Java code on Microsoft Windows 7 and on Ubuntu Linux.
To avoid having to run your code as root to have permission to read the devices, you can create a file /lib/udev/rules.d/99-userusbdevices.rules containing the below. (Instructions for this were adapted from http://usb4java.org/faq.html ).
# Wacom Cintiq 24HD touch SUBSYSTEM=="usb",ATTR{idVendor}=="056a",ATTR{idProduct}=="00f6",MODE="0660",GROUP="mjm" SUBSYSTEM=="usb",ATTR{idVendor}=="056a",ATTR{idProduct}=="00f8",MODE="0660",GROUP="mjm" SUBSYSTEM=="usb",ATTR{idVendor}=="056a",ATTR{idProduct}=="00f5",MODE="0660",GROUP="mjm"
With Ubuntu 13.10, there seems to be a wacom driver included with the distro that interferes with having our own code read events. Blacklisting the wacom driver seems to fix this problem.
Doing "lsmod" as root lists the currently running modules. If you see "wacom" in this list, you can blacklist it by editing the file /etc/modprobe.d/blacklist.conf and adding the lines
# blacklist wacom driver blacklist wacomand rebooting. (Thanks to Shrey Gupta for finding how to do this.)
Check out the references listed at the end of this document.
If you get stuck, you might try asking a question on http://stackoverflow.com/
http://www.usbmadesimple.co.uk/
http://www.beyondlogic.org/usbnutshell/usb1.shtml
Brad Hards, "The Linux USB sub-system", http://www.linux-usb.org/USB-guide/book1.html
http://en.wikipedia.org/wiki/Usb#Device_classes
https://www.kernel.org/doc/Documentation/usb/usbmon.txt
http://libusb.sourceforge.net/api-1.0/
http://sourceforge.net/apps/mediawiki/linuxwacom/index.php?title=How_Wacom_tablets_work (mentions "mode 2")
http://sourceforge.net/apps/mediawiki/linuxwacom/index.php?title=Wacom_Protocol_Overview
http://sourceforge.net/apps/mediawiki/linuxwacom/index.php?title=USB_Protocol