[1/3] drivers: hid: fix G940 axis/button mappings
diff mbox series

Message ID 20190311211913.GA25306@nova.chris.boyle.name
State New
Delegated to: Jiri Kosina
Headers show
Series
  • [1/3] drivers: hid: fix G940 axis/button mappings
Related show

Commit Message

Chris Boyle March 11, 2019, 9:19 p.m. UTC
Provide a complete map of axes and buttons for the Logitech Flight System
G940, which fixes several issues:

Stop conflating the stick X/Y axes with the mini-stick (hat) axes. This
stops reported X jumping between stick X and hat X (likewise Y).

Stop conflating the other three hat switches (non-mini-stick) with each
other. This was caused by commit 190d7f02ce8e ("HID: input: do not
increment usages when a duplicate is found")

Report TRIM1 and TRIM2, previously ignored as unrecognised usage types.

Report the MODE switch and hand sensor, previously ignored as they're in
a vendor page.

Signed-off-by: Chris Boyle <chris@boyle.name>
---
 drivers/hid/hid-lg.c | 100 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 100 insertions(+)

Comments

Jiri Kosina March 19, 2019, 1:48 p.m. UTC | #1
[ CCing Gary Stein ]

On Mon, 11 Mar 2019, Chris Boyle wrote:

> Provide a complete map of axes and buttons for the Logitech Flight System
> G940, which fixes several issues:
> 
> Stop conflating the stick X/Y axes with the mini-stick (hat) axes. This
> stops reported X jumping between stick X and hat X (likewise Y).
> 
> Stop conflating the other three hat switches (non-mini-stick) with each
> other. This was caused by commit 190d7f02ce8e ("HID: input: do not
> increment usages when a duplicate is found")
> 
> Report TRIM1 and TRIM2, previously ignored as unrecognised usage types.
> 
> Report the MODE switch and hand sensor, previously ignored as they're in
> a vendor page.
> 
> Signed-off-by: Chris Boyle <chris@boyle.name>
> ---
>  drivers/hid/hid-lg.c | 100 +++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 100 insertions(+)
> 
> diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
> index 5d419a95b6c2..d822cd98d677 100644
> --- a/drivers/hid/hid-lg.c
> +++ b/drivers/hid/hid-lg.c
> @@ -7,6 +7,7 @@
>   *  Copyright (c) 2006-2007 Jiri Kosina
>   *  Copyright (c) 2008 Jiri Slaby
>   *  Copyright (c) 2010 Hendrik Iben
> + *  Copyright (c) 2019 Chris Boyle
>   */
>  
>  /*
> @@ -648,6 +649,101 @@ static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
>  	return 1;
>  }
>  
> +#define HID_SC_TRIM_AILERON (HID_UP_SIMULATION | 0xb1)
> +#define HID_SC_TRIM_PITCH   (HID_UP_SIMULATION | 0xb9)
> +#define HID_SC_THROTTLE     (HID_UP_SIMULATION | 0xbb)
> +#define HID_VENDOR_USAGE_1  (HID_UP_MSVENDOR   | 0x01)
> +
> +#define map_abs(c) hid_map_usage(hi, usage, bit, max, EV_ABS, (c))
> +#define map_key(c) hid_map_usage(hi, usage, bit, max, EV_KEY, (c))
> +
> +static int lg_g940_mapping(struct hid_input *hi, struct hid_field *field,
> +			   struct hid_usage *usage, unsigned long **bit,
> +			   int *max)
> +{
> +	static const u16 button_map[] = {
> +		/* trigger, red FIRE button, S1-3 (top) */
> +		BTN_TRIGGER, BTN_THUMB, BTN_BASE, BTN_BASE2, BTN_BASE3,
> +		/* S4 (side), S5 (pinkie), press hat, trigger stage 2 */
> +		BTN_THUMB2, BTN_PINKIE, BTN_TOP2, BTN_TOP,
> +		/* T1-4 (on throttle handle) */
> +		BTN_TRIGGER_HAPPY11, BTN_TRIGGER_HAPPY12,
> +		BTN_TRIGGER_HAPPY13, BTN_TRIGGER_HAPPY14,
> +		/* P1-8 (base of throttle, with LEDs) */
> +		BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY2, BTN_TRIGGER_HAPPY3,
> +		BTN_TRIGGER_HAPPY4, BTN_TRIGGER_HAPPY5, BTN_TRIGGER_HAPPY6,
> +		BTN_TRIGGER_HAPPY7, BTN_TRIGGER_HAPPY8
> +	};
> +
> +	static const u16 vendor_1_map[] = {
> +		BTN_TRIGGER_HAPPY9,  /* mode switch = 1    */
> +		BTN_TRIGGER_HAPPY10, /* mode switch = 3    */
> +		0, 0,                /* always 0?          */
> +		0, 0,                /* pedals/throttle ok */
> +		0, 0,                /* always 1?          */
> +		BTN_DEAD,            /* hand sensor        */
> +		0, 0                 /* always 0? / AC ok  */
> +	};
> +
> +	switch (usage->hid) {
> +	case HID_GD_X: case HID_GD_Y:
> +		/* offset 0 is joystick motion; 96 is mini-stick/hat */
> +		map_abs(((field->report_offset > 0) ? ABS_TILT_X : ABS_X)
> +			+ usage->hid - HID_GD_X);
> +		return 1;
> +	case HID_GD_Z:  /* rudder pedals */
> +		map_abs(ABS_RUDDER);
> +		return 1;
> +	case HID_GD_RX:  /* right toe brake */
> +		map_abs(ABS_GAS);
> +		return 1;
> +	case HID_GD_RY:  /* left toe brake */
> +		map_abs(ABS_BRAKE);
> +		return 1;
> +	case HID_GD_RZ:  /* TRIM3 (rudder trim) */
> +		map_abs(ABS_RZ);
> +		return 1;
> +	case HID_GD_DIAL: /* index 0 is R2, 1 is R1 (front/right of throttle) */
> +		map_abs(usage->usage_index ? ABS_WHEEL : ABS_MISC);
> +		return 1;
> +	case HID_GD_HATSWITCH: /* offset 20 on stick, 24 & 28 on throttle */
> +		if (field->report_offset < 20 || field->report_offset > 28 ||
> +		    field->report_offset % 4)
> +			return 0;
> +		usage->hat_min = field->logical_minimum;
> +		usage->hat_max = field->logical_maximum;
> +		map_abs(ABS_HAT0X + (field->report_offset - 20) / 2);
> +		return 1;
> +	case HID_SC_TRIM_AILERON:  /* TRIM1 (pitch trim!) */
> +		map_abs(ABS_RX);
> +		return 1;
> +	case HID_SC_TRIM_PITCH:  /* TRIM2 (aileron trim!) */
> +		map_abs(ABS_RY);
> +		return 1;
> +	case HID_SC_THROTTLE:  /* two halves; index 0 is right-hand */
> +		map_abs(usage->usage_index ? ABS_Z : ABS_THROTTLE);
> +		return 1;
> +	default:
> +		break;
> +	}
> +
> +	if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON &&
> +	    usage->usage_index < ARRAY_SIZE(button_map) &&
> +	    button_map[usage->usage_index] != 0) {
> +		map_key(button_map[usage->usage_index]);
> +		return 1;
> +	}
> +
> +	if (usage->hid == HID_VENDOR_USAGE_1 &&
> +	    usage->usage_index < ARRAY_SIZE(vendor_1_map) &&
> +	    vendor_1_map[usage->usage_index] != 0) {
> +		map_key(vendor_1_map[usage->usage_index]);
> +		return 1;
> +	}
> +
> +	return 0;
> +}
> +
>  static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
>  		struct hid_field *field, struct hid_usage *usage,
>  		unsigned long **bit, int *max)
> @@ -678,6 +774,10 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
>  	if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
>  		return 1;
>  
> +	if (hdev->product == USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 &&
> +	    lg_g940_mapping(hi, field, usage, bit, max))
> +		return 1;
> +
>  	if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
>  		return 0;
>  
> -- 
> 2.17.1
> 
> 
> -- 
> Chris Boyle
> https://chris.boyle.name/
>

