linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/7] HID: sony: Add full support for the Dualshock 4 on Bluetooth
@ 2014-01-29 17:33 Frank Praznik
  2014-01-29 17:33 ` [PATCH 1/7] HID: sony: Change dualshock4_state_worker to use the low-level transport driver function Frank Praznik
                   ` (6 more replies)
  0 siblings, 7 replies; 11+ messages in thread
From: Frank Praznik @ 2014-01-29 17:33 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, Frank Praznik

Since the low-level transport driver changes are queued up, I can submit the
patches which enable full Bluetooth functionality for the Dualshock 4.

Note that BlueZ 5.14 or newer is required to successfully pair a Dualshock 4
controller via Bluetooth.

This uses the new low-level transport driver functions as the Dualshock 4 wants 
output reports sent on the control channel instead of the interrupt channel when
connected via Bluetooth. If someone wants to test this, they will have to merge
the for-3.15/ll-driver-new-callbacks branch into the for-3.15/sony branch.

The HID descriptor had to be rewritten as once an output report is sent to the
controller it starts sending back data in report 17 instead of 1, which is
unmapped in the default descriptor.

The format of the data in the input and output reports is the same on Bluetooth
as on USB, but it starts at different offsets.

When no USB cable is connected and the battery is discharging, the battery level
is reported from 0 to 9 instead of from 0 to 11.

A single controller can show up as two devices in the event that a user connects
it via USB while it is already connected via Bluetooth. To remedy this a global
list of connected controllers and their MAC addresses is maintained and newly
connected controllers are checked against this list. If it is already connected
via some other means, the probe function returns with a code of EEXIST.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/7] HID: sony: Change dualshock4_state_worker to use the low-level transport driver function
  2014-01-29 17:33 [PATCH 0/7] HID: sony: Add full support for the Dualshock 4 on Bluetooth Frank Praznik
@ 2014-01-29 17:33 ` Frank Praznik
  2014-01-29 17:39   ` David Herrmann
  2014-01-29 17:33 ` [PATCH 2/7] HID: sony: Add Bluetooth HID descriptor for Dualshock 4 Frank Praznik
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 11+ messages in thread
From: Frank Praznik @ 2014-01-29 17:33 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, Frank Praznik

Use the new low-level transport driver functions to send the output reports to
the controller.

Remove sony_set_output_report since it is no longer used.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>

---
 drivers/hid/hid-sony.c | 56 +++++++++++++-------------------------------------
 1 file changed, 14 insertions(+), 42 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 2bd3f13..a3cefdec 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -502,7 +502,6 @@ struct sony_sc {
 	spinlock_t lock;
 	struct hid_device *hdev;
 	struct led_classdev *leds[MAX_LEDS];
-	struct hid_report *output_report;
 	unsigned long quirks;
 	struct work_struct state_worker;
 	struct power_supply battery;
@@ -1053,21 +1052,26 @@ static void dualshock4_state_worker(struct work_struct *work)
 {
 	struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
 	struct hid_device *hdev = sc->hdev;
-	struct hid_report *report = sc->output_report;
-	__s32 *value = report->field[0]->value;
+	int offset;
+
+	__u8 buf[32] = { 0 };
 
-	value[0] = 0x03;
+	buf[0] = 0x05;
+	buf[1] = 0x03;
+	offset = 4;
 
 #ifdef CONFIG_SONY_FF
-	value[3] = sc->right;
-	value[4] = sc->left;
+	buf[offset++] = sc->right;
+	buf[offset++] = sc->left;
+#else
+	offset += 2;
 #endif
 
-	value[5] = sc->led_state[0];
-	value[6] = sc->led_state[1];
-	value[7] = sc->led_state[2];
+	buf[offset++] = sc->led_state[0];
+	buf[offset++] = sc->led_state[1];
+	buf[offset++] = sc->led_state[2];
 
-	hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
+	hdev->ll_driver->output_report(hdev, buf, sizeof(buf));
 }
 
 #ifdef CONFIG_SONY_FF
@@ -1200,33 +1204,6 @@ static void sony_battery_remove(struct sony_sc *sc)
 	sc->battery.name = NULL;
 }
 
-static int sony_set_output_report(struct sony_sc *sc, int req_id, int req_size)
-{
-	struct list_head *head, *list;
-	struct hid_report *report;
-	struct hid_device *hdev = sc->hdev;
-
-	list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
-
-	list_for_each(head, list) {
-		report = list_entry(head, struct hid_report, list);
-
-		if (report->id == req_id) {
-			if (report->size < req_size) {
-				hid_err(hdev, "Output report 0x%02x (%i bits) is smaller than requested size (%i bits)\n",
-					req_id, report->size, req_size);
-				return -EINVAL;
-			}
-			sc->output_report = report;
-			return 0;
-		}
-	}
-
-	hid_err(hdev, "Unable to locate output report 0x%02x\n", req_id);
-
-	return -EINVAL;
-}
-
 static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
 					int w, int h)
 {
@@ -1291,11 +1268,6 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
 		ret = sixaxis_set_operational_bt(hdev);
 	else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
-		/* Report 5 (31 bytes) is used to send data to the controller via USB */
-		ret = sony_set_output_report(sc, 0x05, 248);
-		if (ret < 0)
-			goto err_stop;
-
 		/* The Dualshock 4 touchpad supports 2 touches and has a
 		 * resolution of 1920x940.
 		 */
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 2/7] HID: sony: Add Bluetooth HID descriptor for Dualshock 4
  2014-01-29 17:33 [PATCH 0/7] HID: sony: Add full support for the Dualshock 4 on Bluetooth Frank Praznik
  2014-01-29 17:33 ` [PATCH 1/7] HID: sony: Change dualshock4_state_worker to use the low-level transport driver function Frank Praznik
