mirror of
https://github.com/torvalds/linux.git
synced 2024-11-01 01:31:44 +00:00
HID: sony: Add LED controls for the Dualshock 4
Add LED lightbar controls for the Dualshock 4. The Dualshock 4 light bar has 3 separate RGB LEDs that can range in brightness from 0 to 255 so a full byte is now needed to store each LED's state Changed the module to support an arbitrary number of LEDs instead of being hardcoded to 4. Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
parent
0bd88dd3dd
commit
60781cf487
@ -40,7 +40,9 @@
|
||||
#define PS3REMOTE BIT(4)
|
||||
#define DUALSHOCK4_CONTROLLER BIT(5)
|
||||
|
||||
#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER)
|
||||
#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | DUALSHOCK4_CONTROLLER)
|
||||
|
||||
#define MAX_LEDS 4
|
||||
|
||||
static const u8 sixaxis_rdesc_fixup[] = {
|
||||
0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C,
|
||||
@ -227,7 +229,7 @@ static const unsigned int buzz_keymap[] = {
|
||||
|
||||
struct sony_sc {
|
||||
struct hid_device *hdev;
|
||||
struct led_classdev *leds[4];
|
||||
struct led_classdev *leds[MAX_LEDS];
|
||||
unsigned long quirks;
|
||||
struct work_struct state_worker;
|
||||
|
||||
@ -236,7 +238,8 @@ struct sony_sc {
|
||||
__u8 right;
|
||||
#endif
|
||||
|
||||
__u8 led_state;
|
||||
__u8 led_state[MAX_LEDS];
|
||||
__u8 led_count;
|
||||
};
|
||||
|
||||
static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
@ -447,7 +450,7 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev)
|
||||
return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
|
||||
}
|
||||
|
||||
static void buzz_set_leds(struct hid_device *hdev, int leds)
|
||||
static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
|
||||
{
|
||||
struct list_head *report_list =
|
||||
&hdev->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
@ -456,23 +459,28 @@ static void buzz_set_leds(struct hid_device *hdev, int leds)
|
||||
__s32 *value = report->field[0]->value;
|
||||
|
||||
value[0] = 0x00;
|
||||
value[1] = (leds & 1) ? 0xff : 0x00;
|
||||
value[2] = (leds & 2) ? 0xff : 0x00;
|
||||
value[3] = (leds & 4) ? 0xff : 0x00;
|
||||
value[4] = (leds & 8) ? 0xff : 0x00;
|
||||
value[1] = leds[0] ? 0xff : 0x00;
|
||||
value[2] = leds[1] ? 0xff : 0x00;
|
||||
value[3] = leds[2] ? 0xff : 0x00;
|
||||
value[4] = leds[3] ? 0xff : 0x00;
|
||||
value[5] = 0x00;
|
||||
value[6] = 0x00;
|
||||
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
|
||||
}
|
||||
|
||||
static void sony_set_leds(struct hid_device *hdev, __u8 leds)
|
||||
static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count)
|
||||
{
|
||||
struct sony_sc *drv_data = hid_get_drvdata(hdev);
|
||||
int n;
|
||||
|
||||
if (drv_data->quirks & BUZZ_CONTROLLER) {
|
||||
BUG_ON(count > MAX_LEDS);
|
||||
|
||||
if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) {
|
||||
buzz_set_leds(hdev, leds);
|
||||
} else if (drv_data->quirks & SIXAXIS_CONTROLLER_USB) {
|
||||
drv_data->led_state = leds;
|
||||
} else if ((drv_data->quirks & SIXAXIS_CONTROLLER_USB) ||
|
||||
(drv_data->quirks & DUALSHOCK4_CONTROLLER)) {
|
||||
for (n = 0; n < count; n++)
|
||||
drv_data->led_state[n] = leds[n];
|
||||
schedule_work(&drv_data->state_worker);
|
||||
}
|
||||
}
|
||||
@ -492,15 +500,11 @@ static void sony_led_set_brightness(struct led_classdev *led,
|
||||
return;
|
||||
}
|
||||
|
||||
for (n = 0; n < 4; n++) {
|
||||
for (n = 0; n < drv_data->led_count; n++) {
|
||||
if (led == drv_data->leds[n]) {
|
||||
int on = !!(drv_data->led_state & (1 << n));
|
||||
if (value == LED_OFF && on) {
|
||||
drv_data->led_state &= ~(1 << n);
|
||||
sony_set_leds(hdev, drv_data->led_state);
|
||||
} else if (value != LED_OFF && !on) {
|
||||
drv_data->led_state |= (1 << n);
|
||||
sony_set_leds(hdev, drv_data->led_state);
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -522,9 +526,9 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
|
||||
return LED_OFF;
|
||||
}
|
||||
|
||||
for (n = 0; n < 4; n++) {
|
||||
for (n = 0; n < drv_data->led_count; n++) {
|
||||
if (led == drv_data->leds[n]) {
|
||||
on = !!(drv_data->led_state & (1 << n));
|
||||
on = !!(drv_data->led_state[n]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -541,7 +545,7 @@ static void sony_leds_remove(struct hid_device *hdev)
|
||||
drv_data = hid_get_drvdata(hdev);
|
||||
BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
|
||||
|
||||
for (n = 0; n < 4; n++) {
|
||||
for (n = 0; n < drv_data->led_count; n++) {
|
||||
led = drv_data->leds[n];
|
||||
drv_data->leds[n] = NULL;
|
||||
if (!led)
|
||||
@ -549,17 +553,21 @@ static void sony_leds_remove(struct hid_device *hdev)
|
||||
led_classdev_unregister(led);
|
||||
kfree(led);
|
||||
}
|
||||
|
||||
drv_data->led_count = 0;
|
||||
}
|
||||
|
||||
static int sony_leds_init(struct hid_device *hdev)
|
||||
{
|
||||
struct sony_sc *drv_data;
|
||||
int n, ret = 0;
|
||||
int max_brightness;
|
||||
struct led_classdev *led;
|
||||
size_t name_sz;
|
||||
char *name;
|
||||
size_t name_len;
|
||||
const char *name_fmt;
|
||||
static const __u8 initial_values[MAX_LEDS] = { 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
drv_data = hid_get_drvdata(hdev);
|
||||
BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
|
||||
@ -575,14 +583,22 @@ static int sony_leds_init(struct hid_device *hdev)
|
||||
name_fmt = "%s::sony%d";
|
||||
}
|
||||
|
||||
if (drv_data->quirks & DUALSHOCK4_CONTROLLER) {
|
||||
drv_data->led_count = 3;
|
||||
max_brightness = 255;
|
||||
} else {
|
||||
drv_data->led_count = 4;
|
||||
max_brightness = 1;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
sony_set_leds(hdev, 0x00);
|
||||
sony_set_leds(hdev, initial_values, drv_data->led_count);
|
||||
|
||||
name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
|
||||
|
||||
for (n = 0; n < 4; n++) {
|
||||
for (n = 0; n < drv_data->led_count; n++) {
|
||||
led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL);
|
||||
if (!led) {
|
||||
hid_err(hdev, "Couldn't allocate memory for LED %d\n", n);
|
||||
@ -594,7 +610,7 @@ static int sony_leds_init(struct hid_device *hdev)
|
||||
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
|
||||
led->name = name;
|
||||
led->brightness = 0;
|
||||
led->max_brightness = 1;
|
||||
led->max_brightness = max_brightness;
|
||||
led->brightness_get = sony_led_get_brightness;
|
||||
led->brightness_set = sony_led_set_brightness;
|
||||
|
||||
@ -635,7 +651,10 @@ static void sony_state_worker(struct work_struct *work)
|
||||
buf[5] = sc->left;
|
||||
#endif
|
||||
|
||||
buf[10] |= (sc->led_state & 0xf) << 1;
|
||||
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;
|
||||
|
||||
sc->hdev->hid_output_raw_report(sc->hdev, buf, sizeof(buf),
|
||||
HID_OUTPUT_REPORT);
|
||||
@ -660,6 +679,10 @@ static void dualshock4_state_worker(struct work_struct *work)
|
||||
buf[5] = sc->left;
|
||||
#endif
|
||||
|
||||
buf[6] = sc->led_state[0];
|
||||
buf[7] = sc->led_state[1];
|
||||
buf[8] = sc->led_state[2];
|
||||
|
||||
sc->hdev->hid_output_raw_report(sc->hdev, buf, sizeof(buf),
|
||||
HID_OUTPUT_REPORT);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user