HID: add ouya controller driver
diff mbox series

Message ID 20190711034313.32178-1-lorusak@gmail.com
State New
Delegated to: Jiri Kosina
Headers show
Series
  • HID: add ouya controller driver
Related show

Commit Message

Lukas Rusak July 11, 2019, 3:43 a.m. UTC
This driver is a simple implementation to get the controller working and
mapped properly. The original driver was taken from from the ouya linux
tree with the original authors permission.

Signed-off-by: Lukas Rusak <lorusak@gmail.com>
---
 drivers/hid/Kconfig      |   8 +-
 drivers/hid/Makefile     |   1 +
 drivers/hid/hid-ids.h    |   3 +
 drivers/hid/hid-ouya.c   | 259 +++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-quirks.c |   3 +
 5 files changed, 273 insertions(+), 1 deletion(-)
 create mode 100644 drivers/hid/hid-ouya.c

Patch
diff mbox series

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 3872e03d9a59..f095a52d82d7 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -717,6 +717,12 @@  config HID_ORTEK
 	   - Ortek WKB-2000
 	   - Skycable wireless presenter
 
+config HID_OUYA
+	tristate "OUYA Game Controller"
+	depends on HID
+	help
+	Say Y here if you have an Ouya controller and want to enable support for it.
+
 config HID_PANTHERLORD
 	tristate "Pantherlord/GreenAsia game controller"
 	depends on HID
@@ -882,7 +888,7 @@  config HID_SONY
 	  * Logitech Harmony adapter for Sony Playstation 3 (Bluetooth)
 
 config SONY_FF
-	bool "Sony PS2/3/4 accessories force feedback support" 
+	bool "Sony PS2/3/4 accessories force feedback support"
 	depends on HID_SONY
 	select INPUT_FF_MEMLESS
 	---help---
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index cc5d827c9164..a258efa4c0d3 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -75,6 +75,7 @@  obj-$(CONFIG_HID_MULTITOUCH)	+= hid-multitouch.o
 obj-$(CONFIG_HID_NTI)			+= hid-nti.o
 obj-$(CONFIG_HID_NTRIG)		+= hid-ntrig.o
 obj-$(CONFIG_HID_ORTEK)		+= hid-ortek.o
+obj-$(CONFIG_HID_OUYA)		+= hid-ouya.o
 obj-$(CONFIG_HID_PRODIKEYS)	+= hid-prodikeys.o
 obj-$(CONFIG_HID_PANTHERLORD)	+= hid-pl.o
 obj-$(CONFIG_HID_PENMOUNT)	+= hid-penmount.o
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index b032d3899fa3..5a5f9f2ea515 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -899,6 +899,9 @@ 
 #define USB_DEVICE_ID_ORTEK_WKB2000	0x2000
 #define USB_DEVICE_ID_ORTEK_IHOME_IMAC_A210S	0x8003
 
+#define USB_VENDOR_ID_OUYA 0x2836
+#define USB_DEVICE_ID_OUYA_CONTROLLER 0x0001
+
 #define USB_VENDOR_ID_PLANTRONICS	0x047f
 
 #define USB_VENDOR_ID_PANASONIC		0x04da
