Written November 2013 - January 2014

Introduction

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 --list
outputs nothing on my system, even when the Cintiq is connected.

Finding out about my Cintiq on Linux

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/mouse1
become 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").

[Show 975 lines of output...]

There's a way we can monitor traffic on a USB bus. As root, we do

modprobe usbmon
Next, as an example, my mouse is on Bus 3, hence I could do (as root)
cat /sys/kernel/debug/usb/usbmon/3u
and 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/3u
and then plug in the Cintiq, causing the following to appear on my terminal:

[Show 185 lines of output...]

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:

> 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
(with blank lines added in the above output to make it easier to distinguish devices)

Programmatically enumerating your USB devices

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

listUSBDevices.c

[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.ids
According 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.ids
which is a symlink to the first, on my system. I have read [ here ] that some systems store the list in
   /usr/share/usb.ids
and 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/lsusb
So, by obtaining the source code for the usbutils package, one could find how to do this programmatically.

Programmatically reading input events from the Cintiq 24HD touch

A summary of what we have found so far, with notes on what I have determined through trial and error:
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.

How to avoid having to run your code as root

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"

How to blacklist the Wacom driver

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 wacom
and rebooting. (Thanks to Shrey Gupta for finding how to do this.)

Additional Resources

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/

References

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