From d902f4724ccd0c6b8f2b0a7d22af428c637b6dda Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Mon, 27 Jan 2014 10:17:36 -0500 Subject: HID: sony: add battery status reporting for the Sixaxis and Dualshock 4 Add battery status reporting for the Sixaxis and Dualshock 4 controllers. Signed-off-by: Frank Praznik Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 185 insertions(+), 1 deletion(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 12354055d47..04fd611d309 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include "hid-ids.h" @@ -42,6 +44,7 @@ #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 MAX_LEDS 4 @@ -487,18 +490,30 @@ static const unsigned int buzz_keymap[] = { [20] = BTN_TRIGGER_HAPPY20, }; +static enum power_supply_property sony_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_STATUS, +}; + 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; #ifdef CONFIG_SONY_FF __u8 left; __u8 right; #endif + __u8 cable_state; + __u8 battery_charging; + __u8 battery_capacity; __u8 led_state[MAX_LEDS]; __u8 led_count; }; @@ -599,6 +614,63 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, return rdesc; } +static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size) +{ + static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 }; + unsigned long flags; + __u8 cable_state, battery_capacity, battery_charging; + + /* The sixaxis is charging if the battery value is 0xee + * and it is fully charged if the value is 0xef. + * It does not report the actual level while charging so it + * is set to 100% while charging is in progress. + */ + if (rd[30] >= 0xee) { + battery_capacity = 100; + battery_charging = rd[30] & 0x01; + } else { + battery_capacity = sixaxis_battery_capacity[rd[30]]; + battery_charging = 0; + } + cable_state = (rd[31] >> 4) & 0x01; + + spin_lock_irqsave(&sc->lock, flags); + sc->cable_state = cable_state; + sc->battery_capacity = battery_capacity; + sc->battery_charging = battery_charging; + spin_unlock_irqrestore(&sc->lock, flags); +} + +static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size) +{ + unsigned long flags; + __u8 cable_state, battery_capacity, battery_charging; + + /* 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; + + /* On USB the Dualshock 4 battery level goes from 0 to 11. + * A battery level of 11 means fully charged. + */ + if (cable_state && battery_capacity == 11) + battery_charging = 0; + else + battery_charging = 1; + + if (battery_capacity > 10) + battery_capacity--; + battery_capacity *= 10; + + spin_lock_irqsave(&sc->lock, flags); + sc->cable_state = cable_state; + sc->battery_capacity = battery_capacity; + sc->battery_charging = battery_charging; + spin_unlock_irqrestore(&sc->lock, flags); +} + static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, __u8 *rd, int size) { @@ -613,6 +685,11 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, swap(rd[43], rd[44]); swap(rd[45], rd[46]); swap(rd[47], rd[48]); + + sixaxis_parse_report(sc, rd, size); + } else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 && + size == 64) { + dualshock4_parse_report(sc, rd, size); } return 0; @@ -1011,6 +1088,91 @@ static void sony_destroy_ff(struct hid_device *hdev) } #endif +static int sony_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sony_sc *sc = container_of(psy, struct sony_sc, battery); + unsigned long flags; + int ret = 0; + u8 battery_charging, battery_capacity, cable_state; + + spin_lock_irqsave(&sc->lock, flags); + battery_charging = sc->battery_charging; + battery_capacity = sc->battery_capacity; + cable_state = sc->cable_state; + spin_unlock_irqrestore(&sc->lock, flags); + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = battery_capacity; + break; + case POWER_SUPPLY_PROP_STATUS: + if (battery_charging) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + if (battery_capacity == 100 && cable_state) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int sony_battery_probe(struct sony_sc *sc) +{ + static atomic_t power_id_seq = ATOMIC_INIT(0); + unsigned long power_id; + struct hid_device *hdev = sc->hdev; + int ret; + + power_id = (unsigned long)atomic_inc_return(&power_id_seq); + + sc->battery.properties = sony_battery_props; + sc->battery.num_properties = ARRAY_SIZE(sony_battery_props); + sc->battery.get_property = sony_battery_get_property; + sc->battery.type = POWER_SUPPLY_TYPE_BATTERY; + sc->battery.use_for_apm = 0; + sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%lu", + power_id); + if (!sc->battery.name) + return -ENOMEM; + + ret = power_supply_register(&hdev->dev, &sc->battery); + if (ret) { + hid_err(hdev, "Unable to register battery device\n"); + goto err_free; + } + + power_supply_powers(&sc->battery, &hdev->dev); + return 0; + +err_free: + kfree(sc->battery.name); + sc->battery.name = NULL; + return ret; +} + +static void sony_battery_remove(struct sony_sc *sc) +{ + if (!sc->battery.name) + return; + + power_supply_unregister(&sc->battery); + kfree(sc->battery.name); + 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; @@ -1101,14 +1263,31 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_stop; } + if (sc->quirks & SONY_BATTERY_SUPPORT) { + ret = sony_battery_probe(sc); + if (ret < 0) + goto err_stop; + + /* Open the device to receive reports with battery info */ + ret = hid_hw_open(hdev); + if (ret < 0) { + hid_err(hdev, "hw open failed\n"); + goto err_stop; + } + } + ret = sony_init_ff(hdev); if (ret < 0) - goto err_stop; + goto err_close; return 0; +err_close: + hid_hw_close(hdev); err_stop: if (sc->quirks & SONY_LED_SUPPORT) sony_leds_remove(hdev); + if (sc->quirks & SONY_BATTERY_SUPPORT) + sony_battery_remove(sc); hid_hw_stop(hdev); return ret; } @@ -1120,6 +1299,11 @@ static void sony_remove(struct hid_device *hdev) if (sc->quirks & SONY_LED_SUPPORT) sony_leds_remove(hdev); + if (sc->quirks & SONY_BATTERY_SUPPORT) { + hid_hw_close(hdev); + sony_battery_remove(sc); + } + sony_destroy_ff(hdev); hid_hw_stop(hdev); -- cgit v1.2.3-18-g5258 From e560623050693d2550d0bfb3b092e6398249176e Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Mon, 27 Jan 2014 10:17:37 -0500 Subject: HID: sony: add output events for the multi-touch pad on the Dualshock 4 Add output events for the multi-touch pad on the Dualshock 4. The touchpad has a resolution of 1920x940 and is capable of 2 simultaneous touches. A 'Type B' stateful slot protocol is implemented as defined in Documentation/input/multi-touch-protocol.txt Applications can use the touchpad data by processing the ABS_MT_SLOT, ABS_MT_TRACKING_ID, ABS_MT_POSITION_X and ABS_MT_POSITION_Y events. Signed-off-by: Frank Praznik Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 04fd611d309..2bd3f130be2 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "hid-ids.h" @@ -643,7 +644,11 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size) static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size) { + struct hid_input *hidinput = list_entry(sc->hdev->inputs.next, + struct hid_input, list); + struct input_dev *input_dev = hidinput->input; unsigned long flags; + int n, offset = 35; __u8 cable_state, battery_capacity, battery_charging; /* The lower 4 bits of byte 30 contain the battery level @@ -669,6 +674,28 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size) sc->battery_capacity = battery_capacity; sc->battery_charging = battery_charging; spin_unlock_irqrestore(&sc->lock, flags); + + /* The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB. + * 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. + * The data for the second touch is in the same format and immediatly + * follows the data for the first. + */ + for (n = 0; n < 2; n++) { + __u16 x, y; + + x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8); + y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4); + + input_mt_slot(input_dev, n); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, + !(rd[offset] >> 7)); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + + offset += 4; + } } static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, @@ -1200,6 +1227,26 @@ static int sony_set_output_report(struct sony_sc *sc, int req_id, int req_size) return -EINVAL; } +static int sony_register_touchpad(struct sony_sc *sc, int touch_count, + int w, int h) +{ + struct hid_input *hidinput = list_entry(sc->hdev->inputs.next, + struct hid_input, list); + struct input_dev *input_dev = hidinput->input; + int ret; + + ret = input_mt_init_slots(input_dev, touch_count, 0); + if (ret < 0) { + hid_err(sc->hdev, "Unable to initialize multi-touch slots\n"); + return ret; + } + + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, w, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, h, 0, 0); + + return 0; +} + static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; @@ -1249,6 +1296,13 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) if (ret < 0) goto err_stop; + /* The Dualshock 4 touchpad supports 2 touches and has a + * resolution of 1920x940. + */ + ret = sony_register_touchpad(sc, 2, 1920, 940); + if (ret < 0) + goto err_stop; + INIT_WORK(&sc->state_worker, dualshock4_state_worker); } else { ret = 0; -- cgit v1.2.3-18-g5258 From 695baaa7e20dca5826eea52cf49ffe66a353628a Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 3 Feb 2014 11:17:25 +0100 Subject: HID: sony: fix build wrt. power_supply Sony driver now makes use of power supply support. Make kernel config aware of it. Reported-by: fengguang.wu@intel.com Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hid') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index f7220011a00..f5da18bd91c 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -612,6 +612,7 @@ config HID_SONY depends on USB_HID depends on NEW_LEDS depends on LEDS_CLASS + select POWER_SUPPLY ---help--- Support for -- cgit v1.2.3-18-g5258 From e4321c5cebbee9fc4988223af9aa1298d939d388 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Sat, 1 Feb 2014 10:39:58 -0500 Subject: HID: Kconfig updates for the Sony module Update the HID_SONY config with 'select POWER_SUPPLY' to fix build issues relating to undefined references to the power_supply_* functions in certain build configurations. Update the description text to reflect that Playstation 4 controllers are now supported. [jkosina@suse.cz: drop the POWER_SUPPLY hunk, as I've already fixed that] Signed-off-by: Frank Praznik Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index f5da18bd91c..0c3de7ae694 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -608,7 +608,7 @@ config HID_SAMSUNG Support for Samsung InfraRed remote control or keyboards. config HID_SONY - tristate "Sony PS2/3 accessories" + tristate "Sony PS2/3/4 accessories" depends on USB_HID depends on NEW_LEDS depends on LEDS_CLASS @@ -617,17 +617,18 @@ config HID_SONY Support for * Sony PS3 6-axis controllers + * Sony PS4 DualShock 4 controllers * Buzz controllers * Sony PS3 Blue-ray Disk Remote Control (Bluetooth) * Logitech Harmony adapter for Sony Playstation 3 (Bluetooth) config SONY_FF - bool "Sony PS2/3 accessories force feedback support" + bool "Sony PS2/3/4 accessories force feedback support" depends on HID_SONY select INPUT_FF_MEMLESS ---help--- - Say Y here if you have a Sony PS2/3 accessory and want to enable force - feedback support for it. + Say Y here if you have a Sony PS2/3/4 accessory and want to enable + force feedback support for it. config HID_SPEEDLINK tristate "Speedlink VAD Cezanne mouse support" -- cgit v1.2.3-18-g5258 From 7db7504a49b378529793ca9d331318567c496cfe Mon Sep 17 00:00:00 2001 From: Simon Wood Date: Wed, 5 Feb 2014 12:34:18 -0700 Subject: HID: hid-sony: report actual brightness value when reading LED The Dualshock4 controller contains a RGB LED, which is enabled via the '/sys/class/leds' interface. At present the driver only returns whether each of the RGB LEDs is lit (ie not off), but no indication of it's brightness. This patch fixes the reading of the current brightnes so that it returns the value (rather than just off=0, on=LED_FULL). Tested on the DS4 and SixAxis (for compatibility). Signed-off-by: Simon Wood Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 2bd3f130be2..be0d3861159 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -894,7 +894,6 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led) struct sony_sc *drv_data; int n; - int on = 0; drv_data = hid_get_drvdata(hdev); if (!drv_data) { @@ -903,13 +902,11 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led) } for (n = 0; n < drv_data->led_count; n++) { - if (led == drv_data->leds[n]) { - on = !!(drv_data->led_state[n]); - break; - } + if (led == drv_data->leds[n]) + return drv_data->led_state[n]; } - return on ? LED_FULL : LED_OFF; + return LED_OFF; } static void sony_leds_remove(struct hid_device *hdev) -- cgit v1.2.3-18-g5258 From 0e40d35637d68f654b66f4562c9a914be7d06bd1 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:17 -0500 Subject: HID: logitech-dj: remove hidinput_input_event hid-logitech-dj uses its own ->hidinput_input_event() instead of the generic binding in hid-input. Moving the handling of LEDs towards logi_dj_output_hidraw_report() allows two things: - remove hidinput_input_event in struct hid_device - hidraw user space programs can also set the LEDs Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 106 +++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 64 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index f45279c3b11..980ede54782 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -44,14 +44,6 @@ static const char kbd_descriptor[] = { 0x19, 0xE0, /* USAGE_MINIMUM (Left Control) */ 0x29, 0xE7, /* USAGE_MAXIMUM (Right GUI) */ 0x81, 0x02, /* INPUT (Data,Var,Abs) */ - 0x95, 0x05, /* REPORT COUNT (5) */ - 0x05, 0x08, /* USAGE PAGE (LED page) */ - 0x19, 0x01, /* USAGE MINIMUM (1) */ - 0x29, 0x05, /* USAGE MAXIMUM (5) */ - 0x91, 0x02, /* OUTPUT (Data, Variable, Absolute) */ - 0x95, 0x01, /* REPORT COUNT (1) */ - 0x75, 0x03, /* REPORT SIZE (3) */ - 0x91, 0x01, /* OUTPUT (Constant) */ 0x95, 0x06, /* REPORT_COUNT (6) */ 0x75, 0x08, /* REPORT_SIZE (8) */ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ @@ -60,6 +52,18 @@ static const char kbd_descriptor[] = { 0x19, 0x00, /* USAGE_MINIMUM (no event) */ 0x2A, 0xFF, 0x00, /* USAGE_MAXIMUM (reserved) */ 0x81, 0x00, /* INPUT (Data,Ary,Abs) */ + 0x85, 0x0e, /* REPORT_ID (14) */ + 0x05, 0x08, /* USAGE PAGE (LED page) */ + 0x95, 0x05, /* REPORT COUNT (5) */ + 0x75, 0x01, /* REPORT SIZE (1) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x19, 0x01, /* USAGE MINIMUM (1) */ + 0x29, 0x05, /* USAGE MAXIMUM (5) */ + 0x91, 0x02, /* OUTPUT (Data, Variable, Absolute) */ + 0x95, 0x01, /* REPORT COUNT (1) */ + 0x75, 0x03, /* REPORT SIZE (3) */ + 0x91, 0x01, /* OUTPUT (Constant) */ 0xC0 }; @@ -544,10 +548,37 @@ static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, size_t count, unsigned char report_type) { - /* Called by hid raw to send data */ - dbg_hid("%s\n", __func__); + struct dj_device *djdev = hid->driver_data; + struct dj_receiver_dev *djrcv_dev = djdev->dj_receiver_dev; + u8 *out_buf; + int ret; - return 0; + if (buf[0] != REPORT_TYPE_LEDS) + return -EINVAL; + + out_buf = kzalloc(DJREPORT_SHORT_LENGTH, GFP_ATOMIC); + if (!out_buf) + return -ENOMEM; + + if (count < DJREPORT_SHORT_LENGTH - 2) + count = DJREPORT_SHORT_LENGTH - 2; + + out_buf[0] = REPORT_ID_DJ_SHORT; + out_buf[1] = djdev->device_index; + memcpy(out_buf + 2, buf, count); + + /* + * hid-generic calls us with hid_output_raw_report(), but the LEDs + * are set through a SET_REPORT command. It works for USB-HID devices + * because usbhid either calls a SET_REPORT or directly send the output + * report depending if the device presents an urbout. + * Let be simple, send a SET_REPORT request. + */ + ret = hid_hw_raw_request(djrcv_dev->hdev, out_buf[0], out_buf, + DJREPORT_SHORT_LENGTH, report_type, HID_REQ_SET_REPORT); + + kfree(out_buf); + return ret; } static void rdcat(char *rdesc, unsigned int *rsize, const char *data, unsigned int size) @@ -613,58 +644,6 @@ static int logi_dj_ll_parse(struct hid_device *hid) return retval; } -static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, - unsigned int code, int value) -{ - /* Sent by the input layer to handle leds and Force Feedback */ - struct hid_device *dj_hiddev = input_get_drvdata(dev); - struct dj_device *dj_dev = dj_hiddev->driver_data; - - struct dj_receiver_dev *djrcv_dev = - dev_get_drvdata(dj_hiddev->dev.parent); - struct hid_device *dj_rcv_hiddev = djrcv_dev->hdev; - struct hid_report_enum *output_report_enum; - - struct hid_field *field; - struct hid_report *report; - unsigned char *data; - int offset; - - dbg_hid("%s: %s, type:%d | code:%d | value:%d\n", - __func__, dev->phys, type, code, value); - - if (type != EV_LED) - return -1; - - offset = hidinput_find_field(dj_hiddev, type, code, &field); - - if (offset == -1) { - dev_warn(&dev->dev, "event field not found\n"); - return -1; - } - hid_set_field(field, offset, value); - - data = hid_alloc_report_buf(field->report, GFP_ATOMIC); - if (!data) { - dev_warn(&dev->dev, "failed to allocate report buf memory\n"); - return -1; - } - - hid_output_report(field->report, &data[0]); - - output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT]; - report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; - hid_set_field(report->field[0], 0, dj_dev->device_index); - hid_set_field(report->field[0], 1, REPORT_TYPE_LEDS); - hid_set_field(report->field[0], 2, data[1]); - - hid_hw_request(dj_rcv_hiddev, report, HID_REQ_SET_REPORT); - - kfree(data); - - return 0; -} - static int logi_dj_ll_start(struct hid_device *hid) { dbg_hid("%s\n", __func__); @@ -683,7 +662,6 @@ static struct hid_ll_driver logi_dj_ll_driver = { .stop = logi_dj_ll_stop, .open = logi_dj_ll_open, .close = logi_dj_ll_close, - .hidinput_input_event = logi_dj_ll_input_event, }; -- cgit v1.2.3-18-g5258 From b40272e4d0e6d07a0bf9409e5f95d622422cd73d Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:19 -0500 Subject: HID: remove hidinput_input_event handler All the different transport drivers use now the generic event handling in hid-input. We can remove the handler definitively now. Reviewed-by: David Herrmann Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index d50e7313b17..e5bb3c37829 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1263,9 +1263,7 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid) } input_set_drvdata(input_dev, hid); - if (hid->ll_driver->hidinput_input_event) - input_dev->event = hid->ll_driver->hidinput_input_event; - else if (hid->ll_driver->request || hid->hid_output_raw_report) + if (hid->ll_driver->request || hid->hid_output_raw_report) input_dev->event = hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; -- cgit v1.2.3-18-g5258 From f9bcca405624c7f4a0cf4a1b78f8b3a312ca4dab Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:21 -0500 Subject: HID: usbhid: remove duplicated code Well, no use to keep twice the same code. Reviewed-by: David Herrmann Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 64 ++++++++----------------------------------- 1 file changed, 11 insertions(+), 53 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index f8ca312bae1..406497b120e 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -915,59 +915,6 @@ static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum, return ret; } - -static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, - unsigned char report_type) -{ - struct usbhid_device *usbhid = hid->driver_data; - struct usb_device *dev = hid_to_usb_dev(hid); - struct usb_interface *intf = usbhid->intf; - struct usb_host_interface *interface = intf->cur_altsetting; - int ret; - - if (usbhid->urbout && report_type != HID_FEATURE_REPORT) { - int actual_length; - int skipped_report_id = 0; - - if (buf[0] == 0x0) { - /* Don't send the Report ID */ - buf++; - count--; - skipped_report_id = 1; - } - ret = usb_interrupt_msg(dev, usbhid->urbout->pipe, - buf, count, &actual_length, - USB_CTRL_SET_TIMEOUT); - /* return the number of bytes transferred */ - if (ret == 0) { - ret = actual_length; - /* count also the report id */ - if (skipped_report_id) - ret++; - } - } else { - int skipped_report_id = 0; - int report_id = buf[0]; - if (buf[0] == 0x0) { - /* Don't send the Report ID */ - buf++; - count--; - skipped_report_id = 1; - } - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - HID_REQ_SET_REPORT, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - ((report_type + 1) << 8) | report_id, - interface->desc.bInterfaceNumber, buf, count, - USB_CTRL_SET_TIMEOUT); - /* count also the report id, if this was a numbered report. */ - if (ret > 0 && skipped_report_id) - ret++; - } - - return ret; -} - static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count) { struct usbhid_device *usbhid = hid->driver_data; @@ -998,6 +945,17 @@ static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count) return ret; } +static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, + size_t count, unsigned char report_type) +{ + struct usbhid_device *usbhid = hid->driver_data; + + if (usbhid->urbout && report_type != HID_FEATURE_REPORT) + return usbhid_output_report(hid, buf, count); + + return usbhid_set_raw_report(hid, buf[0], buf, count, report_type); +} + static void usbhid_restart_queues(struct usbhid_device *usbhid) { if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl)) -- cgit v1.2.3-18-g5258 From cafebc058bf86e63fff5354864781d3de11e41d3 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:22 -0500 Subject: HID: remove hid_get_raw_report in struct hid_device dev->hid_get_raw_report(X) and hid_hw_raw_request(X, HID_REQ_GET_REPORT) are strictly equivalent. Switch the hid subsystem to the hid_hw notation and remove the field .hid_get_raw_report in struct hid_device. Reviewed-by: David Herrmann Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 6 +++--- drivers/hid/hid-sony.c | 3 ++- drivers/hid/hidraw.c | 7 ++++--- drivers/hid/i2c-hid/i2c-hid.c | 1 - drivers/hid/uhid.c | 1 - drivers/hid/usbhid/hid-core.c | 1 - 6 files changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index e5bb3c37829..5bd17b25685 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -350,9 +350,9 @@ static int hidinput_get_battery_property(struct power_supply *psy, ret = -ENOMEM; break; } - ret = dev->hid_get_raw_report(dev, dev->battery_report_id, - buf, 2, - dev->battery_report_type); + ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2, + dev->battery_report_type, + HID_REQ_GET_REPORT); if (ret != 2) { ret = -ENODATA; diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 12354055d47..3930acbdee9 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -706,7 +706,8 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev) if (!buf) return -ENOMEM; - ret = hdev->hid_get_raw_report(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); if (ret < 0) hid_err(hdev, "can't set operational mode\n"); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index cb0137b3718..4b2dc956c70 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -189,7 +189,7 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t dev = hidraw_table[minor]->hid; - if (!dev->hid_get_raw_report) { + if (!dev->ll_driver->raw_request) { ret = -ENODEV; goto out; } @@ -216,14 +216,15 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t /* * Read the first byte from the user. This is the report number, - * which is passed to dev->hid_get_raw_report(). + * which is passed to hid_hw_raw_request(). */ if (copy_from_user(&report_number, buffer, 1)) { ret = -EFAULT; goto out_free; } - ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type); + ret = hid_hw_raw_request(dev, report_number, buf, count, report_type, + HID_REQ_GET_REPORT); if (ret < 0) goto out_free; diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index e914f275549..f4ea7343e82 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -1005,7 +1005,6 @@ static int i2c_hid_probe(struct i2c_client *client, hid->driver_data = client; hid->ll_driver = &i2c_hid_ll_driver; - hid->hid_get_raw_report = i2c_hid_get_raw_report; hid->hid_output_raw_report = i2c_hid_output_raw_report; hid->dev.parent = &client->dev; ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev)); diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index f5a2b193114..12439e1eeae 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -404,7 +404,6 @@ static int uhid_dev_create(struct uhid_device *uhid, hid->uniq[63] = 0; hid->ll_driver = &uhid_hid_driver; - hid->hid_get_raw_report = uhid_hid_get_raw; hid->hid_output_raw_report = uhid_hid_output_raw; hid->bus = ev->u.create.bus; hid->vendor = ev->u.create.vendor; diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 406497b120e..b9a770f4d7a 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1289,7 +1289,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * usb_set_intfdata(intf, hid); hid->ll_driver = &usb_hid_driver; - hid->hid_get_raw_report = usbhid_get_raw_report; hid->hid_output_raw_report = usbhid_output_raw_report; hid->ff_init = hid_pidff_init; #ifdef CONFIG_USB_HIDDEV -- cgit v1.2.3-18-g5258 From 7e845d46b13e7730a3720e978c28117ce422edf9 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:23 -0500 Subject: HID: introduce helper to access hid_output_raw_report() Add a helper to access hdev->hid_output_raw_report(). To convert the drivers, use the following snippets: for i in drivers/hid/*.c do sed -i.bak "s/[^ \t]*->hid_output_raw_report(/hid_output_raw_report(/g" $i done Then manually fix for checkpatch.pl Reviewed-by: David Herrmann Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 2 +- drivers/hid/hid-lg.c | 6 ++++-- drivers/hid/hid-magicmouse.c | 2 +- drivers/hid/hid-sony.c | 6 +++--- drivers/hid/hid-thingm.c | 4 ++-- drivers/hid/hid-wacom.c | 16 +++++++--------- drivers/hid/hid-wiimote-core.c | 2 +- drivers/hid/hidraw.c | 2 +- 8 files changed, 20 insertions(+), 20 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 5bd17b25685..15959fbae26 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1184,7 +1184,7 @@ static void hidinput_led_worker(struct work_struct *work) hid_output_report(report, buf); /* synchronous output report */ - hid->hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT); + hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT); kfree(buf); } diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 9fe9d4ac311..76ed7e512dc 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -692,7 +692,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) { unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); + ret = hid_output_raw_report(hdev, buf, sizeof(buf), + HID_FEATURE_REPORT); if (ret >= 0) { /* insert a little delay of 10 jiffies ~ 40ms */ @@ -704,7 +705,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) buf[1] = 0xB2; get_random_bytes(&buf[2], 2); - ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); + ret = hid_output_raw_report(hdev, buf, sizeof(buf), + HID_FEATURE_REPORT); } } diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 3b43d1cfa93..cb5db3afc69 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -538,7 +538,7 @@ static int magicmouse_probe(struct hid_device *hdev, * but there seems to be no other way of switching the mode. * Thus the super-ugly hacky success check below. */ - ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature), + ret = hid_output_raw_report(hdev, feature, sizeof(feature), HID_FEATURE_REPORT); if (ret != -EIO && ret != sizeof(feature)) { hid_err(hdev, "unable to request touch data (%d)\n", ret); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 3930acbdee9..075089b3723 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -720,7 +720,8 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev) static int sixaxis_set_operational_bt(struct hid_device *hdev) { unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 }; - return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); + return hid_output_raw_report(hdev, buf, sizeof(buf), + HID_FEATURE_REPORT); } static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds) @@ -942,8 +943,7 @@ static void sixaxis_state_worker(struct work_struct *work) buf[10] |= sc->led_state[2] << 3; buf[10] |= sc->led_state[3] << 4; - sc->hdev->hid_output_raw_report(sc->hdev, buf, sizeof(buf), - HID_OUTPUT_REPORT); + hid_output_raw_report(sc->hdev, buf, sizeof(buf), HID_OUTPUT_REPORT); } static void dualshock4_state_worker(struct work_struct *work) diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c index 99342cfa0ea..7dd3197f3b3 100644 --- a/drivers/hid/hid-thingm.c +++ b/drivers/hid/hid-thingm.c @@ -48,8 +48,8 @@ static int blink1_send_command(struct blink1_data *data, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]); - ret = data->hdev->hid_output_raw_report(data->hdev, buf, - BLINK1_CMD_SIZE, HID_FEATURE_REPORT); + ret = hid_output_raw_report(data->hdev, buf, BLINK1_CMD_SIZE, + HID_FEATURE_REPORT); return ret < 0 ? ret : 0; } diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 60c75dcbbdb..c720db912ed 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -128,8 +128,7 @@ static void wacom_set_image(struct hid_device *hdev, const char *image, rep_data[0] = WAC_CMD_ICON_START_STOP; rep_data[1] = 0; - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, - HID_FEATURE_REPORT); + ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); if (ret < 0) goto err; @@ -143,15 +142,14 @@ static void wacom_set_image(struct hid_device *hdev, const char *image, rep_data[j + 3] = p[(i << 6) + j]; rep_data[2] = i; - ret = hdev->hid_output_raw_report(hdev, rep_data, 67, + ret = hid_output_raw_report(hdev, rep_data, 67, HID_FEATURE_REPORT); } rep_data[0] = WAC_CMD_ICON_START_STOP; rep_data[1] = 0; - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, - HID_FEATURE_REPORT); + ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); err: return; @@ -183,7 +181,7 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev, buf[3] = value; /* use fixed brightness for OLEDs */ buf[4] = 0x08; - hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT); + hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT); kfree(buf); } @@ -339,7 +337,7 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) rep_data[0] = 0x03 ; rep_data[1] = 0x00; limit = 3; do { - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, + ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); } while (ret < 0 && limit-- > 0); @@ -352,7 +350,7 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) rep_data[1] = 0x00; limit = 3; do { - ret = hdev->hid_output_raw_report(hdev, + ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); } while (ret < 0 && limit-- > 0); @@ -378,7 +376,7 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) rep_data[0] = 0x03; rep_data[1] = wdata->features; - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, + ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); if (ret >= 0) wdata->high_speed = speed; diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index abb20db2b44..d7dc6c5bc24 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -35,7 +35,7 @@ static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, if (!buf) return -ENOMEM; - ret = hdev->hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT); + ret = hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT); kfree(buf); return ret; diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 4b2dc956c70..f8708c93f85 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -153,7 +153,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, goto out_free; } - ret = dev->hid_output_raw_report(dev, buf, count, report_type); + ret = hid_output_raw_report(dev, buf, count, report_type); out_free: kfree(buf); out: -- cgit v1.2.3-18-g5258 From 48220237bac3555f2dbba37ccd6f0c1644490269 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 5 Feb 2014 20:03:44 -0500 Subject: HID: sony: Use low-level transport driver functions Switch to the low-level transport driver functions. sony_set_output_report is removed since it is no longer used. Signed-off-by: Frank Praznik Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 56 +++++++++++++------------------------------------- 1 file changed, 14 insertions(+), 42 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index e6995bf9f95..f93f3ca2c23 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; @@ -1051,21 +1050,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); + hid_hw_output_report(hdev, buf, sizeof(buf)); } #ifdef CONFIG_SONY_FF @@ -1198,33 +1202,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) { @@ -1289,11 +1266,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. */ -- cgit v1.2.3-18-g5258 From d829674d29d7eb99aeb3ad11eba61d06cda7aff4 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 5 Feb 2014 20:03:45 -0500 Subject: HID: sony: Add modified Dualshock 4 Bluetooth HID descriptor By default, the Dualshock 4 sends controller data via report 1. Once a valid output report 0x11 is received or a feature report of type 0x02 is requested the controller changes from sending data in report 1 to sending data in report 17, which is unmapped in the default descriptor. The mappings have to be moved to report 17 to let the HID driver properly process the incoming reports. Signed-off-by: Frank Praznik Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index f93f3ca2c23..362fb45954e 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 */ -- cgit v1.2.3-18-g5258 From fdcf105d3d96400fc6fb4b66b28fcff46a854326 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 5 Feb 2014 20:03:46 -0500 Subject: HID: sony: Add Dualshock 4 Bluetooth output report formatting Add formating for the Dualshock 4 output report data in Bluetooth mode. In Bluetooth mode the Dualshock 4 wants output reports sent on the control channel. Signed-off-by: Frank Praznik Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 362fb45954e..88401fc5988 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1266,11 +1266,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; @@ -1283,7 +1290,11 @@ static void dualshock4_state_worker(struct work_struct *work) buf[offset++] = sc->led_state[1]; buf[offset++] = sc->led_state[2]; - hid_hw_output_report(hdev, buf, sizeof(buf)); + if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) + hid_hw_output_report(hdev, buf, 32); + else + hid_hw_raw_request(hdev, 0x11, buf, 78, + HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); } #ifdef CONFIG_SONY_FF -- cgit v1.2.3-18-g5258 From 6c5f860d3f658ff502952a0e57b7e40878391dc1 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 5 Feb 2014 20:03:47 -0500 Subject: HID: sony: Add Dualshock 4 Bluetooth battery and touchpad parsing Add Dualshock 4 battery and touchpad parsing for Bluetooth reports. Signed-off-by: Frank Praznik Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 88401fc5988..40dfa4b4252 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 10, and when running on battery power it ranges from 0 to 9. + * A battery level above 10 when plugged in means charge completed. */ - if (cable_state && battery_capacity == 11) + if (!cable_state || battery_capacity > 10) battery_charging = 0; else battery_charging = 1; + if (!cable_state) + battery_capacity++; if (battery_capacity > 10) - battery_capacity--; + battery_capacity = 10; + 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. -- cgit v1.2.3-18-g5258 From d9a293a951d5cc44842acf340af51ace8e0cfdbe Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 5 Feb 2014 20:03:48 -0500 Subject: HID: sony: Set initial battery level to 100% to avoid false low battery warnings Set the initial battery level to 100% to avoid false low battery warnings if the battery state is polled before a report with the actual battery level is received. Signed-off-by: Frank Praznik Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 40dfa4b4252..24ce8cd556e 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1402,6 +1402,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; -- cgit v1.2.3-18-g5258 From 68330d83c0b35120f70c529f6ddd70750081bbb0 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 5 Feb 2014 20:03:49 -0500 Subject: HID: sony: Add conditionals to enable all features in Bluetooth mode Add the conditionals to enable rumble, battery reporting, LED and touchpad support for the Dualshock 4 in Bluetooth mode. Add dualshock4_set_operational_bt to initialize the controller to the proper operational state. Signed-off-by: Frank Praznik Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'drivers/hid') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 24ce8cd556e..947d2088a70 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -44,8 +44,12 @@ #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 DUALSHOCK4_CONTROLLER (DUALSHOCK4_CONTROLLER_USB |\ + DUALSHOCK4_CONTROLLER_BT) +#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER |\ + DUALSHOCK4_CONTROLLER) +#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT |\ + DUALSHOCK4_CONTROLLER) #define MAX_LEDS 4 @@ -939,8 +943,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]