From 9d3acfd35a13a8ee55d2c21d4c0cb905464cf487 Mon Sep 17 00:00:00 2001 From: nirenjan Date: Wed, 8 Jul 2020 17:28:06 -0700 Subject: [PATCH] Add report parser implementation --- lib/libx52io/Makefile.am | 2 +- lib/libx52io/io_common.h | 2 +- lib/libx52io/io_parser.c | 229 +++++++++++++++++++++++++++++++++++++++ lib/libx52io/libx52io.h | 11 +- 4 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 lib/libx52io/io_parser.c diff --git a/lib/libx52io/Makefile.am b/lib/libx52io/Makefile.am index 2f074f0..748017c 100644 --- a/lib/libx52io/Makefile.am +++ b/lib/libx52io/Makefile.am @@ -13,7 +13,7 @@ lib_LTLIBRARIES = libx52io.la libx52io_v_CUR=0 libx52io_v_AGE=0 libx52io_v_REV=0 -libx52io_la_SOURCES = io_core.c io_axis.c +libx52io_la_SOURCES = io_core.c io_axis.c io_parser.c libx52io_la_CFLAGS = @HIDAPI_CFLAGS@ -DLOCALEDIR=\"$(localedir)\" -I $(top_srcdir) $(WARN_CFLAGS) libx52io_la_LDFLAGS = \ -export-symbols-regex '^libx52io_' \ diff --git a/lib/libx52io/io_common.h b/lib/libx52io/io_common.h index 0c01954..fb030ae 100644 --- a/lib/libx52io/io_common.h +++ b/lib/libx52io/io_common.h @@ -14,7 +14,7 @@ #include "hidapi.h" // Function handler for parsing reports -typedef int (*x52_parse_report)(unsigned char *data, int length); +typedef int (*x52_parse_report)(unsigned char *data, int length, libx52io_report *report); struct libx52io_context { hid_device *handle; diff --git a/lib/libx52io/io_parser.c b/lib/libx52io/io_parser.c new file mode 100644 index 0000000..d6ac443 --- /dev/null +++ b/lib/libx52io/io_parser.c @@ -0,0 +1,229 @@ +/* + * Saitek X52 IO driver - report parser + * + * Copyright (C) 2012-2020 Nirenjan Krishnan (nirenjan@nirenjan.org) + * + * SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0 + */ + +#include +#include "io_common.h" + +static void map_axis(unsigned char *data, int thumb_pos, libx52io_report *report) +{ + /* + * The bytes containing the throttle axes are the same, with only the + * position of the thumbstick report varying between the X52 and X52Pro. + * Therefore, we can share the code between the different parsers + */ + report->axis[LIBX52IO_AXIS_Z] = data[4]; + report->axis[LIBX52IO_AXIS_RX] = data[5]; + report->axis[LIBX52IO_AXIS_RY] = data[6]; + report->axis[LIBX52IO_AXIS_SLIDER] = data[7]; + report->axis[LIBX52IO_AXIS_THUMBX] = data[thumb_pos] >> 4; + report->axis[LIBX52IO_AXIS_THUMBY] = data[thumb_pos] & 0xf; +} + +static void map_buttons(unsigned char *data, const int *button_map, libx52io_report *report) +{ + /* + * The bytes containing the buttons are the same between the X52 and X52Pro. + * Therefore, we can share the code between the two parsers, and we just + * need a different button map for each device. + */ + uint64_t buttons = 0; + int i; + buttons |= data[12]; buttons <<= 8; + buttons |= data[11]; buttons <<= 8; + buttons |= data[10]; buttons <<= 8; + buttons |= data[9]; buttons <<= 8; + buttons |= data[8]; + + for (i = 0; button_map[i] != -1; i++) { + int btn = button_map[i]; + report->button[btn] = !!(buttons & (1 << i)); + } + + if (report->button[LIBX52IO_BTN_MODE_1]) { + report->mode = 1; + } else if (report->button[LIBX52IO_BTN_MODE_2]) { + report->mode = 2; + } else if (report->button[LIBX52IO_BTN_MODE_3]) { + report->mode = 3; + } + /* + * NOTE: It is possible to hold the mode selector in a position such that + * none of the mode buttons actually report as selected. It is also + * possible that it could be in a transient state between two adjacent + * modes. Either way, we don't want to modify the report, so leave it. It + * is up to the application to handle the case where mode doesn't change. + */ +} + +#define B(x) LIBX52IO_BTN_ ## x + +static int parse_x52(unsigned char *data, int length, libx52io_report *report) +{ + /* + * Report layout for X52 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | X axis data | Y axis data | Rz axis data | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Throttle | Rx axis data | Ry axis data | Slider data | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Buttons 7-0 | Buttons 15-8 | Buttons 23-16 | Buttons 31-24 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Hat |///|Btn| MouseX| MouseY| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + uint32_t axis; + + static const int button_map[] = { + B(TRIGGER), + B(FIRE), + B(A), + B(B), + B(C), + B(PINKY), + B(D), + B(E), + B(T1_UP), + B(T1_DN), + B(T2_UP), + B(T2_DN), + B(T3_UP), + B(T3_DN), + B(TRIGGER_2), + B(POV_1_N), + B(POV_1_E), + B(POV_1_S), + B(POV_1_W), + B(POV_2_N), + B(POV_2_E), + B(POV_2_S), + B(POV_2_W), + B(MODE_1), + B(MODE_2), + B(MODE_3), + B(FUNCTION), + B(START_STOP), + B(RESET), + B(CLUTCH), + B(MOUSE_PRIMARY), + B(MOUSE_SECONDARY), + B(MOUSE_SCROLL_UP), + B(MOUSE_SCROLL_DN), + -1 + }; + + if (length != 14) { + return 1; + } + + axis = (data[3] << 24) | + (data[2] << 16) | + (data[1] << 8) | + data[0]; + + report->axis[LIBX52IO_AXIS_X] = axis & 0x7ff; + report->axis[LIBX52IO_AXIS_Y] = (axis >> 11) & 0x7ff; + report->axis[LIBX52IO_AXIS_RZ] = (axis >> 22) & 0x3ff; + map_axis(data, 13, report); + + map_buttons(data, button_map, report); + + return 0; +} + +static int parse_x52pro(unsigned char *data, int length, libx52io_report *report) +{ + /* + * Report layout for X52Pro + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | X axis data | Y axis data |///| Rz axis data | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Throttle | Rx axis data | Ry axis data | Slider data | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Buttons 7-0 | Buttons 15-8 | Buttons 23-16 | Buttons 31-24 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |/| Btns 38-32 | Hat |///////| MouseX| MouseY| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + uint32_t axis; + + static const int button_map[] = { + B(TRIGGER), + B(FIRE), + B(A), + B(B), + B(C), + B(PINKY), + B(D), + B(E), + B(T1_UP), + B(T1_DN), + B(T2_UP), + B(T2_DN), + B(T3_UP), + B(T3_DN), + B(TRIGGER_2), + B(MOUSE_PRIMARY), + B(MOUSE_SCROLL_DN), + B(MOUSE_SCROLL_UP), + B(MOUSE_SECONDARY), + B(POV_1_N), + B(POV_1_E), + B(POV_1_S), + B(POV_1_W), + B(POV_2_N), + B(POV_2_E), + B(POV_2_S), + B(POV_2_W), + B(MODE_1), + B(MODE_2), + B(MODE_3), + B(CLUTCH), + B(FUNCTION), + B(START_STOP), + B(RESET), + B(PG_UP), + B(PG_DN), + B(UP), + B(DN), + B(SELECT), + -1 + }; + + if (length != 14) { + return 1; + } + + axis = (data[3] << 24) | + (data[2] << 16) | + (data[1] << 8) | + data[0]; + + report->axis[LIBX52IO_AXIS_X] = axis & 0x3ff; + report->axis[LIBX52IO_AXIS_Y] = (axis >> 10) & 0x3ff; + report->axis[LIBX52IO_AXIS_RZ] = (axis >> 22) & 0x3ff; + map_axis(data, 14, report); + + map_buttons(data, button_map, report); + + return 0; +} + +void _x52io_set_report_parser(libx52io_context *ctx) +{ + switch (ctx->pid) { + case 0x0255: + case 0x075c: + ctx->parser = parse_x52; + break; + + case 0x0762: + ctx->parser = parse_x52pro; + default: + break; + } +} diff --git a/lib/libx52io/libx52io.h b/lib/libx52io/libx52io.h index e82219d..d55bf9d 100644 --- a/lib/libx52io/libx52io.h +++ b/lib/libx52io/libx52io.h @@ -224,6 +224,15 @@ typedef enum { /** Select button, X52 Pro only */ LIBX52IO_BTN_SELECT, + /** Mode 1 - reported as a button */ + LIBX52IO_BTN_MODE_1, + + /** Mode 2 - reported as a button */ + LIBX52IO_BTN_MODE_2, + + /** Mode 3 - reported as a button */ + LIBX52IO_BTN_MODE_3, + LIBX52IO_BUTTON_MAX } libx52io_button; @@ -234,7 +243,7 @@ typedef enum { */ struct libx52io_report { /** Axis values */ - int32_t axis_value[LIBX52IO_AXIS_MAX]; + int32_t axis[LIBX52IO_AXIS_MAX]; /** Button values, true is pressed */ bool button[LIBX52IO_BUTTON_MAX];