[PATCH] HID: sony: Add rumble and LED support for Nyko Core Controller

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The Nyko Core Controller, vendor:1345 product:3008, is a usb controller marketed
as a PS3 controller. It works as an HID device for input. It has a single left
rumble motor and no accelerometers, so it isn't a sixaxis. This patch adds rumble
and led support in the hid-sony driver.
---
 drivers/hid/hid-core.c |   1 +
 drivers/hid/hid-ids.h  |   3 ++
 drivers/hid/hid-sony.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 107 insertions(+), 6 deletions(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index c6f7a69..bcaf96e 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2008,6 +2008,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 9024a3d..3ce4817 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -874,6 +874,9 @@
 #define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER		0x0002
 #define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER	0x1000
 
+#define USB_VENDOR_ID_SINO_LITE			0x1345
+#define USB_DEVICE_ID_SINO_LITE_CONTROLLER	0x3008
+
 #define USB_VENDOR_ID_SOUNDGRAPH	0x15c2
 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST	0x0034
 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST	0x0046
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 774cd22..7caac8d 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -50,6 +50,7 @@
 #define MOTION_CONTROLLER_BT      BIT(8)
 #define NAVIGATION_CONTROLLER_USB BIT(9)
 #define NAVIGATION_CONTROLLER_BT  BIT(10)
+#define NYKO_CORE_CONTROLLER      BIT(11)
 
 #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
 #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
@@ -59,11 +60,11 @@
 				DUALSHOCK4_CONTROLLER_BT)
 #define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER | BUZZ_CONTROLLER |\
 				DUALSHOCK4_CONTROLLER | MOTION_CONTROLLER |\
-				NAVIGATION_CONTROLLER)
+				NAVIGATION_CONTROLLER | NYKO_CORE_CONTROLLER)
 #define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\
 				MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER)
 #define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\
-				MOTION_CONTROLLER)
+				MOTION_CONTROLLER | NYKO_CORE_CONTROLLER)
 
 #define MAX_LEDS 4
 
@@ -1002,6 +1003,28 @@ union sixaxis_output_report_01 {
 	__u8 buf[36];
 };
 