@ 2014-01-29 17:33 ` Frank Praznik
  2014-01-29 17:33 ` [PATCH 3/7] HID: sony: Add Bluetooth output report formatting for the " Frank Praznik
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 11+ messages in thread
From: Frank Praznik @ 2014-01-29 17:33 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, Frank Praznik

Add the modified Bluetooth HID descriptor for the Dualshock 4.

By default, the Dualshock 4 sends controller data via report 1. Once an output
report is received the controller changes to sending data in report 17, which is
unmapped in the default descriptor. The mappings had to be moved to report 17 to
let the HID driver properly process the incoming reports.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>

---
 drivers/hid/hid-sony.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 214 insertions(+)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index a3cefdec..8e82073f 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -336,6 +336,216 @@ static u8 dualshock4_usb_rdesc[] = {
 	0xC0                /*  End Collection                      */
 };
 
+/* The default behavior of the Dualshock 4 is to send reports using report
+ * type 1 when running over Bluetooth. However, as soon as it receives a
+ * report of type 17 to set the LEDs or rumble it starts returning it's state
+ * in report 17 instead of 1.  Since report 17 is undefined in the default HID
+ * descriptor the button and axis definitions must be moved to report 17 or
+ * the HID layer won't process the received input once a report is sent.
+ */
+static u8 dualshock4_bt_rdesc[] = {
+	0x05, 0x01,         /*  Usage Page (Desktop),               */
+	0x09, 0x05,         /*  Usage (Gamepad),                    */
+	0xA1, 0x01,         /*  Collection (Application),           */
+	0x85, 0x01,         /*      Report ID (1),                  */
+	0x75, 0x08,         /*      Report Size (8),                */
+	0x95, 0x0A,         /*      Report Count (9),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x06, 0x04, 0xFF,   /*      Usage Page (FF04h),             */
+	0x85, 0x02,         /*      Report ID (2),                  */
+	0x09, 0x24,         /*      Usage (24h),                    */
+	0x95, 0x24,         /*      Report Count (36),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0xA3,         /*      Report ID (163),                */
+	0x09, 0x25,         /*      Usage (25h),                    */
+	0x95, 0x30,         /*      Report Count (48),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x05,         /*      Report ID (5),                  */
+	0x09, 0x26,         /*      Usage (26h),                    */
+	0x95, 0x28,         /*      Report Count (40),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x06,         /*      Report ID (6),                  */
+	0x09, 0x27,         /*      Usage (27h),                    */
+	0x95, 0x34,         /*      Report Count (52),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x07,         /*      Report ID (7),                  */
+	0x09, 0x28,         /*      Usage (28h),                    */
+	0x95, 0x30,         /*      Report Count (48),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x08,         /*      Report ID (8),                  */
+	0x09, 0x29,         /*      Usage (29h),                    */
+	0x95, 0x2F,         /*      Report Count (47),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x06, 0x03, 0xFF,   /*      Usage Page (FF03h),             */
+	0x85, 0x03,         /*      Report ID (3),                  */
+	0x09, 0x21,         /*      Usage (21h),                    */
+	0x95, 0x26,         /*      Report Count (38),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x04,         /*      Report ID (4),                  */
+	0x09, 0x22,         /*      Usage (22h),                    */
+	0x95, 0x2E,         /*      Report Count (46),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0xF0,         /*      Report ID (240),                */
+	0x09, 0x47,         /*      Usage (47h),                    */
+	0x95, 0x3F,         /*      Report Count (63),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0xF1,         /*      Report ID (241),                */
+	0x09, 0x48,         /*      Usage (48h),                    */
+	0x95, 0x3F,         /*      Report Count (63),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0xF2,         /*      Report ID (242),                */
+	0x09, 0x49,         /*      Usage (49h),                    */
+	0x95, 0x0F,         /*      Report Count (15),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x11,         /*      Report ID (17),                 */
+	0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */
+	0x09, 0x20,         /*      Usage (20h),                    */
+	0x95, 0x02,         /*      Report Count (2),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x05, 0x01,         /*      Usage Page (Desktop),           */
+	0x09, 0x30,         /*      Usage (X),                      */
+	0x09, 0x31,         /*      Usage (Y),                      */
+	0x09, 0x32,         /*      Usage (Z),                      */
+	0x09, 0x35,         /*      Usage (Rz),                     */
+	0x15, 0x00,         /*      Logical Minimum (0),            */
+	0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
+	0x75, 0x08,         /*      Report Size (8),                */
+	0x95, 0x04,         /*      Report Count (4),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x39,         /*      Usage (Hat Switch),             */
+	0x15, 0x00,         /*      Logical Minimum (0),            */
+	0x25, 0x07,         /*      Logical Maximum (7),            */
+	0x75, 0x04,         /*      Report Size (4),                */
+	0x95, 0x01,         /*      Report Count (1),               */
+	0x81, 0x42,         /*      Input (Variable, Null State),   */
+	0x05, 0x09,         /*      Usage Page (Button),            */
+	0x19, 0x01,         /*      Usage Minimum (01h),            */
+	0x29, 0x0E,         /*      Usage Maximum (0Eh),            */
+	0x15, 0x00,         /*      Logical Minimum (0),            */
+	0x25, 0x01,         /*      Logical Maximum (1),            */
+	0x75, 0x01,         /*      Report Size (1),                */
+	0x95, 0x0E,         /*      Report Count (14),              */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x75, 0x06,         /*      Report Size (6),                */
+	0x95, 0x01,         /*      Report Count (1),               */
+	0x81, 0x01,         /*      Input (Constant),               */
+	0x05, 0x01,         /*      Usage Page (Desktop),           */
+	0x09, 0x33,         /*      Usage (Rx),                     */
+	0x09, 0x34,         /*      Usage (Ry),                     */
+	0x15, 0x00,         /*      Logical Minimum (0),            */
+	0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
+	0x75, 0x08,         /*      Report Size (8),                */
+	0x95, 0x02,         /*      Report Count (2),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */
+	0x09, 0x20,         /*      Usage (20h),                    */
+	0x95, 0x03,         /*      Report Count (3),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x05, 0x01,         /*      Usage Page (Desktop),           */
+	0x19, 0x40,         /*      Usage Minimum (40h),            */
+	0x29, 0x42,         /*      Usage Maximum (42h),            */
+	0x16, 0x00, 0x80,   /*      Logical Minimum (-32768),       */
+	0x26, 0x00, 0x7F,   /*      Logical Maximum (32767),        */
+	0x75, 0x10,         /*      Report Size (16),               */
+	0x95, 0x03,         /*      Report Count (3),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x19, 0x43,         /*      Usage Minimum (43h),            */
+	0x29, 0x45,         /*      Usage Maximum (45h),            */
+	0x16, 0xFF, 0xBF,   /*      Logical Minimum (-16385),       */
+	0x26, 0x00, 0x40,   /*      Logical Maximum (16384),        */
+	0x95, 0x03,         /*      Report Count (3),               */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */
+	0x09, 0x20,         /*      Usage (20h),                    */
+	0x15, 0x00,         /*      Logical Minimum (0),            */
+	0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
+	0x75, 0x08,         /*      Report Size (8),                */
+	0x95, 0x31,         /*      Report Count (51),              */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x21,         /*      Usage (21h),                    */
+	0x75, 0x08,         /*      Report Size (8),                */
+	0x95, 0x4D,         /*      Report Count (77),              */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x12,         /*      Report ID (18),                 */
+	0x09, 0x22,         /*      Usage (22h),                    */
+	0x95, 0x8D,         /*      Report Count (141),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x23,         /*      Usage (23h),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x13,         /*      Report ID (19),                 */
+	0x09, 0x24,         /*      Usage (24h),                    */
+	0x95, 0xCD,         /*      Report Count (205),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x25,         /*      Usage (25h),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x14,         /*      Report ID (20),                 */
+	0x09, 0x26,         /*      Usage (26h),                    */
+	0x96, 0x0D, 0x01,   /*      Report Count (269),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x27,         /*      Usage (27h),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x15,         /*      Report ID (21),                 */
+	0x09, 0x28,         /*      Usage (28h),                    */
+	0x96, 0x4D, 0x01,   /*      Report Count (333),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x29,         /*      Usage (29h),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x16,         /*      Report ID (22),                 */
+	0x09, 0x2A,         /*      Usage (2Ah),                    */
+	0x96, 0x8D, 0x01,   /*      Report Count (397),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x2B,         /*      Usage (2Bh),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x17,         /*      Report ID (23),                 */
+	0x09, 0x2C,         /*      Usage (2Ch),                    */
+	0x96, 0xCD, 0x01,   /*      Report Count (461),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x2D,         /*      Usage (2Dh),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x18,         /*      Report ID (24),                 */
+	0x09, 0x2E,         /*      Usage (2Eh),                    */
+	0x96, 0x0D, 0x02,   /*      Report Count (525),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x2F,         /*      Usage (2Fh),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x85, 0x19,         /*      Report ID (25),                 */
+	0x09, 0x30,         /*      Usage (30h),                    */
+	0x96, 0x22, 0x02,   /*      Report Count (546),             */
+	0x81, 0x02,         /*      Input (Variable),               */
+	0x09, 0x31,         /*      Usage (31h),                    */
+	0x91, 0x02,         /*      Output (Variable),              */
+	0x06, 0x80, 0xFF,   /*      Usage Page (FF80h),             */
+	0x85, 0x82,         /*      Report ID (130),                */
+	0x09, 0x22,         /*      Usage (22h),                    */
+	0x95, 0x3F,         /*      Report Count (63),              */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x83,         /*      Report ID (131),                */
+	0x09, 0x23,         /*      Usage (23h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x84,         /*      Report ID (132),                */
+	0x09, 0x24,         /*      Usage (24h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x90,         /*      Report ID (144),                */
+	0x09, 0x30,         /*      Usage (30h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x91,         /*      Report ID (145),                */
+	0x09, 0x31,         /*      Usage (31h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x92,         /*      Report ID (146),                */
+	0x09, 0x32,         /*      Usage (32h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0x93,         /*      Report ID (147),                */
+	0x09, 0x33,         /*      Usage (33h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0xA0,         /*      Report ID (160),                */
+	0x09, 0x40,         /*      Usage (40h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0x85, 0xA4,         /*      Report ID (164),                */
+	0x09, 0x44,         /*      Usage (44h),                    */
+	0xB1, 0x02,         /*      Feature (Variable),             */
+	0xC0                /*  End Collection                      */
+};
+
 static __u8 ps3remote_rdesc[] = {
 	0x05, 0x01,          /* GUsagePage Generic Desktop */
 	0x09, 0x05,          /* LUsage 0x05 [Game Pad] */
@@ -591,6 +801,10 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 		hid_info(hdev, "Using modified Dualshock 4 report descriptor with gyroscope axes\n");
 		rdesc = dualshock4_usb_rdesc;
 		*rsize = sizeof(dualshock4_usb_rdesc);
+	} else if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) && *rsize == 357) {
+		hid_info(hdev, "Using modified Dualshock 4 Bluetooth report descriptor\n");
+		rdesc = dualshock4_bt_rdesc;
+		*rsize = sizeof(dualshock4_bt_rdesc);
 	}
 
 	/* The HID descriptor exposed over BT has a trailing zero byte */
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 3/7] HID: sony: Add Bluetooth output report formatting for the Dualshock 4
  2014-01-29 17:33 [PATCH 0/7] HID: sony: Add full support for the Dualshock 4 on Bluetooth Frank Praznik
  2014-01-29 17:33 ` [PATCH 1/7] HID: sony: Change dualshock4_state_worker to use the low-level transport driver function Frank Praznik
  2014-01-29 17:33 ` [PATCH 2/7] HID: sony: Add Bluetooth HID descriptor for Dualshock 4 Frank Praznik