diff --git a/drivers/hid/hid-ouya.c b/drivers/hid/hid-ouya.c
new file mode 100644
index 000000000000..b062e9baebe3
--- /dev/null
+++ b/drivers/hid/hid-ouya.c
@@ -0,0 +1,259 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  HID driver for OUYA Game Controller(s)
+ *
+ *  Copyright (c) 2013 OUYA
+ *  Copyright (c) 2013 Gregorios Leach <optikflux@gmail.com>
+ *  Copyright (c) 2018 Lukas Rusak <lorusak@gmail.com>
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define OUYA_TOUCHPAD_FIXUP	(1 << 0)
+
+struct ouya_sc {
+	unsigned long quirks;
+};
+
+/* Fixed report descriptor */
+static __u8 ouya_rdesc_fixed[] = {
+
+	0x05, 0x01,         /*  Usage Page (Desktop),           */
+	0x09, 0x05,         /*  Usage (Game Pad),               */
+
+	0xA1, 0x01,         /*  Collection (Application),       */
+	0x85, 0x07,         /*      Report ID (7),              */
+
+	0xA1, 0x00,         /*      Collection (Physical),      */
+	0x09, 0x30,         /*          Usage (X),              */
+	0x09, 0x31,         /*          Usage (Y),              */
+	0x15, 0x00,         /*          Logical Minimum (0),    */
+	0x26, 0xFF, 0x00,   /*          Logical Maximum (255),  */
+	0x35, 0x00,         /*          Physical Minimum (0),   */
+	0x46, 0xFF, 0x00,   /*          Physical Maximum (255), */
+	0x95, 0x02,         /*          Report Count (2),       */
+	0x75, 0x08,         /*          Report Size (8),        */
+	0x81, 0x02,         /*          Input (Variable),       */
+	0xC0,               /*      End Collection,             */
+
+	0xA1, 0x00,         /*      Collection (Physical),      */
+	0x09, 0x33,         /*          Usage (Rx),             */
+	0x09, 0x34,         /*          Usage (Ry),             */
+	0x15, 0x00,         /*          Logical Minimum (0),    */
+	0x26, 0xFF, 0x00,   /*          Logical Maximum (255),  */
+	0x35, 0x00,         /*          Physical Minimum (0),   */
+	0x46, 0xFF, 0x00,   /*          Physical Maximum (255), */
+	0x95, 0x02,         /*          Report Count (2),       */
+	0x75, 0x08,         /*          Report Size (8),        */
+	0x81, 0x02,         /*          Input (Variable),       */
+	0xC0,               /*      End Collection,             */
+
+	0xA1, 0x00,         /*      Collection (Physical),      */
+	0x09, 0x32,         /*          Usage (Z),              */
+	0x15, 0x00,         /*          Logical Minimum (0),    */
+	0x26, 0xFF, 0x00,   /*          Logical Maximum (255),  */
+	0x35, 0x00,         /*          Physical Minimum (0),   */
+	0x46, 0xFF, 0x00,   /*          Physical Maximum (255), */
+	0x95, 0x01,         /*          Report Count (1),       */
+	0x75, 0x08,         /*          Report Size (8),        */
+	0x81, 0x02,         /*          Input (Variable),       */
+	0xC0,               /*      End Collection,             */
+
+	0xA1, 0x00,         /*      Collection (Physical),      */
+	0x09, 0x35,         /*          Usage (Rz),             */
+	0x15, 0x00,         /*          Logical Minimum (0),    */
+	0x26, 0xFF, 0x00,   /*          Logical Maximum (255),  */
+	0x35, 0x00,         /*          Physical Minimum (0),   */
+	0x46, 0xFF, 0x00,   /*          Physical Maximum (255), */
+	0x95, 0x01,         /*          Report Count (1),       */
+	0x75, 0x08,         /*          Report Size (8),        */
+	0x81, 0x02,         /*          Input (Variable),       */
+	0xC0,               /*      End Collection,             */
+
+	0x05, 0x09,         /*      Usage Page (Button),        */
+	0x19, 0x01,         /*      Usage Minimum (01h),        */
+	0x29, 0x10,         /*      Usage Maximum (10h),        */
+	0x95, 0x10,         /*      Report Count (16),          */
+	0x75, 0x01,         /*      Report Size (1),            */
+	0x81, 0x02,         /*      Input (Variable),           */
+
+	/*   ORIGINAL REPORT DESCRIPTOR FOR TOUCHPAD INPUT  */
+	/* 06 00 ff a1 02 09 02 15 00 26 ff 00 35 00 46 ff 00 95 03 75 08 81 02 c0 */
+
+	0x06, 0x00, 0xFF,   /*      Usage Page (Custom),        */
+	0x09, 0x02,         /*      Usage (Mouse),              */
+	0x09, 0x01,         /*      Usage (Pointer),            */
+	0xA1, 0x00,         /*      Collection (Physical),      */
+	0x05, 0x09,         /*          Usage Page (Button),    */
+	0x19, 0x01,         /*          Usage Minimum (01h),    */
+	0x29, 0x03,         /*          Usage Maximum (03h),    */
+	0x15, 0x00,         /*          Logical Minimum (0),    */
+	0x25, 0x01,         /*          Logical Maximum (1),    */
+	0x95, 0x03,         /*          Report Count (3),       */
+	0x75, 0x01,         /*          Report Size (1),        */
+	0x81, 0x02,         /*          Input (Variable),       */
+	0x95, 0x01,         /*          Report Count (1),       */
+	0x75, 0x05,         /*          Report Size (5),        */
+	0x81, 0x01,         /*          Input (Constant),       */
+	0x05, 0x01,         /*          Usage Page (Desktop),   */
+	0x09, 0x30,         /*          Usage (X),              */
+	0x09, 0x31,         /*          Usage (Y),              */
+	0x15, 0x81,         /*          Logical Minimum (-127), */
+	0x25, 0x7f,         /*          Logical Maximum (127),  */
+	0x95, 0x02,         /*          Report Count (2),       */
+	0x75, 0x08,         /*          Report Size (8),        */
+	0x81, 0x06,         /*          Input (Relative),       */
+	0xC0,               /*      End Collection,             */
+
+	0x06, 0x00, 0xFF,   /*      Usage Page (Custom),        */
+	0xA1, 0x02,         /*      Collection (Logical),       */
+	0x75, 0x08,         /*          Report Size (8),        */
+	0x95, 0x07,         /*          Report Count (7),       */
+	0x46, 0xFF, 0x00,   /*          Physical Maximum (255), */
+	0x26, 0xFF, 0x00,   /*          Logical Maximum (255),  */
+	0x09, 0x01,         /*          Usage (Pointer),        */
+	0x91, 0x02,         /*          Output (Variable),      */
+	0xC0,               /*      End Collection,             */
+
+	0xC0,               /*  End Collection                  */
+
+
+	0x06, 0x00, 0xFF,   /*  Usage Page (Custom),            */
+	0x05, 0x0C,         /*  Usage Page (Consumer),          */
+	0x09, 0x01,         /*  Usage (Consumer Control),       */
+
+	0xA1, 0x01,         /*  Collection (Application),       */
+	0x85, 0x03,         /*      Report ID (3),              */
+	0x05, 0x01,         /*      Usage Page (Desktop),       */
+	0x09, 0x06,         /*      Usage (Keyboard),           */
+	0xA1, 0x02,         /*      Collection (Logical),       */
+	0x05, 0x06,         /*          Usage Page (Generic),   */
+	0x09, 0x20,         /*          Usage (Battery Strgth), */
+	0x15, 0x00,         /*          Logical Minimum (0),    */
+	0x26, 0xFF, 0x00,   /*          Logical Maximum (255),  */
+	0x75, 0x08,         /*          Report Size (8),        */
+	0x95, 0x01,         /*          Report Count (1),       */
+	0x81, 0x02,         /*          Input (Variable),       */
+	0x06, 0xBC, 0xFF,   /*          Usage Page (Custom),    */
+
+	0x0A, 0xAD, 0xBD,   /*  UNKNOWN */
+
+	0x75, 0x08,         /*          Report Size (8),        */
+	0x95, 0x06,         /*          Report Count (6),       */
+	0x81, 0x02,         /*          Input (Variable),       */
+	0xC0,               /*      End Collection,             */
+
+	0xC0,               /*  End Collection                  */
+
+	0x00
+};
+
+static __u8 *ouya_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+		unsigned int *rsize)
+{
+	struct ouya_sc *sc = hid_get_drvdata(hdev);
+
+	if (sc->quirks & OUYA_TOUCHPAD_FIXUP) {
+		rdesc = ouya_rdesc_fixed;
+		*rsize = sizeof(ouya_rdesc_fixed);
+	}
+	return rdesc;
+}
+
+static int ouya_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	struct ouya_sc *sc = hid_get_drvdata(hdev);
+
+	if (!(sc->quirks & OUYA_TOUCHPAD_FIXUP))
+		return 0;
+
+	if ((usage->hid & 0x90000) == 0x90000 &&
+		(field->physical & 0xff000000) == 0xff000000 &&
+		usage->collection_index == 5 &&
+		field->report_count == 3) {
+
+		hid_map_usage_clear(hi, usage, bit, max, EV_KEY,
+				BTN_MOUSE + (usage->hid - 0x90001));
+
+		return 1;
+	}
+
+	return 0;
+}
+
+static int ouya_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	int ret;
+	struct ouya_sc *sc;
+
+	sc = kzalloc(sizeof(*sc), GFP_KERNEL);
+	if (sc == NULL)
+		return -ENOMEM;
+
+	if (((hdev->version & 0xff00) == 0x0100 &&
+		(hdev->version & 0xff) >= 0x04) ||
+		((hdev->version & 0xff00) == 0xe100 &&
+		(hdev->version & 0xff) >= 0x3a)) {
+
+		hid_info(hdev, "ouya controller - new version\n");
+		sc->quirks = OUYA_TOUCHPAD_FIXUP;
+	} else {
+		sc->quirks = 0;
+	}
+
+	hid_set_drvdata(hdev, sc);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		hid_err(hdev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
+			HID_CONNECT_HIDDEV_FORCE);
+	if (ret) {
+		hid_err(hdev, "hw start failed\n");
+		goto err_free;
+	}
+
+	return 0;
+
+err_free:
+	kfree(sc);
+	return ret;
+}
+
+static void ouya_remove(struct hid_device *hdev)
+{
+	hid_hw_stop(hdev);
+	kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id ouya_devices[] = {
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_OUYA, USB_DEVICE_ID_OUYA_CONTROLLER) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, ouya_devices);
+
+static struct hid_driver ouya_driver = {
+	.name = "ouya",
+	.id_table = ouya_devices,
+	.input_mapping = ouya_input_mapping,
+	.probe = ouya_probe,
+	.remove = ouya_remove,
+	.report_fixup = ouya_report_fixup,
+};
+
+module_hid_driver(ouya_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lukas Rusak <lorusak@gmail.com>");
+MODULE_AUTHOR("Gregorios Leach <optikflux@gmail.com>");
+MODULE_DESCRIPTION("Ouya Controller Driver");
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 671a285724f9..9bdc493dad5c 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -531,6 +531,9 @@  static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_IHOME_IMAC_A210S) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
 #endif
+#if IS_ENABLED(CONFIG_HID_OUYA)
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_OUYA, USB_DEVICE_ID_OUYA_CONTROLLER) },
+#endif
 #if IS_ENABLED(CONFIG_HID_PANTHERLORD)
 	{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) },