Merge git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tile
[firefly-linux-kernel-4.4.55.git] / drivers / hid / hid-sony.c
index 908de278921944dfae837284f582e117617c479d..2259eaa8b98886d8ae0ec454a2a0fce8aa99e695 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/power_supply.h>
 #include <linux/spinlock.h>
 #include <linux/list.h>
+#include <linux/idr.h>
 #include <linux/input/mt.h>
 
 #include "hid-ids.h"
@@ -717,8 +718,39 @@ static enum power_supply_property sony_battery_props[] = {
        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 LIST_HEAD(sony_device_list);
+static DEFINE_IDA(sony_device_id_allocator);
 
 struct sony_sc {
        spinlock_t lock;
@@ -728,6 +760,7 @@ struct sony_sc {
        unsigned long quirks;
        struct work_struct state_worker;
        struct power_supply battery;
+       int device_id;
 
 #ifdef CONFIG_SONY_FF
        __u8 left;
@@ -740,6 +773,8 @@ struct sony_sc {
        __u8 battery_charging;
        __u8 battery_capacity;
        __u8 led_state[MAX_LEDS];
+       __u8 led_delay_on[MAX_LEDS];
+       __u8 led_delay_off[MAX_LEDS];
        __u8 led_count;
 };
 
@@ -1048,6 +1083,52 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
                                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)
 {
        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);
 }
 
-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;
 
        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 {
                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;
 
        int n;
+       int force_update;
 
        drv_data = hid_get_drvdata(hdev);
        if (!drv_data) {
@@ -1097,12 +1178,29 @@ static void sony_led_set_brightness(struct led_classdev *led,
                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++) {
-               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;
                }
        }
@@ -1130,63 +1228,112 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
        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;
+       __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++) {
-               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)
                        continue;
                led_classdev_unregister(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 max_brightness;
-       int use_colors;
+       int use_ds4_names;
        struct led_classdev *led;
        size_t name_sz;
        char *name;
        size_t name_len;
        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_fmt = "%s::buzz%d";
                /* Validate expected report characteristics. */
                if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
                        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_fmt = "%s:%s";
        } 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_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
         * 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;
 
-       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);
                if (!led) {
@@ -1213,30 +1360,35 @@ static int sony_leds_init(struct hid_device *hdev)
                }
 
                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
                        snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
                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_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);
                if (ret) {
                        hid_err(hdev, "Failed to register LED %d\n", n);
+                       sc->leds[n] = NULL;
                        kfree(led);
                        goto error_leds;
                }
-
-               drv_data->leds[n] = led;
        }
 
        return ret;
 
 error_leds:
-       sony_leds_remove(hdev);
+       sony_leds_remove(sc);
 
        return ret;
 }
@@ -1244,29 +1396,52 @@ error_leds:
 static void sixaxis_state_worker(struct work_struct *work)
 {
        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
-       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
 
-       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)
@@ -1279,7 +1454,7 @@ static void dualshock4_state_worker(struct work_struct *work)
 
        if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
                buf[0] = 0x05;
-               buf[1] = 0x03;
+               buf[1] = 0xFF;
                offset = 4;
        } else {
                buf[0] = 0x11;
@@ -1295,9 +1470,18 @@ static void dualshock4_state_worker(struct work_struct *work)
        offset += 2;
 #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)
                hid_hw_output_report(hdev, buf, 32);
@@ -1323,9 +1507,9 @@ static int sony_play_effect(struct input_dev *dev, void *data,
        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 input_dev *input_dev = hidinput->input;
 
@@ -1334,7 +1518,7 @@ static int sony_init_ff(struct hid_device *hdev)
 }
 
 #else
-static int sony_init_ff(struct hid_device *hdev)
+static int sony_init_ff(struct sony_sc *sc)
 {
        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 atomic_t power_id_seq = ATOMIC_INIT(0);
-       unsigned long power_id;
        struct hid_device *hdev = sc->hdev;
        int ret;
 
@@ -1395,15 +1577,13 @@ static int sony_battery_probe(struct sony_sc *sc)
         */
        sc->battery_capacity = 100;
 
-       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);
+       sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%pMR",
+                                    sc->mac_address);
        if (!sc->battery.name)
                return -ENOMEM;
 
@@ -1578,6 +1758,52 @@ static int sony_check_add(struct sony_sc *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)
 {
@@ -1615,6 +1841,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                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) {
                /*
                 * 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_SKIP_OUTPUT_REPORT_ID;
                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) {
                /*
                 * 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;
                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) {
                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)
                        goto err_stop;
 
-               sc->worker_initialized = 1;
-               INIT_WORK(&sc->state_worker, dualshock4_state_worker);
+               sony_init_work(sc, dualshock4_state_worker);
        } else {
                ret = 0;
        }
@@ -1675,7 +1904,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                goto err_stop;
 
        if (sc->quirks & SONY_LED_SUPPORT) {
-               ret = sony_leds_init(hdev);
+               ret = sony_leds_init(sc);
                if (ret < 0)
                        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) {
-               ret = sony_init_ff(hdev);
+               ret = sony_init_ff(sc);
                if (ret < 0)
                        goto err_close;
        }
@@ -1704,12 +1933,12 @@ err_close:
        hid_hw_close(hdev);
 err_stop:
        if (sc->quirks & SONY_LED_SUPPORT)
-               sony_leds_remove(hdev);
+               sony_leds_remove(sc);
        if (sc->quirks & SONY_BATTERY_SUPPORT)
                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_release_device_id(sc);
        hid_hw_stop(hdev);
        return ret;
 }
@@ -1719,18 +1948,19 @@ static void sony_remove(struct hid_device *hdev)
        struct sony_sc *sc = hid_get_drvdata(hdev);
 
        if (sc->quirks & SONY_LED_SUPPORT)
-               sony_leds_remove(hdev);
+               sony_leds_remove(sc);
 
        if (sc->quirks & SONY_BATTERY_SUPPORT) {
                hid_hw_close(hdev);
                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_release_device_id(sc);
+
        hid_hw_stop(hdev);
 }
 
@@ -1775,6 +2005,22 @@ static struct hid_driver sony_driver = {
        .report_fixup  = sony_report_fixup,
        .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");