#!/usr/bin/python # Resistive "pen tablet" input device for OLPC XO-1 # Copyright (C) 2008 Jeff Epler # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import os, time, select, glob, struct, fcntl UI_DEV_CREATE = 0x5501 UI_DEV_DESTROY = 0x5502 UI_SET_EVBIT = 0x40045564 UI_SET_KEYBIT = 0x40045565 UI_SET_RELBIT = 0x40045566 UI_SET_ABSBIT = 0x40045567 EV_SYN = 0x0 EV_KEY = 0x1 EV_REL = 0x2 EV_ABS = 0x3 REL_X = 0x0 REL_Y = 0x1 ABS_X = 0x0 ABS_Y = 0x1 BUS_VIRTUAL = 0x6 BTN_LEFT = 0x110 BTN_RIGHT = 0x111 BTN_TOOL_PEN = 0x140 SYN_REPORT = 0x0 class Dev: def __init__(self): self.fd = os.open("/dev/input/uinput", os.O_RDWR) descriptor = struct.pack("80sHHHHi", "OLPC Resistive Touchpad", BUS_VIRTUAL, 1, 1, 1, 0) # for i in range(64): descriptor += struct.pack("I", 0) # absmin # for i in range(64): # if i == 0: absmax = 1024 # elif i == 1: absmax = 256 # else: absmax=0 # descriptor += struct.pack("I", absmax) # absmax # for i in range(64): # descriptor += struct.pack("II", 0, 0) # fuzz, flat print repr(descriptor) absmin = struct.pack("I", 0) * 64 absmax = struct.pack("I", 1024) * 64 absfuzz = struct.pack("I", 0) * 64 absflat = struct.pack("I", 0) * 64 os.write(self.fd, descriptor + absmin + absmax + absfuzz + absflat) fcntl.ioctl(self.fd, UI_SET_EVBIT, EV_REL) fcntl.ioctl(self.fd, UI_SET_EVBIT, EV_KEY) fcntl.ioctl(self.fd, UI_SET_EVBIT, EV_SYN) fcntl.ioctl(self.fd, UI_SET_RELBIT, REL_X) fcntl.ioctl(self.fd, UI_SET_RELBIT, REL_Y) fcntl.ioctl(self.fd, UI_SET_KEYBIT, BTN_LEFT) fcntl.ioctl(self.fd, UI_SET_KEYBIT, BTN_RIGHT) fcntl.ioctl(self.fd, UI_DEV_CREATE) self.last = None self.button = 0, 0 def send(self, mark, typ, code, val): event = struct.pack("LLHHi", mark, 0, typ, code, val) os.write( self.fd, event) def see(self, x, y, z, a, b): t0 = time.time() if z: if self.last is not None: dx = x - self.last[0] dy = y - self.last[1] print dx, dy self.send(t0, EV_REL, REL_X, dx) self.send(t0, EV_REL, REL_Y, dy) self.last = (x,y) else: self.last = None if a != self.button[0]: self.send(t0, EV_KEY, BTN_LEFT, a) if b != self.button[1]: self.send(t0, EV_KEY, BTN_RIGHT, b) self.button = a, b self.send(t0, EV_SYN, SYN_REPORT, 0) VERBOSE=0 def X(s, dir="<"): if not VERBOSE: return print dir, for c in s: print "%02x" % ord(c), print def read(f, sz, timeout=None): if timeout is not None: r, w, x = select.select([f], [], [], timeout) if not r: print "Timeout reading %d bytes" % sz return None d = os.read(f, sz) X(d) return d def fullread(f, sz, timeout=None): result = "" while len(result) < sz: result += read(f, sz-len(result)) return result def write(f, d): X(d, 40*" " + ">") return os.write(f, d) def transact(f, d, sz): for ch in d: write(f, ch) read(f, 1) return fullread(f, sz) def resync(f): print "syncing" while 1: c = read(f, 1) if c == '\xcf': break fullread(f, 5) def main(f, dev): reset(f) while read(f, 1, .5): pass print "getting identifier" #write(f, "\xe7\xe7\xe7\xe9") #id = read(f, 4) id = transact(f, "\xe7\xe7\xe7\xe9", 3) print id.encode("hex") assert id[:2] == "\x67\x00", "not an olpc touchpad? %s" % id.encode("hex") print "revision", ord(id[2]) print "Setting pad to advanced mode" transact(f, "\xf5\xf5\xf5\xf5", 0); # read(f, 4, 1) # transact(f, "\xf5", 0); # read(f, 4, 1) print "Setting pad to PT-only mode" transact(f, "\xf2\xf2\xf2\xe7", 0); print "reenabling pad" transact(f, "\xf4", 0); resync(f) while 1: packet = fullread(f, 6) if packet[0] != "\xcf": print "First byte of packet not 0xcf" packet = resync(f) continue for i in range(1, 6): if packet[i] > "\x7f": print "Byte %d of packet has high bit set" % i resync(f) continue packet = map(ord, packet) x = packet[1] + (((packet[2] >> 3) & 0x7)<<7) y = packet[4] + (((packet[3] >> 4) & 0x7)<<7) z = packet[5] #if x == 0 and y == 0 and z == 0: continue pt_dsw = bool(packet[2] & 0x2) sw_r = bool(packet[3] & 0x2) sw_l = bool(packet[3] & 0x1) dev.see(x, y, z, sw_l, sw_r) print "%4d %4d %4d %1d %1d %1d" % (x,y,z,pt_dsw,sw_r,sw_l), print " ".join("%02X" % i for i in packet) def reset(f): print "Resetting pad to PS/2 mode" write(f, "\xff") while read(f, 1, .15): pass f = None os.system("modprobe uevent") dev = Dev() try: drvctl = open("/sys/devices/platform/i8042/serio1/drvctl", "w") drvctl.write("serio_raw") drvctl.close() time.sleep(.5) devpath = glob.glob("/dev/serio_raw*")[0] print devpath f = os.open(devpath, os.O_RDWR) try: main(f, dev) except KeyboardInterrupt: raise SystemExit, 0 finally: if f is not None: reset(f) os.close(f) open("/sys/devices/platform/i8042/serio1/drvctl", "w").write("rescan")