BetterOS.org : An attempt to make computer machines run better


home | better linux | games | software | tutorials | reference | web log |

Web Log: index |

Web Log

April 10, 2014 - Hanvon Drawing Tablet and FreeBSD Syscalls

April 10, 2014

Hanvon Drawing Tablet and FreeBSD Syscalls

Last night, a good friend of mine came over and we drank some beer. We talked about computer stuff and he asked me about this dusty old drawing tablet that I had lying around.

The thing is a Hanvon Graphicpal that I have had for the longest time. I got it in 2007 for Christmas and I have never used it.

My friend and I took to trying to figure out exactly how it worked and ended up taking the stylus apart. I don’t now a lot of hardware stuff, but I was really confused by that thing, theres no battery or capacitor or anything inside there, but there is a coil and a button, and a bunch of what looks like resistors. It must get power from somewhere, but I’m pretty stumped. Anyways, that’s not real important now.

The reason I never used it is because I could never get it to function properly in Linux. There seem to be Linux drivers for it, but I could never get them to work right. At the time I was trying to get a job with a company that was making some android device and I wanted to take a some time to learn some Linux kernel module programming. So I took up the challenge of writing a kernel driver for my Hanvon Graphicpal.

I didn’t bother to look at any documentation or even the sources of the other drivers, instead, I took the hardest possible route and went about reverse engineering the protocol. Fortunately, its a really simple device and the reverse engineering took no time at all.

The device sends out a 10 byte packet through USB when in use. Four of those bytes are used for the X,Y coordinates and the rest is pressure and button/stylus states.

I wrote the driver and got basic functionality working, but it was always super hacky. I had it acting like a mouse, totally ignoring all pressure and making lots of silly assumptions about things like the max X and Y. Nevertheless, I could touch the stylus to the pad and see my mouse cursor jump to the place where I touched, I could click by tapping the stylus on the pad and right click with the button on the stylus side. Once I had that working, I got bored and gave up on the project, telling myself that I had succeeded and there was no need to do more.

I put the tablet on a shelf and it collected dust there for 6 years. Now, the source for that driver is lost and I’m back to square one (except now I can skip the reverse engineering mostly).

I don’t really feel like writing a FreeBSD kernel module right now, but it seems that FreeBSD already recognizes the device. It gets mapped to /dev/usb/4.3.1 and the raw output from the device gets written to that file.

So, all I have to do it read that file and interpret the data, easy.

To make the task a bit more challenging (and learn about FreeBSD), I decided to not use the standard C library at all, and instead use indirect system calls (which actually still uses libc).

This is actually about the same as it would be on Linux. FreeBSD provides a function called __syscall() to make indirect syscalls. Most of the FreeBSD syscalls are even the same as Linux. However, what is really interesting is that unlink Linux, FreeBSD has over 500 syscalls, while Linux only has about 300. Unfortunately, looking through them, it seems many of them are for backwards compatibility with older FreeBSD systems so it’s not all that interesting.

The syscalls that I needed were just open, read, write, close, and fcntl. Basically all I want to do for now is get the data and print out the X,Y coordinates of the stylus.

Now, the code is not pretty at all, and it doesn’t even bother to do the job perfectly. There are a few problems.

When the stylus gets near the top or left edges of the pad (0,0) then at some point the coordinates get read as very high numbers. I think I have some problem with my bit shifts, but it doesn’t really make perfect sense to me yet. The code is also just ugly.

Anyways, here is the code:

#include <sys/syscall.h>

#include <unistd.h>

#include <fcntl.h>

struct packet

{

char flags[2]; uint16_t x,y;

char x[2],y[2];

char press;

char xyp;

char pad[2];

};

//Write the value of a byte

void writebyte(int fd, uint8_t byte)

{

  char res[4];

  int len = 0;

  for (; byte>0; len++)

  {

     res[len] = byte%10+'0';

     byte=byte/10;

  }

  res[len] = 0; //null-terminating

  //now we need to reverse res

int i;

  for (i = len; i>=0; i--)

  {

__syscall(SYS_write,fd,&res[i],1);

  }

}

//write the value of a 16bit intager

void writeint(int fd, uint16_t num)

{

  char res[6];

  int len = 0;

  for (; num>0; len++)

  {

     res[len] = num%10+'0';

     num=num/10;

  }

  res[len] = 0; //null-terminating

  //now we need to reverse res

int i;

  for (i = len; i>=0; i--)

  {

 __syscall(SYS_write,fd,&res[i],1);

  }

}

int main()

{

struct packet packet;

int fd = __syscall(SYS_open,"/dev/usb/4.3.1",O_RDONLY);

int stdout = __syscall(SYS_fcntl,STDOUT_FILENO,F_DUPFD,0);

while (1)

{

 uint16_t x,y;

 __syscall(SYS_read,fd,&packet,10);

 x=(packet.x[0]<<9)+(packet.x[1]<<1);

 y=(packet.y[0]<<9)+(packet.y[1]<<1);

 

 __syscall(SYS_write,stdout,"(",1);

 writeint(stdout,x);

 __syscall(SYS_write,stdout,",",1);

 writeint(stdout,y);

 __syscall(SYS_write,stdout,")",1);

 __syscall(SYS_write,stdout,"\n",1);

}

}