static int lg_g15_probe()

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;
}