@ 2014-01-29 17:33 ` Frank Praznik
  2014-01-29 17:33 ` [PATCH 4/7] HID: sony: Add offsets and battery calculations for parsing Dualshock 4 reports sent via Bluetooth Frank Praznik
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 11+ messages in thread
From: Frank Praznik @ 2014-01-29 17:33 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, Frank Praznik

When connected via Bluetooth the Dualshock 4 wants rumble and LED data sent in
report 17, which is 77 bytes.

The format of the rumble and LED data is identical to running over USB, but the
starting offset is at 6 bytes instead of 4.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>

---
 drivers/hid/hid-sony.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 8e82073f..b35535e 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1268,11 +1268,18 @@ static void dualshock4_state_worker(struct work_struct *work)
 	struct hid_device *hdev = sc->hdev;
 	int offset;
 
-	__u8 buf[32] = { 0 };
+	__u8 buf[78] = { 0 };
 
-	buf[0] = 0x05;
-	buf[1] = 0x03;
-	offset = 4;
+	if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
+		buf[0] = 0x05;
+		buf[1] = 0x03;
+		offset = 4;
+	} else {
+		buf[0] = 0x11;
+		buf[1] = 0xB0;
+		buf[3] = 0x0F;
+		offset = 6;
+	}
 
 #ifdef CONFIG_SONY_FF
 	buf[offset++] = sc->right;
