Re: Litra Glow on Linux

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



So after some tinkering I have code now that succeeds in retrieving state
via sending reports once. After that all following sends time out.
I am at a loss what I am doing wrong, tbh. RFC below.

On Thu, 10 Nov 2022, Benjamin Tissoires wrote:

>
> >
> > I had a look at the hidpp utility
> > sources:
> > https://github.com/cvuchener/hidpp/blob/057407fbb7248bbc6cefcfaa860758d0711c01b9/src/libhidpp/hidpp/Device.cpp#L82
> > Which seems to do a similar thing. From the top of my head the only
> > difference seems to be that they are sending `0x1` as a ping value instead
> > of `0x5a`. Might give that a shot next.
> > Anyway hidpp-list-features successfully reads the protocol version in
> > userspace (4, 2) as seen here:
> > https://github.com/abergmeier/litra_glow_linux/blob/main/hidpp-list-features
>
> Hmm... It would seem wrong for me if the firmware suddenly expects to
> have a specific ping value.
> If it works in userspace, it might also mean that the timing is not
> right and we are talking to the device too early, and it can't answer
> yet.
I needed to set some specific quirk flags to make communication work. See
below.

RFC on current PATCH:
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index dad953f66996..78265f7235ce 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -856,6 +856,7 @@
 #define USB_DEVICE_ID_MX5500_RECEIVER_MOUSE_DEV		0xc71c
 #define USB_DEVICE_ID_DINOVO_MINI_RECEIVER_KBD_DEV	0xc71e
 #define USB_DEVICE_ID_DINOVO_MINI_RECEIVER_MOUSE_DEV	0xc71f
+#define USB_DEVICE_ID_LOGITECH_LITRA_GLOW   0xc900
 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2	0xca03
 #define USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL	0xca04

diff --git a/drivers/hid/hid-logitech-hidpp.c
b/drivers/hid/hid-logitech-hidpp.c
index 71a9c258a20b..949fd09d2b43 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -11,6 +11,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

 #include <linux/device.h>
+#include <linux/dmi.h>
 #include <linux/input.h>
 #include <linux/usb.h>
 #include <linux/hid.h>
@@ -99,6 +100,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL	BIT(7)
 #define HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL	BIT(8)
 #define HIDPP_CAPABILITY_HIDPP10_FAST_SCROLL	BIT(9)
+#define HIDPP_CAPABILITY_ILLUMINATION_LIGHT		BIT(10)

 #define lg_map_key_clear(c)  hid_map_usage_clear(hi, usage, bit, max,
EV_KEY, (c))

@@ -206,7 +208,10 @@ struct hidpp_device {
 	struct hidpp_battery battery;
 	struct hidpp_scroll_counter vertical_wheel_counter;

-	u8 wireless_feature_index;
+	union {
+		u8 wireless_feature_index;
+		u8 illumination_feature_index;
+	};
 };

 /* HID++ 1.0 error codes */
@@ -355,15 +360,16 @@ static int hidpp_send_fap_command_sync(struct
hidpp_device *hidpp,
 }

 static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
-	u8 report_id, u8 sub_id, u8 reg_address, u8 *params, int
param_count,
+	u8 sub_id, u8 reg_address, u8 *params, int param_count,
 	struct hidpp_report *response)
 {
 	struct hidpp_report *message;
 	int ret, max_count;
+	u8 report_id;

-	/* Send as long report if short reports are not supported. */
-	if (report_id == REPORT_ID_HIDPP_SHORT &&
-	    !(hidpp_dev->supported_reports &
HIDPP_REPORT_SHORT_SUPPORTED))
+	if (hidpp_dev->supported_reports & HIDPP_REPORT_SHORT_SUPPORTED)
+		report_id = REPORT_ID_HIDPP_SHORT;
+	else
 		report_id = REPORT_ID_HIDPP_LONG;

 	switch (report_id) {
@@ -544,7 +550,6 @@ static int hidpp10_set_register(struct hidpp_device
*hidpp_dev,
 	u8 params[3] = { 0 };

 	ret = hidpp_send_rap_command_sync(hidpp_dev,
-					  REPORT_ID_HIDPP_SHORT,
 					  HIDPP_GET_REGISTER,
 					  register_address,
 					  NULL, 0, &response);
@@ -557,7 +562,6 @@ static int hidpp10_set_register(struct hidpp_device
*hidpp_dev,
 	params[byte] |= value & mask;

 	return hidpp_send_rap_command_sync(hidpp_dev,
-					   REPORT_ID_HIDPP_SHORT,
 					   HIDPP_SET_REGISTER,
 					   register_address,
 					   params, 3, &response);
@@ -653,7 +657,6 @@ static int hidpp10_query_battery_status(struct
hidpp_device *hidpp)
 	int ret, status;

 	ret = hidpp_send_rap_command_sync(hidpp,
-					REPORT_ID_HIDPP_SHORT,
 					HIDPP_GET_REGISTER,
 					HIDPP_REG_BATTERY_STATUS,
 					NULL, 0, &response);
@@ -705,7 +708,6 @@ static int hidpp10_query_battery_mileage(struct
hidpp_device *hidpp)
 	int ret, status;

 	ret = hidpp_send_rap_command_sync(hidpp,
-					REPORT_ID_HIDPP_SHORT,
 					HIDPP_GET_REGISTER,
 					HIDPP_REG_BATTERY_MILEAGE,
 					NULL, 0, &response);
@@ -777,7 +779,6 @@ static char *hidpp_unifying_get_name(struct
hidpp_device *hidpp_dev)
 	int len;

 	ret = hidpp_send_rap_command_sync(hidpp_dev,
-					REPORT_ID_HIDPP_SHORT,
 					HIDPP_GET_LONG_REGISTER,
 					HIDPP_REG_PAIRING_INFORMATION,
 					params, 1, &response);
@@ -811,7 +812,6 @@ static int hidpp_unifying_get_serial(struct
hidpp_device *hidpp, u32 *serial)
 	u8 params[1] = { HIDPP_EXTENDED_PAIRING };

 	ret = hidpp_send_rap_command_sync(hidpp,
-					REPORT_ID_HIDPP_SHORT,
 					HIDPP_GET_LONG_REGISTER,
 					HIDPP_REG_PAIRING_INFORMATION,
 					params, 1, &response);
@@ -862,6 +862,8 @@ static int hidpp_unifying_init(struct hidpp_device
*hidpp)
 #define CMD_ROOT_GET_FEATURE				0x00
 #define CMD_ROOT_GET_PROTOCOL_VERSION			0x10

+#define HIDPP_FEATURE_TYPE_HIDDEN 0x70
+
 static int hidpp_root_get_feature(struct hidpp_device *hidpp, u16
feature,
 	u8 *feature_index, u8 *feature_type)
 {
@@ -893,9 +895,8 @@ static int hidpp_root_get_protocol_version(struct
hidpp_device *hidpp)
 	int ret;

 	ret = hidpp_send_rap_command_sync(hidpp,
-			REPORT_ID_HIDPP_SHORT,
 			HIDPP_PAGE_ROOT_IDX,
-			CMD_ROOT_GET_PROTOCOL_VERSION,
+			CMD_ROOT_GET_PROTOCOL_VERSION |
LINUX_KERNEL_SW_ID,
 			ping_data, sizeof(ping_data), &response);

 	if (ret == HIDPP_ERROR_INVALID_SUBID) {
@@ -1729,6 +1730,361 @@ static int hidpp_set_wireless_feature_index(struct
hidpp_device *hidpp)
 	return ret;
 }

+/*
--------------------------------------------------------------------------
*/
+/* 0x1990: Illumination Light
*/
+/*
--------------------------------------------------------------------------
*/
+
+#define HIDPP_PAGE_ILLUMINATION_LIGHT 0x1990
+
+#define HIDPP_ILLUMINATION_FUNC_GET 0x00
+#define HIDPP_ILLUMINATION_FUNC_SET 0x10
+#define HIDPP_ILLUMINATION_FUNC_GET_BRIGHTNESS_INFO 0x20
+#define HIDPP_ILLUMINATION_FUNC_GET_BRIGHTNESS 0x30
+#define HIDPP_ILLUMINATION_FUNC_SET_BRIGHTNESS 0x40
+
+/* Not yet supported
+#define HIDPP_ILLUMINATION_FUNC_GET_BRIGHTNESS_LEVELS 0x50
+#define HIDPP_ILLUMINATION_FUNC_SET_BRIGHTNESS_LEVELS 0x60
+*/
+
+#define HIDPP_ILLUMINATION_FUNC_GET_COLOR_TEMPERATURE_INFO 0x70
+#define HIDPP_ILLUMINATION_FUNC_GET_COLOR_TEMPERATURE 0x80
+#define HIDPP_ILLUMINATION_FUNC_SET_COLOR_TEMPERATURE 0x90
+
+/* Not yet supported
+#define HIDPP_ILLUMINATION_FUNC_GET_COLOR_TEMPERATURE_LEVELS 0xA0
+#define HIDPP_ILLUMINATION_FUNC_SET_COLOR_TEMPERATURE_LEVELS 0xB0
+*/
+
+#define HIDPP_ILLUMINATION_EVENT_CHANGE 0x00
+#define HIDPP_ILLUMINATION_EVENT_BRIGHTNESS_CHANGE 0x10
+#define HIDPP_ILLUMINATION_EVENT_COLOR_TEMPERATURE_CHANGE 0x20
+
+#define HIDPP_ILLUMINATION_CAP_EVENTS BIT(0)
+#define HIDPP_ILLUMINATION_CAP_LINEAR_LEVELS BIT(1)
+#define HIDPP_ILLUMINATION_CAP_NON_LINEAR_LEVELS BIT(2)
+
+struct control_info {
+	u16 min;
+	u16 max;
+	u16 res;
+	u8 capabilities;
+	u8 max_levels;
+};
+
+struct led_data {
+	struct led_classdev cdev;
+	struct hidpp_device *drv_data;
+	struct hid_device *hdev;
+	u16 feature_index;
+	bool on;
+	u16 brightness;
+	struct control_info brightness_info;
+	struct control_info color_temperature_info;
+	char dirname[256];
+	bool removed;
+};
+
+/* kernel led interface designates 0 as off. To not lose the ability to
chose
+ * minimal brightness, we thus need to increase the reported range by 1
+ */
+static unsigned device_to_led_brightness_value(struct led_data* led, u16
device_brightness) {
+	u16 relative = device_brightness - led->brightness_info.min;
+	u16 step = relative / led->brightness_info.res;
+	return step + 1;
+}
+
+static unsigned device_to_led_brightness(struct led_data* led) {
+	return device_to_led_brightness_value(led, led->brightness);
+}
+
+static u16 led_to_device_brightness_value(struct led_data* led, unsigned
led_brightness) {
+	unsigned step = led_brightness - 1;
+	unsigned relative = step * led->brightness_info.res;
+	return led->brightness_info.min + relative;
+}
+
+static enum led_brightness led_brightness_get(struct led_classdev
*led_cdev)
+{
+	struct led_data *led = container_of(led_cdev, struct led_data,
cdev);
+	struct hidpp_device *hidpp = led->drv_data;
+	u8 params[1] = { 0 };
+	struct hidpp_report report;
+	int ret;
+	u16 be_brightness;
+
+
+	ret = hidpp_send_fap_command_sync(hidpp,
+
hidpp->illumination_feature_index,
+					  HIDPP_ILLUMINATION_FUNC_GET,
params,
+					  0, &report);
+	if (ret) {
+		hid_err(hidpp->hid_dev, "Getting Illumination failed\n");
+		goto exit;
+	}
+
+
+	led->on = report.fap.params[0] & 0x01;
+	if (!led->on) {
+		return LED_OFF;
+	}
+
+	ret = hidpp_send_fap_command_sync(
+		hidpp, hidpp->illumination_feature_index,
+		HIDPP_ILLUMINATION_FUNC_GET_BRIGHTNESS, params, 0,
&report);
+	if (ret) {
+		hid_err(hidpp->hid_dev,
+			"Getting Illumination Brightness failed\n");
+		goto exit;
+	}
+
+	be_brightness = (report.fap.params[0] << 8) |
+			(report.fap.params[1] << 0);
+	led->brightness = be16_to_cpu(be_brightness);
+exit:
+	return device_to_led_brightness(led);
+}
+
+static void led_brightness_set_dummy(struct led_classdev *led_cdev,
+			enum led_brightness brightness) {
+}
+
+static int led_brightness_set_sync(struct led_classdev *led_cdev,
+			       enum led_brightness brightness)
+{
+	struct led_data *led = container_of(led_cdev, struct led_data,
cdev);
+	struct hidpp_device *hidpp = led->drv_data;
+	u16 be_brightness;
+	struct hidpp_report report;
+	u8 params[2];
+	int params_count = sizeof(params) / sizeof(*params);
+	int ret;
+
+
+	be_brightness = cpu_to_be16(led->brightness);
+	led->on = brightness != 0;
+	if (led->on) {
+		led->brightness = led_to_device_brightness_value(led,
brightness);
+	}
+
+	memzero_explicit(params, params_count);
+	params[0] = led->on ? 0x01 : 0x00;
+	ret = hidpp_send_fap_command_sync(hidpp,
+
hidpp->illumination_feature_index,
+					  HIDPP_ILLUMINATION_FUNC_SET,
params,
+					  params_count, &report);
+	if (ret) {
+		hid_err(hidpp->hid_dev, "Setting Illumination failed\n");
+		return ret;
+	}
+
+	if (!led->on)
+		return 0;
+	params[0] = (be_brightness & 0xFF00) >> 8;
+	params[1] = (be_brightness & 0x00FF) >> 0;
+	ret = hidpp_send_fap_command_sync(
+		hidpp, hidpp->illumination_feature_index,
+		HIDPP_ILLUMINATION_FUNC_SET_BRIGHTNESS, params,
params_count,
+		&report);
+	if (ret) {
+		hid_err(hidpp->hid_dev,
+			"Setting Illumination Brightness failed\n");
+		return ret;
+	}
+	return ret;
+}
+
+static int get_brightness_info_sync(struct hidpp_device *hidpp,
+				    struct control_info *info)
+{
+	struct hidpp_report resp;
+	int ret = hidpp_send_fap_command_sync(
+		hidpp, hidpp->illumination_feature_index,
+		HIDPP_ILLUMINATION_FUNC_GET_BRIGHTNESS_INFO, NULL, 0,
&resp);
+	if (ret) {
+		hid_err(hidpp->hid_dev,
+			"get_brightness_info_sync failed with %d\n", ret);
+		return ret;
+	}
+
+	info->capabilities = resp.fap.params[0];
+	info->min =
+		be16_to_cpu(resp.fap.params[1] << 8 | resp.fap.params[2]
<< 0);
+	info->max =
+		be16_to_cpu(resp.fap.params[3] << 8 | resp.fap.params[4]
<< 0);
+	info->res =
+		be16_to_cpu(resp.fap.params[5] << 8 | resp.fap.params[6]
<< 0);
+	info->max_levels = resp.fap.params[7];
+	return 0;
+}
+
+static int get_color_temperature_info_sync(struct hidpp_device *hidpp,
+					   struct control_info *info)
+{
+	struct hidpp_report resp;
+	int ret = hidpp_send_fap_command_sync(
+		hidpp, hidpp->illumination_feature_index,
+		HIDPP_ILLUMINATION_FUNC_GET_COLOR_TEMPERATURE_INFO, NULL,
0,
+		&resp);
+	if (ret) {
+		hid_err(hidpp->hid_dev,
+			"get_color_temperature_info_sync failed with
%d\n",
+			ret);
+		return ret;
+	}
+
+	info->capabilities = resp.fap.params[0];
+	info->min =
+		be16_to_cpu(resp.fap.params[1] << 8 | resp.fap.params[2]
<< 0);
+	info->max =
+		be16_to_cpu(resp.fap.params[3] << 8 | resp.fap.params[4]
<< 0);
+	info->res =
+		be16_to_cpu(resp.fap.params[5] << 8 | resp.fap.params[6]
<< 0);
+	info->max_levels = resp.fap.params[7];
+	return 0;
+}
+
+static int register_led(struct hidpp_device *hidpp)
+{
+	char buf[256];
+	int ret;
+	unsigned brightness_range, r = 0, w = 0;
+	struct led_data *led = devm_kzalloc(&hidpp->hid_dev->dev,
sizeof(struct led_data),
+			  GFP_KERNEL);
+
+	if (!led)
+		return -ENOMEM;
+
+	ret = get_brightness_info_sync(hidpp, &led->brightness_info);
+	if (ret)
+		goto cleanup;
+
+	ret = get_color_temperature_info_sync(hidpp,
+
&led->color_temperature_info);
+	if (ret)
+		goto cleanup;
+
+	led->drv_data = hidpp;
+	led->removed = false;
+	memzero_explicit(buf, 256);
+	strscpy(buf, hidpp->name, 256);
+	while (w != 256) {
+		char c = buf[r];
+		if (c == '\'' || c == '\"') {
+			if (r != 255) {
+				r++;
+			}
+			continue;
+		}
+		buf[w] = buf[r];
+		w++;
+		if (r != 255) {
+			r++;
+		}
+	}
+	strreplace(buf, ' ', '_');
+	snprintf(led->dirname, sizeof(led->dirname) /
sizeof(*led->dirname),
+		 "%s::illumination", buf);
+	led->cdev.name = led->dirname;
+	led->cdev.flags = LED_HW_PLUGGABLE | LED_BRIGHT_HW_CHANGED;
+	led->cdev.max_brightness = device_to_led_brightness_value(led,
led->brightness_info.max);
+	if (brightness_range == 0) {
+		/* According to docs set value is not supported under
these
+		 * conditions.
+		 * LED interface enforces a set function.
+		 */
+		led->cdev.brightness_set = led_brightness_set_dummy;
+	} else {
+		led->cdev.brightness_set_blocking =
led_brightness_set_sync;
+	}
+	led->cdev.brightness_get = led_brightness_get;
+
+	ret = devm_led_classdev_register(&hidpp->hid_dev->dev,
&led->cdev);
+	if (ret < 0) {
+		goto cleanup;
+	}
+	hidpp->private_data = led;
+	return 0;
+cleanup:
+	devm_kfree(&hidpp->hid_dev->dev, led);
+	return ret;
+}
+
+static int hidpp_initialize_illumination(struct hidpp_device *hidpp)
+{
+	int ret;
+	unsigned long capabilities = hidpp->capabilities;
+
+	if (hidpp->protocol_major >= 2) {
+		u8 feature_index;
+		u8 feature_type;
+
+		ret = hidpp_root_get_feature(hidpp,
+
HIDPP_PAGE_ILLUMINATION_LIGHT,
+					     &feature_index,
&feature_type);
+		if (!ret && !(feature_type & HIDPP_FEATURE_TYPE_HIDDEN)) {
+			hidpp->capabilities |=
+				HIDPP_CAPABILITY_ILLUMINATION_LIGHT;
+			hidpp->illumination_feature_index = feature_index;
+			hid_dbg(hidpp->hid_dev,
+				"Detected HID++ 2.0 Illumination
Light\n");
+			return 0;
+		}
+	}
+
+	if (hidpp->capabilities == capabilities)
+		hid_dbg(hidpp->hid_dev,
+			"Did not detect HID++ Illumination Light hardware
support\n");
+	return 0;
+}
+
+static int hidpp20_illumination_raw_event(struct hidpp_device *hidpp, u8
*data,
+					  int size)
+{
+	struct led_data *led = (struct led_data *)hidpp->private_data;
+	struct hidpp_report *report = (struct hidpp_report *)data;
+	switch (report->report_id) {
+	case REPORT_ID_HIDPP_LONG:
+		/* size is already checked in hidpp_raw_event.
+		 * only leave long through
+		 */
+		break;
+	default:
+		return 0;
+	}
+
+	if (report->fap.feature_index !=
hidpp->illumination_feature_index) {
+		return 0;
+	}
+
+
+	if (report->fap.funcindex_clientid ==
HIDPP_ILLUMINATION_EVENT_CHANGE) {
+		led->on = report->fap.params[0] & 0x1;
+		if (led->on) {
+			unsigned led_brightness =
device_to_led_brightness(led);
+			led_classdev_notify_brightness_hw_changed(
+				&led->cdev, led_brightness);
+		} else
+
led_classdev_notify_brightness_hw_changed(&led->cdev,
+
LED_OFF);
+		return 0;
+	}
+
+	if (report->fap.funcindex_clientid ==
+	    HIDPP_ILLUMINATION_EVENT_BRIGHTNESS_CHANGE) {
+		unsigned led_brightness;
+		u16 brightness = be16_to_cpu(report->fap.params[0] << 8 |
+					     report->fap.params[1] << 0);
+		led->brightness = brightness;
+		led_brightness = device_to_led_brightness(led);
+		led_classdev_notify_brightness_hw_changed(&led->cdev,
+							  led_brightness);
+		return 0;
+	}
+
+	return 0;
+}
+
 /*
--------------------------------------------------------------------------
*/
 /* 0x2120: Hi-resolution scrolling
*/
 /*
--------------------------------------------------------------------------
*/
@@ -2929,7 +3285,6 @@ static int m560_send_config_command(struct
hid_device *hdev, bool connected)

 	return hidpp_send_rap_command_sync(
 		hidpp_dev,
-		REPORT_ID_HIDPP_SHORT,
 		M560_SUB_ID,
 		M560_BUTTON_MODE_REGISTER,
 		(u8 *)m560_config_parameter,
@@ -3468,7 +3823,6 @@ static int hidpp_initialize_hires_scroll(struct
hidpp_device *hidpp)
 		struct hidpp_report response;

 		ret = hidpp_send_rap_command_sync(hidpp,
-						  REPORT_ID_HIDPP_SHORT,
 						  HIDPP_GET_REGISTER,

HIDPP_ENABLE_FAST_SCROLL,
 						  NULL, 0, &response);
@@ -3648,6 +4002,12 @@ static int hidpp_raw_hidpp_event(struct
hidpp_device *hidpp, u8 *data,
 			return ret;
 	}

+	if (hidpp->capabilities & HIDPP_CAPABILITY_ILLUMINATION_LIGHT) {
+		ret = hidpp20_illumination_raw_event(hidpp, data, size);
+		if (ret != 0)
+			return ret;
+	}
+
 	if (hidpp->quirks & HIDPP_QUIRK_HIDPP_WHEELS) {
 		ret = hidpp10_wheel_raw_event(hidpp, data, size);
 		if (ret != 0)
@@ -3972,6 +4332,7 @@ static void hidpp_connect_event(struct hidpp_device
*hidpp)

 	hidpp_initialize_battery(hidpp);
 	hidpp_initialize_hires_scroll(hidpp);
+	hidpp_initialize_illumination(hidpp);

 	/* forward current battery state */
 	if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
@@ -3994,6 +4355,14 @@ static void hidpp_connect_event(struct hidpp_device
*hidpp)
 	if (hidpp->capabilities & HIDPP_CAPABILITY_HI_RES_SCROLL)
 		hi_res_scroll_enable(hidpp);

+	if (hidpp->capabilities & HIDPP_CAPABILITY_ILLUMINATION_LIGHT) {
+		ret = register_led(hidpp);
+		if (ret) {
+			hid_err(hdev, "Registering leds failed.\n");
+			return;
+		}
+	}
+
 	if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) ||
hidpp->delayed_input)
 		/* if the input nodes are already created, we can stop now
*/
 		return;
@@ -4187,12 +4556,16 @@ static int hidpp_probe(struct hid_device *hdev,
const struct hid_device_id *id)
 	if (hidpp->quirks & HIDPP_QUIRK_UNIFYING)
 		hidpp_unifying_init(hidpp);

-	connected = hidpp_root_get_protocol_version(hidpp) == 0;
+	ret = hidpp_root_get_protocol_version(hidpp);
+	connected = ret == 0;
 	atomic_set(&hidpp->connected, connected);
 	if (!(hidpp->quirks & HIDPP_QUIRK_UNIFYING)) {
 		if (!connected) {
+			if (ret == -ETIMEDOUT)
+				hid_err(hdev, "Device connection timed
out");
+			else
+				hid_err(hdev, "Device not connected");
 			ret = -ENODEV;
-			hid_err(hdev, "Device not connected");
 			goto hid_hw_init_fail;
 		}

@@ -4357,6 +4730,9 @@ static const struct hid_device_id hidpp_devices[] =
{
 		.driver_data = HIDPP_QUIRK_CLASS_G920 |
HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
 	{ /* Logitech G Pro Gaming Mouse over USB */
 	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) },
+	{ /* Logitech Litra Glow over USB*/
+	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_LITRA_GLOW),
+		.driver_data = HIDPP_QUIRK_DELAYED_INIT |
HIDPP_QUIRK_FORCE_OUTPUT_REPORTS },

 	{ /* MX5000 keyboard over Bluetooth */
 	  HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb305),
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 50e1c717fc0a..0332662692d2 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -491,6 +491,7 @@ static const struct hid_device_id
hid_have_special_driver[] = {
 #endif
 #if IS_ENABLED(CONFIG_HID_LOGITECH_HIDPP)
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_G920_WHEEL) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_LITRA_GLOW) },
 #endif
 #if IS_ENABLED(CONFIG_HID_MAGICMOUSE)
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_MAGICMOUSE) },





[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux