summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/hid/hid-sony.c120
1 files changed, 72 insertions, 48 deletions
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 2f93aab..b60bc389 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -39,6 +39,8 @@
#define BUZZ_CONTROLLER BIT(3)
#define PS3REMOTE BIT(4)
+#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER)
+
static const u8 sixaxis_rdesc_fixup[] = {
0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C,
0x81, 0x01, 0x75, 0x10, 0x95, 0x04, 0x26, 0xFF,
@@ -223,12 +225,12 @@ static const unsigned int buzz_keymap[] = {
};
struct sony_sc {
+ struct hid_device *hdev;
struct led_classdev *leds[4];
unsigned long quirks;
+ struct work_struct state_worker;
#ifdef CONFIG_SONY_FF
- struct work_struct state_worker;
- struct hid_device *hdev;
__u8 left;
__u8 right;
#endif
@@ -462,6 +464,18 @@ static void buzz_set_leds(struct hid_device *hdev, int leds)
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
}
+static void sony_set_leds(struct hid_device *hdev, __u8 leds)
+{
+ struct sony_sc *drv_data = hid_get_drvdata(hdev);
+
+ if (drv_data->quirks & BUZZ_CONTROLLER) {
+ buzz_set_leds(hdev, leds);
+ } else if (drv_data->quirks & SIXAXIS_CONTROLLER_USB) {
+ drv_data->led_state = leds;
+ schedule_work(&drv_data->state_worker);
+ }
+}
+
static void sony_led_set_brightness(struct led_classdev *led,
enum led_brightness value)
{
@@ -482,10 +496,10 @@ static void sony_led_set_brightness(struct led_classdev *led,
int on = !!(drv_data->led_state & (1 << n));
if (value == LED_OFF && on) {
drv_data->led_state &= ~(1 << n);
- buzz_set_leds(hdev, drv_data->led_state);
+ sony_set_leds(hdev, drv_data->led_state);
} else if (value != LED_OFF && !on) {
drv_data->led_state |= (1 << n);
- buzz_set_leds(hdev, drv_data->led_state);
+ sony_set_leds(hdev, drv_data->led_state);
}
break;
}
@@ -517,6 +531,25 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
return on ? LED_FULL : LED_OFF;
}
+static void sony_leds_remove(struct hid_device *hdev)
+{
+ struct sony_sc *drv_data;
+ struct led_classdev *led;
+ int n;
+
+ drv_data = hid_get_drvdata(hdev);
+ BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
+
+ for (n = 0; n < 4; n++) {
+ led = drv_data->leds[n];
+ drv_data->leds[n] = NULL;
+ if (!led)
+ continue;
+ led_classdev_unregister(led);
+ kfree(led);
+ }
+}
+
static int sony_leds_init(struct hid_device *hdev)
{
struct sony_sc *drv_data;
@@ -524,20 +557,29 @@ static int sony_leds_init(struct hid_device *hdev)
struct led_classdev *led;
size_t name_sz;
char *name;
+ size_t name_len;
+ const char *name_fmt;
drv_data = hid_get_drvdata(hdev);
- BUG_ON(!(drv_data->quirks & BUZZ_CONTROLLER));
-
- /* Validate expected report characteristics. */
- if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
- return -ENODEV;
+ BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
+
+ if (drv_data->quirks & BUZZ_CONTROLLER) {
+ 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 {
+ name_len = strlen("::sony#");
+ name_fmt = "%s::sony%d";
+ }
/* Clear LEDs as we have no way of reading their initial state. This is
* only relevant if the driver is loaded after somebody actively set the
* LEDs to on */
- buzz_set_leds(hdev, 0x00);
+ sony_set_leds(hdev, 0x00);
- name_sz = strlen(dev_name(&hdev->dev)) + strlen("::buzz#") + 1;
+ name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
for (n = 0; n < 4; n++) {
led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL);
@@ -547,7 +589,7 @@ static int sony_leds_init(struct hid_device *hdev)
}
name = (void *)(&led[1]);
- snprintf(name, name_sz, "%s::buzz%d", dev_name(&hdev->dev), n + 1);
+ snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
led->name = name;
led->brightness = 0;
led->max_brightness = 1;
@@ -566,45 +608,18 @@ static int sony_leds_init(struct hid_device *hdev)
return ret;
error_leds:
- for (n = 0; n < 4; n++) {
- led = drv_data->leds[n];
- drv_data->leds[n] = NULL;
- if (!led)
- continue;
- led_classdev_unregister(led);
- kfree(led);
- }
+ sony_leds_remove(hdev);
return ret;
}
-static void sony_leds_remove(struct hid_device *hdev)
-{
- struct sony_sc *drv_data;
- struct led_classdev *led;
- int n;
-
- drv_data = hid_get_drvdata(hdev);
- BUG_ON(!(drv_data->quirks & BUZZ_CONTROLLER));
-
- for (n = 0; n < 4; n++) {
- led = drv_data->leds[n];
- drv_data->leds[n] = NULL;
- if (!led)
- continue;
- led_classdev_unregister(led);
- kfree(led);
- }
-}
-
-#ifdef CONFIG_SONY_FF
static void sony_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, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0x27, 0x10, 0x00, 0x32,
0xff, 0x27, 0x10, 0x00, 0x32,
0xff, 0x27, 0x10, 0x00, 0x32,
@@ -612,13 +627,18 @@ static void sony_state_worker(struct work_struct *work)
0x00, 0x00, 0x00, 0x00, 0x00
};
+#ifdef CONFIG_SONY_FF
buf[3] = sc->right;
buf[5] = sc->left;
+#endif
+
+ buf[10] |= (sc->led_state & 0xf) << 1;
sc->hdev->hid_output_raw_report(sc->hdev, buf, sizeof(buf),
HID_OUTPUT_REPORT);
}
+#ifdef CONFIG_SONY_FF
static int sony_play_effect(struct input_dev *dev, void *data,
struct ff_effect *effect)
{
@@ -640,10 +660,6 @@ static int sony_init_ff(struct hid_device *hdev)
struct hid_input *hidinput = list_entry(hdev->inputs.next,
struct hid_input, list);
struct input_dev *input_dev = hidinput->input;
- struct sony_sc *sc = hid_get_drvdata(hdev);
-
- sc->hdev = hdev;
- INIT_WORK(&sc->state_worker, sony_state_worker);
input_set_capability(input_dev, EV_FF, FF_RUMBLE);
return input_ff_create_memless(input_dev, NULL, sony_play_effect);
@@ -682,6 +698,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
sc->quirks = quirks;
hid_set_drvdata(hdev, sc);
+ sc->hdev = hdev;
ret = hid_parse(hdev);
if (ret) {
@@ -705,23 +722,30 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
hdev->hid_output_raw_report = sixaxis_usb_output_raw_report;
ret = sixaxis_set_operational_usb(hdev);
+ INIT_WORK(&sc->state_worker, sony_state_worker);
}
else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
ret = sixaxis_set_operational_bt(hdev);
- else if (sc->quirks & BUZZ_CONTROLLER)
- ret = sony_leds_init(hdev);
else
ret = 0;
if (ret < 0)
goto err_stop;
+ if (sc->quirks & SONY_LED_SUPPORT) {
+ ret = sony_leds_init(hdev);
+ if (ret < 0)
+ goto err_stop;
+ }
+
ret = sony_init_ff(hdev);
if (ret < 0)
goto err_stop;
return 0;
err_stop:
+ if (sc->quirks & SONY_LED_SUPPORT)
+ sony_leds_remove(hdev);
hid_hw_stop(hdev);
return ret;
}
@@ -730,7 +754,7 @@ static void sony_remove(struct hid_device *hdev)
{
struct sony_sc *sc = hid_get_drvdata(hdev);
- if (sc->quirks & BUZZ_CONTROLLER)
+ if (sc->quirks & SONY_LED_SUPPORT)
sony_leds_remove(hdev);
sony_destroy_ff(hdev);
OpenPOWER on IntegriCloud