brintos

brintos / linux-shallow public Read only

0
0
Text · 24.4 KiB · 53e7b90 Raw
959 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 *  HID driver for gaming keys on Logitech gaming keyboards (such as the G15)4 *5 *  Copyright (c) 2019 Hans de Goede <hdegoede@redhat.com>6 */7 8#include <linux/device.h>9#include <linux/hid.h>10#include <linux/leds.h>11#include <linux/module.h>12#include <linux/random.h>13#include <linux/sched.h>14#include <linux/usb.h>15#include <linux/wait.h>16 17#include "hid-ids.h"18 19#define LG_G15_TRANSFER_BUF_SIZE	2020 21#define LG_G15_FEATURE_REPORT		0x0222 23#define LG_G510_FEATURE_M_KEYS_LEDS	0x0424#define LG_G510_FEATURE_BACKLIGHT_RGB	0x0525#define LG_G510_FEATURE_POWER_ON_RGB	0x0626 27enum lg_g15_model {28	LG_G15,29	LG_G15_V2,30	LG_G510,31	LG_G510_USB_AUDIO,32	LG_Z10,33};34 35enum lg_g15_led_type {36	LG_G15_KBD_BRIGHTNESS,37	LG_G15_LCD_BRIGHTNESS,38	LG_G15_BRIGHTNESS_MAX,39	LG_G15_MACRO_PRESET1 = 2,40	LG_G15_MACRO_PRESET2,41	LG_G15_MACRO_PRESET3,42	LG_G15_MACRO_RECORD,43	LG_G15_LED_MAX44};45 46struct lg_g15_led {47	struct led_classdev cdev;48	enum led_brightness brightness;49	enum lg_g15_led_type led;50	u8 red, green, blue;51};52 53struct lg_g15_data {54	/* Must be first for proper dma alignment */55	u8 transfer_buf[LG_G15_TRANSFER_BUF_SIZE];56	/* Protects the transfer_buf and led brightness */57	struct mutex mutex;58	struct work_struct work;59	struct input_dev *input;60	struct hid_device *hdev;61	enum lg_g15_model model;62	struct lg_g15_led leds[LG_G15_LED_MAX];63	bool game_mode_enabled;64};65 66/******** G15 and G15 v2 LED functions ********/67 68static int lg_g15_update_led_brightness(struct lg_g15_data *g15)69{70	int ret;71 72	ret = hid_hw_raw_request(g15->hdev, LG_G15_FEATURE_REPORT,73				 g15->transfer_buf, 4,74				 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);75	if (ret != 4) {76		hid_err(g15->hdev, "Error getting LED brightness: %d\n", ret);77		return (ret < 0) ? ret : -EIO;78	}79 80	g15->leds[LG_G15_KBD_BRIGHTNESS].brightness = g15->transfer_buf[1];81	g15->leds[LG_G15_LCD_BRIGHTNESS].brightness = g15->transfer_buf[2];82 83	g15->leds[LG_G15_MACRO_PRESET1].brightness =84		!(g15->transfer_buf[3] & 0x01);85	g15->leds[LG_G15_MACRO_PRESET2].brightness =86		!(g15->transfer_buf[3] & 0x02);87	g15->leds[LG_G15_MACRO_PRESET3].brightness =88		!(g15->transfer_buf[3] & 0x04);89	g15->leds[LG_G15_MACRO_RECORD].brightness =90		!(g15->transfer_buf[3] & 0x08);91 92	return 0;93}94 95static enum led_brightness lg_g15_led_get(struct led_classdev *led_cdev)96{97	struct lg_g15_led *g15_led =98		container_of(led_cdev, struct lg_g15_led, cdev);99	struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);100	enum led_brightness brightness;101 102	mutex_lock(&g15->mutex);103	lg_g15_update_led_brightness(g15);104	brightness = g15->leds[g15_led->led].brightness;105	mutex_unlock(&g15->mutex);106 107	return brightness;108}109 110static int lg_g15_led_set(struct led_classdev *led_cdev,111			  enum led_brightness brightness)112{113	struct lg_g15_led *g15_led =114		container_of(led_cdev, struct lg_g15_led, cdev);115	struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);116	u8 val, mask = 0;117	int i, ret;118 119	/* Ignore LED off on unregister / keyboard unplug */120	if (led_cdev->flags & LED_UNREGISTERING)121		return 0;122 123	mutex_lock(&g15->mutex);124 125	g15->transfer_buf[0] = LG_G15_FEATURE_REPORT;126	g15->transfer_buf[3] = 0;127 128	if (g15_led->led < LG_G15_BRIGHTNESS_MAX) {129		g15->transfer_buf[1] = g15_led->led + 1;130		g15->transfer_buf[2] = brightness << (g15_led->led * 4);131	} else {132		for (i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; i++) {133			if (i == g15_led->led)134				val = brightness;135			else136				val = g15->leds[i].brightness;137 138			if (val)139				mask |= 1 << (i - LG_G15_MACRO_PRESET1);140		}141 142		g15->transfer_buf[1] = 0x04;143		g15->transfer_buf[2] = ~mask;144	}145 146	ret = hid_hw_raw_request(g15->hdev, LG_G15_FEATURE_REPORT,147				 g15->transfer_buf, 4,148				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);149	if (ret == 4) {150		/* Success */151		g15_led->brightness = brightness;152		ret = 0;153	} else {154		hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret);155		ret = (ret < 0) ? ret : -EIO;156	}157 158	mutex_unlock(&g15->mutex);159 160	return ret;161}162 163static void lg_g15_leds_changed_work(struct work_struct *work)164{165	struct lg_g15_data *g15 = container_of(work, struct lg_g15_data, work);166	enum led_brightness old_brightness[LG_G15_BRIGHTNESS_MAX];167	enum led_brightness brightness[LG_G15_BRIGHTNESS_MAX];168	int i, ret;169 170	mutex_lock(&g15->mutex);171	for (i = 0; i < LG_G15_BRIGHTNESS_MAX; i++)172		old_brightness[i] = g15->leds[i].brightness;173 174	ret = lg_g15_update_led_brightness(g15);175 176	for (i = 0; i < LG_G15_BRIGHTNESS_MAX; i++)177		brightness[i] = g15->leds[i].brightness;178	mutex_unlock(&g15->mutex);179 180	if (ret)181		return;182 183	for (i = 0; i < LG_G15_BRIGHTNESS_MAX; i++) {184		if (brightness[i] == old_brightness[i])185			continue;186 187		led_classdev_notify_brightness_hw_changed(&g15->leds[i].cdev,188							  brightness[i]);189	}190}191 192/******** G510 LED functions ********/193 194static int lg_g510_get_initial_led_brightness(struct lg_g15_data *g15, int i)195{196	int ret, high;197 198	ret = hid_hw_raw_request(g15->hdev, LG_G510_FEATURE_BACKLIGHT_RGB + i,199				 g15->transfer_buf, 4,200				 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);201	if (ret != 4) {202		hid_err(g15->hdev, "Error getting LED brightness: %d\n", ret);203		return (ret < 0) ? ret : -EIO;204	}205 206	high = max3(g15->transfer_buf[1], g15->transfer_buf[2],207		    g15->transfer_buf[3]);208 209	if (high) {210		g15->leds[i].red =211			DIV_ROUND_CLOSEST(g15->transfer_buf[1] * 255, high);212		g15->leds[i].green =213			DIV_ROUND_CLOSEST(g15->transfer_buf[2] * 255, high);214		g15->leds[i].blue =215			DIV_ROUND_CLOSEST(g15->transfer_buf[3] * 255, high);216		g15->leds[i].brightness = high;217	} else {218		g15->leds[i].red   = 255;219		g15->leds[i].green = 255;220		g15->leds[i].blue  = 255;221		g15->leds[i].brightness = 0;222	}223 224	return 0;225}226 227/* Must be called with g15->mutex locked */228static int lg_g510_kbd_led_write(struct lg_g15_data *g15,229				 struct lg_g15_led *g15_led,230				 enum led_brightness brightness)231{232	int ret;233 234	g15->transfer_buf[0] = 5 + g15_led->led;235	g15->transfer_buf[1] =236		DIV_ROUND_CLOSEST(g15_led->red * brightness, 255);237	g15->transfer_buf[2] =238		DIV_ROUND_CLOSEST(g15_led->green * brightness, 255);239	g15->transfer_buf[3] =240		DIV_ROUND_CLOSEST(g15_led->blue * brightness, 255);241 242	ret = hid_hw_raw_request(g15->hdev,243				 LG_G510_FEATURE_BACKLIGHT_RGB + g15_led->led,244				 g15->transfer_buf, 4,245				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);246	if (ret == 4) {247		/* Success */248		g15_led->brightness = brightness;249		ret = 0;250	} else {251		hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret);252		ret = (ret < 0) ? ret : -EIO;253	}254 255	return ret;256}257 258static int lg_g510_kbd_led_set(struct led_classdev *led_cdev,259			       enum led_brightness brightness)260{261	struct lg_g15_led *g15_led =262		container_of(led_cdev, struct lg_g15_led, cdev);263	struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);264	int ret;265 266	/* Ignore LED off on unregister / keyboard unplug */267	if (led_cdev->flags & LED_UNREGISTERING)268		return 0;269 270	mutex_lock(&g15->mutex);271	ret = lg_g510_kbd_led_write(g15, g15_led, brightness);272	mutex_unlock(&g15->mutex);273 274	return ret;275}276 277static enum led_brightness lg_g510_kbd_led_get(struct led_classdev *led_cdev)278{279	struct lg_g15_led *g15_led =280		container_of(led_cdev, struct lg_g15_led, cdev);281 282	return g15_led->brightness;283}284 285static ssize_t color_store(struct device *dev, struct device_attribute *attr,286			   const char *buf, size_t count)287{288	struct led_classdev *led_cdev = dev_get_drvdata(dev);289	struct lg_g15_led *g15_led =290		container_of(led_cdev, struct lg_g15_led, cdev);291	struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);292	unsigned long value;293	int ret;294 295	if (count < 7 || (count == 8 && buf[7] != '\n') || count > 8)296		return -EINVAL;297 298	if (buf[0] != '#')299		return -EINVAL;300 301	ret = kstrtoul(buf + 1, 16, &value);302	if (ret)303		return ret;304 305	mutex_lock(&g15->mutex);306	g15_led->red   = (value & 0xff0000) >> 16;307	g15_led->green = (value & 0x00ff00) >> 8;308	g15_led->blue  = (value & 0x0000ff);309	ret = lg_g510_kbd_led_write(g15, g15_led, g15_led->brightness);310	mutex_unlock(&g15->mutex);311 312	return (ret < 0) ? ret : count;313}314 315static ssize_t color_show(struct device *dev, struct device_attribute *attr,316			  char *buf)317{318	struct led_classdev *led_cdev = dev_get_drvdata(dev);319	struct lg_g15_led *g15_led =320		container_of(led_cdev, struct lg_g15_led, cdev);321	struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);322	ssize_t ret;323 324	mutex_lock(&g15->mutex);325	ret = sprintf(buf, "#%02x%02x%02x\n",326		      g15_led->red, g15_led->green, g15_led->blue);327	mutex_unlock(&g15->mutex);328 329	return ret;330}331 332static DEVICE_ATTR_RW(color);333 334static struct attribute *lg_g510_kbd_led_attrs[] = {335	&dev_attr_color.attr,336	NULL,337};338 339static const struct attribute_group lg_g510_kbd_led_group = {340	.attrs = lg_g510_kbd_led_attrs,341};342 343static const struct attribute_group *lg_g510_kbd_led_groups[] = {344	&lg_g510_kbd_led_group,345	NULL,346};347 348static void lg_g510_leds_sync_work(struct work_struct *work)349{350	struct lg_g15_data *g15 = container_of(work, struct lg_g15_data, work);351 352	mutex_lock(&g15->mutex);353	lg_g510_kbd_led_write(g15, &g15->leds[LG_G15_KBD_BRIGHTNESS],354			      g15->leds[LG_G15_KBD_BRIGHTNESS].brightness);355	mutex_unlock(&g15->mutex);356}357 358static int lg_g510_update_mkey_led_brightness(struct lg_g15_data *g15)359{360	int ret;361 362	ret = hid_hw_raw_request(g15->hdev, LG_G510_FEATURE_M_KEYS_LEDS,363				 g15->transfer_buf, 2,364				 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);365	if (ret != 2) {366		hid_err(g15->hdev, "Error getting LED brightness: %d\n", ret);367		ret = (ret < 0) ? ret : -EIO;368	}369 370	g15->leds[LG_G15_MACRO_PRESET1].brightness =371		!!(g15->transfer_buf[1] & 0x80);372	g15->leds[LG_G15_MACRO_PRESET2].brightness =373		!!(g15->transfer_buf[1] & 0x40);374	g15->leds[LG_G15_MACRO_PRESET3].brightness =375		!!(g15->transfer_buf[1] & 0x20);376	g15->leds[LG_G15_MACRO_RECORD].brightness =377		!!(g15->transfer_buf[1] & 0x10);378 379	return 0;380}381 382static enum led_brightness lg_g510_mkey_led_get(struct led_classdev *led_cdev)383{384	struct lg_g15_led *g15_led =385		container_of(led_cdev, struct lg_g15_led, cdev);386	struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);387	enum led_brightness brightness;388 389	mutex_lock(&g15->mutex);390	lg_g510_update_mkey_led_brightness(g15);391	brightness = g15->leds[g15_led->led].brightness;392	mutex_unlock(&g15->mutex);393 394	return brightness;395}396 397static int lg_g510_mkey_led_set(struct led_classdev *led_cdev,398				enum led_brightness brightness)399{400	struct lg_g15_led *g15_led =401		container_of(led_cdev, struct lg_g15_led, cdev);402	struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);403	u8 val, mask = 0;404	int i, ret;405 406	/* Ignore LED off on unregister / keyboard unplug */407	if (led_cdev->flags & LED_UNREGISTERING)408		return 0;409 410	mutex_lock(&g15->mutex);411 412	for (i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; i++) {413		if (i == g15_led->led)414			val = brightness;415		else416			val = g15->leds[i].brightness;417 418		if (val)419			mask |= 0x80 >> (i - LG_G15_MACRO_PRESET1);420	}421 422	g15->transfer_buf[0] = LG_G510_FEATURE_M_KEYS_LEDS;423	g15->transfer_buf[1] = mask;424 425	ret = hid_hw_raw_request(g15->hdev, LG_G510_FEATURE_M_KEYS_LEDS,426				 g15->transfer_buf, 2,427				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);428	if (ret == 2) {429		/* Success */430		g15_led->brightness = brightness;431		ret = 0;432	} else {433		hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret);434		ret = (ret < 0) ? ret : -EIO;435	}436 437	mutex_unlock(&g15->mutex);438 439	return ret;440}441 442/******** Generic LED functions ********/443static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15)444{445	int ret;446 447	switch (g15->model) {448	case LG_G15:449	case LG_G15_V2:450		return lg_g15_update_led_brightness(g15);451	case LG_G510:452	case LG_G510_USB_AUDIO:453		ret = lg_g510_get_initial_led_brightness(g15, 0);454		if (ret)455			return ret;456 457		ret = lg_g510_get_initial_led_brightness(g15, 1);458		if (ret)459			return ret;460 461		return lg_g510_update_mkey_led_brightness(g15);462	case LG_Z10:463		/*464		 * Getting the LCD backlight brightness is not supported.465		 * Reading Feature(2) fails with -EPIPE and this crashes466		 * the LCD and touch keys part of the speakers.467		 */468		return 0;469	}470	return -EINVAL; /* Never reached */471}472 473/******** Input functions ********/474 475/* On the G15 Mark I Logitech has been quite creative with which bit is what */476static void lg_g15_handle_lcd_menu_keys(struct lg_g15_data *g15, u8 *data)477{478	int i, val;479 480	/* Most left (round/display) button below the LCD */481	input_report_key(g15->input, KEY_KBD_LCD_MENU1, data[8] & 0x80);482	/* 4 other buttons below the LCD */483	for (i = 0; i < 4; i++) {484		val = data[i + 2] & 0x80;485		input_report_key(g15->input, KEY_KBD_LCD_MENU2 + i, val);486	}487}488 489static int lg_g15_event(struct lg_g15_data *g15, u8 *data)490{491	int i, val;492 493	/* G1 - G6 */494	for (i = 0; i < 6; i++) {495		val = data[i + 1] & (1 << i);496		input_report_key(g15->input, KEY_MACRO1 + i, val);497	}498	/* G7 - G12 */499	for (i = 0; i < 6; i++) {500		val = data[i + 2] & (1 << i);501		input_report_key(g15->input, KEY_MACRO7 + i, val);502	}503	/* G13 - G17 */504	for (i = 0; i < 5; i++) {505		val = data[i + 1] & (4 << i);506		input_report_key(g15->input, KEY_MACRO13 + i, val);507	}508	/* G18 */509	input_report_key(g15->input, KEY_MACRO18, data[8] & 0x40);510 511	/* M1 - M3 */512	for (i = 0; i < 3; i++) {513		val = data[i + 6] & (1 << i);514		input_report_key(g15->input, KEY_MACRO_PRESET1 + i, val);515	}516	/* MR */517	input_report_key(g15->input, KEY_MACRO_RECORD_START, data[7] & 0x40);518 519	lg_g15_handle_lcd_menu_keys(g15, data);520 521	/* Backlight cycle button pressed? */522	if (data[1] & 0x80)523		schedule_work(&g15->work);524 525	input_sync(g15->input);526	return 0;527}528 529static int lg_g15_v2_event(struct lg_g15_data *g15, u8 *data)530{531	int i, val;532 533	/* G1 - G6 */534	for (i = 0; i < 6; i++) {535		val = data[1] & (1 << i);536		input_report_key(g15->input, KEY_MACRO1 + i, val);537	}538 539	/* M1 - M3 + MR */540	input_report_key(g15->input, KEY_MACRO_PRESET1, data[1] & 0x40);541	input_report_key(g15->input, KEY_MACRO_PRESET2, data[1] & 0x80);542	input_report_key(g15->input, KEY_MACRO_PRESET3, data[2] & 0x20);543	input_report_key(g15->input, KEY_MACRO_RECORD_START, data[2] & 0x40);544 545	/* Round button to the left of the LCD */546	input_report_key(g15->input, KEY_KBD_LCD_MENU1, data[2] & 0x80);547	/* 4 buttons below the LCD */548	for (i = 0; i < 4; i++) {549		val = data[2] & (2 << i);550		input_report_key(g15->input, KEY_KBD_LCD_MENU2 + i, val);551	}552 553	/* Backlight cycle button pressed? */554	if (data[2] & 0x01)555		schedule_work(&g15->work);556 557	input_sync(g15->input);558	return 0;559}560 561static int lg_g510_event(struct lg_g15_data *g15, u8 *data)562{563	bool game_mode_enabled;564	int i, val;565 566	/* G1 - G18 */567	for (i = 0; i < 18; i++) {568		val = data[i / 8 + 1] & (1 << (i % 8));569		input_report_key(g15->input, KEY_MACRO1 + i, val);570	}571 572	/* Game mode on/off slider */573	game_mode_enabled = data[3] & 0x04;574	if (game_mode_enabled != g15->game_mode_enabled) {575		if (game_mode_enabled)576			hid_info(g15->hdev, "Game Mode enabled, Windows (super) key is disabled\n");577		else578			hid_info(g15->hdev, "Game Mode disabled\n");579		g15->game_mode_enabled = game_mode_enabled;580	}581 582	/* M1 - M3 */583	for (i = 0; i < 3; i++) {584		val = data[3] & (0x10 << i);585		input_report_key(g15->input, KEY_MACRO_PRESET1 + i, val);586	}587	/* MR */588	input_report_key(g15->input, KEY_MACRO_RECORD_START, data[3] & 0x80);589 590	/* LCD menu keys */591	for (i = 0; i < 5; i++) {592		val = data[4] & (1 << i);593		input_report_key(g15->input, KEY_KBD_LCD_MENU1 + i, val);594	}595 596	/* Headphone Mute */597	input_report_key(g15->input, KEY_MUTE, data[4] & 0x20);598	/* Microphone Mute */599	input_report_key(g15->input, KEY_F20, data[4] & 0x40);600 601	input_sync(g15->input);602	return 0;603}604 605static int lg_g510_leds_event(struct lg_g15_data *g15, u8 *data)606{607	bool backlight_disabled;608 609	/*610	 * The G510 ignores backlight updates when the backlight is turned off611	 * through the light toggle button on the keyboard, to work around this612	 * we queue a workitem to sync values when the backlight is turned on.613	 */614	backlight_disabled = data[1] & 0x04;615	if (!backlight_disabled)616		schedule_work(&g15->work);617 618	return 0;619}620 621static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report,622			    u8 *data, int size)623{624	struct lg_g15_data *g15 = hid_get_drvdata(hdev);625 626	if (!g15)627		return 0;628 629	switch (g15->model) {630	case LG_G15:631		if (data[0] == 0x02 && size == 9)632			return lg_g15_event(g15, data);633		break;634	case LG_G15_V2:635		if (data[0] == 0x02 && size == 5)636			return lg_g15_v2_event(g15, data);637		break;638	case LG_Z10:639		if (data[0] == 0x02 && size == 9) {640			lg_g15_handle_lcd_menu_keys(g15, data);641			input_sync(g15->input);642		}643		break;644	case LG_G510:645	case LG_G510_USB_AUDIO:646		if (data[0] == 0x03 && size == 5)647			return lg_g510_event(g15, data);648		if (data[0] == 0x04 && size == 2)649			return lg_g510_leds_event(g15, data);650		break;651	}652 653	return 0;654}655 656static int lg_g15_input_open(struct input_dev *dev)657{658	struct hid_device *hdev = input_get_drvdata(dev);659 660	return hid_hw_open(hdev);661}662 663static void lg_g15_input_close(struct input_dev *dev)664{665	struct hid_device *hdev = input_get_drvdata(dev);666 667	hid_hw_close(hdev);668}669 670static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name)671{672	g15->leds[i].led = i;673	g15->leds[i].cdev.name = name;674 675	switch (g15->model) {676	case LG_G15:677	case LG_G15_V2:678		g15->leds[i].cdev.brightness_get = lg_g15_led_get;679		fallthrough;680	case LG_Z10:681		g15->leds[i].cdev.brightness_set_blocking = lg_g15_led_set;682		if (i < LG_G15_BRIGHTNESS_MAX) {683			g15->leds[i].cdev.flags = LED_BRIGHT_HW_CHANGED;684			g15->leds[i].cdev.max_brightness = 2;685		} else {686			g15->leds[i].cdev.max_brightness = 1;687		}688		break;689	case LG_G510:690	case LG_G510_USB_AUDIO:691		switch (i) {692		case LG_G15_LCD_BRIGHTNESS:693			/*694			 * The G510 does not have a separate LCD brightness,695			 * but it does have a separate power-on (reset) value.696			 */697			g15->leds[i].cdev.name = "g15::power_on_backlight_val";698			fallthrough;699		case LG_G15_KBD_BRIGHTNESS:700			g15->leds[i].cdev.brightness_set_blocking =701				lg_g510_kbd_led_set;702			g15->leds[i].cdev.brightness_get =703				lg_g510_kbd_led_get;704			g15->leds[i].cdev.max_brightness = 255;705			g15->leds[i].cdev.groups = lg_g510_kbd_led_groups;706			break;707		default:708			g15->leds[i].cdev.brightness_set_blocking =709				lg_g510_mkey_led_set;710			g15->leds[i].cdev.brightness_get =711				lg_g510_mkey_led_get;712			g15->leds[i].cdev.max_brightness = 1;713		}714		break;715	}716 717	return devm_led_classdev_register(&g15->hdev->dev, &g15->leds[i].cdev);718}719 720/* Common input device init code shared between keyboards and Z-10 speaker handling */721static void lg_g15_init_input_dev(struct hid_device *hdev, struct input_dev *input,722				  const char *name)723{724	int i;725 726	input->name = name;727	input->phys = hdev->phys;728	input->uniq = hdev->uniq;729	input->id.bustype = hdev->bus;730	input->id.vendor  = hdev->vendor;731	input->id.product = hdev->product;732	input->id.version = hdev->version;733	input->dev.parent = &hdev->dev;734	input->open = lg_g15_input_open;735	input->close = lg_g15_input_close;736 737	/* Keys below the LCD, intended for controlling a menu on the LCD */738	for (i = 0; i < 5; i++)739		input_set_capability(input, EV_KEY, KEY_KBD_LCD_MENU1 + i);740}741 742static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)743{744	static const char * const led_names[] = {745		"g15::kbd_backlight",746		"g15::lcd_backlight",747		"g15::macro_preset1",748		"g15::macro_preset2",749		"g15::macro_preset3",750		"g15::macro_record",751	};752	u8 gkeys_settings_output_report = 0;753	u8 gkeys_settings_feature_report = 0;754	struct hid_report_enum *rep_enum;755	unsigned int connect_mask = 0;756	bool has_ff000000 = false;757	struct lg_g15_data *g15;758	struct input_dev *input;759	struct hid_report *rep;760	int ret, i, gkeys = 0;761 762	hdev->quirks |= HID_QUIRK_INPUT_PER_APP;763 764	ret = hid_parse(hdev);765	if (ret)766		return ret;767 768	/*769	 * Some models have multiple interfaces, we want the interface with770	 * the f000.0000 application input report.771	 */772	rep_enum = &hdev->report_enum[HID_INPUT_REPORT];773	list_for_each_entry(rep, &rep_enum->report_list, list) {774		if (rep->application == 0xff000000)775			has_ff000000 = true;776	}777	if (!has_ff000000)778		return hid_hw_start(hdev, HID_CONNECT_DEFAULT);779 780	g15 = devm_kzalloc(&hdev->dev, sizeof(*g15), GFP_KERNEL);781	if (!g15)782		return -ENOMEM;783 784	mutex_init(&g15->mutex);785 786	input = devm_input_allocate_device(&hdev->dev);787	if (!input)788		return -ENOMEM;789 790	g15->hdev = hdev;791	g15->model = id->driver_data;792	g15->input = input;793	input_set_drvdata(input, hdev);794	hid_set_drvdata(hdev, (void *)g15);795 796	switch (g15->model) {797	case LG_G15:798		INIT_WORK(&g15->work, lg_g15_leds_changed_work);799		/*800		 * The G15 and G15 v2 use a separate usb-device (on a builtin801		 * hub) which emulates a keyboard for the F1 - F12 emulation802		 * on the G-keys, which we disable, rendering the emulated kbd803		 * non-functional, so we do not let hid-input connect.804		 */805		connect_mask = HID_CONNECT_HIDRAW;806		gkeys_settings_output_report = 0x02;807		gkeys = 18;808		break;809	case LG_G15_V2:810		INIT_WORK(&g15->work, lg_g15_leds_changed_work);811		connect_mask = HID_CONNECT_HIDRAW;812		gkeys_settings_output_report = 0x02;813		gkeys = 6;814		break;815	case LG_G510:816	case LG_G510_USB_AUDIO:817		INIT_WORK(&g15->work, lg_g510_leds_sync_work);818		connect_mask = HID_CONNECT_HIDINPUT | HID_CONNECT_HIDRAW;819		gkeys_settings_feature_report = 0x01;820		gkeys = 18;821		break;822	case LG_Z10:823		connect_mask = HID_CONNECT_HIDRAW;824		break;825	}826 827	ret = hid_hw_start(hdev, connect_mask);828	if (ret)829		return ret;830 831	/* Tell the keyboard to stop sending F1-F12 + 1-6 for G1 - G18 */832	if (gkeys_settings_output_report) {833		g15->transfer_buf[0] = gkeys_settings_output_report;834		memset(g15->transfer_buf + 1, 0, gkeys);835		/*836		 * The kbd ignores our output report if we do not queue837		 * an URB on the USB input endpoint first...838		 */839		ret = hid_hw_open(hdev);840		if (ret)841			goto error_hw_stop;842		ret = hid_hw_output_report(hdev, g15->transfer_buf, gkeys + 1);843		hid_hw_close(hdev);844	}845 846	if (gkeys_settings_feature_report) {847		g15->transfer_buf[0] = gkeys_settings_feature_report;848		memset(g15->transfer_buf + 1, 0, gkeys);849		ret = hid_hw_raw_request(g15->hdev,850				gkeys_settings_feature_report,851				g15->transfer_buf, gkeys + 1,852				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);853	}854 855	if (ret < 0) {856		hid_err(hdev, "Error %d disabling keyboard emulation for the G-keys, falling back to generic hid-input driver\n",857			ret);858		hid_set_drvdata(hdev, NULL);859		return 0;860	}861 862	/* Get initial brightness levels */863	ret = lg_g15_get_initial_led_brightness(g15);864	if (ret)865		goto error_hw_stop;866 867	if (g15->model == LG_Z10) {868		lg_g15_init_input_dev(hdev, g15->input, "Logitech Z-10 LCD Menu Keys");869		ret = input_register_device(g15->input);870		if (ret)871			goto error_hw_stop;872 873		ret = lg_g15_register_led(g15, 1, "z-10::lcd_backlight");874		if (ret)875			goto error_hw_stop;876 877		return 0; /* All done */878	}879 880	/* Setup and register input device */881	lg_g15_init_input_dev(hdev, input, "Logitech Gaming Keyboard Gaming Keys");882 883	/* G-keys */884	for (i = 0; i < gkeys; i++)885		input_set_capability(input, EV_KEY, KEY_MACRO1 + i);886 887	/* M1 - M3 and MR keys */888	for (i = 0; i < 3; i++)889		input_set_capability(input, EV_KEY, KEY_MACRO_PRESET1 + i);890	input_set_capability(input, EV_KEY, KEY_MACRO_RECORD_START);891 892	/*893	 * On the G510 only report headphone and mic mute keys when *not* using894	 * the builtin USB audio device. When the builtin audio is used these895	 * keys directly toggle mute (and the LEDs) on/off.896	 */897	if (g15->model == LG_G510) {898		input_set_capability(input, EV_KEY, KEY_MUTE);899		/* Userspace expects F20 for micmute */900		input_set_capability(input, EV_KEY, KEY_F20);901	}902 903	ret = input_register_device(input);904	if (ret)905		goto error_hw_stop;906 907	/* Register LED devices */908	for (i = 0; i < LG_G15_LED_MAX; i++) {909		ret = lg_g15_register_led(g15, i, led_names[i]);910		if (ret)911			goto error_hw_stop;912	}913 914	return 0;915 916error_hw_stop:917	hid_hw_stop(hdev);918	return ret;919}920 921static const struct hid_device_id lg_g15_devices[] = {922	/* The G11 is a G15 without the LCD, treat it as a G15 */923	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,924		USB_DEVICE_ID_LOGITECH_G11),925		.driver_data = LG_G15 },926	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,927			 USB_DEVICE_ID_LOGITECH_G15_LCD),928		.driver_data = LG_G15 },929	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,930			 USB_DEVICE_ID_LOGITECH_G15_V2_LCD),931		.driver_data = LG_G15_V2 },932	/* G510 without a headset plugged in */933	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,934			 USB_DEVICE_ID_LOGITECH_G510),935		.driver_data = LG_G510 },936	/* G510 with headset plugged in / with extra USB audio interface */937	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,938			 USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO),939		.driver_data = LG_G510_USB_AUDIO },940	/* Z-10 speakers */941	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,942			 USB_DEVICE_ID_LOGITECH_Z_10_SPK),943		.driver_data = LG_Z10 },944	{ }945};946MODULE_DEVICE_TABLE(hid, lg_g15_devices);947 948static struct hid_driver lg_g15_driver = {949	.name			= "lg-g15",950	.id_table		= lg_g15_devices,951	.raw_event		= lg_g15_raw_event,952	.probe			= lg_g15_probe,953};954module_hid_driver(lg_g15_driver);955 956MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");957MODULE_DESCRIPTION("HID driver for gaming keys on Logitech gaming keyboards");958MODULE_LICENSE("GPL");959