+struct nyko_rumble {
+	u8 right_duration; /* Right motor duration (0xff means forever) */
+	u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
+	u8 left_duration;    /* Left motor duration (0xff means forever) */
+	u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
+	u8 padding[4];
+} __packed;
+
+struct nyko_output_report {
+	u8 report_id;
+	struct nyko_rumble rumble;
+	u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
+	struct sixaxis_led led[4];    /* LEDx at (4 - x) */
+	struct sixaxis_led _reserved; /* LED5, not actually soldered */
+	u8 padding;
+} __packed;
+
+union nyko_output_report_01 {
+	struct nyko_output_report data;
+	u8 buf[36];
+};
+
 struct motion_output_report_02 {
 	u8 type, zero;
 	u8 r, g, b;
@@ -1116,6 +1139,9 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 {
 	struct sony_sc *sc = hid_get_drvdata(hdev);
 
+	if (sc->quirks & NYKO_CORE_CONTROLLER)
+		return rdesc;
+
 	/*
 	 * Some Sony RF receivers wrongly declare the mouse pointer as a
 	 * a constant non-data variable.
@@ -1393,6 +1419,7 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
 	const int buf_size =
 		max(SIXAXIS_REPORT_0xF2_SIZE, SIXAXIS_REPORT_0xF5_SIZE);
 	__u8 *buf;
+	struct sony_sc *sc = hid_get_drvdata(hdev);
 	int ret;
 
 	buf = kmalloc(buf_size, GFP_KERNEL);
@@ -1406,6 +1433,9 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
 		goto out;
 	}
 
+	if (sc->quirks & NYKO_CORE_CONTROLLER)
+		goto out;
+
 	/*
 	 * Some compatible controllers like the Speedlink Strike FX and
 	 * Gasia need another query plus an USB interrupt to get operational.
@@ -1568,7 +1598,8 @@ static void sony_led_set_brightness(struct led_classdev *led,
 	 * controller to avoid having to toggle the state of an LED just to
 	 * stop the flashing later on.
 	 */
-	force_update = !!(drv_data->quirks & SIXAXIS_CONTROLLER_USB);
+	force_update = !!(drv_data->quirks &
+			(SIXAXIS_CONTROLLER_USB | NYKO_CORE_CONTROLLER));
 
 	for (n = 0; n < drv_data->led_count; n++) {
 		if (led == drv_data->leds[n] && (force_update ||
@@ -1803,6 +1834,7 @@ static void sixaxis_state_worker(struct work_struct *work)
 			0x00, 0x00, 0x00, 0x00, 0x00
 		}
 	};
+
 	struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
 	struct sixaxis_output_report *report =
 		(struct sixaxis_output_report *)sc->output_report_dmabuf;
@@ -1846,6 +1878,62 @@ static void sixaxis_state_worker(struct work_struct *work)
 			HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
 }
 
+static void nyko_state_worker(struct work_struct *work)
+{
+	static const union nyko_output_report_01 default_report = {
+		.buf = {
+			0x01,
+			0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0xff,
+			0x30, 0x27, 0x30, 0x30, 0xff,
+			0x30, 0x27, 0x30, 0x30, 0xff,
+			0x30, 0x27, 0x30, 0x30, 0xff,
+			0x30, 0x27, 0x30, 0x30, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00
+		}
+	};
+
+	struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
+	struct nyko_output_report *report =
+		(struct nyko_output_report *)sc->output_report_dmabuf;
+	int n;
+
+	/* Initialize the report with default values */
+	memcpy(report, &default_report, sizeof(struct nyko_output_report));
+
+#ifdef CONFIG_SONY_FF
+	report->rumble.left_motor_force = (sc->right / 2) + (sc->left * 2);
+	report->rumble.right_duration = 0x00;
+	report->rumble.left_duration = 0xff;
+#endif
+
+	report->leds_bitmap |= sc->led_state[0] << 1;
+	report->leds_bitmap |= sc->led_state[1] << 2;
+	report->leds_bitmap |= sc->led_state[2] << 3;
+	report->leds_bitmap |= sc->led_state[3] << 4;
+
+	/*
+	 * The LEDs in the report are indexed in reverse order to their
+	 * corresponding light on the controller.
+	 * Index 0 = LED 4, index 1 = LED 3, etc...
+	 *
+	 * In the case of both delay values being zero (blinking disabled) the
+	 * default report values should be used or the controller LED will be
+	 * always off.
+	 */
+	for (n = 0; n < 4; n++) {
+		if (sc->led_delay_on[n] || sc->led_delay_off[n]) {
+			report->led[3 - n].duty_off = sc->led_delay_off[n];
+			report->led[3 - n].duty_on = sc->led_delay_on[n];
+		}
+	}
+
+	hid_hw_raw_request(sc->hdev, report->report_id, (u8 *)report,
+			sizeof(struct nyko_output_report),
+			HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
+
+}
+
 static void dualshock4_state_worker(struct work_struct *work)
 {
 	struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
@@ -1917,7 +2005,8 @@ static void motion_state_worker(struct work_struct *work)
 static int sony_allocate_output_report(struct sony_sc *sc)
 {
 	if ((sc->quirks & SIXAXIS_CONTROLLER) ||
-			(sc->quirks & NAVIGATION_CONTROLLER))
+			(sc->quirks & NAVIGATION_CONTROLLER) ||
+			(sc->quirks & NYKO_CORE_CONTROLLER))
 		sc->output_report_dmabuf =
 			kmalloc(sizeof(union sixaxis_output_report_01),
 				GFP_KERNEL);
@@ -2170,7 +2259,8 @@ static int sony_check_add(struct sony_sc *sc)
 
 		memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
 	} else if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
-			(sc->quirks & NAVIGATION_CONTROLLER_USB)) {
+			(sc->quirks & NAVIGATION_CONTROLLER_USB) ||
+			(sc->quirks & NYKO_CORE_CONTROLLER)) {
 		buf = kmalloc(SIXAXIS_REPORT_0xF2_SIZE, GFP_KERNEL);
 		if (!buf)
 			return -ENOMEM;
@@ -2218,7 +2308,8 @@ static int sony_set_device_id(struct sony_sc *sc)
 	 * All others are set to -1.
 	 */
 	if ((sc->quirks & SIXAXIS_CONTROLLER) ||
-	    (sc->quirks & DUALSHOCK4_CONTROLLER)) {
+	    (sc->quirks & DUALSHOCK4_CONTROLLER) ||
+	    (sc->quirks & NYKO_CORE_CONTROLLER)) {
 		ret = ida_simple_get(&sony_device_id_allocator, 0, 0,
 					GFP_KERNEL);
 		if (ret < 0) {
@@ -2320,6 +2411,9 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
 		ret = sixaxis_set_operational_usb(hdev);
 		sony_init_work(sc, sixaxis_state_worker);
+	} else if (sc->quirks & NYKO_CORE_CONTROLLER) {
+		ret = sixaxis_set_operational_usb(hdev);
+		sony_init_work(sc, nyko_state_worker);
 	} else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) ||
 			(sc->quirks & NAVIGATION_CONTROLLER_BT)) {
 		/*
@@ -2458,6 +2552,9 @@ static const struct hid_device_id sony_devices[] = {
 		.driver_data = DUALSHOCK4_CONTROLLER_USB },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER),
 		.driver_data = DUALSHOCK4_CONTROLLER_BT },
+	/* Nyko Core Controller for PS3 */
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER),
+		.driver_data = NYKO_CORE_CONTROLLER },
 	{ }
 };
 MODULE_DEVICE_TABLE(hid, sony_devices);
-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux