[DIGImend-devel] PT-1001 digitizer
Brought to you by:
spb_nick
|
From: Yann V. <yan...@or...> - 2015-01-20 14:08:59
|
I have a couple of PT-1001 tablets, also known as " Digi Tablet" over USB.
Mine happen to be branded Leogics, but many other brands occur, including none.
The USB vendor is 0x099a Zippy Technology Corp.
It presents three interfaces: one keyboard and two pointers. The purpose of
the second pointer device is somewhat unclear as it does not seem to produce
events, but its descriptors do describe it as a stylus with only three buttons
plus presence.
With recent Linux kernels these produce no events and a couple of warnings:
[ 6719.887522] hid-generic 0003:099A:2620.001C: input,hidraw3: USB HID v1.10 Keyboard [ Digi Tablet] on usb-0000:00:14.0-7/input0
[ 6719.997108] hid-generic 0003:099A:2620.001D: unknown main item tag 0x0
[ 6719.997114] hid-generic 0003:099A:2620.001D: unbalanced collection at end of report description
[ 6719.997121] hid-generic: probe of 0003:099A:2620.001D failed with error -22
I've tracked that problem down; Linux requests precisely the exact buffer and
transfer size for the report descriptors, and this device has an off by one
bug where it doesn't send the last byte. In the particular descriptor for the
events it does send, that last byte is a C0 = End Collection, so the structure
breaks and Linux ignores that interface (it has three). USB transfer dumps
show Windows requests 64 bytes more than the descriptor size, which explains
why it does not trigger this bug.
Possible workarounds include patching in that extra byte or requesting more
(both tested and work). A proper fix would probably include not trying to
parse more descriptor data than the device actually returned. I do suggest
requesting more than the descriptor length by default, since this bug may
be present in other devices (possibly as a misinterpretation of USB's use
of a full frame to indicate more data is available). My own test was simply
to add 1 to rsize and dev_rsize in kmalloc and hid_get_class_descriptor
calls within drivers/hid/usbhid/hid-core.c, but that doesn't check how much
was actually received.
That's not the end of the story, however. Even with proper parsing of the
descriptors it seems the tablet only has one interface that reports a stylus,
and it's not the one sending events. This causes the Xorg driver to deduce it
is a touchpad, not a pen tablet, with all the resulting defects (relative
mode, ignoring events when the nib isn't pressed). It also reports a rather
mystifying set of buttons (from xinput list-props, but do come from kernel,
as confirmed using input-events):
Button Labels (276): "Button Left" (145), "Button Middle" (146),
"Button Right" (147), "Button Wheel Up" (148), "Button Wheel Down" (149),
"Button Horiz Wheel Left" (150), "Button Horiz Wheel Right" (151),
"Button Side" (265), "Button Extra" (266), "Button Forward" (714),
"Button Back" (715), "Button Task" (716), "Button Unknown" (264),
"Button Unknown" (264), "Button Unknown" (264), "Button Unknown" (264)
The second pointer device reported, which does not produce events, has an
equally useless button map:
"Button 0" (630), "Button Unknown" (264), "Button Unknown" (264),
"Button Wheel Up" (148), "Button Wheel Down" (149)
The actual buttons (nib and two side buttons on the stylus) are those
reported as Forward, Back and Task. Remapping them with xinput set-button-map
and setting absolute mode makes it usable but with the serious issue of not
moving the pointer if the nib isn't pressed (the pressure level works in
inkscape). I did not find a way to tell Xorg's evdev driver that the device
is a tablet.
Among the firmware controlled soft buttons (tappable with the stylus) on
the tablet itself is a mouse/stylus switch control. The other side fields
produce other events, mostly keyboard combinations (common zoom hotkeys,
save, open, copy, paste etc) and scroll events. Using this toggle to set
mouse mode causes the first group of buttons to be used, so does map to
mouse buttons by default, but the pointer motion is ignored by X (since
it does not report pressure, this might be an artifact of its touchpad
misinterpretation). The keyboard events are sent on interface 0.
So, on interface 1, we have a bunch of possible reports:
1 5 button relative XY, sent in mouse mode (boot protocol compatible?)
2 scroll wheel? duplicated in descriptor. usages Wheel and AC Pan
3 Unknown use, looks like keyboard reports
5 multimedia buttons
9 tablet mode stylus events
As it happens, it looks like it sticks to report 9 and keyboard events
on interface 0, switching to report 1 in mouse mode.
And the kernel has dutifully collected all those possible buttons in one
device, placing the tablet ones at number 10-12. The hint that this is
a pen tablet is in interface 2, which maps to another device. It could
be that Windows associates the two, maybe by bInterfaceProtocol=3?
The failing heuristic in Xorg's evdev driver is:
if ((libevdev_has_event_code(pEvdev->dev, EV_ABS, ABS_X) &&
libevdev_has_event_code(pEvdev->dev, EV_ABS, ABS_Y))) {
xf86IDrvMsg(pInfo, X_PROBED, "Found x and y absolute axes\n");
if (libevdev_has_event_code(pEvdev->dev, EV_KEY, BTN_TOOL_PEN) ||
libevdev_has_event_code(pEvdev->dev, EV_KEY, BTN_STYLUS) ||
libevdev_has_event_code(pEvdev->dev, EV_KEY, BTN_STYLUS2))
{
xf86IDrvMsg(pInfo, X_PROBED, "Found absolute tablet.\n");
pEvdev->flags |= EVDEV_TABLET;
if (!pEvdev->num_buttons)
{
pEvdev->num_buttons = 7; /* LMR + scroll wheels */
pEvdev->flags |= EVDEV_BUTTON_EVENTS;
}
} else if (libevdev_has_event_code(pEvdev->dev, EV_ABS, ABS_PRESSURE) ||
libevdev_has_event_code(pEvdev->dev, EV_KEY, BTN_TOUCH)) {
if (has_lmr || libevdev_has_event_code(pEvdev->dev, EV_KEY, BTN_TOOL_FINGER)) {
xf86IDrvMsg(pInfo, X_PROBED, "Found absolute touchpad.\n");
pEvdev->flags |= EVDEV_TOUCHPAD;
} else {
xf86IDrvMsg(pInfo, X_PROBED, "Found absolute touchscreen\n");
pEvdev->flags |= EVDEV_TOUCHSCREEN;
pEvdev->flags |= EVDEV_BUTTON_EVENTS;
}
} else if (!(libevdev_has_event_code(pEvdev->dev, EV_REL, REL_X) &&
libevdev_has_event_code(pEvdev->dev, EV_REL, REL_Y)) && has_lmr) {
/* some touchscreens use BTN_LEFT rather than BTN_TOUCH */
xf86IDrvMsg(pInfo, X_PROBED, "Found absolute touchscreen\n");
pEvdev->flags |= EVDEV_TOUCHSCREEN;
pEvdev->flags |= EVDEV_BUTTON_EVENTS;
}
} else {
#ifdef MULTITOUCH
if (!libevdev_has_event_code(pEvdev->dev, EV_ABS, ABS_MT_POSITION_X) ||
!libevdev_has_event_code(pEvdev->dev, EV_ABS, ABS_MT_POSITION_Y))
#endif
EvdevForceXY(pInfo, Absolute);
}
For interface 1 this code finds absolute axis, including pressure, but no
button marked stylus (it is marked as mouse button 1, but mislabeled as Back
by the kernel somehow), and so it decides on a touchpad. This single error
causes it to set relative mode and ignore any motion without pressure.
So, opinions? How should these flaws be patched? |