summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/hid/hid-ids.h2
-rw-r--r--drivers/hid/hid-lg-g15.c126
2 files changed, 124 insertions, 4 deletions
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index df37a0fd4efa..75d8cf7c29bc 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -749,6 +749,8 @@
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219
#define USB_DEVICE_ID_LOGITECH_G15_LCD 0xc222
#define USB_DEVICE_ID_LOGITECH_G15_V2_LCD 0xc227
+#define USB_DEVICE_ID_LOGITECH_G510 0xc22d
+#define USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO 0xc22e
#define USB_DEVICE_ID_LOGITECH_G29_WHEEL 0xc24f
#define USB_DEVICE_ID_LOGITECH_G920_WHEEL 0xc262
#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283
diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c
index ef06a85de60b..b0891503fbcf 100644
--- a/drivers/hid/hid-lg-g15.c
+++ b/drivers/hid/hid-lg-g15.c
@@ -22,6 +22,8 @@
enum lg_g15_model {
LG_G15,
LG_G15_V2,
+ LG_G510,
+ LG_G510_USB_AUDIO,
};
enum lg_g15_led_type {
@@ -51,12 +53,16 @@ struct lg_g15_data {
struct hid_device *hdev;
enum lg_g15_model model;
struct lg_g15_led leds[LG_G15_LED_MAX];
+ bool game_mode_enabled;
};
static int lg_g15_update_led_brightness(struct lg_g15_data *g15)
{
int ret;
+ if (g15->model == LG_G510 || g15->model == LG_G510_USB_AUDIO)
+ return 0;
+
ret = hid_hw_raw_request(g15->hdev, LG_G15_FEATURE_REPORT,
g15->transfer_buf, 4,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
@@ -256,16 +262,73 @@ static int lg_g15_v2_event(struct lg_g15_data *g15, u8 *data, int size)
return 0;
}
+static int lg_g510_event(struct lg_g15_data *g15, u8 *data, int size)
+{
+ bool game_mode_enabled;
+ int i, val;
+
+ /* G1 - G18 */
+ for (i = 0; i < 18; i++) {
+ val = data[i / 8 + 1] & (1 << (i % 8));
+ input_report_key(g15->input, KEY_MACRO1 + i, val);
+ }
+
+ /* Game mode on/off slider */
+ game_mode_enabled = data[3] & 0x04;
+ if (game_mode_enabled != g15->game_mode_enabled) {
+ if (game_mode_enabled)
+ hid_info(g15->hdev, "Game Mode enabled, Windows (super) key is disabled\n");
+ else
+ hid_info(g15->hdev, "Game Mode disabled\n");
+ g15->game_mode_enabled = game_mode_enabled;
+ }
+
+ /* M1 - M3 */
+ for (i = 0; i < 3; i++) {
+ val = data[3] & (0x10 << i);
+ input_report_key(g15->input, KEY_MACRO_PRESET1 + i, val);
+ }
+ /* MR */
+ input_report_key(g15->input, KEY_MACRO_RECORD_START, data[3] & 0x80);
+
+ /* LCD menu keys */
+ for (i = 0; i < 5; i++) {
+ val = data[4] & (1 << i);
+ input_report_key(g15->input, KEY_KBD_LCD_MENU1 + i, val);
+ }
+
+ /* Headphone Mute */
+ input_report_key(g15->input, KEY_MUTE, data[4] & 0x20);
+ /* Microphone Mute */
+ input_report_key(g15->input, KEY_F20, data[4] & 0x40);
+
+ input_sync(g15->input);
+ return 0;
+}
+
static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
struct lg_g15_data *g15 = hid_get_drvdata(hdev);
- if (g15->model == LG_G15 && data[0] == 0x02 && size == 9)
- return lg_g15_event(g15, data, size);
+ if (!g15)
+ return 0;
- if (g15->model == LG_G15_V2 && data[0] == 0x02 && size == 5)
- return lg_g15_v2_event(g15, data, size);
+ switch (g15->model) {
+ case LG_G15:
+ if (data[0] == 0x02 && size == 9)
+ return lg_g15_event(g15, data, size);
+ break;
+ case LG_G15_V2:
+ if (data[0] == 0x02 && size == 5)
+ return lg_g15_v2_event(g15, data, size);
+ break;
+ case LG_G510:
+ case LG_G510_USB_AUDIO:
+ if (data[0] == 0x03 && size == 5)
+ return lg_g510_event(g15, data, size);
+ break;
+ }
return 0;
}
@@ -312,15 +375,33 @@ static int lg_g15_register_led(struct lg_g15_data *g15, int i)
static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
u8 gkeys_settings_output_report = 0;
+ u8 gkeys_settings_feature_report = 0;
+ struct hid_report_enum *rep_enum;
unsigned int connect_mask = 0;
+ bool has_ff000000 = false;
struct lg_g15_data *g15;
struct input_dev *input;
+ struct hid_report *rep;
int ret, i, gkeys = 0;
+ hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
+
ret = hid_parse(hdev);
if (ret)
return ret;
+ /*
+ * Some models have multiple interfaces, we want the interface with
+ * with the f000.0000 application input report.
+ */
+ rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
+ list_for_each_entry(rep, &rep_enum->report_list, list) {
+ if (rep->application == 0xff000000)
+ has_ff000000 = true;
+ }
+ if (!has_ff000000)
+ return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+
g15 = devm_kzalloc(&hdev->dev, sizeof(*g15), GFP_KERNEL);
if (!g15)
return -ENOMEM;
@@ -353,6 +434,12 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
gkeys_settings_output_report = 0x02;
gkeys = 6;
break;
+ case LG_G510:
+ case LG_G510_USB_AUDIO:
+ connect_mask = HID_CONNECT_HIDINPUT | HID_CONNECT_HIDRAW;
+ gkeys_settings_feature_report = 0x01;
+ gkeys = 18;
+ break;
}
ret = hid_hw_start(hdev, connect_mask);
@@ -374,6 +461,15 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
hid_hw_close(hdev);
}
+ if (gkeys_settings_feature_report) {
+ g15->transfer_buf[0] = gkeys_settings_feature_report;
+ memset(g15->transfer_buf + 1, 0, gkeys);
+ ret = hid_hw_raw_request(g15->hdev,
+ gkeys_settings_feature_report,
+ g15->transfer_buf, gkeys + 1,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ }
+
if (ret < 0) {
hid_err(hdev, "Error disabling keyboard emulation for the G-keys\n");
goto error_hw_stop;
@@ -409,6 +505,17 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
for (i = 0; i < 5; i++)
input_set_capability(input, EV_KEY, KEY_KBD_LCD_MENU1 + i);
+ /*
+ * On the G510 only report headphone and mic mute keys when *not* using
+ * the builtin USB audio device. When the builtin audio is used these
+ * keys directly toggle mute (and the LEDs) on/off.
+ */
+ if (g15->model == LG_G510) {
+ input_set_capability(input, EV_KEY, KEY_MUTE);
+ /* Userspace expects F20 for micmute */
+ input_set_capability(input, EV_KEY, KEY_F20);
+ }
+
g15->input = input;
input_set_drvdata(input, hdev);
@@ -416,6 +523,9 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret)
goto error_hw_stop;
+ if (g15->model == LG_G510 || g15->model == LG_G510_USB_AUDIO)
+ return 0;
+
/* Register LED devices */
for (i = 0; i < LG_G15_LED_MAX; i++) {
ret = lg_g15_register_led(g15, i);
@@ -437,6 +547,14 @@ static const struct hid_device_id lg_g15_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_G15_V2_LCD),
.driver_data = LG_G15_V2 },
+ /* G510 without a headset plugged in */
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_G510),
+ .driver_data = LG_G510 },
+ /* G510 with headset plugged in / with extra USB audio interface */
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO),
+ .driver_data = LG_G510_USB_AUDIO },
{ }
};
MODULE_DEVICE_TABLE(hid, lg_g15_devices);