summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2008-03-20 14:45:32 +0000
committerMarcel Holtmann <marcel@holtmann.org>2008-03-20 14:45:32 +0000
commit835dd438bc43c1483480aaee08b7a5bfa49a21b6 (patch)
treee9460174b51ec8a6fe240582cc28ba380d11396c
parent9e344aba0489ceddb0fc9a12e1f012fd0f6250f9 (diff)
Add support for PS3 remote devices
-rw-r--r--input/fakehid.c338
1 files changed, 335 insertions, 3 deletions
diff --git a/input/fakehid.c b/input/fakehid.c
index 84c8e9ee..44c3ff6d 100644
--- a/input/fakehid.c
+++ b/input/fakehid.c
@@ -26,13 +26,15 @@
#endif
#include <stdio.h>
-#include <stdlib.h>
#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
#include <sys/types.h>
#include <bluetooth/bluetooth.h>
-#include <bluetooth/hidp.h>
#include <bluetooth/l2cap.h>
+#include <bluetooth/hidp.h>
#include <glib.h>
#include <dbus/dbus.h>
@@ -40,8 +42,338 @@
#include "logging.h"
#include "device.h"
#include "fakehid.h"
+#include "uinput.h"
+
+#ifndef KEY_REMOTE_1
+#define KEY_REMOTE_1 0x1b6
+#endif
+#ifndef KEY_REMOTE_2
+#define KEY_REMOTE_2 0x1b7
+#endif
+#ifndef KEY_REMOTE_3
+#define KEY_REMOTE_3 0x1b8
+#endif
+#ifndef KEY_REMOTE_4
+#define KEY_REMOTE_4 0x1b9
+#endif
+#ifndef KEY_REMOTE_5
+#define KEY_REMOTE_5 0x1ba
+#endif
+#ifndef KEY_REMOTE_6
+#define KEY_REMOTE_6 0x1bb
+#endif
+#ifndef KEY_REMOTE_7
+#define KEY_REMOTE_7 0x1bc
+#endif
+#ifndef KEY_REMOTE_8
+#define KEY_REMOTE_8 0x1bd
+#endif
+#ifndef KEY_REMOTE_9
+#define KEY_REMOTE_9 0x1be
+#endif
+#ifndef KEY_REMOTE_0
+#define KEY_REMOTE_0 0x1bf
+#endif
+
+#define PS3_FLAGS_MASK 0xFFFFFF00
+
+enum ps3remote_special_keys {
+ PS3R_BIT_PS = 0,
+ PS3R_BIT_ENTER = 3,
+ PS3R_BIT_L2 = 8,
+ PS3R_BIT_R2 = 9,
+ PS3R_BIT_L1 = 10,
+ PS3R_BIT_R1 = 11,
+ PS3R_BIT_TRIANGLE = 12,
+ PS3R_BIT_CIRCLE = 13,
+ PS3R_BIT_CROSS = 14,
+ PS3R_BIT_SQUARE = 15,
+ PS3R_BIT_SELECT = 16,
+ PS3R_BIT_L3 = 17,
+ PS3R_BIT_R3 = 18,
+ PS3R_BIT_START = 19,
+ PS3R_BIT_UP = 20,
+ PS3R_BIT_RIGHT = 21,
+ PS3R_BIT_DOWN = 22,
+ PS3R_BIT_LEFT = 23,
+};
+
+static unsigned int ps3remote_bits[] = {
+ [PS3R_BIT_ENTER] = 0x0b,
+ [PS3R_BIT_PS] = 0x43,
+ [PS3R_BIT_SQUARE] = 0x5f,
+ [PS3R_BIT_CROSS] = 0x5e,
+ [PS3R_BIT_CIRCLE] = 0x5d,
+ [PS3R_BIT_TRIANGLE] = 0x5c,
+ [PS3R_BIT_R1] = 0x5b,
+ [PS3R_BIT_L1] = 0x5a,
+ [PS3R_BIT_R2] = 0x59,
+ [PS3R_BIT_L2] = 0x58,
+ [PS3R_BIT_LEFT] = 0x57,
+ [PS3R_BIT_DOWN] = 0x56,
+ [PS3R_BIT_RIGHT] = 0x55,
+ [PS3R_BIT_UP] = 0x54,
+ [PS3R_BIT_START] = 0x53,
+ [PS3R_BIT_R3] = 0x52,
+ [PS3R_BIT_L3] = 0x51,
+ [PS3R_BIT_SELECT] = 0x50,
+};
+
+static unsigned int ps3remote_keymap[] = {
+ [0x16] = KEY_EJECTCD,
+ [0x64] = KEY_AUDIO,
+ [0x65] = KEY_ANGLE,
+ [0x63] = KEY_SUBTITLE,
+ [0x0f] = KEY_CLEAR,
+ [0x28] = KEY_TIME,
+ [0x00] = KEY_REMOTE_1,
+ [0x01] = KEY_REMOTE_2,
+ [0x02] = KEY_REMOTE_3,
+ [0x03] = KEY_REMOTE_4,
+ [0x04] = KEY_REMOTE_5,
+ [0x05] = KEY_REMOTE_6,
+ [0x06] = KEY_REMOTE_7,
+ [0x07] = KEY_REMOTE_8,
+ [0x08] = KEY_REMOTE_9,
+ [0x09] = KEY_REMOTE_0,
+ [0x81] = KEY_RED,
+ [0x82] = KEY_GREEN,
+ [0x80] = KEY_BLUE,
+ [0x83] = KEY_YELLOW,
+ [0x70] = KEY_INFO, /* display */
+ [0x1a] = KEY_MENU, /* top menu */
+ [0x40] = KEY_CONTEXT_MENU, /* pop up/menu */
+ [0x0e] = KEY_ESC, /* return */
+ [0x5c] = KEY_OPTION, /* options/triangle */
+ [0x5d] = KEY_BACK, /* back/circle */
+ [0x5f] = KEY_SCREEN, /* view/square */
+ [0x5e] = BTN_0, /* cross */
+ [0x54] = KEY_UP,
+ [0x56] = KEY_DOWN,
+ [0x57] = KEY_LEFT,
+ [0x55] = KEY_RIGHT,
+ [0x0b] = KEY_ENTER,
+ [0x5a] = BTN_TL, /* L1 */
+ [0x58] = BTN_TL2, /* L2 */
+ [0x51] = BTN_THUMBL, /* L3 */
+ [0x5b] = BTN_TR, /* R1 */
+ [0x59] = BTN_TR2, /* R2 */
+ [0x52] = BTN_THUMBR, /* R3 */
+ [0x43] = KEY_HOMEPAGE, /* PS button */
+ [0x50] = KEY_SELECT,
+ [0x53] = BTN_START,
+ [0x33] = KEY_REWIND, /* scan back */
+ [0x32] = KEY_PLAY,
+ [0x34] = KEY_FORWARD, /* scan forward */
+ [0x30] = KEY_PREVIOUS,
+ [0x38] = KEY_STOP,
+ [0x31] = KEY_NEXT,
+ [0x60] = KEY_FRAMEBACK, /* slow/step back */
+ [0x39] = KEY_PAUSE,
+ [0x61] = KEY_FRAMEFORWARD, /* slow/step forward */
+ [0xff] = KEY_MAX,
+};
+
+static int ps3remote_decode(char *buff, int size, unsigned int *value)
+{
+ static unsigned int lastkey = 0;
+ static unsigned int lastmask = 0;
+ int retval, mask, key, i;
+
+ if (size < 12) {
+ error("Got a shorter packet! (size %i)\n", size);
+ return KEY_RESERVED;
+ }
+
+ mask = (buff[2] << 16) + (buff[3] << 8) + buff[4];
+ key = buff[5];
+
+ /* first, check flags */
+ for (i = 0; i < 24; i++) {
+ if ((lastmask & (1 << i)) == (mask & (1 << i)))
+ continue;
+ if (ps3remote_bits[i] == 0)
+ goto error;
+ retval = ps3remote_keymap[ps3remote_bits[i]];
+ if (mask & (1 << i))
+ /* key pressed */
+ *value = 1;
+ else
+ /* key released */
+ *value = 0;
+
+ goto out;
+ }
+
+ *value = buff[11];
+ if (buff[11] == 1) {
+ retval = ps3remote_keymap[key];
+ } else
+ retval = lastkey;
+
+ if (retval == KEY_RESERVED)
+ goto error;
+ if (retval == KEY_MAX)
+ return retval;
+
+ lastkey = retval;
+
+out:
+ fflush(stdout);
+
+ lastmask = mask;
+
+ return retval;
+
+error:
+ 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);
+ return -1;
+}
+
+static gboolean ps3remote_event(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct fake_input *fake = data;
+ struct uinput_event event;
+ unsigned int key, value = 0;
+ gsize size;
+ char buff[50];
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_HUP | G_IO_ERR)) {
+ error("Hangup or error on rfcomm server socket");
+ goto failed;
+ }
+
+ memset(buff, 0, sizeof(buff));
+
+ if (g_io_channel_read(chan, buff, sizeof(buff), &size) !=
+ G_IO_ERROR_NONE) {
+ error("IO Channel read error");
+ 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)
+ 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;
+ }
+
+ return TRUE;
+
+failed:
+ ioctl(fake->uinput, UI_DEV_DESTROY);
+ close(fake->uinput);
+ fake->uinput = -1;
+ g_io_channel_unref(fake->io);
+
+ return FALSE;
+}
+
+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) {
+ error("Error opening uinput device file");
+ return 1;
+ }
+ }
+
+ memset(&dev, 0, sizeof(dev));
+ snprintf(dev.name, sizeof(dev.name), "%s", "PS3 Remote Controller");
+ dev.id.bustype = BUS_BLUETOOTH;
+ dev.id.vendor = fake_hid->vendor;
+ dev.id.product = fake_hid->product;
+
+ if (write(fake->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) {
+ error("Error enabling uinput device key events");
+ goto err;
+ }
+
+ /* 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]);
+ goto err;
+ }
+
+ /* creating the device */
+ if (ioctl(fake->uinput, UI_DEV_CREATE) < 0) {
+ error("Error creating uinput device");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ close(fake->uinput);
+ return 1;
+}
+
+static gboolean fake_hid_common_connect(struct fake_input *fake)
+{
+ return TRUE;
+}
+
+static int fake_hid_common_disconnect(struct fake_input *fake)
+{
+ return 0;
+}
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,
+ .event = ps3remote_event,
+ .setup_uinput = ps3remote_setup_uinput,
+ },
+
{ },
};
@@ -73,7 +405,7 @@ int fake_hid_connadd(struct fake_input *fake, int intr_sk,
fake->io = g_io_channel_unix_new(intr_sk);
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,
- (GIOFunc) fake_hid->event, fake);
+ (GIOFunc) fake_hid->event, fake);
return 0;
}