feat: Add libx52io function to compare two reports

This stems from a static analysis warning about using memcmp to compare
two libx52io_report structures, since there is likely padding which
could invisibly be containing different values, especially if the
reports are allocated on the stack and not explicitly initialized.

The function returns a bitmask of the changed fields, which could
conceivably be used by a downstream application to quickly determine the
changes.
copy-and-reset-reports
nirenjan 2026-10-21 13:16:47 -07:00
parent 90d8af7964
commit b3d587e9bf
6 changed files with 391 additions and 3 deletions

View File

@ -1,7 +1,7 @@
/*
* Saitek X52 Pro MFD & LED driver - Event test utility
*
* Copyright (C) 2012-2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
* Copyright (C) 2012-2026 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
@ -136,7 +136,7 @@ int main(void)
* Successful read, compare the current report against the previous
* one and display the result
*/
if (memcmp(&last, &curr, sizeof(curr)) == 0) {
if (libx52io_compare_reports(&last, &curr) == 0) {
/* No change, ignore the output */
continue;
}

View File

@ -1,7 +1,7 @@
/*
* Saitek X52 IO driver
*
* Copyright (C) 2012-2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
* Copyright (C) 2012-2026 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
@ -278,6 +278,66 @@ struct libx52io_report {
*/
typedef struct libx52io_report libx52io_report;
/**
* @name Bitmask Definitions
* @anchor libx52io_report_bitmask
* @brief These bitmasks correspond to the individual bits that changed
* @{
*/
#define LIBX52IO_REPORT_AXIS_X (1ULL << (LIBX52IO_AXIS_X))
#define LIBX52IO_REPORT_AXIS_Y (1ULL << (LIBX52IO_AXIS_Y))
#define LIBX52IO_REPORT_AXIS_RZ (1ULL << (LIBX52IO_AXIS_RZ))
#define LIBX52IO_REPORT_AXIS_Z (1ULL << (LIBX52IO_AXIS_Z))
#define LIBX52IO_REPORT_AXIS_RX (1ULL << (LIBX52IO_AXIS_RX))
#define LIBX52IO_REPORT_AXIS_RY (1ULL << (LIBX52IO_AXIS_RY))
#define LIBX52IO_REPORT_AXIS_SLIDER (1ULL << (LIBX52IO_AXIS_SLIDER))
#define LIBX52IO_REPORT_AXIS_THUMBX (1ULL << (LIBX52IO_AXIS_THUMBX))
#define LIBX52IO_REPORT_AXIS_THUMBY (1ULL << (LIBX52IO_AXIS_THUMBY))
#define LIBX52IO_REPORT_AXIS_HATX (1ULL << (LIBX52IO_AXIS_HATX))
#define LIBX52IO_REPORT_AXIS_HATY (1ULL << (LIBX52IO_AXIS_HATY))
#define LIBX52IO_REPORT_BTN_TRIGGER (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_TRIGGER))
#define LIBX52IO_REPORT_BTN_TRIGGER_2 (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_TRIGGER_2))
#define LIBX52IO_REPORT_BTN_FIRE (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_FIRE))
#define LIBX52IO_REPORT_BTN_PINKY (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_PINKY))
#define LIBX52IO_REPORT_BTN_A (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_A))
#define LIBX52IO_REPORT_BTN_B (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_B))
#define LIBX52IO_REPORT_BTN_C (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_C))
#define LIBX52IO_REPORT_BTN_D (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_D))
#define LIBX52IO_REPORT_BTN_E (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_E))
#define LIBX52IO_REPORT_BTN_T1_UP (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_T1_UP))
#define LIBX52IO_REPORT_BTN_T1_DN (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_T1_DN))
#define LIBX52IO_REPORT_BTN_T2_UP (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_T2_UP))
#define LIBX52IO_REPORT_BTN_T2_DN (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_T2_DN))
#define LIBX52IO_REPORT_BTN_T3_UP (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_T3_UP))
#define LIBX52IO_REPORT_BTN_T3_DN (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_T3_DN))
#define LIBX52IO_REPORT_BTN_POV_1_N (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_1_N))
#define LIBX52IO_REPORT_BTN_POV_1_E (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_1_E))
#define LIBX52IO_REPORT_BTN_POV_1_S (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_1_S))
#define LIBX52IO_REPORT_BTN_POV_1_W (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_1_W))
#define LIBX52IO_REPORT_BTN_POV_2_N (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_2_N))
#define LIBX52IO_REPORT_BTN_POV_2_E (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_2_E))
#define LIBX52IO_REPORT_BTN_POV_2_S (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_2_S))
#define LIBX52IO_REPORT_BTN_POV_2_W (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_2_W))
#define LIBX52IO_REPORT_BTN_CLUTCH (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_CLUTCH))
#define LIBX52IO_REPORT_BTN_MOUSE_PRIMARY (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MOUSE_PRIMARY))
#define LIBX52IO_REPORT_BTN_MOUSE_SECONDARY (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MOUSE_SECONDARY))
#define LIBX52IO_REPORT_BTN_MOUSE_SCROLL_UP (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MOUSE_SCROLL_UP))
#define LIBX52IO_REPORT_BTN_MOUSE_SCROLL_DN (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MOUSE_SCROLL_DN))
#define LIBX52IO_REPORT_BTN_FUNCTION (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_FUNCTION))
#define LIBX52IO_REPORT_BTN_START_STOP (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_START_STOP))
#define LIBX52IO_REPORT_BTN_RESET (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_RESET))
#define LIBX52IO_REPORT_BTN_PG_UP (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_PG_UP))
#define LIBX52IO_REPORT_BTN_PG_DN (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_PG_DN))
#define LIBX52IO_REPORT_BTN_UP (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_UP))
#define LIBX52IO_REPORT_BTN_DN (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_DN))
#define LIBX52IO_REPORT_BTN_SELECT (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_SELECT))
#define LIBX52IO_REPORT_BTN_MODE_1 (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MODE_1))
#define LIBX52IO_REPORT_BTN_MODE_2 (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MODE_2))
#define LIBX52IO_REPORT_BTN_MODE_3 (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MODE_3))
#define LIBX52IO_REPORT_MODE (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BUTTON_MAX))
#define LIBX52IO_REPORT_HAT (1ULL << (LIBX52IO_AXIS_MAX + LIBX52IO_BUTTON_MAX + 1))
/** @} */
/**
* @brief Initialize the IO library
*
@ -556,6 +616,19 @@ LIBX52IO_API const char *libx52io_get_product_string(libx52io_context *ctx);
*/
LIBX52IO_API const char *libx52io_get_serial_number_string(libx52io_context *ctx);
/**
* @brief Compare two reports for differences
*
* Returns a bitmask of fields that changed between the \c prev and \c curr
* reports. If there is no difference, then returns 0. Passing a NULL pointer
* to either input parameter also returns a 0.
*
* @param[in] prev Pointer to the previous report
* @param[in] curr Pointer to the current report
*
* @returns 64-bit bitmask of which fields changed. See \ref libx52io_report_bitmask
*/
LIBX52IO_API uint64_t libx52io_compare_reports(const libx52io_report *prev, const libx52io_report *curr);
/** @} */
#ifdef __cplusplus

101
libx52io/compare.c 100644
View File

@ -0,0 +1,101 @@
/*
* Saitek X52 IO driver
*
* Copyright (C) 2012-2026 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#include "build-config.h"
#include <stdlib.h>
#include <string.h>
#include "common.h"
#include "usb-ids.h"
#include "gettext.h"
uint64_t libx52io_compare_reports(const libx52io_report *prev,
const libx52io_report *curr)
{
uint64_t status = 0;
if (prev == NULL || curr == NULL) {
// Treat either input being NULL as no change.
return 0;
}
#define CHECK_AXIS(axs) do { \
if (prev->axis[LIBX52IO_AXIS_ ## axs] != curr->axis[LIBX52IO_AXIS_ ## axs]) { \
status |= LIBX52IO_REPORT_AXIS_ ## axs; \
} \
} while(0)
CHECK_AXIS(X);
CHECK_AXIS(Y);
CHECK_AXIS(RZ);
CHECK_AXIS(Z);
CHECK_AXIS(RX);
CHECK_AXIS(RY);
CHECK_AXIS(SLIDER);
CHECK_AXIS(THUMBX);
CHECK_AXIS(THUMBY);
CHECK_AXIS(HATX);
CHECK_AXIS(HATY);
#undef CHECK_AXIS
#define CHECK_BUTTON(btn) do { \
if (prev->button[LIBX52IO_BTN_ ## btn] != curr->button[LIBX52IO_BTN_ ## btn]) { \
status |= LIBX52IO_REPORT_BTN_ ## btn; \
} \
} while(0)
CHECK_BUTTON(TRIGGER);
CHECK_BUTTON(TRIGGER_2);
CHECK_BUTTON(FIRE);
CHECK_BUTTON(PINKY);
CHECK_BUTTON(A);
CHECK_BUTTON(B);
CHECK_BUTTON(C);
CHECK_BUTTON(D);
CHECK_BUTTON(E);
CHECK_BUTTON(T1_UP);
CHECK_BUTTON(T1_DN);
CHECK_BUTTON(T2_UP);
CHECK_BUTTON(T2_DN);
CHECK_BUTTON(T3_UP);
CHECK_BUTTON(T3_DN);
CHECK_BUTTON(POV_1_N);
CHECK_BUTTON(POV_1_E);
CHECK_BUTTON(POV_1_S);
CHECK_BUTTON(POV_1_W);
CHECK_BUTTON(POV_2_N);
CHECK_BUTTON(POV_2_E);
CHECK_BUTTON(POV_2_S);
CHECK_BUTTON(POV_2_W);
CHECK_BUTTON(CLUTCH);
CHECK_BUTTON(MOUSE_PRIMARY);
CHECK_BUTTON(MOUSE_SECONDARY);
CHECK_BUTTON(MOUSE_SCROLL_UP);
CHECK_BUTTON(MOUSE_SCROLL_DN);
CHECK_BUTTON(FUNCTION);
CHECK_BUTTON(START_STOP);
CHECK_BUTTON(RESET);
CHECK_BUTTON(PG_UP);
CHECK_BUTTON(PG_DN);
CHECK_BUTTON(UP);
CHECK_BUTTON(DN);
CHECK_BUTTON(SELECT);
CHECK_BUTTON(MODE_1);
CHECK_BUTTON(MODE_2);
CHECK_BUTTON(MODE_3);
#undef CHECK_BUTTON
if (prev->mode != curr->mode) {
status |= LIBX52IO_REPORT_MODE;
}
if (prev->hat != curr->hat) {
status |= LIBX52IO_REPORT_HAT;
}
return status;
}

View File

@ -6,6 +6,8 @@ libx52io_files = files(
'parser.c',
'strings.c',
'device.c',
'validation.c',
'compare.c',
)
lib_libx52io = library('x52io', libx52io_files,
@ -42,4 +44,10 @@ test_io_strings = executable('test-io-strings', 'test_io_strings.c', libx52io_fi
)
test('test-io-strings', test_io_strings, protocol: 'tap')
test_compare = executable('test-io-compare', 'test_compare.c', libx52io_files,
build_by_default: false,
dependencies: [dep_cmocka, dep_hidapi, dep_intl],
include_directories: [includes],
)
test('test-io-compare', test_compare, protocol: 'tap')

View File

@ -0,0 +1,136 @@
/*
* Saitek X52 IO driver - Compare test suite
*
* Copyright (C) 2012-2026 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include <string.h>
#include "common.h"
#define TEST_AXIS \
FUNC(AXIS_X) \
FUNC(AXIS_Y) \
FUNC(AXIS_RZ) \
FUNC(AXIS_Z) \
FUNC(AXIS_RX) \
FUNC(AXIS_RY) \
FUNC(AXIS_SLIDER) \
FUNC(AXIS_THUMBX) \
FUNC(AXIS_THUMBY) \
FUNC(AXIS_HATX) \
FUNC(AXIS_HATY)
#define FUNC(axis_name) static void test_ ## axis_name (void **state) \
{ \
(void)state; \
libx52io_report prev = { 0 }; \
libx52io_report curr = { 0 }; \
uint64_t diff; \
curr.axis[LIBX52IO_ ## axis_name] = 1; \
diff = libx52io_compare_reports(&prev, &curr); \
assert_int_equal(diff, LIBX52IO_REPORT_ ## axis_name); \
}
TEST_AXIS
#undef FUNC
#define TEST_BUTTON \
FUNC(BTN_TRIGGER) \
FUNC(BTN_TRIGGER_2) \
FUNC(BTN_FIRE) \
FUNC(BTN_PINKY) \
FUNC(BTN_A) \
FUNC(BTN_B) \
FUNC(BTN_C) \
FUNC(BTN_D) \
FUNC(BTN_E) \
FUNC(BTN_T1_UP) \
FUNC(BTN_T1_DN) \
FUNC(BTN_T2_UP) \
FUNC(BTN_T2_DN) \
FUNC(BTN_T3_UP) \
FUNC(BTN_T3_DN) \
FUNC(BTN_POV_1_N) \
FUNC(BTN_POV_1_E) \
FUNC(BTN_POV_1_S) \
FUNC(BTN_POV_1_W) \
FUNC(BTN_POV_2_N) \
FUNC(BTN_POV_2_E) \
FUNC(BTN_POV_2_S) \
FUNC(BTN_POV_2_W) \
FUNC(BTN_CLUTCH) \
FUNC(BTN_MOUSE_PRIMARY) \
FUNC(BTN_MOUSE_SECONDARY) \
FUNC(BTN_MOUSE_SCROLL_UP) \
FUNC(BTN_MOUSE_SCROLL_DN) \
FUNC(BTN_FUNCTION) \
FUNC(BTN_START_STOP) \
FUNC(BTN_RESET) \
FUNC(BTN_PG_UP) \
FUNC(BTN_PG_DN) \
FUNC(BTN_UP) \
FUNC(BTN_DN) \
FUNC(BTN_SELECT) \
FUNC(BTN_MODE_1) \
FUNC(BTN_MODE_2) \
FUNC(BTN_MODE_3)
#define FUNC(button_name) static void test_ ## button_name (void **state) \
{ \
(void)state; \
libx52io_report prev = { 0 }; \
libx52io_report curr = { 0 }; \
uint64_t diff; \
curr.button[LIBX52IO_ ## button_name] = 1; \
diff = libx52io_compare_reports(&prev, &curr); \
assert_int_equal(diff, LIBX52IO_REPORT_ ## button_name); \
}
TEST_BUTTON
#undef FUNC
static void test_mode(void **state)
{
(void)state;
libx52io_report prev = { 0 };
libx52io_report curr = { 0 };
uint64_t diff;
curr.mode = 1;
diff = libx52io_compare_reports(&prev, &curr);
assert_int_equal(diff, LIBX52IO_REPORT_MODE);
}
static void test_hat(void **state)
{
(void)state;
libx52io_report prev = { 0 };
libx52io_report curr = { 0 };
uint64_t diff;
curr.hat = 1;
diff = libx52io_compare_reports(&prev, &curr);
assert_int_equal(diff, LIBX52IO_REPORT_HAT);
}
#define FUNC(x) cmocka_unit_test(test_ ## x),
const struct CMUnitTest tests[] = {
TEST_AXIS
TEST_BUTTON
FUNC(mode)
FUNC(hat)
};
#undef FUNC
int main(void)
{
cmocka_set_message_output(CM_OUTPUT_TAP);
cmocka_run_group_tests(tests, NULL, NULL);
return 0;
}

View File

@ -0,0 +1,70 @@
/*
* Saitek X52 IO driver
*
* Copyright (C) 2012-2026 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#include "build-config.h"
#include <stdlib.h>
#include <libx52/libx52io.h>
_Static_assert((LIBX52IO_AXIS_MAX + LIBX52IO_BUTTON_MAX + 1) < 64,
"CRITICAL: Report bitmask definitions exceed 64-bit allocation capacity!");
// __attribute__((unused)) stops the compiler from throwing an unused variable warning
__attribute__((unused)) static const char unique_bit_check[] = {
[LIBX52IO_AXIS_X] = 1,
[LIBX52IO_AXIS_Y] = 1,
[LIBX52IO_AXIS_RZ] = 1,
[LIBX52IO_AXIS_Z] = 1,
[LIBX52IO_AXIS_RX] = 1,
[LIBX52IO_AXIS_RY] = 1,
[LIBX52IO_AXIS_SLIDER] = 1,
[LIBX52IO_AXIS_THUMBX] = 1,
[LIBX52IO_AXIS_THUMBY] = 1,
[LIBX52IO_AXIS_HATX] = 1,
[LIBX52IO_AXIS_HATY] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_TRIGGER] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_TRIGGER_2] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_FIRE] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_PINKY] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_A] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_B] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_C] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_D] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_E] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_T1_UP] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_T1_DN] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_T2_UP] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_T2_DN] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_T3_UP] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_T3_DN] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_1_N] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_1_E] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_1_S] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_1_W] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_2_N] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_2_E] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_2_S] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_POV_2_W] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_CLUTCH] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MOUSE_PRIMARY] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MOUSE_SECONDARY] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MOUSE_SCROLL_UP] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MOUSE_SCROLL_DN] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_FUNCTION] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_START_STOP] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_RESET] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_PG_UP] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_PG_DN] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_UP] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_DN] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_SELECT] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MODE_1] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MODE_2] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BTN_MODE_3] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BUTTON_MAX] = 1,
[LIBX52IO_AXIS_MAX + LIBX52IO_BUTTON_MAX + 1] = 1,
};