söndag 13 mars 2011

Pen device

I have bought a cheap Chinese writing pad
I payed 35rmb for it, its about 5$ And i was not too surprised to find out that there where no linux drivers for it. 
So what to do?
lsusb gave this:

Bus 006 Device 003: ID 04b4:fef3 Cypress Semiconductor Corp.

I opened a terminal and run dmesg and got this:
[35842.176268] input: PenPower Touchpad PenPower Touchpad as /devices/pci0000:00/0000:00:1d.0/usb6/6-1/6-1:1.0/input/input12
[35842.176547] generic-usb 0003:04B4:FEF3.0005: input,hidraw1: USB HID v1.10 Device [PenPower Touchpad PenPower Touchpad] on usb-0000:00:1d.0-1/input0
This looked promising, but could not find a program to use it.

i checked "xinput list" which gave me:
⎡ Virtual core pointer                        id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                  id=4    [slave  pointer  (2)]
⎜   ↳ ETPS/2 Elantech Touchpad                    id=13    [slave  pointer  (2)]
⎜   ↳ Dexin Corp.  Saitek  Game  Mouse            id=10    [slave  pointer  (2)]

So my pad have not been added as an input device.

i checked that i actually got any input by running:
sudo cat /dev/hidraw1

and i got something like this on random input:
� {� ~� �� �� � � � �# �& �& �V 7^ 7h 7u 7� 7


I wondered what kind of interface it used..
so i piped the input from /dev/input/hidraw1 to a text file

 sudo cat /dev/hidraw1 > lol

And then i pressed one button several times. and then aborted and checked it with a hex editor

ghex2 lol

 i got something like this:

00 00 00 00 02 00 00 00 00 02 00 00 00 00 02 00 00
00 00 02 00 00 00 00 02 00 00 00 00 02 00 00 00 00

which can be changed re-formated to:


00 00 00 00 02
00 00 00 00 02
00 00 00 00 02
00 00 00 00 02
00 00 00 00 02
00 00 00 00 02
00 00 00 00

and i did the same procedure for all the three buttons.

button1 got
00 00 00 00 02

button2 got
00 00 00 00 04

button3 got
00 00 00 00 01

pen down got
XX XX XX XX 08

to make this more easy to see i printed it in binary.
b1 = 0000 0000 0000 0010
b2 = 0000 0000 0000 0100
b3 = 0000 0000 0000 0001
P = XXXX XXXX XXXX 1000

After analysing the pen input for a while it became obvious that the input was formated as follows:

the first byte is the X positon and when it overflows it increases the second byte.
1111 0000 -> 0000 0001
The same applies for the third and fourth byte but for the Y axis.
I actually got disappointed that it was so easy to reverse engineer the interface..

No pressure sensitivity here! but i did not expect that.

I created a wrapper to send the input to the Xserver:
first some offsets:
/* gcc -g -Wall -o xtestmotion xtestmotion.c -L /usr/X11R6/lib/ -lX11 -lXtst */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <X11/extensions/XTest.h>

#define messageSize 5

int main( int argc, char *argv[] )
{
float pad_x = 70, pad_y = 180, pad_width=1255, pad_height=1354;
float canvas_x = 0, canvas_y = 0, canvas_width=1366, canvas_height=768;

        int x,y;
        Display *dpy;
        FILE *hiddev;
        int packet[messageSize];

        dpy = XOpenDisplay( NULL );
        assert(dpy);

        hiddev = fopen("/dev/hidraw1", "r");
        assert(hiddev);
      for(;;)
        {
            packet[0] = getc(hiddev);
            packet[1] = getc(hiddev);
            packet[2] = getc(hiddev);
            packet[3] = getc(hiddev);
            packet[4] = getc(hiddev);
           

            if(packet[4] & (1 << 1))
                printf("\nButton 1\n");
            if(packet[4] & (1 << 2))
                printf("\nButton 2\n");
            if(packet[4] & (1 << 0))
                printf("\nButton 3\n");
            if(packet[4] & (1 << 3))
                printf("\nPen Down\n");
                           

            //if the pen is down aka you are drawing/writing something
            if(packet[4] & (1 << 3))
            {
                    x = (packet[0]+255*packet[1] -pad_x)*(canvas_width/pad_width) + canvas_x;
                    y = (packet[2]+255*packet[3] -pad_y)*(canvas_height/pad_height) + canvas_y;

                    XTestFakeMotionEvent(dpy, -1, x, y, CurrentTime);
                    XTestFakeButtonEvent(dpy, 1, 1, CurrentTime);
            }
            else
            {
                    //mouse button is not pressed any more
                    XTestFakeButtonEvent(dpy, 1, 0, CurrentTime);
                    //mouse button released is not registered in all applications if the mouse is not moved
                    XTestFakeMotionEvent(dpy, -1, x+1, y+1, CurrentTime);

            }

            XFlush(dpy);
        }
        return 0;
}

and then compile, and start a painting program. i used myPaint.
well, i am not an artist but here is my first drawing in myPaint using a pad.
Next up, how to write a real driver for this pad. (when i get time and motivation)

1 kommentar:

  1. Congratulations, sounds like you put a lot of work into getting that to work!

    If only I knew linux well enough to do things like this.

    SvaraRadera