Patch
diff mbox series

diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index 5d419a95b6c2..d822cd98d677 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -7,6 +7,7 @@ 
  *  Copyright (c) 2006-2007 Jiri Kosina
  *  Copyright (c) 2008 Jiri Slaby
  *  Copyright (c) 2010 Hendrik Iben
+ *  Copyright (c) 2019 Chris Boyle
  */
 
 /*
@@ -648,6 +649,101 @@  static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
 	return 1;
 }
 
+#define HID_SC_TRIM_AILERON (HID_UP_SIMULATION | 0xb1)
+#define HID_SC_TRIM_PITCH   (HID_UP_SIMULATION | 0xb9)
+#define HID_SC_THROTTLE     (HID_UP_SIMULATION | 0xbb)
+#define HID_VENDOR_USAGE_1  (HID_UP_MSVENDOR   | 0x01)
+
+#define map_abs(c) hid_map_usage(hi, usage, bit, max, EV_ABS, (c))
+#define map_key(c) hid_map_usage(hi, usage, bit, max, EV_KEY, (c))
+
+static int lg_g940_mapping(struct hid_input *hi, struct hid_field *field,
+			   struct hid_usage *usage, unsigned long **bit,
+			   int *max)
+{
+	static const u16 button_map[] = {
+		/* trigger, red FIRE button, S1-3 (top) */
+		BTN_TRIGGER, BTN_THUMB, BTN_BASE, BTN_BASE2, BTN_BASE3,
+		/* S4 (side), S5 (pinkie), press hat, trigger stage 2 */
+		BTN_THUMB2, BTN_PINKIE, BTN_TOP2, BTN_TOP,
+		/* T1-4 (on throttle handle) */
+		BTN_TRIGGER_HAPPY11, BTN_TRIGGER_HAPPY12,
+		BTN_TRIGGER_HAPPY13, BTN_TRIGGER_HAPPY14,
+		/* P1-8 (base of throttle, with LEDs) */
+		BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY2, BTN_TRIGGER_HAPPY3,
+		BTN_TRIGGER_HAPPY4, BTN_TRIGGER_HAPPY5, BTN_TRIGGER_HAPPY6,
+		BTN_TRIGGER_HAPPY7, BTN_TRIGGER_HAPPY8
+	};
+
+	static const u16 vendor_1_map[] = {
+		BTN_TRIGGER_HAPPY9,  /* mode switch = 1    */
+		BTN_TRIGGER_HAPPY10, /* mode switch = 3    */
+		0, 0,                /* always 0?          */
+		0, 0,                /* pedals/throttle ok */
+		0, 0,                /* always 1?          */
+		BTN_DEAD,            /* hand sensor        */
+		0, 0                 /* always 0? / AC ok  */
+	};
+
+	switch (usage->hid) {
+	case HID_GD_X: case HID_GD_Y:
+		/* offset 0 is joystick motion; 96 is mini-stick/hat */
+		map_abs(((field->report_offset > 0) ? ABS_TILT_X : ABS_X)
+			+ usage->hid - HID_GD_X);
+		return 1;
+	case HID_GD_Z:  /* rudder pedals */
+		map_abs(ABS_RUDDER);
+		return 1;
+	case HID_GD_RX:  /* right toe brake */
+		map_abs(ABS_GAS);
+		return 1;
+	case HID_GD_RY:  /* left toe brake */
+		map_abs(ABS_BRAKE);
+		return 1;
+	case HID_GD_RZ:  /* TRIM3 (rudder trim) */
+		map_abs(ABS_RZ);
+		return 1;
+	case HID_GD_DIAL: /* index 0 is R2, 1 is R1 (front/right of throttle) */
+		map_abs(usage->usage_index ? ABS_WHEEL : ABS_MISC);
+		return 1;
+	case HID_GD_HATSWITCH: /* offset 20 on stick, 24 & 28 on throttle */
+		if (field->report_offset < 20 || field->report_offset > 28 ||
+		    field->report_offset % 4)
+			return 0;
+		usage->hat_min = field->logical_minimum;
+		usage->hat_max = field->logical_maximum;
+		map_abs(ABS_HAT0X + (field->report_offset - 20) / 2);
+		return 1;
+	case HID_SC_TRIM_AILERON:  /* TRIM1 (pitch trim!) */
+		map_abs(ABS_RX);
+		return 1;
+	case HID_SC_TRIM_PITCH:  /* TRIM2 (aileron trim!) */
+		map_abs(ABS_RY);
+		return 1;
+	case HID_SC_THROTTLE:  /* two halves; index 0 is right-hand */
+		map_abs(usage->usage_index ? ABS_Z : ABS_THROTTLE);
+		return 1;
+	default:
+		break;
+	}
+
+	if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON &&
+	    usage->usage_index < ARRAY_SIZE(button_map) &&
+	    button_map[usage->usage_index] != 0) {
+		map_key(button_map[usage->usage_index]);
+		return 1;
+	}
+
+	if (usage->hid == HID_VENDOR_USAGE_1 &&
+	    usage->usage_index < ARRAY_SIZE(vendor_1_map) &&
+	    vendor_1_map[usage->usage_index] != 0) {
+		map_key(vendor_1_map[usage->usage_index]);
+		return 1;
+	}
+
+	return 0;
+}
+
 static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 		struct hid_field *field, struct hid_usage *usage,
 		unsigned long **bit, int *max)
@@ -678,6 +774,10 @@  static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 	if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
 		return 1;
 
+	if (hdev->product == USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 &&
+	    lg_g940_mapping(hi, field, usage, bit, max))
+		return 1;
+
 	if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
 		return 0;