@@ -1285,7 +1292,11 @@ static void dualshock4_state_worker(struct work_struct *work)
 	buf[offset++] = sc->led_state[1];
 	buf[offset++] = sc->led_state[2];
 
-	hdev->ll_driver->output_report(hdev, buf, sizeof(buf));
+	if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
+		hdev->ll_driver->output_report(hdev, buf, 32);
+	else
+		hdev->ll_driver->raw_request(hdev, 0x11, buf, 78,
+				HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
 }
 
 #ifdef CONFIG_SONY_FF
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 4/7] HID: sony: Add offsets and battery calculations for parsing Dualshock 4 reports sent via Bluetooth.
  2014-01-29 17:33 [PATCH 0/7] HID: sony: Add full support for the Dualshock 4 on Bluetooth Frank Praznik
                   ` (2 preceding siblings ...)
  2014-01-29 17:33 ` [PATCH 3/7] HID: sony: Add Bluetooth output report formatting for the " Frank Praznik
@ 2014-01-29 17:33 ` Frank Praznik
  2014-01-29 21:36   ` simon
  2014-01-29 17:33 ` [PATCH 5/7] HID: sony: Set inital battery level to 100% to avoid false low battery warnings Frank Praznik
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 11+ messages in thread
From: Frank Praznik @ 2014-01-29 17:33 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, Frank Praznik

The battery and touch data starts at offset 32 instead of 30 in the Bluetooth
reports.

When the controller isn't connected to a power source and the battery is
discharging, the battery level is reported from 0 to 9 instead of 1 to 11.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>

---
 drivers/hid/hid-sony.c | 26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index b35535e..e243c3d 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -861,25 +861,34 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
 						struct hid_input, list);
 	struct input_dev *input_dev = hidinput->input;
 	unsigned long flags;
-	int n, offset = 35;
+	int n, offset;
 	__u8 cable_state, battery_capacity, battery_charging;
 
+	/* Battery and touchpad data starts at byte 30 in the USB report and
+	 * 32 in Bluetooth report.
+	 */
+	offset = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 30 : 32;
+
 	/* The lower 4 bits of byte 30 contain the battery level
 	 * and the 5th bit contains the USB cable state.
 	 */
-	cable_state = (rd[30] >> 4) & 0x01;
-	battery_capacity = rd[30] & 0x0F;
+	cable_state = (rd[offset] >> 4) & 0x01;
+	battery_capacity = rd[offset] & 0x0F;
 
-	/* On USB the Dualshock 4 battery level goes from 0 to 11.
-	 * A battery level of 11 means fully charged.
+	/* When a USB power source is connected the battery level ranges from
+	 * 0 to 11, and when running on battery power it ranges from 0 to 9.
+	 * A battery level of 11 means charge completed.
 	 */
-	if (cable_state && battery_capacity == 11)
+	if (!cable_state || battery_capacity == 11)
 		battery_charging = 0;
 	else
 		battery_charging = 1;
 
+	if (!cable_state)
+		battery_capacity++;
 	if (battery_capacity > 10)
 		battery_capacity--;
+
 	battery_capacity *= 10;
 
 	spin_lock_irqsave(&sc->lock, flags);
@@ -888,7 +897,10 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
 	sc->battery_charging = battery_charging;
 	spin_unlock_irqrestore(&sc->lock, flags);
 
-	/* The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB.
+	offset += 5;
+
+	/* The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB
+	 * and 37 on Bluetooth.
 	 * The first 7 bits of the first byte is a counter and bit 8 is a touch
 	 * indicator that is 0 when pressed and 1 when not pressed.
 	 * The next 3 bytes are two 12 bit touch coordinates, X and Y.
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 5/7] HID: sony: Set inital battery level to 100% to avoid false low battery warnings
  2014-01-29 17:33 [PATCH 0/7] HID: sony: Add full support for the Dualshock 4 on Bluetooth Frank Praznik
                   ` (3 preceding siblings ...)
  2014-01-29 17:33 ` [PATCH 4/7] HID: sony: Add offsets and battery calculations for parsing Dualshock 4 reports sent via Bluetooth Frank Praznik
@ 2014-01-29 17:33 ` Frank Praznik
  2014-01-29 17:33 ` [PATCH 6/7] HID: sony: Add conditionals to enable LED, rumble, battery and touchpad support on the Dualshock 4 Frank Praznik
  2014-01-29 17:33 ` [PATCH 7/7] HID: sony: Prevent devices from being connected twice Frank Praznik
  6 siblings, 0 replies; 11+ messages in thread
