|
@@ -33,6 +33,7 @@
|
|
#include <linux/power_supply.h>
|
|
#include <linux/power_supply.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/list.h>
|
|
#include <linux/list.h>
|
|
|
|
+#include <linux/idr.h>
|
|
#include <linux/input/mt.h>
|
|
#include <linux/input/mt.h>
|
|
|
|
|
|
#include "hid-ids.h"
|
|
#include "hid-ids.h"
|
|
@@ -717,8 +718,39 @@ static enum power_supply_property sony_battery_props[] = {
|
|
POWER_SUPPLY_PROP_STATUS,
|
|
POWER_SUPPLY_PROP_STATUS,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+struct sixaxis_led {
|
|
|
|
+ __u8 time_enabled; /* the total time the led is active (0xff means forever) */
|
|
|
|
+ __u8 duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
|
|
|
|
+ __u8 enabled;
|
|
|
|
+ __u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */
|
|
|
|
+ __u8 duty_on; /* % of duty_length the led is on (0xff mean 100%) */
|
|
|
|
+} __packed;
|
|
|
|
+
|
|
|
|
+struct sixaxis_rumble {
|
|
|
|
+ __u8 padding;
|
|
|
|
+ __u8 right_duration; /* Right motor duration (0xff means forever) */
|
|
|
|
+ __u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
|
|
|
|
+ __u8 left_duration; /* Left motor duration (0xff means forever) */
|
|
|
|
+ __u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
|
|
|
|
+} __packed;
|
|
|
|
+
|
|
|
|
+struct sixaxis_output_report {
|
|
|
|
+ __u8 report_id;
|
|
|
|
+ struct sixaxis_rumble rumble;
|
|
|
|
+ __u8 padding[4];
|
|
|
|
+ __u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
|
|
|
|
+ struct sixaxis_led led[4]; /* LEDx at (4 - x) */
|
|
|
|
+ struct sixaxis_led _reserved; /* LED5, not actually soldered */
|
|
|
|
+} __packed;
|
|
|
|
+
|
|
|
|
+union sixaxis_output_report_01 {
|
|
|
|
+ struct sixaxis_output_report data;
|
|
|
|
+ __u8 buf[36];
|
|
|
|
+};
|
|
|
|
+
|
|
static spinlock_t sony_dev_list_lock;
|
|
static spinlock_t sony_dev_list_lock;
|
|
static LIST_HEAD(sony_device_list);
|
|
static LIST_HEAD(sony_device_list);
|
|
|
|
+static DEFINE_IDA(sony_device_id_allocator);
|
|
|
|
|
|
struct sony_sc {
|
|
struct sony_sc {
|
|
spinlock_t lock;
|
|
spinlock_t lock;
|
|
@@ -728,6 +760,7 @@ struct sony_sc {
|
|
unsigned long quirks;
|
|
unsigned long quirks;
|
|
struct work_struct state_worker;
|
|
struct work_struct state_worker;
|
|
struct power_supply battery;
|
|
struct power_supply battery;
|
|
|
|
+ int device_id;
|
|
|
|
|
|
#ifdef CONFIG_SONY_FF
|
|
#ifdef CONFIG_SONY_FF
|
|
__u8 left;
|
|
__u8 left;
|
|
@@ -740,6 +773,8 @@ struct sony_sc {
|
|
__u8 battery_charging;
|
|
__u8 battery_charging;
|
|
__u8 battery_capacity;
|
|
__u8 battery_capacity;
|
|
__u8 led_state[MAX_LEDS];
|
|
__u8 led_state[MAX_LEDS];
|
|
|
|
+ __u8 led_delay_on[MAX_LEDS];
|
|
|
|
+ __u8 led_delay_off[MAX_LEDS];
|
|
__u8 led_count;
|
|
__u8 led_count;
|
|
};
|
|
};
|
|
|
|
|
|
@@ -1048,6 +1083,52 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
|
|
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
|
|
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
|
|
|
|
+{
|
|
|
|
+ static const __u8 sixaxis_leds[10][4] = {
|
|
|
|
+ { 0x01, 0x00, 0x00, 0x00 },
|
|
|
|
+ { 0x00, 0x01, 0x00, 0x00 },
|
|
|
|
+ { 0x00, 0x00, 0x01, 0x00 },
|
|
|
|
+ { 0x00, 0x00, 0x00, 0x01 },
|
|
|
|
+ { 0x01, 0x00, 0x00, 0x01 },
|
|
|
|
+ { 0x00, 0x01, 0x00, 0x01 },
|
|
|
|
+ { 0x00, 0x00, 0x01, 0x01 },
|
|
|
|
+ { 0x01, 0x00, 0x01, 0x01 },
|
|
|
|
+ { 0x00, 0x01, 0x01, 0x01 },
|
|
|
|
+ { 0x01, 0x01, 0x01, 0x01 }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
|
|
|
|
+
|
|
|
|
+ if (id < 0)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ id %= 10;
|
|
|
|
+ memcpy(values, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
|
|
|
|
+{
|
|
|
|
+ /* The first 4 color/index entries match what the PS4 assigns */
|
|
|
|
+ static const __u8 color_code[7][3] = {
|
|
|
|
+ /* Blue */ { 0x00, 0x00, 0x01 },
|
|
|
|
+ /* Red */ { 0x01, 0x00, 0x00 },
|
|
|
|
+ /* Green */ { 0x00, 0x01, 0x00 },
|
|
|
|
+ /* Pink */ { 0x02, 0x00, 0x01 },
|
|
|
|
+ /* Orange */ { 0x02, 0x01, 0x00 },
|
|
|
|
+ /* Teal */ { 0x00, 0x01, 0x01 },
|
|
|
|
+ /* White */ { 0x01, 0x01, 0x01 }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
|
|
|
|
+
|
|
|
|
+ if (id < 0)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ id %= 7;
|
|
|
|
+ memcpy(values, color_code[id], sizeof(color_code[id]));
|
|
|
|
+}
|
|
|
|
+
|
|
static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
|
|
static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
|
|
{
|
|
{
|
|
struct list_head *report_list =
|
|
struct list_head *report_list =
|
|
@@ -1066,19 +1147,18 @@ static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
|
|
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
|
|
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
|
|
}
|
|
}
|
|
|
|
|
|
-static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count)
|
|
|
|
|
|
+static void sony_set_leds(struct sony_sc *sc, const __u8 *leds, int count)
|
|
{
|
|
{
|
|
- struct sony_sc *drv_data = hid_get_drvdata(hdev);
|
|
|
|
int n;
|
|
int n;
|
|
|
|
|
|
BUG_ON(count > MAX_LEDS);
|
|
BUG_ON(count > MAX_LEDS);
|
|
|
|
|
|
- if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) {
|
|
|
|
- buzz_set_leds(hdev, leds);
|
|
|
|
|
|
+ if (sc->quirks & BUZZ_CONTROLLER && count == 4) {
|
|
|
|
+ buzz_set_leds(sc->hdev, leds);
|
|
} else {
|
|
} else {
|
|
for (n = 0; n < count; n++)
|
|
for (n = 0; n < count; n++)
|
|
- drv_data->led_state[n] = leds[n];
|
|
|
|
- schedule_work(&drv_data->state_worker);
|
|
|
|
|
|
+ sc->led_state[n] = leds[n];
|
|
|
|
+ schedule_work(&sc->state_worker);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1090,6 +1170,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
|
|
struct sony_sc *drv_data;
|
|
struct sony_sc *drv_data;
|
|
|
|
|
|
int n;
|
|
int n;
|
|
|
|
+ int force_update;
|
|
|
|
|
|
drv_data = hid_get_drvdata(hdev);
|
|
drv_data = hid_get_drvdata(hdev);
|
|
if (!drv_data) {
|
|
if (!drv_data) {
|
|
@@ -1097,12 +1178,29 @@ static void sony_led_set_brightness(struct led_classdev *led,
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * The Sixaxis on USB will override any LED settings sent to it
|
|
|
|
+ * and keep flashing all of the LEDs until the PS button is pressed.
|
|
|
|
+ * Updates, even if redundant, must be always be sent to the
|
|
|
|
+ * controller to avoid having to toggle the state of an LED just to
|
|
|
|
+ * stop the flashing later on.
|
|
|
|
+ */
|
|
|
|
+ force_update = !!(drv_data->quirks & SIXAXIS_CONTROLLER_USB);
|
|
|
|
+
|
|
for (n = 0; n < drv_data->led_count; n++) {
|
|
for (n = 0; n < drv_data->led_count; n++) {
|
|
- if (led == drv_data->leds[n]) {
|
|
|
|
- if (value != drv_data->led_state[n]) {
|
|
|
|
- drv_data->led_state[n] = value;
|
|
|
|
- sony_set_leds(hdev, drv_data->led_state, drv_data->led_count);
|
|
|
|
- }
|
|
|
|
|
|
+ if (led == drv_data->leds[n] && (force_update ||
|
|
|
|
+ (value != drv_data->led_state[n] ||
|
|
|
|
+ drv_data->led_delay_on[n] ||
|
|
|
|
+ drv_data->led_delay_off[n]))) {
|
|
|
|
+
|
|
|
|
+ drv_data->led_state[n] = value;
|
|
|
|
+
|
|
|
|
+ /* Setting the brightness stops the blinking */
|
|
|
|
+ drv_data->led_delay_on[n] = 0;
|
|
|
|
+ drv_data->led_delay_off[n] = 0;
|
|
|
|
+
|
|
|
|
+ sony_set_leds(drv_data, drv_data->led_state,
|
|
|
|
+ drv_data->led_count);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -1130,63 +1228,112 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
|
|
return LED_OFF;
|
|
return LED_OFF;
|
|
}
|
|
}
|
|
|
|
|
|
-static void sony_leds_remove(struct hid_device *hdev)
|
|
|
|
|
|
+static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
|
|
|
|
+ unsigned long *delay_off)
|
|
{
|
|
{
|
|
- struct sony_sc *drv_data;
|
|
|
|
- struct led_classdev *led;
|
|
|
|
|
|
+ struct device *dev = led->dev->parent;
|
|
|
|
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
|
|
|
|
+ struct sony_sc *drv_data = hid_get_drvdata(hdev);
|
|
int n;
|
|
int n;
|
|
|
|
+ __u8 new_on, new_off;
|
|
|
|
|
|
- drv_data = hid_get_drvdata(hdev);
|
|
|
|
- BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
|
|
|
|
|
|
+ if (!drv_data) {
|
|
|
|
+ hid_err(hdev, "No device data\n");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Max delay is 255 deciseconds or 2550 milliseconds */
|
|
|
|
+ if (*delay_on > 2550)
|
|
|
|
+ *delay_on = 2550;
|
|
|
|
+ if (*delay_off > 2550)
|
|
|
|
+ *delay_off = 2550;
|
|
|
|
+
|
|
|
|
+ /* Blink at 1 Hz if both values are zero */
|
|
|
|
+ if (!*delay_on && !*delay_off)
|
|
|
|
+ *delay_on = *delay_off = 500;
|
|
|
|
+
|
|
|
|
+ new_on = *delay_on / 10;
|
|
|
|
+ new_off = *delay_off / 10;
|
|
|
|
|
|
for (n = 0; n < drv_data->led_count; n++) {
|
|
for (n = 0; n < drv_data->led_count; n++) {
|
|
- led = drv_data->leds[n];
|
|
|
|
- drv_data->leds[n] = NULL;
|
|
|
|
|
|
+ if (led == drv_data->leds[n])
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* This LED is not registered on this device */
|
|
|
|
+ if (n >= drv_data->led_count)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ /* Don't schedule work if the values didn't change */
|
|
|
|
+ if (new_on != drv_data->led_delay_on[n] ||
|
|
|
|
+ new_off != drv_data->led_delay_off[n]) {
|
|
|
|
+ drv_data->led_delay_on[n] = new_on;
|
|
|
|
+ drv_data->led_delay_off[n] = new_off;
|
|
|
|
+ schedule_work(&drv_data->state_worker);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void sony_leds_remove(struct sony_sc *sc)
|
|
|
|
+{
|
|
|
|
+ struct led_classdev *led;
|
|
|
|
+ int n;
|
|
|
|
+
|
|
|
|
+ BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
|
|
|
|
+
|
|
|
|
+ for (n = 0; n < sc->led_count; n++) {
|
|
|
|
+ led = sc->leds[n];
|
|
|
|
+ sc->leds[n] = NULL;
|
|
if (!led)
|
|
if (!led)
|
|
continue;
|
|
continue;
|
|
led_classdev_unregister(led);
|
|
led_classdev_unregister(led);
|
|
kfree(led);
|
|
kfree(led);
|
|
}
|
|
}
|
|
|
|
|
|
- drv_data->led_count = 0;
|
|
|
|
|
|
+ sc->led_count = 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static int sony_leds_init(struct hid_device *hdev)
|
|
|
|
|
|
+static int sony_leds_init(struct sony_sc *sc)
|
|
{
|
|
{
|
|
- struct sony_sc *drv_data;
|
|
|
|
|
|
+ struct hid_device *hdev = sc->hdev;
|
|
int n, ret = 0;
|
|
int n, ret = 0;
|
|
- int max_brightness;
|
|
|
|
- int use_colors;
|
|
|
|
|
|
+ int use_ds4_names;
|
|
struct led_classdev *led;
|
|
struct led_classdev *led;
|
|
size_t name_sz;
|
|
size_t name_sz;
|
|
char *name;
|
|
char *name;
|
|
size_t name_len;
|
|
size_t name_len;
|
|
const char *name_fmt;
|
|
const char *name_fmt;
|
|
- static const char * const color_str[] = { "red", "green", "blue" };
|
|
|
|
- static const __u8 initial_values[MAX_LEDS] = { 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
|
|
+ static const char * const ds4_name_str[] = { "red", "green", "blue",
|
|
|
|
+ "global" };
|
|
|
|
+ __u8 initial_values[MAX_LEDS] = { 0 };
|
|
|
|
+ __u8 max_brightness[MAX_LEDS] = { 1 };
|
|
|
|
+ __u8 use_hw_blink[MAX_LEDS] = { 0 };
|
|
|
|
|
|
- drv_data = hid_get_drvdata(hdev);
|
|
|
|
- BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
|
|
|
|
|
|
+ BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
|
|
|
|
|
|
- if (drv_data->quirks & BUZZ_CONTROLLER) {
|
|
|
|
- drv_data->led_count = 4;
|
|
|
|
- max_brightness = 1;
|
|
|
|
- use_colors = 0;
|
|
|
|
|
|
+ if (sc->quirks & BUZZ_CONTROLLER) {
|
|
|
|
+ sc->led_count = 4;
|
|
|
|
+ use_ds4_names = 0;
|
|
name_len = strlen("::buzz#");
|
|
name_len = strlen("::buzz#");
|
|
name_fmt = "%s::buzz%d";
|
|
name_fmt = "%s::buzz%d";
|
|
/* Validate expected report characteristics. */
|
|
/* Validate expected report characteristics. */
|
|
if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
|
|
if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
- } else if (drv_data->quirks & DUALSHOCK4_CONTROLLER) {
|
|
|
|
- drv_data->led_count = 3;
|
|
|
|
- max_brightness = 255;
|
|
|
|
- use_colors = 1;
|
|
|
|
|
|
+ } else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
|
|
|
|
+ dualshock4_set_leds_from_id(sc->device_id, initial_values);
|
|
|
|
+ initial_values[3] = 1;
|
|
|
|
+ sc->led_count = 4;
|
|
|
|
+ memset(max_brightness, 255, 3);
|
|
|
|
+ use_hw_blink[3] = 1;
|
|
|
|
+ use_ds4_names = 1;
|
|
name_len = 0;
|
|
name_len = 0;
|
|
name_fmt = "%s:%s";
|
|
name_fmt = "%s:%s";
|
|
} else {
|
|
} else {
|
|
- drv_data->led_count = 4;
|
|
|
|
- max_brightness = 1;
|
|
|
|
- use_colors = 0;
|
|
|
|
|
|
+ sixaxis_set_leds_from_id(sc->device_id, initial_values);
|
|
|
|
+ sc->led_count = 4;
|
|
|
|
+ memset(use_hw_blink, 1, 4);
|
|
|
|
+ use_ds4_names = 0;
|
|
name_len = strlen("::sony#");
|
|
name_len = strlen("::sony#");
|
|
name_fmt = "%s::sony%d";
|
|
name_fmt = "%s::sony%d";
|
|
}
|
|
}
|
|
@@ -1196,14 +1343,14 @@ static int sony_leds_init(struct hid_device *hdev)
|
|
* only relevant if the driver is loaded after somebody actively set the
|
|
* only relevant if the driver is loaded after somebody actively set the
|
|
* LEDs to on
|
|
* LEDs to on
|
|
*/
|
|
*/
|
|
- sony_set_leds(hdev, initial_values, drv_data->led_count);
|
|
|
|
|
|
+ sony_set_leds(sc, initial_values, sc->led_count);
|
|
|
|
|
|
name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
|
|
name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
|
|
|
|
|
|
- for (n = 0; n < drv_data->led_count; n++) {
|
|
|
|
|
|
+ for (n = 0; n < sc->led_count; n++) {
|
|
|
|
|
|
- if (use_colors)
|
|
|
|
- name_sz = strlen(dev_name(&hdev->dev)) + strlen(color_str[n]) + 2;
|
|
|
|
|
|
+ if (use_ds4_names)
|
|
|
|
+ name_sz = strlen(dev_name(&hdev->dev)) + strlen(ds4_name_str[n]) + 2;
|
|
|
|
|
|
led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL);
|
|
led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL);
|
|
if (!led) {
|
|
if (!led) {
|
|
@@ -1213,30 +1360,35 @@ static int sony_leds_init(struct hid_device *hdev)
|
|
}
|
|
}
|
|
|
|
|
|
name = (void *)(&led[1]);
|
|
name = (void *)(&led[1]);
|
|
- if (use_colors)
|
|
|
|
- snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), color_str[n]);
|
|
|
|
|
|
+ if (use_ds4_names)
|
|
|
|
+ snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev),
|
|
|
|
+ ds4_name_str[n]);
|
|
else
|
|
else
|
|
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
|
|
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
|
|
led->name = name;
|
|
led->name = name;
|
|
- led->brightness = 0;
|
|
|
|
- led->max_brightness = max_brightness;
|
|
|
|
|
|
+ led->brightness = initial_values[n];
|
|
|
|
+ led->max_brightness = max_brightness[n];
|
|
led->brightness_get = sony_led_get_brightness;
|
|
led->brightness_get = sony_led_get_brightness;
|
|
led->brightness_set = sony_led_set_brightness;
|
|
led->brightness_set = sony_led_set_brightness;
|
|
|
|
|
|
|
|
+ if (use_hw_blink[n])
|
|
|
|
+ led->blink_set = sony_led_blink_set;
|
|
|
|
+
|
|
|
|
+ sc->leds[n] = led;
|
|
|
|
+
|
|
ret = led_classdev_register(&hdev->dev, led);
|
|
ret = led_classdev_register(&hdev->dev, led);
|
|
if (ret) {
|
|
if (ret) {
|
|
hid_err(hdev, "Failed to register LED %d\n", n);
|
|
hid_err(hdev, "Failed to register LED %d\n", n);
|
|
|
|
+ sc->leds[n] = NULL;
|
|
kfree(led);
|
|
kfree(led);
|
|
goto error_leds;
|
|
goto error_leds;
|
|
}
|
|
}
|
|
-
|
|
|
|
- drv_data->leds[n] = led;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
error_leds:
|
|
error_leds:
|
|
- sony_leds_remove(hdev);
|
|
|
|
|
|
+ sony_leds_remove(sc);
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -1244,29 +1396,52 @@ error_leds:
|
|
static void sixaxis_state_worker(struct work_struct *work)
|
|
static void sixaxis_state_worker(struct work_struct *work)
|
|
{
|
|
{
|
|
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
|
|
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
|
|
- unsigned char buf[] = {
|
|
|
|
- 0x01,
|
|
|
|
- 0x00, 0xff, 0x00, 0xff, 0x00,
|
|
|
|
- 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
- 0xff, 0x27, 0x10, 0x00, 0x32,
|
|
|
|
- 0xff, 0x27, 0x10, 0x00, 0x32,
|
|
|
|
- 0xff, 0x27, 0x10, 0x00, 0x32,
|
|
|
|
- 0xff, 0x27, 0x10, 0x00, 0x32,
|
|
|
|
- 0x00, 0x00, 0x00, 0x00, 0x00
|
|
|
|
|
|
+ int n;
|
|
|
|
+ union sixaxis_output_report_01 report = {
|
|
|
|
+ .buf = {
|
|
|
|
+ 0x01,
|
|
|
|
+ 0x00, 0xff, 0x00, 0xff, 0x00,
|
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
+ 0xff, 0x27, 0x10, 0x00, 0x32,
|
|
|
|
+ 0xff, 0x27, 0x10, 0x00, 0x32,
|
|
|
|
+ 0xff, 0x27, 0x10, 0x00, 0x32,
|
|
|
|
+ 0xff, 0x27, 0x10, 0x00, 0x32,
|
|
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00
|
|
|
|
+ }
|
|
};
|
|
};
|
|
|
|
|
|
#ifdef CONFIG_SONY_FF
|
|
#ifdef CONFIG_SONY_FF
|
|
- buf[3] = sc->right ? 1 : 0;
|
|
|
|
- buf[5] = sc->left;
|
|
|
|
|
|
+ report.data.rumble.right_motor_on = sc->right ? 1 : 0;
|
|
|
|
+ report.data.rumble.left_motor_force = sc->left;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
- buf[10] |= sc->led_state[0] << 1;
|
|
|
|
- buf[10] |= sc->led_state[1] << 2;
|
|
|
|
- buf[10] |= sc->led_state[2] << 3;
|
|
|
|
- buf[10] |= sc->led_state[3] << 4;
|
|
|
|
|
|
+ report.data.leds_bitmap |= sc->led_state[0] << 1;
|
|
|
|
+ report.data.leds_bitmap |= sc->led_state[1] << 2;
|
|
|
|
+ report.data.leds_bitmap |= sc->led_state[2] << 3;
|
|
|
|
+ report.data.leds_bitmap |= sc->led_state[3] << 4;
|
|
|
|
+
|
|
|
|
+ /* Set flag for all leds off, required for 3rd party INTEC controller */
|
|
|
|
+ if ((report.data.leds_bitmap & 0x1E) == 0)
|
|
|
|
+ report.data.leds_bitmap |= 0x20;
|
|
|
|
|
|
- hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT,
|
|
|
|
- HID_REQ_SET_REPORT);
|
|
|
|
|
|
+ /*
|
|
|
|
+ * The LEDs in the report are indexed in reverse order to their
|
|
|
|
+ * corresponding light on the controller.
|
|
|
|
+ * Index 0 = LED 4, index 1 = LED 3, etc...
|
|
|
|
+ *
|
|
|
|
+ * In the case of both delay values being zero (blinking disabled) the
|
|
|
|
+ * default report values should be used or the controller LED will be
|
|
|
|
+ * always off.
|
|
|
|
+ */
|
|
|
|
+ for (n = 0; n < 4; n++) {
|
|
|
|
+ if (sc->led_delay_on[n] || sc->led_delay_off[n]) {
|
|
|
|
+ report.data.led[3 - n].duty_off = sc->led_delay_off[n];
|
|
|
|
+ report.data.led[3 - n].duty_on = sc->led_delay_on[n];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf,
|
|
|
|
+ sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
|
|
}
|
|
}
|
|
|
|
|
|
static void dualshock4_state_worker(struct work_struct *work)
|
|
static void dualshock4_state_worker(struct work_struct *work)
|
|
@@ -1279,7 +1454,7 @@ static void dualshock4_state_worker(struct work_struct *work)
|
|
|
|
|
|
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
|
|
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
|
|
buf[0] = 0x05;
|
|
buf[0] = 0x05;
|
|
- buf[1] = 0x03;
|
|
|
|
|
|
+ buf[1] = 0xFF;
|
|
offset = 4;
|
|
offset = 4;
|
|
} else {
|
|
} else {
|
|
buf[0] = 0x11;
|
|
buf[0] = 0x11;
|
|
@@ -1295,9 +1470,18 @@ static void dualshock4_state_worker(struct work_struct *work)
|
|
offset += 2;
|
|
offset += 2;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
- buf[offset++] = sc->led_state[0];
|
|
|
|
- buf[offset++] = sc->led_state[1];
|
|
|
|
- buf[offset++] = sc->led_state[2];
|
|
|
|
|
|
+ /* LED 3 is the global control */
|
|
|
|
+ if (sc->led_state[3]) {
|
|
|
|
+ buf[offset++] = sc->led_state[0];
|
|
|
|
+ buf[offset++] = sc->led_state[1];
|
|
|
|
+ buf[offset++] = sc->led_state[2];
|
|
|
|
+ } else {
|
|
|
|
+ offset += 3;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* If both delay values are zero the DualShock 4 disables blinking. */
|
|
|
|
+ buf[offset++] = sc->led_delay_on[3];
|
|
|
|
+ buf[offset++] = sc->led_delay_off[3];
|
|
|
|
|
|
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
|
|
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
|
|
hid_hw_output_report(hdev, buf, 32);
|
|
hid_hw_output_report(hdev, buf, 32);
|
|
@@ -1323,9 +1507,9 @@ static int sony_play_effect(struct input_dev *dev, void *data,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static int sony_init_ff(struct hid_device *hdev)
|
|
|
|
|
|
+static int sony_init_ff(struct sony_sc *sc)
|
|
{
|
|
{
|
|
- struct hid_input *hidinput = list_entry(hdev->inputs.next,
|
|
|
|
|
|
+ struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
|
|
struct hid_input, list);
|
|
struct hid_input, list);
|
|
struct input_dev *input_dev = hidinput->input;
|
|
struct input_dev *input_dev = hidinput->input;
|
|
|
|
|
|
@@ -1334,7 +1518,7 @@ static int sony_init_ff(struct hid_device *hdev)
|
|
}
|
|
}
|
|
|
|
|
|
#else
|
|
#else
|
|
-static int sony_init_ff(struct hid_device *hdev)
|
|
|
|
|
|
+static int sony_init_ff(struct sony_sc *sc)
|
|
{
|
|
{
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -1384,8 +1568,6 @@ static int sony_battery_get_property(struct power_supply *psy,
|
|
|
|
|
|
static int sony_battery_probe(struct sony_sc *sc)
|
|
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;
|
|
struct hid_device *hdev = sc->hdev;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
@@ -1395,15 +1577,13 @@ static int sony_battery_probe(struct sony_sc *sc)
|
|
*/
|
|
*/
|
|
sc->battery_capacity = 100;
|
|
sc->battery_capacity = 100;
|
|
|
|
|
|
- power_id = (unsigned long)atomic_inc_return(&power_id_seq);
|
|
|
|
-
|
|
|
|
sc->battery.properties = sony_battery_props;
|
|
sc->battery.properties = sony_battery_props;
|
|
sc->battery.num_properties = ARRAY_SIZE(sony_battery_props);
|
|
sc->battery.num_properties = ARRAY_SIZE(sony_battery_props);
|
|
sc->battery.get_property = sony_battery_get_property;
|
|
sc->battery.get_property = sony_battery_get_property;
|
|
sc->battery.type = POWER_SUPPLY_TYPE_BATTERY;
|
|
sc->battery.type = POWER_SUPPLY_TYPE_BATTERY;
|
|
sc->battery.use_for_apm = 0;
|
|
sc->battery.use_for_apm = 0;
|
|
- sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%lu",
|
|
|
|
- power_id);
|
|
|
|
|
|
+ sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%pMR",
|
|
|
|
+ sc->mac_address);
|
|
if (!sc->battery.name)
|
|
if (!sc->battery.name)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
@@ -1578,6 +1758,52 @@ static int sony_check_add(struct sony_sc *sc)
|
|
return sony_check_add_dev_list(sc);
|
|
return sony_check_add_dev_list(sc);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int sony_set_device_id(struct sony_sc *sc)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Only DualShock 4 or Sixaxis controllers get an id.
|
|
|
|
+ * All others are set to -1.
|
|
|
|
+ */
|
|
|
|
+ if ((sc->quirks & SIXAXIS_CONTROLLER) ||
|
|
|
|
+ (sc->quirks & DUALSHOCK4_CONTROLLER)) {
|
|
|
|
+ ret = ida_simple_get(&sony_device_id_allocator, 0, 0,
|
|
|
|
+ GFP_KERNEL);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ sc->device_id = -1;
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+ sc->device_id = ret;
|
|
|
|
+ } else {
|
|
|
|
+ sc->device_id = -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void sony_release_device_id(struct sony_sc *sc)
|
|
|
|
+{
|
|
|
|
+ if (sc->device_id >= 0) {
|
|
|
|
+ ida_simple_remove(&sony_device_id_allocator, sc->device_id);
|
|
|
|
+ sc->device_id = -1;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline void sony_init_work(struct sony_sc *sc,
|
|
|
|
+ void (*worker)(struct work_struct *))
|
|
|
|
+{
|
|
|
|
+ if (!sc->worker_initialized)
|
|
|
|
+ INIT_WORK(&sc->state_worker, worker);
|
|
|
|
+
|
|
|
|
+ sc->worker_initialized = 1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline void sony_cancel_work_sync(struct sony_sc *sc)
|
|
|
|
+{
|
|
|
|
+ if (sc->worker_initialized)
|
|
|
|
+ cancel_work_sync(&sc->state_worker);
|
|
|
|
+}
|
|
|
|
|
|
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
{
|
|
{
|
|
@@ -1615,6 +1841,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ ret = sony_set_device_id(sc);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ hid_err(hdev, "failed to allocate the device id\n");
|
|
|
|
+ goto err_stop;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
|
|
if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
|
|
/*
|
|
/*
|
|
* The Sony Sixaxis does not handle HID Output Reports on the
|
|
* The Sony Sixaxis does not handle HID Output Reports on the
|
|
@@ -1629,8 +1861,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
|
|
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
|
|
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
|
|
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
|
|
ret = sixaxis_set_operational_usb(hdev);
|
|
ret = sixaxis_set_operational_usb(hdev);
|
|
- sc->worker_initialized = 1;
|
|
|
|
- INIT_WORK(&sc->state_worker, sixaxis_state_worker);
|
|
|
|
|
|
+ sony_init_work(sc, sixaxis_state_worker);
|
|
} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
|
|
} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
|
|
/*
|
|
/*
|
|
* The Sixaxis wants output reports sent on the ctrl endpoint
|
|
* The Sixaxis wants output reports sent on the ctrl endpoint
|
|
@@ -1638,8 +1869,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
*/
|
|
*/
|
|
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
|
|
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
|
|
ret = sixaxis_set_operational_bt(hdev);
|
|
ret = sixaxis_set_operational_bt(hdev);
|
|
- sc->worker_initialized = 1;
|
|
|
|
- INIT_WORK(&sc->state_worker, sixaxis_state_worker);
|
|
|
|
|
|
+ sony_init_work(sc, sixaxis_state_worker);
|
|
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
|
|
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
|
|
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
|
|
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
|
|
/*
|
|
/*
|
|
@@ -1661,8 +1891,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
goto err_stop;
|
|
goto err_stop;
|
|
|
|
|
|
- sc->worker_initialized = 1;
|
|
|
|
- INIT_WORK(&sc->state_worker, dualshock4_state_worker);
|
|
|
|
|
|
+ sony_init_work(sc, dualshock4_state_worker);
|
|
} else {
|
|
} else {
|
|
ret = 0;
|
|
ret = 0;
|
|
}
|
|
}
|
|
@@ -1675,7 +1904,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
goto err_stop;
|
|
goto err_stop;
|
|
|
|
|
|
if (sc->quirks & SONY_LED_SUPPORT) {
|
|
if (sc->quirks & SONY_LED_SUPPORT) {
|
|
- ret = sony_leds_init(hdev);
|
|
|
|
|
|
+ ret = sony_leds_init(sc);
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
goto err_stop;
|
|
goto err_stop;
|
|
}
|
|
}
|
|
@@ -1694,7 +1923,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
}
|
|
}
|
|
|
|
|
|
if (sc->quirks & SONY_FF_SUPPORT) {
|
|
if (sc->quirks & SONY_FF_SUPPORT) {
|
|
- ret = sony_init_ff(hdev);
|
|
|
|
|
|
+ ret = sony_init_ff(sc);
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
goto err_close;
|
|
goto err_close;
|
|
}
|
|
}
|
|
@@ -1704,12 +1933,12 @@ err_close:
|
|
hid_hw_close(hdev);
|
|
hid_hw_close(hdev);
|
|
err_stop:
|
|
err_stop:
|
|
if (sc->quirks & SONY_LED_SUPPORT)
|
|
if (sc->quirks & SONY_LED_SUPPORT)
|
|
- sony_leds_remove(hdev);
|
|
|
|
|
|
+ sony_leds_remove(sc);
|
|
if (sc->quirks & SONY_BATTERY_SUPPORT)
|
|
if (sc->quirks & SONY_BATTERY_SUPPORT)
|
|
sony_battery_remove(sc);
|
|
sony_battery_remove(sc);
|
|
- if (sc->worker_initialized)
|
|
|
|
- cancel_work_sync(&sc->state_worker);
|
|
|
|
|
|
+ sony_cancel_work_sync(sc);
|
|
sony_remove_dev_list(sc);
|
|
sony_remove_dev_list(sc);
|
|
|
|
+ sony_release_device_id(sc);
|
|
hid_hw_stop(hdev);
|
|
hid_hw_stop(hdev);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -1719,18 +1948,19 @@ static void sony_remove(struct hid_device *hdev)
|
|
struct sony_sc *sc = hid_get_drvdata(hdev);
|
|
struct sony_sc *sc = hid_get_drvdata(hdev);
|
|
|
|
|
|
if (sc->quirks & SONY_LED_SUPPORT)
|
|
if (sc->quirks & SONY_LED_SUPPORT)
|
|
- sony_leds_remove(hdev);
|
|
|
|
|
|
+ sony_leds_remove(sc);
|
|
|
|
|
|
if (sc->quirks & SONY_BATTERY_SUPPORT) {
|
|
if (sc->quirks & SONY_BATTERY_SUPPORT) {
|
|
hid_hw_close(hdev);
|
|
hid_hw_close(hdev);
|
|
sony_battery_remove(sc);
|
|
sony_battery_remove(sc);
|
|
}
|
|
}
|
|
|
|
|
|
- if (sc->worker_initialized)
|
|
|
|
- cancel_work_sync(&sc->state_worker);
|
|
|
|
|
|
+ sony_cancel_work_sync(sc);
|
|
|
|
|
|
sony_remove_dev_list(sc);
|
|
sony_remove_dev_list(sc);
|
|
|
|
|
|
|
|
+ sony_release_device_id(sc);
|
|
|
|
+
|
|
hid_hw_stop(hdev);
|
|
hid_hw_stop(hdev);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1775,6 +2005,22 @@ static struct hid_driver sony_driver = {
|
|
.report_fixup = sony_report_fixup,
|
|
.report_fixup = sony_report_fixup,
|
|
.raw_event = sony_raw_event
|
|
.raw_event = sony_raw_event
|
|
};
|
|
};
|
|
-module_hid_driver(sony_driver);
|
|
|
|
|
|
+
|
|
|
|
+static int __init sony_init(void)
|
|
|
|
+{
|
|
|
|
+ dbg_hid("Sony:%s\n", __func__);
|
|
|
|
+
|
|
|
|
+ return hid_register_driver(&sony_driver);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void __exit sony_exit(void)
|
|
|
|
+{
|
|
|
|
+ dbg_hid("Sony:%s\n", __func__);
|
|
|
|
+
|
|
|
|
+ ida_destroy(&sony_device_id_allocator);
|
|
|
|
+ hid_unregister_driver(&sony_driver);
|
|
|
|
+}
|
|
|
|
+module_init(sony_init);
|
|
|
|
+module_exit(sony_exit);
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_LICENSE("GPL");
|