#!/usr/bin/python # coding=utf-8 # # cakemote.py - uinput driver for PS3 remote # Heavily hacked from work by Will Woods, Brett Rodgers and the WMD team # by Jo Shields # # Immeasurable thanks go out to: # Will Woods # # Thanks go to Brett Rodgers for butchering # Will Woods' Python Wii-mote test script to work with the PS3 Bluetooth remote # # requires pybluez - http://org.csail.mit.edu/pybluez/ # # ******************************************* # # IMPORTANT!!!!!!!!!!!!!!! # # ******************************************* # # The lines starting "class uinput()" are PLATFORM SPECIFIC # # This means they must be changed for different types of # computer # # The lines currently shown are tested on x86-64 ONLY! # # For PPC64 (e.g. PS3), use: # UI_DEV_CREATE = 0x20005501 # UI_DEV_DESTROY = 0x20005502 # UI_SET_EVBIT = 0x80045564 # UI_SET_KEYBIT = 0x80045565 # UI_SET_RELBIT = 0x80045566 # # Settings for i386 are currently UNKNOWN! from optparse import OptionParser import bluetooth import os import sys import math import time import fcntl,struct version = "0.cake" # I like cake! class uinput(): UI_DEV_CREATE = 0x5501 UI_DEV_DESTROY = 0x5502 UI_SET_EVBIT = 0x40045564 UI_SET_KEYBIT = 0x40045565 UI_SET_RELBIT = 0x40045566 EV_SYN = 0x00 EV_KEY = 0x01 EV_REL = 0x02 REL_X = 0x00 REL_Y = 0x01 BUS_USB = 0x03 ABS_MAX = 0x3f BTN_MOUSE = 0x110 SYN_REPORT = 0 uinput_user_dev = "80sHHHHi" + 64*4*'I' input_event = "LLHHi" class PSRDiscoverer(bluetooth.DeviceDiscoverer): def __init__(self,maxdevs=1): bluetooth.DeviceDiscoverer.__init__(self) # init parent self.Ps3motes = [] self.done = False self.inprogress = False self.maxdevs = maxdevs # We identify Ps3motes by their device name at the moment def device_discovered(self,address,device_class,name): if not name: name = bluetooth.lookup_name(address) if name.startswith('BD Remote Control'): print "Found remote at address %s" % address w=Ps3mote(address,len(self.Ps3motes)) self.Ps3motes.append(w) if len(self.Ps3motes) == self.maxdevs: self.done = True def pre_inquiry(self): self.inprogress = True def inquiry_complete(self): self.inprogress = False self.done = True def find_uinput(): for n in ("/dev/uinput","/dev/input/uinput","/dev/misc/uinput"): if os.path.exists(n): return n return None def init_uinput(dev): # Refs: http://svn.navi.cx/misc/trunk/python/uinput_test.py # http://blog.davr.org/ + http://davr.org/wiimotulator.py.txt # http://www.popies.net/ams/ (ABS_[XY] device used as mouse) absmax = [0] * (uinput.ABS_MAX+1) absmin = [0] * (uinput.ABS_MAX+1) absfuzz = [0] * (uinput.ABS_MAX+1) absflat = [0] * (uinput.ABS_MAX+1) fd = os.open(dev,os.O_RDWR) user_dev_data = struct.pack(uinput.uinput_user_dev,"Sony PS3 Remote (cakemote driver)", uinput.BUS_USB,1,1,1,0,*(absmax + absmin + absfuzz + absflat)) print "user_dev_data: %s" % user_dev_data.encode("hex") os.write(fd,user_dev_data) # Set the event bits # fcntl.ioctl(fd,uinput.UI_SET_EVBIT, uinput.EV_ABS) # fcntl.ioctl(fd,uinput.UI_SET_ABSBIT, uinput.ABS_X) # fcntl.ioctl(fd,uinput.UI_SET_ABSBIT, uinput.ABS_Y) fcntl.ioctl(fd,uinput.UI_SET_EVBIT, uinput.EV_KEY) # fcntl.ioctl(fd,uinput.UI_SET_EVBIT, uinput.EV_SYN) # fcntl.ioctl(fd,uinput.UI_SET_KEYBIT, uinput.BTN_MOUSE) # TODO: Other bits... for button in Ps3mote.PS3keys: fcntl.ioctl(fd,uinput.UI_SET_KEYBIT, Ps3mote.PS3keys[button]) # Create the device! fcntl.ioctl(fd,uinput.UI_DEV_CREATE) return fd def destroy_uinput(fd): fcntl.ioctl(fd,uinput.UI_DEV_DESTROY) class Ps3mote(object): PS3keys = { "KEY_RESERVED": 0, "KEY_ESC": 1, "KEY_1": 2, "KEY_2": 3, "KEY_3": 4, "KEY_4": 5, "KEY_5": 6, "KEY_6": 7, "KEY_7": 8, "KEY_8": 9, "KEY_9": 10, "KEY_0": 11, "KEY_MINUS": 12, "KEY_EQUAL": 13, "KEY_BACKSPACE": 14, "KEY_TAB": 15, "KEY_Q": 16, "KEY_W": 17, "KEY_E": 18, "KEY_R": 19, "KEY_T": 20, "KEY_Y": 21, "KEY_U": 22, "KEY_I": 23, "KEY_O": 24, "KEY_P": 25, "KEY_LEFTBRACE": 26, "KEY_RIGHTBRACE": 27, "KEY_ENTER": 28, "KEY_LEFTCTRL": 29, "KEY_A": 30, "KEY_S": 31, "KEY_D": 32, "KEY_F": 33, "KEY_G": 34, "KEY_H": 35, "KEY_J": 36, "KEY_K": 37, "KEY_L": 38, "KEY_SEMICOLON": 39, "KEY_APOSTROPHE": 40, "KEY_GRAVE": 41, "KEY_LEFTSHIFT": 42, "KEY_BACKSLASH": 43, "KEY_Z": 44, "KEY_X": 45, "KEY_C": 46, "KEY_V": 47, "KEY_B": 48, "KEY_N": 49, "KEY_M": 50, "KEY_COMMA": 51, "KEY_DOT": 52, "KEY_SLASH": 53, "KEY_RIGHTSHIFT": 54, "KEY_KPASTERISK": 55, "KEY_LEFTALT": 56, "KEY_SPACE": 57, "KEY_CAPSLOCK": 58, "KEY_F1": 59, "KEY_F2": 60, "KEY_F3": 61, "KEY_F4": 62, "KEY_F5": 63, "KEY_F6": 64, "KEY_F7": 65, "KEY_F8": 66, "KEY_F9": 67, "KEY_F10": 68, "KEY_NUMLOCK": 69, "KEY_SCROLLLOCK": 70, "KEY_KP7": 71, "KEY_KP8": 72, "KEY_KP9": 73, "KEY_KPMINUS": 74, "KEY_KP4": 75, "KEY_KP5": 76, "KEY_KP6": 77, "KEY_KPPLUS": 78, "KEY_KP1": 79, "KEY_KP2": 80, "KEY_KP3": 81, "KEY_KP0": 82, "KEY_KPDOT": 83, "KEY_103RD": 84, "KEY_F13": 85, "KEY_102ND": 86, "KEY_F11": 87, "KEY_F12": 88, "KEY_F14": 89, "KEY_F15": 90, "KEY_F16": 91, "KEY_F17": 92, "KEY_F18": 93, "KEY_F19": 94, "KEY_F20": 95, "KEY_KPENTER": 96, "KEY_RIGHTCTRL": 97, "KEY_KPSLASH": 98, "KEY_SYSRQ": 99, "KEY_RIGHTALT": 100, "KEY_LINEFEED": 101, "KEY_HOME": 102, "KEY_UP": 103, "KEY_PAGEUP": 104, "KEY_LEFT": 105, "KEY_RIGHT": 106, "KEY_END": 107, "KEY_DOWN": 108, "KEY_PAGEDOWN": 109, "KEY_INSERT": 110, "KEY_DELETE": 111, "KEY_MACRO": 112, "KEY_MUTE": 113, "KEY_VOLUMEDOWN": 114, "KEY_VOLUMEUP": 115, "KEY_POWER": 116, "KEY_KPEQUAL": 117, "KEY_KPPLUSMINUS": 118, "KEY_PAUSE": 119, "KEY_F21": 120, "KEY_F22": 121, "KEY_F23": 122, "KEY_F24": 123, "KEY_KPCOMMA": 124, "KEY_LEFTMETA": 125, "KEY_RIGHTMETA": 126, "KEY_COMPOSE": 127, "KEY_STOP": 128, "KEY_AGAIN": 129, "KEY_PROPS": 130, "KEY_UNDO": 131, "KEY_FRONT": 132, "KEY_COPY": 133, "KEY_OPEN": 134, "KEY_PASTE": 135, "KEY_FIND": 136, "KEY_CUT": 137, "KEY_HELP": 138, "KEY_MENU": 139, "KEY_CALC": 140, "KEY_SETUP": 141, "KEY_SLEEP": 142, "KEY_WAKEUP": 143, "KEY_FILE": 144, "KEY_SENDFILE": 145, "KEY_DELETEFILE": 146, "KEY_XFER": 147, "KEY_PROG1": 148, "KEY_PROG2": 149, "KEY_WWW": 150, "KEY_MSDOS": 151, "KEY_COFFEE": 152, "KEY_DIRECTION": 153, "KEY_CYCLEWINDOWS": 154, "KEY_MAIL": 155, "KEY_BOOKMARKS": 156, "KEY_COMPUTER": 157, "KEY_BACK": 158, "KEY_FORWARD": 159, "KEY_CLOSECD": 160, "KEY_EJECTCD": 161, "KEY_EJECTCLOSECD": 162, "KEY_NEXTSONG": 163, "KEY_PLAYPAUSE": 164, "KEY_PREVIOUSSONG": 165, "KEY_STOPCD": 166, "KEY_RECORD": 167, "KEY_REWIND": 168, "KEY_PHONE": 169, "KEY_ISO": 170, "KEY_CONFIG": 171, "KEY_HOMEPAGE": 172, "KEY_REFRESH": 173, "KEY_EXIT": 174, "KEY_MOVE": 175, "KEY_EDIT": 176, "KEY_SCROLLUP": 177, "KEY_SCROLLDOWN": 178, "KEY_KPLEFTPAREN": 179, "KEY_KPRIGHTPAREN": 180, "KEY_INTL1": 181, "KEY_INTL2": 182, "KEY_INTL3": 183, "KEY_INTL4": 184, "KEY_INTL5": 185, "KEY_INTL6": 186, "KEY_INTL7": 187, "KEY_INTL8": 188, "KEY_INTL9": 189, "KEY_LANG1": 190, "KEY_LANG2": 191, "KEY_LANG3": 192, "KEY_LANG4": 193, "KEY_LANG5": 194, "KEY_LANG6": 195, "KEY_LANG7": 196, "KEY_LANG8": 197, "KEY_LANG9": 198, "KEY_PLAYCD": 200, "KEY_PAUSECD": 201, "KEY_PROG3": 202, "KEY_PROG4": 203, "KEY_SUSPEND": 205, "KEY_CLOSE": 206, "KEY_UNKNOWN": 220, "KEY_BRIGHTNESSDOWN": 224, "KEY_BRIGHTNESSUP": 225 } Keymap = { "00": "KEY_1", #num1 "01": "KEY_2", #num2 "02": "KEY_3", #num3 "03": "KEY_4", #num4 "04": "KEY_5", #num5 "05": "KEY_6", #num6 "06": "KEY_7", #num7 "07": "KEY_8", #num8 "08": "KEY_9", #num9 "09": "KEY_0", #num0 "0b": "KEY_ENTER", #enter "0e": "KEY_ESC", #return "0f": "KEY_C", #clear "16": "KEY_F1", #eject "1a": "KEY_F2", #topmenu "28": "KEY_A", #time "30": "KEY_COMMA", #prev "31": "KEY_DOT", #next "32": "KEY_P", #play "33": "KEY_B", #scan/rev "34": "KEY_N", #scan/fwd "38": "KEY_S", #stop "39": "KEY_D", #pause "40": "KEY_M", #popup/menu "43": "KEY_F3", #playstation "50": "KEY_F4", #select "51": "KEY_F5", #l3 "52": "KEY_F6", #r3 "53": "KEY_F7", #start "54": "KEY_UP", #up "55": "KEY_RIGHT", #right "56": "KEY_DOWN", #down "57": "KEY_LEFT", #left "58": "KEY_MINUS", #l2 "59": "KEY_EQUAL", #r2 "5a": "KEY_LEFTBRACE", #l1 "5b": "KEY_RIGHTBRACE", #r1 "5c": "KEY_F", #triangle "5d": "KEY_G", #circle "5e": "KEY_H", #cross "5f": "KEY_J", #square "60": "KEY_K", #step/rev "61": "KEY_L", #step/fwd "63": "KEY_Z", #subtitle "64": "KEY_V", #audio "65": "KEY_F8", #angle "70": "KEY_I", #display "80": "KEY_F12", #blue "81": "KEY_F9", #red "82": "KEY_F10", #green "83": "KEY_F11" #yellow } Maskmap = { 8388608: "57", 4194304: "56", 2097152: "55", 1048576: "54", 524288: "53", 262144: "52", 131072: "51", 65536: "50", 32768: "5f", 16384: "5e", 8192: "5d", 4096: "5c", 2048: "5b", 1024: "5a", 512: "59", 256: "58", 8: "0b", 1: "43" } def __init__(self,addr,number=0): self.connected=False self.done=False self.addr=addr self.number=number self.lastdata="" self.lastmasknum=0 self.rx = bluetooth.BluetoothSocket(bluetooth.L2CAP) def connect(self): print "Attempting to pair with remote %s" % (self.addr) self.rx.connect((self.addr,19)) self.connected=True def disconnect(self): self.rx.close() self.connected=False def mainloop(self): while not self.done: self._getpacket() def _handle_force_data(self,data): if len(data) != 3: return False self.force = [ord(d) for d in data] return True def _getpacket(self): data=self.rx.recv(1024) mask="000000" masknum=0 if data.encode("hex") != self.lastdata: if self.lastdata[22:24] == "01": if (self.lastdata[10:12]) != "ff": print "***Unpressing key" if (self.Keymap[self.lastdata[10:12]]): uinput_send_keyup(self.PS3keys[self.Keymap[self.lastdata[10:12]]]) self.lastdata = data.encode("hex") if len(data) == 13: # PS3-Mote Button if (data.encode("hex")[22:24]) == "03": print "***Unknown/multiple keys pressed: %s" % (data.encode("hex")) elif (data.encode("hex")[22:24]) == "01": if (data.encode("hex")[10:12]) != "ff": print "***Keypress" if (self.Keymap[data.encode("hex")[10:12]]): uinput_send_keydown(self.PS3keys[self.Keymap[data.encode("hex")[10:12]]]) elif (data.encode("hex")[10:12]) == "ff": print "***Multiple Pad buttons" mask=data.encode("hex")[4:10] masknum=int(mask,16) if (masknum != self.lastmasknum): tempmask = self.lastmasknum - masknum if tempmask > 0: for mask in self.Maskmap: if (tempmask - mask) >= 0: tempmask=tempmask-mask uinput_send_keyup(self.PS3keys[self.Keymap[self.Maskmap[mask]]]) self.lastmasknum = masknum for mask in self.Maskmap: if (masknum - mask) >= 0: masknum=masknum-mask uinput_send_multikeydown(self.PS3keys[self.Keymap[self.Maskmap[mask]]]) uinput_send_endmultikeydown() else: print "Unknown/multiple keys pressed: %s" % (data.encode("hex")) else: print "Unknown packet len %i: 0x%s" % (len(data),data.encode("hex")) % "\r" def _waitforpacket(self,header,max=32): r='' n=0 while (n