From: Frank Praznik @ 2014-01-29 17:33 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, Frank Praznik

Set the inital battery level to 100% to avoid false low battery warnings if the
battery level is polled before a device report is received.
 
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>

---
 drivers/hid/hid-sony.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 7005086..cee752f 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1404,6 +1404,11 @@ static int sony_battery_probe(struct sony_sc *sc)
 	struct hid_device *hdev = sc->hdev;
 	int ret;
 
+	/* Set the default battery level to 100% to avoid low battery warnings
+	 * if the battery is polled before the first device report is received.
+	 */
+	sc->battery_capacity = 100;
+
 	power_id = (unsigned long)atomic_inc_return(&power_id_seq);
 
 	sc->battery.properties = sony_battery_props;
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 6/7] HID: sony: Add conditionals to enable LED, rumble, battery and touchpad support on the Dualshock 4.
  2014-01-29 17:33 [PATCH 0/7] HID: sony: Add full support for the Dualshock 4 on Bluetooth Frank Praznik
                   ` (4 preceding siblings ...)
  2014-01-29 17:33 ` [PATCH 5/7] HID: sony: Set inital battery level to 100% to avoid false low battery warnings Frank Praznik
@ 2014-01-29 17:33 ` Frank Praznik
  2014-01-29 17:33 ` [PATCH 7/7] HID: sony: Prevent devices from being connected twice Frank Praznik
  6 siblings, 0 replies; 11+ messages in thread
From: Frank Praznik @ 2014-01-29 17:33 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, Frank Praznik

Add the necessary conditionals to enable LED, rumble, battery and touchpad
support on the Dualshock 4 when connected via Bluetooth.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>

---
 drivers/hid/hid-sony.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index cee752f..b40a2e6 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -44,8 +44,10 @@
 #define DUALSHOCK4_CONTROLLER_USB BIT(5)
 #define DUALSHOCK4_CONTROLLER_BT  BIT(6)
 
-#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | DUALSHOCK4_CONTROLLER_USB)
-#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT | DUALSHOCK4_CONTROLLER_USB)
+#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | \
+			  DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_CONTROLLER_BT)
+#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT | \
+			      DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_CONTROLLER_BT)
 
 #define MAX_LEDS 4
 
@@ -939,8 +941,9 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
 		swap(rd[47], rd[48]);
 
 		sixaxis_parse_report(sc, rd, size);
-	} else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
-			size == 64) {
+	} else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
+			size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT)
+			&& rd[0] == 0x11 && size == 78)) {
 		dualshock4_parse_report(sc, rd, size);
 	}
 
@@ -1079,7 +1082,8 @@ static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count)
 	if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) {
 		buzz_set_leds(hdev, leds);
 	} else if ((drv_data->quirks & SIXAXIS_CONTROLLER_USB) ||
-		   (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB)) {
+		   (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB) ||
+		   (drv_data->quirks & DUALSHOCK4_CONTROLLER_BT)) {
 		for (n = 0; n < count; n++)
 			drv_data->led_state[n] = leds[n];
 		schedule_work(&drv_data->state_worker);
@@ -1184,7 +1188,8 @@ static int sony_leds_init(struct hid_device *hdev)
 		/* Validate expected report characteristics. */
 		if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
 			return -ENODEV;
-	} else if (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB) {
+	} else if ((drv_data->quirks & DUALSHOCK4_CONTROLLER_USB) ||
+		   (drv_data->quirks & DUALSHOCK4_CONTROLLER_BT)) {
 		drv_data->led_count = 3;
 		max_brightness = 255;
 		use_colors = 1;
@@ -1509,7 +1514,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	}
 	else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
 		ret = sixaxis_set_operational_bt(hdev);
-	else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
+	else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) ||
+		 (sc->quirks & DUALSHOCK4_CONTROLLER_BT)) {
 		/* The Dualshock 4 touchpad supports 2 touches and has a
 		 * resolution of 1920x940.
 		 */
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 7/7] HID: sony: Prevent devices from being connected twice
  2014-01-29 17:33 [PATCH 0/7] HID: sony: Add full support for the Dualshock 4 on Bluetooth Frank Praznik
                   ` (5 preceding siblings ...)
  2014-01-29 17:33 ` [PATCH 6/7] HID: sony: Add conditionals to enable LED, rumble, battery and touchpad support on the Dualshock 4 Frank Praznik
@ 2014-01-29 17:33 ` Frank Praznik
  2014-01-29 17:42   ` David Herrmann
  6 siblings, 1 reply; 11+ messages in thread
From: Frank Praznik @ 2014-01-29 17:33 UTC (permalink / raw)
  To: linux-input; +Cc: jkosina, Frank Praznik

Prevent one controller from being connected twice and showing up as two devices
if a USB cable is plugged into the controller when it is already connected via
Bluetooth.

A global list of connected devices is maintained and newly connected controllers
are checked against this list. If it is found to already be present, the probe
function exits with an return value of EEXIST.

The MAC address of the Dualshock 4 is used as an identifier to track connected
controllers. It is retrieved with feature report 0x81 when connected via USB and
via the UNIQ identifier on a Bluetooth connection.

Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>

---
 drivers/hid/hid-sony.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 105 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index e478265..a24d021 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -710,8 +710,12 @@ static enum power_supply_property sony_battery_props[] = {
 	POWER_SUPPLY_PROP_STATUS,
 };
 
