This version of the BD Patch supports re-mapping from /etc/bluetooth/input.conf
.
http://kitlaan.twinaxis.com/holdingcell/bluez_ps3remote.diff
=== modified file 'input/device.c' --- input/device.c 2009-11-14 19:57:42 +0000 +++ input/device.c 2009-11-25 15:02:08 +0000 @@ -465,14 +465,14 @@ static gboolean fake_hid_connect(struct input_conn *iconn, GError **err) { - struct fake_hid *fhid = iconn->fake->priv; + struct fake_hid *fhid = iconn->fake->hidd; return fhid->connect(iconn->fake, err); } static int fake_hid_disconnect(struct input_conn *iconn) { - struct fake_hid *fhid = iconn->fake->priv; + struct fake_hid *fhid = iconn->fake->hidd; return fhid->disconnect(iconn->fake); } @@ -603,7 +603,7 @@ } static int hidp_add_connection(const struct input_device *idev, - const struct input_conn *iconn) + struct input_conn *iconn) { struct hidp_connadd_req *req; struct fake_hid *fake_hid; @@ -639,8 +639,12 @@ fake = g_new0(struct fake_input, 1); fake->connect = fake_hid_connect; fake->disconnect = fake_hid_disconnect; - fake->priv = fake_hid; + fake->hidd = fake_hid; + bacpy(&fake->ba_src, &idev->src); + bacpy(&fake->ba_dst, &idev->dst); + fake->idle_timeout = iconn->timeout; err = fake_hid_connadd(fake, iconn->intr_io, fake_hid); + iconn->fake = fake; goto cleanup; } @@ -787,7 +791,7 @@ struct input_device *idev = user_data; int flags; - info("Input: disconnect %s", idev->path); + debug("Input: disconnect %s", idev->path); flags = removal ? (1 << HIDP_VIRTUAL_CABLE_UNPLUG) : 0; @@ -1291,3 +1295,15 @@ return 0; } + +void input_device_request_disconnect(const bdaddr_t *src, const bdaddr_t *dst) +{ + struct input_device *idev = find_device(src, dst); + + if (!idev) + return; + + // or call disconnect() ? + device_request_disconnect(idev->device, NULL); +} + === modified file 'input/device.h' --- input/device.h 2009-11-14 19:57:42 +0000 +++ input/device.h 2009-11-25 06:19:57 +0000 @@ -29,6 +29,7 @@ struct input_device; struct input_conn; +struct fake_hid; struct fake_input { int flags; @@ -36,9 +37,14 @@ int uinput; /* uinput socket */ int rfcomm; /* RFCOMM socket */ uint8_t ch; /* RFCOMM channel number */ + int idle_timeout; + guint timer; gboolean (*connect) (struct input_conn *iconn, GError **err); int (*disconnect) (struct input_conn *iconn); + bdaddr_t ba_src, ba_dst; + struct fake_hid *hidd; void *priv; + guint sid_in; }; int fake_input_register(DBusConnection *conn, struct btd_device *device, @@ -53,3 +59,5 @@ int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm, GIOChannel *io); int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst); +void input_device_request_disconnect(const bdaddr_t *src, const bdaddr_t *dst); + === modified file 'input/fakehid.c' --- input/fakehid.c 2009-11-14 19:57:42 +0000 +++ input/fakehid.c 2009-12-02 20:03:49 +0000 @@ -48,8 +48,479 @@ #include "fakehid.h" #include "uinput.h" +static int fake_hid_common_connect(struct fake_input *fake, GError **err); +static int fake_hid_common_disconnect(struct fake_input *fake); +static gboolean fake_hid_common_timeout(gpointer data); + +/* Possible inputs, based off of keys in uinput.h */ +#define ENTRY(x) [x] = #x +static char *uinput_map[] = { + ENTRY(KEY_ESC), + ENTRY(KEY_1), + ENTRY(KEY_2), + ENTRY(KEY_3), + ENTRY(KEY_4), + ENTRY(KEY_5), + ENTRY(KEY_6), + ENTRY(KEY_7), + ENTRY(KEY_8), + ENTRY(KEY_9), + ENTRY(KEY_0), + ENTRY(KEY_MINUS), + ENTRY(KEY_EQUAL), + ENTRY(KEY_BACKSPACE), + ENTRY(KEY_TAB), + ENTRY(KEY_Q), + ENTRY(KEY_W), + ENTRY(KEY_E), + ENTRY(KEY_R), + ENTRY(KEY_T), + ENTRY(KEY_Y), + ENTRY(KEY_U), + ENTRY(KEY_I), + ENTRY(KEY_O), + ENTRY(KEY_P), + ENTRY(KEY_LEFTBRACE), + ENTRY(KEY_RIGHTBRACE), + ENTRY(KEY_ENTER), + ENTRY(KEY_LEFTCTRL), + ENTRY(KEY_A), + ENTRY(KEY_S), + ENTRY(KEY_D), + ENTRY(KEY_F), + ENTRY(KEY_G), + ENTRY(KEY_H), + ENTRY(KEY_J), + ENTRY(KEY_K), + ENTRY(KEY_L), + ENTRY(KEY_SEMICOLON), + ENTRY(KEY_APOSTROPHE), + ENTRY(KEY_GRAVE), + ENTRY(KEY_LEFTSHIFT), + ENTRY(KEY_BACKSLASH), + ENTRY(KEY_Z), + ENTRY(KEY_X), + ENTRY(KEY_C), + ENTRY(KEY_V), + ENTRY(KEY_B), + ENTRY(KEY_N), + ENTRY(KEY_M), + ENTRY(KEY_COMMA), + ENTRY(KEY_DOT), + ENTRY(KEY_SLASH), + ENTRY(KEY_RIGHTSHIFT), + ENTRY(KEY_KPASTERISK), + ENTRY(KEY_LEFTALT), + ENTRY(KEY_SPACE), + ENTRY(KEY_CAPSLOCK), + ENTRY(KEY_F1), + ENTRY(KEY_F2), + ENTRY(KEY_F3), + ENTRY(KEY_F4), + ENTRY(KEY_F5), + ENTRY(KEY_F6), + ENTRY(KEY_F7), + ENTRY(KEY_F8), + ENTRY(KEY_F9), + ENTRY(KEY_F10), + ENTRY(KEY_NUMLOCK), + ENTRY(KEY_SCROLLLOCK), + ENTRY(KEY_KP7), + ENTRY(KEY_KP8), + ENTRY(KEY_KP9), + ENTRY(KEY_KPMINUS), + ENTRY(KEY_KP4), + ENTRY(KEY_KP5), + ENTRY(KEY_KP6), + ENTRY(KEY_KPPLUS), + ENTRY(KEY_KP1), + ENTRY(KEY_KP2), + ENTRY(KEY_KP3), + ENTRY(KEY_KP0), + ENTRY(KEY_KPDOT), + + ENTRY(KEY_ZENKAKUHANKAKU), + ENTRY(KEY_102ND), + ENTRY(KEY_F11), + ENTRY(KEY_F12), + ENTRY(KEY_RO), + ENTRY(KEY_KATAKANA), + ENTRY(KEY_HIRAGANA), + ENTRY(KEY_HENKAN), + ENTRY(KEY_KATAKANAHIRAGANA), + ENTRY(KEY_MUHENKAN), + ENTRY(KEY_KPJPCOMMA), + ENTRY(KEY_KPENTER), + ENTRY(KEY_RIGHTCTRL), + ENTRY(KEY_KPSLASH), + ENTRY(KEY_SYSRQ), + ENTRY(KEY_RIGHTALT), + ENTRY(KEY_LINEFEED), + ENTRY(KEY_HOME), + ENTRY(KEY_UP), + ENTRY(KEY_PAGEUP), + ENTRY(KEY_LEFT), + ENTRY(KEY_RIGHT), + ENTRY(KEY_END), + ENTRY(KEY_DOWN), + ENTRY(KEY_PAGEDOWN), + ENTRY(KEY_INSERT), + ENTRY(KEY_DELETE), + ENTRY(KEY_MACRO), + ENTRY(KEY_MUTE), + ENTRY(KEY_VOLUMEDOWN), + ENTRY(KEY_VOLUMEUP), + ENTRY(KEY_POWER), + ENTRY(KEY_KPEQUAL), + ENTRY(KEY_KPPLUSMINUS), + ENTRY(KEY_PAUSE), + + ENTRY(KEY_KPCOMMA), + ENTRY(KEY_HANGEUL), + ENTRY(KEY_HANGUEL), + ENTRY(KEY_HANJA), + ENTRY(KEY_YEN), + ENTRY(KEY_LEFTMETA), + ENTRY(KEY_RIGHTMETA), + ENTRY(KEY_COMPOSE), + + ENTRY(KEY_STOP), + ENTRY(KEY_AGAIN), + ENTRY(KEY_PROPS), + ENTRY(KEY_UNDO), + ENTRY(KEY_FRONT), + ENTRY(KEY_COPY), + ENTRY(KEY_OPEN), + ENTRY(KEY_PASTE), + ENTRY(KEY_FIND), + ENTRY(KEY_CUT), + ENTRY(KEY_HELP), + ENTRY(KEY_MENU), + ENTRY(KEY_CALC), + ENTRY(KEY_SETUP), + ENTRY(KEY_SLEEP), + ENTRY(KEY_WAKEUP), + ENTRY(KEY_FILE), + ENTRY(KEY_SENDFILE), + ENTRY(KEY_DELETEFILE), + ENTRY(KEY_XFER), + ENTRY(KEY_PROG1), + ENTRY(KEY_PROG2), + ENTRY(KEY_WWW), + ENTRY(KEY_MSDOS), + ENTRY(KEY_COFFEE), + ENTRY(KEY_SCREENLOCK), + ENTRY(KEY_DIRECTION), + ENTRY(KEY_CYCLEWINDOWS), + ENTRY(KEY_MAIL), + ENTRY(KEY_BOOKMARKS), + ENTRY(KEY_COMPUTER), + ENTRY(KEY_BACK), + ENTRY(KEY_FORWARD), + ENTRY(KEY_CLOSECD), + ENTRY(KEY_EJECTCD), + ENTRY(KEY_EJECTCLOSECD), + ENTRY(KEY_NEXTSONG), + ENTRY(KEY_PLAYPAUSE), + ENTRY(KEY_PREVIOUSSONG), + ENTRY(KEY_STOPCD), + ENTRY(KEY_RECORD), + ENTRY(KEY_REWIND), + ENTRY(KEY_PHONE), + ENTRY(KEY_ISO), + ENTRY(KEY_CONFIG), + ENTRY(KEY_HOMEPAGE), + ENTRY(KEY_REFRESH), + ENTRY(KEY_EXIT), + ENTRY(KEY_MOVE), + ENTRY(KEY_EDIT), + ENTRY(KEY_SCROLLUP), + ENTRY(KEY_SCROLLDOWN), + ENTRY(KEY_KPLEFTPAREN), + ENTRY(KEY_KPRIGHTPAREN), + ENTRY(KEY_NEW), + ENTRY(KEY_REDO), + + ENTRY(KEY_F13), + ENTRY(KEY_F14), + ENTRY(KEY_F15), + ENTRY(KEY_F16), + ENTRY(KEY_F17), + ENTRY(KEY_F18), + ENTRY(KEY_F19), + ENTRY(KEY_F20), + ENTRY(KEY_F21), + ENTRY(KEY_F22), + ENTRY(KEY_F23), + ENTRY(KEY_F24), + + ENTRY(KEY_PLAYCD), + ENTRY(KEY_PAUSECD), + ENTRY(KEY_PROG3), + ENTRY(KEY_PROG4), + ENTRY(KEY_SUSPEND), + ENTRY(KEY_CLOSE), + ENTRY(KEY_PLAY), + ENTRY(KEY_FASTFORWARD), + ENTRY(KEY_BASSBOOST), + ENTRY(KEY_PRINT), + ENTRY(KEY_HP), + ENTRY(KEY_CAMERA), + ENTRY(KEY_SOUND), + ENTRY(KEY_QUESTION), + ENTRY(KEY_EMAIL), + ENTRY(KEY_CHAT), + ENTRY(KEY_SEARCH), + ENTRY(KEY_CONNECT), + ENTRY(KEY_FINANCE), + ENTRY(KEY_SPORT), + ENTRY(KEY_SHOP), + ENTRY(KEY_ALTERASE), + ENTRY(KEY_CANCEL), + ENTRY(KEY_BRIGHTNESSDOWN), + ENTRY(KEY_BRIGHTNESSUP), + ENTRY(KEY_MEDIA), + + ENTRY(KEY_SWITCHVIDEOMODE), + ENTRY(KEY_KBDILLUMTOGGLE), + ENTRY(KEY_KBDILLUMDOWN), + ENTRY(KEY_KBDILLUMUP), + + ENTRY(KEY_SEND), + ENTRY(KEY_REPLY), + ENTRY(KEY_FORWARDMAIL), + ENTRY(KEY_SAVE), + ENTRY(KEY_DOCUMENTS), + + ENTRY(KEY_BATTERY), + + ENTRY(KEY_BLUETOOTH), + ENTRY(KEY_WLAN), + ENTRY(KEY_UWB), + + ENTRY(KEY_UNKNOWN), + + ENTRY(KEY_VIDEO_NEXT), + ENTRY(KEY_VIDEO_PREV), + ENTRY(KEY_BRIGHTNESS_CYCLE), + ENTRY(KEY_BRIGHTNESS_ZERO), + ENTRY(KEY_DISPLAY_OFF), + + ENTRY(KEY_WIMAX), + + ENTRY(BTN_MISC), + ENTRY(BTN_0), + ENTRY(BTN_1), + ENTRY(BTN_2), + ENTRY(BTN_3), + ENTRY(BTN_4), + ENTRY(BTN_5), + ENTRY(BTN_6), + ENTRY(BTN_7), + ENTRY(BTN_8), + ENTRY(BTN_9), + + ENTRY(BTN_MOUSE), + ENTRY(BTN_LEFT), + ENTRY(BTN_RIGHT), + ENTRY(BTN_MIDDLE), + ENTRY(BTN_SIDE), + ENTRY(BTN_EXTRA), + ENTRY(BTN_FORWARD), + ENTRY(BTN_BACK), + ENTRY(BTN_TASK), + + ENTRY(BTN_JOYSTICK), + ENTRY(BTN_TRIGGER), + ENTRY(BTN_THUMB), + ENTRY(BTN_THUMB2), + ENTRY(BTN_TOP), + ENTRY(BTN_TOP2), + ENTRY(BTN_PINKIE), + ENTRY(BTN_BASE), + ENTRY(BTN_BASE2), + ENTRY(BTN_BASE3), + ENTRY(BTN_BASE4), + ENTRY(BTN_BASE5), + ENTRY(BTN_BASE6), + ENTRY(BTN_DEAD), + + ENTRY(BTN_GAMEPAD), + ENTRY(BTN_A), + ENTRY(BTN_B), + ENTRY(BTN_C), + ENTRY(BTN_X), + ENTRY(BTN_Y), + ENTRY(BTN_Z), + ENTRY(BTN_TL), + ENTRY(BTN_TR), + ENTRY(BTN_TL2), + ENTRY(BTN_TR2), + ENTRY(BTN_SELECT), + ENTRY(BTN_START), + ENTRY(BTN_MODE), + ENTRY(BTN_THUMBL), + ENTRY(BTN_THUMBR), + + ENTRY(BTN_DIGI), + ENTRY(BTN_TOOL_PEN), + ENTRY(BTN_TOOL_RUBBER), + ENTRY(BTN_TOOL_BRUSH), + ENTRY(BTN_TOOL_PENCIL), + ENTRY(BTN_TOOL_AIRBRUSH), + ENTRY(BTN_TOOL_FINGER), + ENTRY(BTN_TOOL_MOUSE), + ENTRY(BTN_TOOL_LENS), + ENTRY(BTN_TOUCH), + ENTRY(BTN_STYLUS), + ENTRY(BTN_STYLUS2), + ENTRY(BTN_TOOL_DOUBLETAP), + ENTRY(BTN_TOOL_TRIPLETAP), + + ENTRY(BTN_WHEEL), + ENTRY(BTN_GEAR_DOWN), + ENTRY(BTN_GEAR_UP), + + ENTRY(KEY_OK), + ENTRY(KEY_SELECT), + ENTRY(KEY_GOTO), + ENTRY(KEY_CLEAR), + ENTRY(KEY_POWER2), + ENTRY(KEY_OPTION), + ENTRY(KEY_INFO), + ENTRY(KEY_TIME), + ENTRY(KEY_VENDOR), + ENTRY(KEY_ARCHIVE), + ENTRY(KEY_PROGRAM), + ENTRY(KEY_CHANNEL), + ENTRY(KEY_FAVORITES), + ENTRY(KEY_EPG), + ENTRY(KEY_PVR), + ENTRY(KEY_MHP), + ENTRY(KEY_LANGUAGE), + ENTRY(KEY_TITLE), + ENTRY(KEY_SUBTITLE), + ENTRY(KEY_ANGLE), + ENTRY(KEY_ZOOM), + ENTRY(KEY_MODE), + ENTRY(KEY_KEYBOARD), + ENTRY(KEY_SCREEN), + ENTRY(KEY_PC), + ENTRY(KEY_TV), + ENTRY(KEY_TV2), + ENTRY(KEY_VCR), + ENTRY(KEY_VCR2), + ENTRY(KEY_SAT), + ENTRY(KEY_SAT2), + ENTRY(KEY_CD), + ENTRY(KEY_TAPE), + ENTRY(KEY_RADIO), + ENTRY(KEY_TUNER), + ENTRY(KEY_PLAYER), + ENTRY(KEY_TEXT), + ENTRY(KEY_DVD), + ENTRY(KEY_AUX), + ENTRY(KEY_MP3), + ENTRY(KEY_AUDIO), + ENTRY(KEY_VIDEO), + ENTRY(KEY_DIRECTORY), + ENTRY(KEY_LIST), + ENTRY(KEY_MEMO), + ENTRY(KEY_CALENDAR), + ENTRY(KEY_RED), + ENTRY(KEY_GREEN), + ENTRY(KEY_YELLOW), + ENTRY(KEY_BLUE), + ENTRY(KEY_CHANNELUP), + ENTRY(KEY_CHANNELDOWN), + ENTRY(KEY_FIRST), + ENTRY(KEY_LAST), + ENTRY(KEY_AB), + ENTRY(KEY_NEXT), + ENTRY(KEY_RESTART), + ENTRY(KEY_SLOW), + ENTRY(KEY_SHUFFLE), + ENTRY(KEY_BREAK), + ENTRY(KEY_PREVIOUS), + ENTRY(KEY_DIGITS), + ENTRY(KEY_TEEN), + ENTRY(KEY_TWEN), + ENTRY(KEY_VIDEOPHONE), + ENTRY(KEY_GAMES), + ENTRY(KEY_ZOOMIN), + ENTRY(KEY_ZOOMOUT), + ENTRY(KEY_ZOOMRESET), + ENTRY(KEY_WORDPROCESSOR), + ENTRY(KEY_EDITOR), + ENTRY(KEY_SPREADSHEET), + ENTRY(KEY_GRAPHICSEDITOR), + ENTRY(KEY_PRESENTATION), + ENTRY(KEY_DATABASE), + ENTRY(KEY_NEWS), + ENTRY(KEY_VOICEMAIL), + ENTRY(KEY_ADDRESSBOOK), + ENTRY(KEY_MESSENGER), + ENTRY(KEY_DISPLAYTOGGLE), + ENTRY(KEY_SPELLCHECK), + ENTRY(KEY_LOGOFF), + + ENTRY(KEY_DOLLAR), + ENTRY(KEY_EURO), + + ENTRY(KEY_FRAMEBACK), + ENTRY(KEY_FRAMEFORWARD), + ENTRY(KEY_CONTEXT_MENU), + ENTRY(KEY_MEDIA_REPEAT), + + ENTRY(KEY_DEL_EOL), + ENTRY(KEY_DEL_EOS), + ENTRY(KEY_INS_LINE), + ENTRY(KEY_DEL_LINE), + + ENTRY(KEY_FN), + ENTRY(KEY_FN_ESC), + ENTRY(KEY_FN_F1), + ENTRY(KEY_FN_F2), + ENTRY(KEY_FN_F3), + ENTRY(KEY_FN_F4), + ENTRY(KEY_FN_F5), + ENTRY(KEY_FN_F6), + ENTRY(KEY_FN_F7), + ENTRY(KEY_FN_F8), + ENTRY(KEY_FN_F9), + ENTRY(KEY_FN_F10), + ENTRY(KEY_FN_F11), + ENTRY(KEY_FN_F12), + ENTRY(KEY_FN_1), + ENTRY(KEY_FN_2), + ENTRY(KEY_FN_D), + ENTRY(KEY_FN_E), + ENTRY(KEY_FN_F), + ENTRY(KEY_FN_S), + ENTRY(KEY_FN_B), + + ENTRY(KEY_BRL_DOT1), + ENTRY(KEY_BRL_DOT2), + ENTRY(KEY_BRL_DOT3), + ENTRY(KEY_BRL_DOT4), + ENTRY(KEY_BRL_DOT5), + ENTRY(KEY_BRL_DOT6), + ENTRY(KEY_BRL_DOT7), + ENTRY(KEY_BRL_DOT8), + ENTRY(KEY_BRL_DOT9), + ENTRY(KEY_BRL_DOT10), + + ENTRY(KEY_MAX) +}; + #define PS3_FLAGS_MASK 0xFFFFFF00 +struct ps3remote_data { + unsigned int lastkey; + unsigned int lastval; + unsigned int lastmask; +}; + enum ps3remote_special_keys { PS3R_BIT_PS = 0, PS3R_BIT_ENTER = 3, @@ -92,6 +563,8 @@ [PS3R_BIT_SELECT] = 0x50, }; +static const char *ps3remote_mapname = "PS3 Remote Map"; + static unsigned int ps3remote_keymap[] = { [0x16] = KEY_EJECTCD, [0x64] = KEY_AUDIO, @@ -147,10 +620,38 @@ [0xff] = KEY_MAX, }; -static int ps3remote_decode(char *buff, int size, unsigned int *value) -{ - static unsigned int lastkey = 0; - static unsigned int lastmask = 0; +static int ps3remote_uinput = -1; + +static gboolean uinput_sendkey(int uinput, unsigned int key, + unsigned int value) +{ + struct uinput_event event; + + memset(&event, 0, sizeof(event)); + gettimeofday(&event.time, NULL); + event.type = EV_KEY; + event.code = key; + event.value = value; + if (write(uinput, &event, sizeof(event)) != sizeof(event)) { + error("Error writing to uinput device"); + return FALSE; + } + + memset(&event, 0, sizeof(event)); + gettimeofday(&event.time, NULL); + event.type = EV_SYN; + event.code = SYN_REPORT; + if (write(uinput, &event, sizeof(event)) != sizeof(event)) { + error("Error writing to uinput device"); + return FALSE; + } + + return TRUE; +} + +static int ps3remote_decode(char *buff, int size, unsigned int *value, + struct ps3remote_data *ps3data) +{ unsigned int i, mask; int retval; guint8 key; @@ -165,17 +666,18 @@ /* first, check flags */ for (i = 0; i < 24; i++) { - if ((lastmask & (1 << i)) == (mask & (1 << i))) + if ((ps3data->lastmask & (1 << i)) == (mask & (1 << i))) continue; if (ps3remote_bits[i] == 0) goto error; retval = ps3remote_keymap[ps3remote_bits[i]]; - if (mask & (1 << i)) + if (mask & (1 << i)) { /* key pressed */ *value = 1; - else + } else { /* key released */ *value = 0; + } goto out; } @@ -183,20 +685,21 @@ *value = buff[11]; if (buff[11] == 1) { retval = ps3remote_keymap[key]; - } else - retval = lastkey; + } else { + retval = ps3data->lastkey; + } if (retval == KEY_RESERVED) goto error; if (retval == KEY_MAX) return retval; - lastkey = retval; + ps3data->lastkey = retval; out: fflush(stdout); - lastmask = mask; + ps3data->lastmask = mask; return retval; @@ -204,8 +707,8 @@ error("ps3remote: unrecognized sequence [%#x][%#x][%#x][%#x] [%#x]," "last: [%#x][%#x][%#x][%#x]", buff[2], buff[3], buff[4], buff[5], buff[11], - lastmask >> 16, lastmask >> 8 & 0xff, - lastmask & 0xff, lastkey); + ps3data->lastmask >> 16, ps3data->lastmask >> 8 & 0xff, + ps3data->lastmask & 0xff, ps3data->lastkey); return -1; } @@ -213,19 +716,28 @@ gpointer data) { struct fake_input *fake = data; - struct uinput_event event; + struct ps3remote_data *ps3data = fake->priv; unsigned int key, value = 0; gsize size; char buff[50]; - - if (cond & G_IO_NVAL) - return FALSE; + + if (cond & G_IO_NVAL) { + goto failed; + } if (cond & (G_IO_HUP | G_IO_ERR)) { - error("Hangup or error on rfcomm server socket"); + if (ps3data) + error("Hangup or error on ps3remote server socket"); goto failed; } + /* reset the timeout if needed */ + if (fake->timer > 0) { + g_source_remove(fake->timer); + fake->timer = g_timeout_add_seconds(fake->idle_timeout, + fake_hid_common_timeout, fake); + } + memset(buff, 0, sizeof(buff)); if (g_io_channel_read(chan, buff, sizeof(buff), &size) != @@ -234,55 +746,107 @@ goto failed; } - key = ps3remote_decode(buff, size, &value); - if (key == KEY_RESERVED) { - error("Got invalid key from decode"); - goto failed; - } else if (key == KEY_MAX) + key = ps3remote_decode(buff, size, &value, ps3data); + debug("Got key: %d [%d]", key, value); + if (key == KEY_RESERVED || key == KEY_MAX) return TRUE; - memset(&event, 0, sizeof(event)); - gettimeofday(&event.time, NULL); - event.type = EV_KEY; - event.code = key; - event.value = value; - if (write(fake->uinput, &event, sizeof(event)) != sizeof(event)) { - error("Error writing to uinput device"); - goto failed; - } - - memset(&event, 0, sizeof(event)); - gettimeofday(&event.time, NULL); - event.type = EV_SYN; - event.code = SYN_REPORT; - if (write(fake->uinput, &event, sizeof(event)) != sizeof(event)) { - error("Error writing to uinput device"); - goto failed; - } + if (key == KEY_HOMEPAGE) { + /* delay transmit of this key til release, assuming possible turn-off */ + if (ps3data->lastkey == KEY_HOMEPAGE && + value == 0 && ps3data->lastval == 1) { + debug("homepage key released"); + uinput_sendkey(fake->uinput, key, 1); + uinput_sendkey(fake->uinput, key, 0); + } + } + else if (!uinput_sendkey(fake->uinput, key, value)) { + goto failed; + } + + ps3data->lastkey = key; + ps3data->lastval = value; return TRUE; failed: - ioctl(fake->uinput, UI_DEV_DESTROY); - close(fake->uinput); - fake->uinput = -1; + g_source_remove(fake->timer); + fake->timer = 0; + g_source_remove(fake->sid_in); g_io_channel_unref(fake->io); return FALSE; } +static gboolean ps3remote_connect(struct fake_input *fake, GError **err) +{ + struct ps3remote_data *ps3data; + char devaddr[18]; + GKeyFile *config; + GError *cfgerr; + + ps3data = g_new0(struct ps3remote_data, 1); + fake->priv = ps3data; + + ba2str(&fake->ba_dst, devaddr); + debug("Processing PS3 device: %s", devaddr); + + /* Load config file */ + config = g_key_file_new(); + cfgerr = NULL; + if (!g_key_file_load_from_file(config, CONFIGDIR "/input.conf", 0, &cfgerr)) { + g_error_free(cfgerr); + } else { + if (g_key_file_has_group(config, devaddr)) { + int timeout; + + cfgerr = NULL; + timeout = g_key_file_get_integer(config, devaddr, + "IdleTimeout", &cfgerr); + if (cfgerr) { + g_error_free(cfgerr); + } else { + fake->idle_timeout = timeout; + debug("[%s] Using timeout of %d seconds", + devaddr, fake->idle_timeout); + } + } + + g_key_file_free(config); + } + + return fake_hid_common_connect(fake, err); +} + +static int ps3remote_disconnect(struct fake_input *fake) +{ + debug("Disconnecting PS3 remote"); + + g_free(fake->priv); + fake->priv = NULL; + + return fake_hid_common_disconnect(fake); +} + static int ps3remote_setup_uinput(struct fake_input *fake, struct fake_hid *fake_hid) { struct uinput_dev dev; int i; - fake->uinput = open("/dev/input/uinput", O_RDWR); - if (fake->uinput < 0) { - fake->uinput = open("/dev/uinput", O_RDWR); - if (fake->uinput < 0) { - fake->uinput = open("/dev/misc/uinput", O_RDWR); - if (fake->uinput < 0) { + if (ps3remote_uinput >= 0) { + fake->uinput = ps3remote_uinput; + return 0; + } + + debug("Setting up PS3 Remote uinput"); + + ps3remote_uinput = open("/dev/input/uinput", O_RDWR); + if (ps3remote_uinput < 0) { + ps3remote_uinput = open("/dev/uinput", O_RDWR); + if (ps3remote_uinput < 0) { + ps3remote_uinput = open("/dev/misc/uinput", O_RDWR); + if (ps3remote_uinput < 0) { error("Error opening uinput device file"); return 1; } @@ -295,13 +859,13 @@ dev.id.vendor = fake_hid->vendor; dev.id.product = fake_hid->product; - if (write(fake->uinput, &dev, sizeof(dev)) != sizeof(dev)) { + if (write(ps3remote_uinput, &dev, sizeof(dev)) != sizeof(dev)) { error("Error creating uinput device"); goto err; } /* enabling key events */ - if (ioctl(fake->uinput, UI_SET_EVBIT, EV_KEY) < 0) { + if (ioctl(ps3remote_uinput, UI_SET_EVBIT, EV_KEY) < 0) { error("Error enabling uinput device key events"); goto err; } @@ -309,43 +873,79 @@ /* enabling keys */ for (i = 0; i < 256; i++) if (ps3remote_keymap[i] != KEY_RESERVED) - if (ioctl(fake->uinput, UI_SET_KEYBIT, - ps3remote_keymap[i]) < 0) { - error("Error enabling uinput key %i", - ps3remote_keymap[i]); + if (ioctl(ps3remote_uinput, UI_SET_KEYBIT, ps3remote_keymap[i]) < 0) { + error("Error enabling uinput key %i", ps3remote_keymap[i]); goto err; } /* creating the device */ - if (ioctl(fake->uinput, UI_DEV_CREATE) < 0) { + if (ioctl(ps3remote_uinput, UI_DEV_CREATE) < 0) { error("Error creating uinput device"); goto err; } + fake->uinput = ps3remote_uinput; + return 0; err: - close(fake->uinput); + close(ps3remote_uinput); + ps3remote_uinput = -1; + return 1; } static gboolean fake_hid_common_connect(struct fake_input *fake, GError **err) { + fake->timer = 0; + if (fake->idle_timeout > 0) { + debug("Creating timeout"); + fake->timer = g_timeout_add_seconds(fake->idle_timeout, + fake_hid_common_timeout, fake); + } + return TRUE; } static int fake_hid_common_disconnect(struct fake_input *fake) { + if (fake->timer > 0) { + debug("Destroying timer"); + g_source_remove(fake->timer); + fake->timer = 0; + } + return 0; } +static gboolean fake_hid_common_timeout(gpointer data) +{ + struct fake_input *fake = data; + + debug("Disconnecting device because of timeout"); + input_device_request_disconnect(&fake->ba_src, &fake->ba_dst); + + fake->timer = 0; + return FALSE; +} + static struct fake_hid fake_hid_table[] = { /* Sony PS3 remote device */ { .vendor = 0x054c, .product = 0x0306, - .connect = fake_hid_common_connect, - .disconnect = fake_hid_common_disconnect, + .connect = ps3remote_connect, + .disconnect = ps3remote_disconnect, + .event = ps3remote_event, + .setup_uinput = ps3remote_setup_uinput, + }, + + /* Blu-Link PS3 remote device */ + { + .vendor = 0x0609, + .product = 0x0306, + .connect = ps3remote_connect, + .disconnect = ps3remote_disconnect, .event = ps3remote_event, .setup_uinput = ps3remote_setup_uinput, }, @@ -373,6 +973,11 @@ int fake_hid_connadd(struct fake_input *fake, GIOChannel *intr_io, struct fake_hid *fake_hid) { + if (!fake_hid->connect(fake, NULL)) { + error("Error connecting device"); + return ENOMEM; + } + if (fake_hid->setup_uinput(fake, fake_hid)) { error("Error setting up uinput"); return ENOMEM; @@ -380,8 +985,74 @@ fake->io = g_io_channel_ref(intr_io); g_io_channel_set_close_on_unref(fake->io, TRUE); - g_io_add_watch(fake->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + fake->sid_in = g_io_add_watch(fake->io, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) fake_hid->event, fake); return 0; } + +void fake_hid_init(GKeyFile *config) +{ + /* Load PS3 keymap */ + if (config && g_key_file_has_group(config, ps3remote_mapname)) { + GHashTable *maphash; + int i; + GError *err; + + info("Loading PS3 Remote Map..."); + + maphash = g_hash_table_new(g_str_hash, g_str_equal); + for (i = 0; i <= KEY_MAX; i++) + if (uinput_map[i]) + g_hash_table_insert(maphash, uinput_map[i], GINT_TO_POINTER(i)); + + err = NULL; + if (!g_key_file_get_boolean(config, ps3remote_mapname, + "OverlayBuiltin", &err) && !err) { + DBG("Clearing Default PS3 Remote Map"); + memset(ps3remote_keymap, 0, sizeof(ps3remote_keymap)); + } + + for (i = 0; i < 0x100; i++) { + char keyindex[5]; + char *keyvalue; + + snprintf(keyindex, 5, "0x%2.2x", i); + + err = NULL; + keyvalue = g_key_file_get_string(config, ps3remote_mapname, + keyindex, &err); + if (err) + g_error_free(err); + if (keyvalue) { + /* blindly ignore anything after any whitespace/comments */ + char *whitespace = g_strstr_len(keyvalue, -1, "#"); + if (whitespace) + *whitespace = '\0'; + g_strstrip(keyvalue); + + ps3remote_keymap[i] = + GPOINTER_TO_INT(g_hash_table_lookup(maphash, keyvalue)); + if (ps3remote_keymap[i] == 0) + ps3remote_keymap[i] = strtoul(keyvalue, NULL, 10); + if (ps3remote_keymap[i] == 0) + info("input key '%s' not valid", keyvalue); + + g_free(keyvalue); + } + } + + g_hash_table_destroy(maphash); + } +} + +void fake_hid_exit(void) +{ + /* Clean up PS3 uinput device */ + if (ps3remote_uinput >= 0) { + ioctl(ps3remote_uinput, UI_DEV_DESTROY); + close(ps3remote_uinput); + } +} + === modified file 'input/fakehid.h' --- input/fakehid.h 2009-11-14 19:57:42 +0000 +++ input/fakehid.h 2009-11-25 06:58:12 +0000 @@ -37,3 +37,8 @@ int fake_hid_connadd(struct fake_input *fake, GIOChannel *intr_io, struct fake_hid *fake_hid); + +void fake_hid_init(GKeyFile *config); + +void fake_hid_exit(void); + === modified file 'input/input.conf' --- input/input.conf 2009-11-14 19:57:42 +0000 +++ input/input.conf 2009-12-02 20:05:07 +0000 @@ -4,6 +4,79 @@ # particular interface [General] -# Set idle timeout (in minutes) before the connection will +# Set idle timeout (in seconds) before the connection will # be disconnect (defaults to 0 for no timeout) +#IdleTimeout=600 + + + +# This section contains options that are specific to a device +#[00:11:22:33:44:55] +# +# Set a custom idle timeout (in seconds) for this specific device #IdleTimeout=30 + + + +# This section is the PS3 Remote keymap. It is loaded when bluez starts. +# Use 'uinput.h' from bluez sources or '/usr/include/linux/input.h' for +# a list of possible KEY_* values. +# +[PS3 Remote Map] +# When the 'OverlayBuiltin' option is TRUE (the default), the keymap uses +# the built-in keymap as a starting point. When FALSE, an empty keymap is +# the starting point. +#OverlayBuiltin = TRUE +0x16 = KEY_EJECTCD # EJECT +0x64 = KEY_AUDIO # AUDIO (XBMC recommendation: KEY_A) +0x65 = KEY_ANGLE # ANGLE (XBMC recommendation: KEY_Z) +0x63 = KEY_SUBTITLE # SUBTITLE (XBMC recommendation: KEY_T) +0x0f = KEY_CLEAR # CLEAR (XBMC recommendation: KEY_DELETE) +0x28 = KEY_TIME # TIMER (XBMC recommendation: KEY_END) +0x00 = KEY_1 # NUM-1 +0x01 = KEY_2 # NUM-2 +0x02 = KEY_3 # NUM-3 +0x03 = KEY_4 # NUM-4 +0x04 = KEY_5 # NUM-5 +0x05 = KEY_6 # NUM-6 +0x06 = KEY_7 # NUM-7 +0x07 = KEY_8 # NUM-8 +0x08 = KEY_9 # NUM-9 +0x09 = KEY_0 # NUM-0 +0x81 = KEY_RED # RED (XBMC recommendation: KEY_F7) +0x82 = KEY_GREEN # GREEN (XBMC recommendation: KEY_F8) +0x80 = KEY_BLUE # BLUE (XBMC recommendation: KEY_F9) +0x83 = KEY_YELLOW # YELLOW (XBMC recommendation: KEY_F10) +0x70 = KEY_INFO # DISPLAY (XBMC recommendation: KEY_D) +0x1a = KEY_MENU # TOP MENU +0x40 = KEY_CONTEXT_MENU # POP UP/MENU (XBMC recommendation: KEY_F11) +0x0e = KEY_ESC # RETURN +0x5c = KEY_OPTION # TRIANGLE/OPTIONS (XBMC recommendation: KEY_C) +0x5d = KEY_BACK # CIRCLE/BACK +0x5f = KEY_SCREEN # SQUARE/VIEW (XBMC recommendation: KEY_V) +0x5e = BTN_0 # CROSS (XBMC recommendation: KEY_X) +0x54 = KEY_UP # UP +0x56 = KEY_DOWN # DOWN +0x57 = KEY_LEFT # LEFT +0x55 = KEY_RIGHT # RIGHT +0x0b = KEY_ENTER # ENTER +0x5a = BTN_TL # L1 (XBMC recommendation: KEY_F1) +0x58 = BTN_TL2 # L2 (XBMC recommendation: KEY_F2) +0x51 = BTN_THUMBL # L3 (XBMC recommendation: KEY_F3) +0x5b = BTN_TR # R1 (XBMC recommendation: KEY_F4) +0x59 = BTN_TR2 # R2 (XBMC recommendation: KEY_F5) +0x52 = BTN_THUMBR # R3 (XBMC recommendation: KEY_F6) +0x43 = KEY_HOMEPAGE # PS button +0x50 = KEY_SELECT # SELECT (XBMC recommendation: KEY_INSERT) +0x53 = BTN_START # START (XBMC recommendation: KEY_HOME) +0x33 = KEY_REWIND # SCAN BACK (XBMC recommendation: KEY_R) +0x32 = KEY_PLAY # PLAY +0x34 = KEY_FORWARD # SCAN FORWARD (XBMC recommendation: KEY_F) +0x30 = KEY_PREVIOUS # PREVIOUS (XBMC recommendationL KEY_PAGEUP) +0x38 = KEY_STOP # STOP +0x31 = KEY_NEXT # NEXT (XBMC recommendation: KEY_PAGEDOWN) +0x60 = KEY_FRAMEBACK # SLOW/STEP BACK (XBMC recommendation: KEY_COMMA) +0x39 = KEY_PAUSE # PAUSE +0x61 = KEY_FRAMEFORWARD # SLOW/STEP FORWARD (XBMC recommendation: KEY_DOT) +0xff = KEY_MAX + === modified file 'input/manager.c' --- input/manager.c 2009-11-14 19:57:42 +0000 +++ input/manager.c 2009-12-02 20:04:07 +0000 @@ -41,6 +41,7 @@ #include "device.h" #include "server.h" #include "manager.h" +#include "fakehid.h" static int idle_timeout = 0; @@ -72,7 +73,7 @@ device_get_address(device, &dst); return input_device_register(connection, device, path, &src, &dst, - HID_UUID, rec->handle, idle_timeout * 60); + HID_UUID, rec->handle, idle_timeout); } static void hid_device_remove(struct btd_device *device) @@ -184,6 +185,8 @@ } } + fake_hid_init(config); + connection = dbus_connection_ref(conn); btd_register_adapter_driver(&input_server_driver); @@ -203,5 +206,7 @@ dbus_connection_unref(connection); + fake_hid_exit(); + connection = NULL; } === modified file 'src/device.c' --- src/device.c 2009-11-14 19:57:42 +0000 +++ src/device.c 2009-11-14 19:57:47 +0000 @@ -835,6 +835,7 @@ const gchar *adapter_path = adapter_get_path(adapter); bdaddr_t src; char srcaddr[18]; + uint16_t vendor, product, version; device = g_try_malloc0(sizeof(struct btd_device)); if (device == NULL) @@ -860,6 +861,10 @@ ba2str(&src, srcaddr); read_device_name(srcaddr, address, device->name); + read_device_id(srcaddr, address, NULL, &vendor, &product, &version); + debug("Device %s has vendor=0x%04x product=0x%04x version=0x%04x", + device->path, vendor, product, version); + device->auth = 0xff; if (read_link_key(&src, &device->bdaddr, NULL, NULL) == 0) @@ -1077,6 +1082,8 @@ if (!probe_uuids) continue; + debug("Driver match %s for %s", driver->name, device->path); + driver_data = g_new0(struct btd_driver_data, 1); err = driver->probe(device, probe_uuids);