@@ -0,0 +1,199 @@
+/*
+ * HID driver for Sony PS3 BD Remote
+ *
+ * Copyright (c) 2012 David Dillow <dave-i1Mk8JYDVaaSihdK6806/g@public.gmane.org>
+ * Based on a blend of the bluez fakehid user-space code by Marcel Holtmann
+ * and other kernel HID drivers.
+ */
+
+/*
+ * 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.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/*
+ * The first byte of the raw event report seems to contain a fixed number,
+ * possibly a controller number. The next three bytes contain a bitmask,
+ * in big-endian order, of a portion of the buttons available.
+ *
+ * Button Bit Scan Mapped Key
+ * PS 0 0x43 KEY_HOMEPAGE
+ * Unknown 1-2
+ * Enter 3 0x0b KEY_ENTER
+ * Unknown 4-7
+ * L2 8 0x58 BTN_TL2
+ * R2 9 0x59 BTN_TR2
+ * L1 10 0x5a BTN_TL
+ * R1 11 0x5b BTN_TR
+ * Triangle 12 0x5c KEY_OPTION
+ * Circle 13 0x5d KEY_BACK
+ * Cross 14 0x5e BTN_0
+ * Square 15 0x5f KEY_SCREEN
+ * Select 16 0x50 KEY_SELECT
+ * L3 17 0x51 BTN_THUMBL
+ * R3 18 0x52 BTN_THUMBR
+ * Start 19 0x53 BTN_START
+ * Up 20 0x54 KEY_UP
+ * Right 21 0x55 KEY_RIGHT
+ * Down 22 0x56 KEY_DOWN
+ * Left 23 0x57 KEY_LEFT
+ *
+ * The keymap is generally ordered by the physical location of the buttons,
+ * as this makes it easier to verify a correct mapping during testing.
+ */
+static const unsigned int ps3remote_keymap[] = {
+ [0x16] = KEY_EJECTCD,
+ [0x64] = KEY_AUDIO,
+ [0x65] = KEY_ANGLE,
+ [0x63] = KEY_SUBTITLE,
+ [0x0f] = KEY_CLEAR,
+ [0x28] = KEY_TIME,
+ [0x00] = KEY_1,
+ [0x01] = KEY_2,
+ [0x02] = KEY_3,
+ [0x03] = KEY_4,
+ [0x04] = KEY_5,
+ [0x05] = KEY_6,
+ [0x06] = KEY_7,
+ [0x07] = KEY_8,
+ [0x08] = KEY_9,
+ [0x09] = KEY_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 */
+};
+
+static __u8 ps3remote_rdesc[] = {
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */
+ 0x09, 0x05, /* USAGE (Game Pad) */
+ 0xa1, 0x01, /* COLLECTION (Application) */
+
+ /* First four bytes contain a bitmask for some of the buttons, and
+ * possibly a controller number. We don't need this information,
+ * as the keys will be reported in the next field as well.
+ */
+ 0x75, 0x20, /* REPORT SIZE (32) */
+ 0x95, 0x01, /* REPORT COUNT (1) */
+ 0x81, 0x01, /* INPUT (Constant) */
+
+ /* All key presses are reported in this field */
+ 0x05, 0x09, /* USAGE PAGE (Button) */
+ 0x19, 0x00, /* USAGE MINIMUM (0) */
+ 0x29, 0xfe, /* USAGE MAXIMUM (254) */
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */
+ 0x25, 0xfe, /* LOGICAL MAXIMUM (254) */
+ 0x75, 0x08, /* REPORT SIZE (8) */
+ 0x95, 0x06, /* REPORT COUNT (6) */
+ 0x81, 0x00, /* INPUT (Array, Absolute) */
+
+ /* Ignore press indication */
+ 0x75, 0x08, /* REPORT SIZE (8) */
+ 0x95, 0x01, /* REPORT COUNT (1) */
+ 0x81, 0x01, /* INPUT (Constant) */
+
+ /* Report the battery level */
+ 0x05, 0x06, /* USAGE PAGE (Generic Device) */
+ 0x09, 0x20, /* USAGE (Battery Strength) */
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */
+ 0x25, 0x05, /* LOGICAL MAXIMUM (5) */
+ 0x75, 0x08, /* REPORT SIZE (8) */
+ 0x95, 0x01, /* REPORT COUNT (1) */
+ 0x81, 0x02, /* INPUT (Variable, Absolute) */
+ 0xc0, /* END_COLLECTION */
+};
+
+static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ *rsize = sizeof(ps3remote_rdesc);
+ return ps3remote_rdesc;
+}
+
+static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ unsigned int key = usage->hid & HID_USAGE;
+
+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON ||
+ key >= ARRAY_SIZE(ps3remote_keymap))
+ return -1;
+
+ key = ps3remote_keymap[key];
+ if (!key)
+ return -1;
+
+ hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key);
+ return 1;
+}
+
+static const struct hid_device_id ps3remote_devices[] = {
+ /* PS3 BD Remote */
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
+ /* Logitech Harmony Adapter for PS3 */
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, ps3remote_devices);
+
+static struct hid_driver ps3remote_driver = {
+ .name = "ps3_remote",
+ .id_table = ps3remote_devices,
+ .report_fixup = ps3remote_fixup,
+ .input_mapping = ps3remote_mapping,
+};
+
+static int __init ps3remote_init(void)
+{
+ return hid_register_driver(&ps3remote_driver);
+}
+
+static void __exit ps3remote_exit(void)
+{
+ hid_unregister_driver(&ps3remote_driver);
+}
+
+module_init(ps3remote_init);
+module_exit(ps3remote_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Dillow <dave-i1Mk8JYDVaaSihdK6806/g@public.gmane.org>");