+static spinlock_t sony_dev_list_lock;
+static LIST_HEAD(sony_device_list);
+
 struct sony_sc {
 	spinlock_t lock;
+	struct list_head device_list;
 	struct hid_device *hdev;
 	struct led_classdev *leds[MAX_LEDS];
 	unsigned long quirks;
@@ -723,6 +727,7 @@ struct sony_sc {
 	__u8 right;
 #endif
 
+	__u8 mac_address[6];
 	__u8 cable_state;
 	__u8 battery_charging;
 	__u8 battery_capacity;
@@ -1471,6 +1476,94 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
 	return 0;
 }
 
+/* If a controller is plugged in via USB while already connected via Bluetooth
+ * it will show up as two devices. A global list of connected controllers and
+ * their MAC addresses is maintained to ensure that a device is only connected
+ * once.
+ */
+static int sony_check_add_dev_list(struct sony_sc *sc)
+{
+	struct sony_sc *entry;
+	struct list_head *pos;
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&sony_dev_list_lock, flags);
+
+	list_for_each(pos, &sony_device_list) {
+		entry = list_entry(pos, struct sony_sc, device_list);
+		ret = memcmp(sc->mac_address, entry->mac_address,
+				FIELD_SIZEOF(struct sony_sc, mac_address));
+		if (!ret) {
+			hid_info(sc->hdev, "Controller already connected\n");
+			return -EEXIST;
+		}
+	}
+
+	list_add(&(sc->device_list), &sony_device_list);
+
+	spin_unlock_irqrestore(&sony_dev_list_lock, flags);
+
+	return 0;
+}
+
+static void sony_remove_dev_list(struct sony_sc *sc)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sony_dev_list_lock, flags);
+	list_del(&(sc->device_list));
+	spin_unlock_irqrestore(&sony_dev_list_lock, flags);
+}
+
+static int dualshock4_check_add(struct sony_sc *sc)
+{
+	int ret;
+
+	if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
+		int n;
+		unsigned int mac_addr[6];
+
+		/* HIDP stores the device MAC address in the uniq member */
+		ret = strlen(sc->hdev->uniq);
+		if (ret != 17) {
+			hid_err(sc->hdev, "Malformed controller MAC address\n");
+			return -EINVAL;
+		}
+
+		ret = sscanf(sc->hdev->uniq, "%02x:%02x:%02x:%02x:%02x:%02x",
+			&mac_addr[5], &mac_addr[4], &mac_addr[3], &mac_addr[2],
+			&mac_addr[1], &mac_addr[0]);
+
+		if (ret != 6) {
+			hid_err(sc->hdev, "Error parsing controller MAC address\n");
+			return -EINVAL;
+		}
+
+		for (n = 5; n >= 0; n--)
+			sc->mac_address[n] = (__u8)mac_addr[n];
+	} else {
+		__u8 buf[7];
+
+		/* The MAC address of a DS4 controller connected via USB can be
+		 * retrieved with feature report 0x81.
+		 */
+		ret = sc->hdev->ll_driver->raw_request(sc->hdev, 0x81,
+			buf, sizeof(buf), HID_FEATURE_REPORT,
+			HID_REQ_GET_REPORT);
+
+		if (ret != 7) {
+			hid_err(sc->hdev, "Error retrieving with controller MAC address\n");
+			return ret;
+		}
+
+		memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
+	}
+
+	ret = sony_check_add_dev_list(sc);
+	return ret;
+}
+
 static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
 	int ret;
@@ -1515,8 +1608,13 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
 		ret = sixaxis_set_operational_bt(hdev);
 	else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) ||
-		 (sc->quirks & DUALSHOCK4_CONTROLLER_BT)) {
-		/* The Dualshock 4 touchpad supports 2 touches and has a
+		(sc->quirks & DUALSHOCK4_CONTROLLER_BT)) {
+
+		ret = dualshock4_check_add(sc);
+		if (ret)
+			goto err_stop;
+
+		 /* The Dualshock 4 touchpad supports 2 touches and has a
 		 * resolution of 1920x940.
 		 */
 		ret = sony_register_touchpad(sc, 2, 1920, 940);
@@ -1580,6 +1678,11 @@ static void sony_remove(struct hid_device *hdev)
 
 	sony_destroy_ff(hdev);
 
+	if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) ||
+	    (sc->quirks & DUALSHOCK4_CONTROLLER_BT)) {
+		sony_remove_dev_list(sc);
+	}
+
 	hid_hw_stop(hdev);
 }
 
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/7] HID: sony: Change dualshock4_state_worker to use the low-level transport driver function
  2014-01-29 17:33 ` [PATCH 1/7] HID: sony: Change dualshock4_state_worker to use the low-level transport driver function Frank Praznik
@ 2014-01-29 17:39   ` David Herrmann
  0 siblings, 0 replies; 11+ messages in thread
From: David Herrmann @ 2014-01-29 17:39 UTC (permalink / raw)
  To: Frank Praznik; +Cc: open list:HID CORE LAYER, Jiri Kosina

Hi

