diff options
| author | Marcel Holtmann <marcel@holtmann.org> | 2008-03-20 14:45:32 +0000 | 
|---|---|---|
| committer | Marcel Holtmann <marcel@holtmann.org> | 2008-03-20 14:45:32 +0000 | 
| commit | 835dd438bc43c1483480aaee08b7a5bfa49a21b6 (patch) | |
| tree | e9460174b51ec8a6fe240582cc28ba380d11396c | |
| parent | 9e344aba0489ceddb0fc9a12e1f012fd0f6250f9 (diff) | |
Add support for PS3 remote devices
| -rw-r--r-- | input/fakehid.c | 338 | 
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;  } | 
