in hid-lg-g15.c [741:918]
static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
static const char * const led_names[] = {
"g15::kbd_backlight",
"g15::lcd_backlight",
"g15::macro_preset1",
"g15::macro_preset2",
"g15::macro_preset3",
"g15::macro_record",
};
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;
mutex_init(&g15->mutex);
input = devm_input_allocate_device(&hdev->dev);
if (!input)
return -ENOMEM;
g15->hdev = hdev;
g15->model = id->driver_data;
g15->input = input;
input_set_drvdata(input, hdev);
hid_set_drvdata(hdev, (void *)g15);
switch (g15->model) {
case LG_G15:
INIT_WORK(&g15->work, lg_g15_leds_changed_work);
/*
* The G15 and G15 v2 use a separate usb-device (on a builtin
* hub) which emulates a keyboard for the F1 - F12 emulation
* on the G-keys, which we disable, rendering the emulated kbd
* non-functional, so we do not let hid-input connect.
*/
connect_mask = HID_CONNECT_HIDRAW;
gkeys_settings_output_report = 0x02;
gkeys = 18;
break;
case LG_G15_V2:
INIT_WORK(&g15->work, lg_g15_leds_changed_work);
connect_mask = HID_CONNECT_HIDRAW;
gkeys_settings_output_report = 0x02;
gkeys = 6;
break;
case LG_G510:
case LG_G510_USB_AUDIO:
INIT_WORK(&g15->work, lg_g510_leds_sync_work);
connect_mask = HID_CONNECT_HIDINPUT | HID_CONNECT_HIDRAW;
gkeys_settings_feature_report = 0x01;
gkeys = 18;
break;
case LG_Z10:
connect_mask = HID_CONNECT_HIDRAW;
break;
}
ret = hid_hw_start(hdev, connect_mask);
if (ret)
return ret;
/* Tell the keyboard to stop sending F1-F12 + 1-6 for G1 - G18 */
if (gkeys_settings_output_report) {
g15->transfer_buf[0] = gkeys_settings_output_report;
memset(g15->transfer_buf + 1, 0, gkeys);
/*
* The kbd ignores our output report if we do not queue
* an URB on the USB input endpoint first...
*/
ret = hid_hw_open(hdev);
if (ret)
goto error_hw_stop;
ret = hid_hw_output_report(hdev, g15->transfer_buf, gkeys + 1);
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 %d disabling keyboard emulation for the G-keys, falling back to generic hid-input driver\n",
ret);
hid_set_drvdata(hdev, NULL);
return 0;
}
/* Get initial brightness levels */
ret = lg_g15_get_initial_led_brightness(g15);
if (ret)
goto error_hw_stop;
if (g15->model == LG_Z10) {
lg_g15_init_input_dev(hdev, g15->input, "Logitech Z-10 LCD Menu Keys");
ret = input_register_device(g15->input);
if (ret)
goto error_hw_stop;
ret = lg_g15_register_led(g15, 1, "z-10::lcd_backlight");
if (ret)
goto error_hw_stop;
return 0; /* All done */
}
/* Setup and register input device */
lg_g15_init_input_dev(hdev, input, "Logitech Gaming Keyboard Gaming Keys");
/* G-keys */
for (i = 0; i < gkeys; i++)
input_set_capability(input, EV_KEY, KEY_MACRO1 + i);
/* M1 - M3 and MR keys */
for (i = 0; i < 3; i++)
input_set_capability(input, EV_KEY, KEY_MACRO_PRESET1 + i);
input_set_capability(input, EV_KEY, KEY_MACRO_RECORD_START);
/*
* 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);
}
ret = input_register_device(input);
if (ret)
goto error_hw_stop;
/* Register LED devices */
for (i = 0; i < LG_G15_LED_MAX; i++) {
ret = lg_g15_register_led(g15, i, led_names[i]);
if (ret)
goto error_hw_stop;
}
return 0;
error_hw_stop:
hid_hw_stop(hdev);
return ret;
}