On Wed, Jan 29, 2014 at 6:33 PM, Frank Praznik <frank.praznik@oh.rr.com> wrote:
> Use the new low-level transport driver functions to send the output reports to
> the controller.
>
> Remove sony_set_output_report since it is no longer used.
>
> Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
>
> ---
>  drivers/hid/hid-sony.c | 56 +++++++++++++-------------------------------------
>  1 file changed, 14 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
> index 2bd3f13..a3cefdec 100644
> --- a/drivers/hid/hid-sony.c
> +++ b/drivers/hid/hid-sony.c
> @@ -502,7 +502,6 @@ struct sony_sc {
>         spinlock_t lock;
>         struct hid_device *hdev;
>         struct led_classdev *leds[MAX_LEDS];
> -       struct hid_report *output_report;
>         unsigned long quirks;
>         struct work_struct state_worker;
>         struct power_supply battery;
> @@ -1053,21 +1052,26 @@ static void dualshock4_state_worker(struct work_struct *work)
>  {
>         struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
>         struct hid_device *hdev = sc->hdev;
> -       struct hid_report *report = sc->output_report;
> -       __s32 *value = report->field[0]->value;
> +       int offset;
> +
> +       __u8 buf[32] = { 0 };
>
> -       value[0] = 0x03;
> +       buf[0] = 0x05;
> +       buf[1] = 0x03;
> +       offset = 4;
>
>  #ifdef CONFIG_SONY_FF
> -       value[3] = sc->right;
> -       value[4] = sc->left;
> +       buf[offset++] = sc->right;
> +       buf[offset++] = sc->left;
> +#else
> +       offset += 2;
>  #endif
>
> -       value[5] = sc->led_state[0];
> -       value[6] = sc->led_state[1];
> -       value[7] = sc->led_state[2];
> +       buf[offset++] = sc->led_state[0];
> +       buf[offset++] = sc->led_state[1];
> +       buf[offset++] = sc->led_state[2];
>
> -       hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
> +       hdev->ll_driver->output_report(hdev, buf, sizeof(buf));

You should check that "output_report" is non-NULL. Feel free to do
that in ->probe(). Not all transport-drivers currently implement it
and we allow user-space to rebind drivers via sysfs.

Thanks
David

>  }
>
>  #ifdef CONFIG_SONY_FF
> @@ -1200,33 +1204,6 @@ static void sony_battery_remove(struct sony_sc *sc)
>         sc->battery.name = NULL;
>  }
>
> -static int sony_set_output_report(struct sony_sc *sc, int req_id, int req_size)
> -{
> -       struct list_head *head, *list;
> -       struct hid_report *report;
> -       struct hid_device *hdev = sc->hdev;
> -
> -       list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
> -
> -       list_for_each(head, list) {
> -               report = list_entry(head, struct hid_report, list);
> -
> -               if (report->id == req_id) {
> -                       if (report->size < req_size) {
> -                               hid_err(hdev, "Output report 0x%02x (%i bits) is smaller than requested size (%i bits)\n",
> -                                       req_id, report->size, req_size);
> -                               return -EINVAL;
> -                       }
> -                       sc->output_report = report;
> -                       return 0;
> -               }
> -       }
> -
> -       hid_err(hdev, "Unable to locate output report 0x%02x\n", req_id);
> -
> -       return -EINVAL;
> -}
> -
>  static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
>                                         int w, int h)
>  {
> @@ -1291,11 +1268,6 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
>         else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
>                 ret = sixaxis_set_operational_bt(hdev);
>         else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
> -               /* Report 5 (31 bytes) is used to send data to the controller via USB */
> -               ret = sony_set_output_report(sc, 0x05, 248);
> -               if (ret < 0)
> -                       goto err_stop;
> -
>                 /* The Dualshock 4 touchpad supports 2 touches and has a
>                  * resolution of 1920x940.
>                  */
> --
> 1.8.5.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 7/7] HID: sony: Prevent devices from being connected twice
  2014-01-29 17:33 ` [PATCH 7/7] HID: sony: Prevent devices from being connected twice Frank Praznik
@ 2014-01-29 17:42   ` David Herrmann
  0 siblings, 0 replies; 11+ messages in thread
From: David Herrmann @ 2014-01-29 17:42 UTC (permalink / raw)
  To: Frank Praznik; +Cc: open list:HID CORE LAYER, Jiri Kosina

Hi

