tslib not recognizing ft6236 touchscreen due to missing ABS_PRESSURE capability

In my earlier post on Complete rotation support for the Adafruit PiTFT 2.8″ capacitive touchscreen display I described that support for the touchscreen has landed in the mainline kernel in form of the ft6236 driver. I also described that my test program would work correctly now.

Turns out this way not entirely true.

But first here’s an updated version of the test program that reports on the X axis only:

import os
import pygame
import time

# Initialize environment variables 
os.putenv('SDL_FBDEV', '/dev/fb1')

# Initialize pygame and screen
(width, height) = pygame.display.list_modes(0)[0]
screen = pygame.display.set_mode((width, height))

font_big = pygame.font.Font(None, 100)

count = 0
last_x = 0

def DrawNumber(nr):
    screen.fill((255, 255, 255))
    text_surface = font_big.render("{0}".format(count), True, (0,0,0))
    rect = text_surface.get_rect(center=(width/2,height/2))
    screen.blit(text_surface, rect)

while True:
    for event in pygame.event.get():
        cur_x = pygame.mouse.get_pos()[0]

        if event.type is pygame.MOUSEBUTTONDOWN:
            last_x = cur_x
            print "Touched at x: {0}".format(cur_x)
        elif event.type is pygame.MOUSEBUTTONUP:
            last_x = 0
            print "Released at x: {0}".format(cur_x)
        elif event.type is pygame.MOUSEMOTION:
            print "Movement to x: {0}".format(cur_x)

            delta_x = cur_x - last_x
            if delta_x > 0:
              count = count + 1
              print "count + 1"
            elif delta_x < 0:
              count = count - 1
              print "count - 1"


            last_x = cur_x

With this updated version I noticed that while the axes get detected correctly (swiping right increases the X coordinate), the coordinate itself didn't: if I began the swipe gesture at the middle left side of the touchscreen and moved to the right, X coordinate 319 was already reached in the middle of the screen, causing further swiping to not really have an effect. Looks like a scaling issue of some kind.

Since I knew that pygame was using tslib in the background, I thought "maybe the touchscreen needs calibration", although Adafruit says the capacitive version doesn't. So I tried to run

pi@raspberrypi:~ $ sudo TSLIB_FBDEVICE=/dev/fb1 TSLIB_TSDEVICE=/dev/input/event0 ts_calibrate
xres = 320, yres = 240
selected device is not a touchscreen I understand

Huh? What’s this? The touchscreen doesn’t work indeed! Let’s try ts_test:

pi@raspberrypi:~ $ sudo TSLIB_FBDEVICE=/dev/fb1 TSLIB_TSDEVICE=/dev/input/event0 ts_test
selected device is not a touchscreen I understand

Same thing. But this used to work before, didn’t it?

To verify, I downgraded to Adafruit’s kernel and the pitft28c overlay. And indeed, tslib worked again. Hm. Let’s have a look at the sources, after all it’s Open Source, right?

At first I grepped the Github tslib sources for the message shown above. No match. Apparantly the sources changed between the version used in Raspbian and later versions.

So I installed the sources of Raspbian’s libts-0.0-0 package and looked at those:

pi@raspberrypi:~/tslib-1.0 $ grep -ri "selected device is not a touchscreen I understand" *
Binary file debian/tmp/usr/lib/arm-linux-gnueabihf/ts0/input.so matches
debian/patches/32bitBE-support.diff: 		fprintf(stderr, "selected device is not a touchscreen I understand\n");
Binary file debian/libts-0.0-0/usr/lib/arm-linux-gnueabihf/ts0/input.so matches
Binary file plugins/.libs/input-raw.o matches
Binary file plugins/.libs/input.so matches
plugins/input-raw.c:		fprintf(stderr, "selected device is not a touchscreen I understand\n");

So plugins/input-raw.c is what we want to look at. check_fd() is the function that checks a file descriptor for certain attributes to determine whether it is a touchscreen that tslib understands. In this particular version used by Raspbian that function used a big monolithic check as follows:

static int check_fd(struct tslib_input *i)
        if (! ((ioctl(ts->fd, EVIOCGVERSION, &version) >= 0) &&
                (version == EV_VERSION || version == 0x010000) &&
                (ioctl(ts->fd, EVIOCGBIT(0, sizeof(bit)), bit) >= 0) &&
                (bit[0] & (1 < < EV_ABS)) &&
                (ioctl(ts->fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) &&
                (absbit[0] & (1 < < ABS_X)) &&
                (absbit[0] & (1 << ABS_Y)) && (absbit[0] & (1 << ABS_PRESSURE)))) {
                fprintf(stderr, "selected device is not a touchscreen I understand\n");
                return -1;

This does a number of things:

  1. First it attempts to execute the EVIOCGVERSION ioctl, which tries to obtain the version of the underlying Linux input subsystem into the version variable.
  2. If this was successful, it tests whether the version equals the constant EV_VERSION (defined in /usr/src/include/linux/input.h as 0x01001) or the constant 0x010000.
  3. Next it attempts to execute the EVIOCGBIT ioctl with the parameter 0, which attempts to determine the device's capabilities into the bit variable.
  4. Now it tests whether the EV_ABS bit is set, ie. the driver supports reporting absolute integer values.
  5. If this was successful again, it executes the EVIOCGBIT ioctl another time, but this time with the parameter EV_ABS, so as to determine capabilities with regard to reporting absolute integer values.
  6. Finally three checks are performed on the reported capability value:
    1. does the device support reporting ABS_X values (X coordinates)?
    2. does the device support reporting ABS_Y values (Y coordinates)?
    3. does the device support reporting ABS_PRESSURE values?

One of these tests fails as we have seen above. I unwounded the combined check into single ones and found out: with the new ft6236 driver it failed on the ABS_PRESSURE test. And indeed:

pi@raspberrypi:~/tslib-1.0 $ sudo evtest /dev/input/touchscreen 
Input driver version is 1.0.1
Input device ID: bus 0x18 vendor 0x0 product 0x0 version 0x0
Input device name: "ft6236"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 330 (BTN_TOUCH)
  Event type 3 (EV_ABS)
    Event code 0 (ABS_X)
      Value    193
      Min        0
      Max      320
    Event code 1 (ABS_Y)
      Value    148
      Min        0
      Max      240
    Event code 47 (ABS_MT_SLOT)
      Value      0
      Min        0
      Max        1
    Event code 53 (ABS_MT_POSITION_X)
      Value      0
      Min        0
      Max      320
    Event code 54 (ABS_MT_POSITION_Y)
      Value      0
      Min        0
      Max      240
    Event code 57 (ABS_MT_TRACKING_ID)
      Value      0
      Min        0
      Max    65535
  Property type 1 (INPUT_PROP_DIRECT)
Testing ... (interrupt to exit)


Noralf Trønnes and Sean Cross, who have contributed the new driver to the Linux kernel, must have had their reasons, why they didn’t port the ABS_PRESSURE support over from Adafruit’s driver. I’ll have to ask them why.

In the mean time, however, if we look at the current Github version of check_fd(), the code has changed somewhat:

static int check_fd(struct tslib_input *i)
	/* Since some touchscreens (eg. infrared) physically can't measure pressure,
	the input system doesn't report it on those. Tslib relies on pressure, thus
	we set it to constant 255. It's still controlled by BTN_TOUCH/BTN_LEFT -
	when not touched, the pressure is forced to 0. */

		i->current_p = 255;

		if ((ioctl(ts->fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
			  keybit[BIT_WORD(BTN_LEFT)] & BIT_MASK(BTN_LEFT))) {
			fprintf(stderr, "tslib: Selected device is not a touchscreen (must support BTN_TOUCH or BTN_LEFT events)\n");
			return -1;

I won't go into much detail on this, the comment block basically says it all. I assume this newer tslib version would make things a bit easier, gotta look into that -- in a future post ;)