On Wed, Jan 29, 2014 at 6:33 PM, Frank Praznik <frank.praznik@oh.rr.com> wrote:
> Prevent one controller from being connected twice and showing up as two devices
> if a USB cable is plugged into the controller when it is already connected via
> Bluetooth.
>
> A global list of connected devices is maintained and newly connected controllers
> are checked against this list. If it is found to already be present, the probe
> function exits with an return value of EEXIST.
>
> The MAC address of the Dualshock 4 is used as an identifier to track connected
> controllers. It is retrieved with feature report 0x81 when connected via USB and
> via the UNIQ identifier on a Bluetooth connection.
>
> Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
>
> ---
>  drivers/hid/hid-sony.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 105 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
> index e478265..a24d021 100644
> --- a/drivers/hid/hid-sony.c
> +++ b/drivers/hid/hid-sony.c
> @@ -710,8 +710,12 @@ static enum power_supply_property sony_battery_props[] = {
>         POWER_SUPPLY_PROP_STATUS,
>  };
>
> +static spinlock_t sony_dev_list_lock;
> +static LIST_HEAD(sony_device_list);
> +
>  struct sony_sc {
>         spinlock_t lock;
> +       struct list_head device_list;
>         struct hid_device *hdev;
>         struct led_classdev *leds[MAX_LEDS];
>         unsigned long quirks;
> @@ -723,6 +727,7 @@ struct sony_sc {
>         __u8 right;
>  #endif
>
> +       __u8 mac_address[6];
>         __u8 cable_state;
>         __u8 battery_charging;
>         __u8 battery_capacity;
> @@ -1471,6 +1476,94 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
>         return 0;
>  }
>
> +/* If a controller is plugged in via USB while already connected via Bluetooth
> + * it will show up as two devices. A global list of connected controllers and
> + * their MAC addresses is maintained to ensure that a device is only connected
> + * once.
> + */
> +static int sony_check_add_dev_list(struct sony_sc *sc)
> +{
> +       struct sony_sc *entry;
> +       struct list_head *pos;
> +       unsigned long flags;
> +       int ret;
> +
> +       spin_lock_irqsave(&sony_dev_list_lock, flags);
> +
> +       list_for_each(pos, &sony_device_list) {
> +               entry = list_entry(pos, struct sony_sc, device_list);
> +               ret = memcmp(sc->mac_address, entry->mac_address,
> +                               FIELD_SIZEOF(struct sony_sc, mac_address));
> +               if (!ret) {
> +                       hid_info(sc->hdev, "Controller already connected\n");
> +                       return -EEXIST;
> +               }
> +       }
> +
> +       list_add(&(sc->device_list), &sony_device_list);
> +
> +       spin_unlock_irqrestore(&sony_dev_list_lock, flags);
> +
> +       return 0;
> +}
> +
> +static void sony_remove_dev_list(struct sony_sc *sc)
> +{
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&sony_dev_list_lock, flags);
> +       list_del(&(sc->device_list));
> +       spin_unlock_irqrestore(&sony_dev_list_lock, flags);
> +}
> +
> +static int dualshock4_check_add(struct sony_sc *sc)
> +{
> +       int ret;
> +
> +       if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
> +               int n;
> +               unsigned int mac_addr[6];
> +
> +               /* HIDP stores the device MAC address in the uniq member */
> +               ret = strlen(sc->hdev->uniq);
> +               if (ret != 17) {
> +                       hid_err(sc->hdev, "Malformed controller MAC address\n");
> +                       return -EINVAL;
> +               }
> +
> +               ret = sscanf(sc->hdev->uniq, "%02x:%02x:%02x:%02x:%02x:%02x",
> +                       &mac_addr[5], &mac_addr[4], &mac_addr[3], &mac_addr[2],
> +                       &mac_addr[1], &mac_addr[0]);

Don't do this. UNIQ is a way to provide unique identifiers for
devices, but it's not guaranteed to stay the same.
Is it not possible to use the same FEATURE_REPORT as with USB?

Thanks
David

> +
> +               if (ret != 6) {
> +                       hid_err(sc->hdev, "Error parsing controller MAC address\n");
> +                       return -EINVAL;
> +               }
> +
> +               for (n = 5; n >= 0; n--)
> +                       sc->mac_address[n] = (__u8)mac_addr[n];
> +       } else {
> +               __u8 buf[7];
> +
> +               /* The MAC address of a DS4 controller connected via USB can be
> +                * retrieved with feature report 0x81.
> +                */
> +               ret = sc->hdev->ll_driver->raw_request(sc->hdev, 0x81,
> +                       buf, sizeof(buf), HID_FEATURE_REPORT,
> +                       HID_REQ_GET_REPORT);
> +
> +               if (ret != 7) {
> +                       hid_err(sc->hdev, "Error retrieving with controller MAC address\n");
> +                       return ret;
> +               }
> +
> +               memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
> +       }
> +
> +       ret = sony_check_add_dev_list(sc);
> +       return ret;
> +}
> +
>  static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
>  {
>         int ret;
> @@ -1515,8 +1608,13 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
>         else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
>                 ret = sixaxis_set_operational_bt(hdev);
>         else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) ||
> -                (sc->quirks & DUALSHOCK4_CONTROLLER_BT)) {
> -               /* The Dualshock 4 touchpad supports 2 touches and has a
> +               (sc->quirks & DUALSHOCK4_CONTROLLER_BT)) {
> +
> +               ret = dualshock4_check_add(sc);
> +               if (ret)
> +                       goto err_stop;
> +
> +                /* The Dualshock 4 touchpad supports 2 touches and has a
>                  * resolution of 1920x940.
>                  */
>                 ret = sony_register_touchpad(sc, 2, 1920, 940);
> @@ -1580,6 +1678,11 @@ static void sony_remove(struct hid_device *hdev)
>
>         sony_destroy_ff(hdev);
>
> +       if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) ||
> +           (sc->quirks & DUALSHOCK4_CONTROLLER_BT)) {
> +               sony_remove_dev_list(sc);
> +       }
> +
>         hid_hw_stop(hdev);
>  }
>
> --
> 1.8.5.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 4/7] HID: sony: Add offsets and battery calculations for parsing Dualshock 4 reports sent via Bluetooth.
  2014-01-29 17:33 ` [PATCH 4/7] HID: sony: Add offsets and battery calculations for parsing Dualshock 4 reports sent via Bluetooth Frank Praznik
@ 2014-01-29 21:36   ` simon
  0 siblings, 0 replies; 11+ messages in thread
From: simon @ 2014-01-29 21:36 UTC (permalink / raw)
  Cc: linux-input, jkosina, Frank Praznik


> When the controller isn't connected to a power source and the battery is
> discharging, the battery level is reported from 0 to 9 instead of 1 to 11.

> +	battery_capacity = rd[offset] & 0x0F;

>  	if (battery_capacity > 10)
>  		battery_capacity--;

Hi Frank,
You might want to change this to
--
  	if (battery_capacity > 10)
  		battery_capacity = 10;
--

As the report could return values of 12..15, it's just that you haven't
seen them yet.

It might be that they're just giving a rough estimation based on cell
voltage, which might change over temp/life-time... this would also explain
the slightly lower value when the battery is under load.

It would be nice if they had another bit indicating charging, rather than
relying on the (assumed) voltage measurement. But it is what it is.

Cheers,
Simon.




^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2014-01-29 21:36 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-01-29 17:33 [PATCH 0/7] HID: sony: Add full support for the Dualshock 4 on Bluetooth Frank Praznik
2014-01-29 17:33 ` [PATCH 1/7] HID: sony: Change dualshock4_state_worker to use the low-level transport driver function Frank Praznik
2014-01-29 17:39   ` David Herrmann
2014-01-29 17:33 ` [PATCH 2/7] HID: sony: Add Bluetooth HID descriptor for Dualshock 4 Frank Praznik
2014-01-29 17:33 ` [PATCH 3/7] HID: sony: Add Bluetooth output report formatting for the " Frank Praznik
2014-01-29 17:33 ` [PATCH 4/7] HID: sony: Add offsets and battery calculations for parsing Dualshock 4 reports sent via Bluetooth Frank Praznik
2014-01-29 21:36   ` simon
2014-01-29 17:33 ` [PATCH 5/7] HID: sony: Set inital battery level to 100% to avoid false low battery warnings Frank Praznik
2014-01-29 17:33 ` [PATCH 6/7] HID: sony: Add conditionals to enable LED, rumble, battery and touchpad support on the Dualshock 4 Frank Praznik
2014-01-29 17:33 ` [PATCH 7/7] HID: sony: Prevent devices from being connected twice Frank Praznik
2014-01-29 17:42   ` David Herrmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).