mirror of https://github.com/nirenjan/libx52.git
Merge branch 'interface-io'
commit
9486d1dbe4
|
@ -37,14 +37,17 @@ addons:
|
|||
- autopoint
|
||||
- faketime
|
||||
- libcmocka-dev
|
||||
- libhidapi-dev
|
||||
homebrew:
|
||||
packages:
|
||||
- libusb
|
||||
- gettext
|
||||
- cmocka
|
||||
- hidapi
|
||||
|
||||
# Ensure that build dependencies are available on OSX
|
||||
before_script:
|
||||
- if [ "$TRAVIS_OS_NAME" = osx ]; then brew install libusb gettext cmocka ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = osx ]; then brew install libusb gettext cmocka hidapi; fi
|
||||
|
||||
# Enable parallel make
|
||||
env:
|
||||
|
|
|
@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based upon [Keep a Changelog].
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- IO library to read and parse events from a supported joystick.
|
||||
- Event test utility which displays the events similar to evtest.
|
||||
|
||||
## [0.2.1] - 2020-06-28
|
||||
### Added
|
||||
|
|
|
@ -858,6 +858,7 @@ INPUT_ENCODING = UTF-8
|
|||
|
||||
FILE_PATTERNS = \
|
||||
libx52.h \
|
||||
libx52io.h \
|
||||
libx52util.h \
|
||||
x52_cli.c \
|
||||
*.dox
|
||||
|
|
|
@ -17,6 +17,7 @@ Build has been tested on the following operating systems (x86-64 only):
|
|||
* autoconf
|
||||
* autopoint
|
||||
* gettext
|
||||
* hidapi
|
||||
* libtool
|
||||
* libusb-1.0 + headers
|
||||
* pkg-config
|
||||
|
@ -26,8 +27,8 @@ Build has been tested on the following operating systems (x86-64 only):
|
|||
|
||||
| Platform | Install instructions |
|
||||
| -------- | -------------------- |
|
||||
| Ubuntu | `sudo apt-get install automake autoconf gettext autopoint libtool libusb-1.0-0-dev pkg-config python3` |
|
||||
| MacOS + Homebrew | `brew install automake autoconf gettext libtool libusb pkg-config python3` |
|
||||
| Ubuntu | `sudo apt-get install automake autoconf gettext autopoint libhidapi-dev libtool libusb-1.0-0-dev pkg-config python3` |
|
||||
| MacOS + Homebrew | `brew install automake autoconf gettext hidapi libtool libusb pkg-config python3` |
|
||||
|
||||
## Optional Packages
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ EXTRA_DIST = \
|
|||
README.md \
|
||||
config.rpath \
|
||||
gettext.h \
|
||||
usb-ids.h \
|
||||
po/README.md
|
||||
|
||||
# Doxygen support
|
||||
|
|
10
configure.ac
10
configure.ac
|
@ -44,6 +44,14 @@ AC_SUBST([LIBUSB_LIBS])
|
|||
AC_SUBST([X52_PKG_VERSION], [0.1])
|
||||
AC_SUBST([X52_INCLUDE], ["-I \$(top_srcdir)/lib/libx52"])
|
||||
|
||||
# Check for hidapi. This uses a different pkg-config file on Linux vs other
|
||||
# hosts, so check accordingly
|
||||
AM_COND_IF([LINUX], [hidapi_backend=hidapi-hidraw], [hidapi_backend=hidapi])
|
||||
AX_PKG_CHECK_MODULES([HIDAPI], [${hidapi_backend}])
|
||||
AC_SUBST([HIDAPI_CFLAGS])
|
||||
AC_SUBST([HIDAPI_LDFLAGS])
|
||||
AC_SUBST([HIDAPI_LIBS])
|
||||
|
||||
# Check for pthreads
|
||||
ACX_PTHREAD
|
||||
|
||||
|
@ -97,10 +105,12 @@ AC_CONFIG_FILES([ po/Makefile.in
|
|||
lib/libx52/libx52.pc
|
||||
lib/libusbx52/Makefile
|
||||
lib/libx52util/Makefile
|
||||
lib/libx52io/Makefile
|
||||
udev/Makefile
|
||||
utils/Makefile
|
||||
utils/cli/Makefile
|
||||
utils/test/Makefile
|
||||
utils/evtest/Makefile
|
||||
tests/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
|
|
@ -106,5 +106,5 @@ A report would look like the following:
|
|||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Buttons 7-0 | Buttons 15-8 | Buttons 23-16 | Buttons 31-24 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Hat |///|Btn| MouseX| MouseY|
|
||||
| Hat |///|Btn| MouseY| MouseX|
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
|
|
@ -97,5 +97,5 @@ A report would look like the following:
|
|||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Buttons 7-0 | Buttons 15-8 | Buttons 23-16 | Buttons 31-24 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|/| Btns 38-32 | Hat |///////| MouseX| MouseY|
|
||||
|/| Btns 38-32 | Hat |///////| MouseY| MouseX|
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
|
||||
SUBDIRS = libx52 libx52util libusbx52
|
||||
SUBDIRS = libx52 libx52util libusbx52 libx52io
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ See the documentation for the following files for a complete list of all
|
|||
functions.
|
||||
|
||||
- libx52.h
|
||||
- libx52io.h
|
||||
- libx52util.h
|
||||
|
||||
*/
|
||||
|
|
|
@ -14,11 +14,6 @@
|
|||
#include <libusb.h>
|
||||
#include "libx52.h"
|
||||
|
||||
#define VENDOR_SAITEK 0x06a3
|
||||
#define X52_PROD_X52PRO 0x0762
|
||||
#define X52_PROD_X52_1 0x0255
|
||||
#define X52_PROD_X52_2 0x075C
|
||||
|
||||
/*
|
||||
* The X52 MFD supports the following:
|
||||
* - 3 lines of 16 characters each
|
||||
|
@ -38,7 +33,6 @@ struct x52_mfd_line {
|
|||
struct libx52_device {
|
||||
libusb_context *ctx;
|
||||
libusb_device_handle *hdl;
|
||||
uint16_t pid;
|
||||
|
||||
uint32_t update_mask;
|
||||
uint32_t flags;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "usb-ids.h"
|
||||
#include "libx52.h"
|
||||
#include "x52_commands.h"
|
||||
#include "x52_common.h"
|
||||
|
@ -96,7 +97,6 @@ int libx52_connect(libx52_device *dev)
|
|||
}
|
||||
|
||||
dev->hdl = hdl;
|
||||
dev->pid = desc.idProduct;
|
||||
|
||||
if (libx52_device_is_x52pro(desc.idProduct)) {
|
||||
set_bit(&(dev->flags), X52_FLAG_IS_PRO);
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
# Automake for libx52io
|
||||
#
|
||||
# Copyright (C) 2012-2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
|
||||
lib_LTLIBRARIES = libx52io.la
|
||||
|
||||
# X52 IO library
|
||||
# This library handles the HID parsing of the X52 USB reports
|
||||
# Libtool Version Info
|
||||
# See: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
libx52io_v_CUR=0
|
||||
libx52io_v_AGE=0
|
||||
libx52io_v_REV=0
|
||||
libx52io_la_SOURCES = io_core.c io_axis.c io_parser.c io_strings.c io_device.c
|
||||
libx52io_la_CFLAGS = @HIDAPI_CFLAGS@ -DLOCALEDIR=\"$(localedir)\" -I $(top_srcdir) $(WARN_CFLAGS)
|
||||
libx52io_la_LDFLAGS = \
|
||||
-export-symbols-regex '^libx52io_' \
|
||||
-version-info $(libx52io_v_CUR):$(libx52io_v_REV):$(libx52io_v_AGE) @HIDAPI_LIBS@ \
|
||||
$(WARN_LDFLAGS)
|
||||
libx52io_la_LIBADD = @LTLIBINTL@
|
||||
|
||||
# Header files that need to be copied
|
||||
x52includedir = $(includedir)/x52pro
|
||||
x52include_HEADERS = libx52io.h
|
||||
|
||||
# pkg-config files
|
||||
# pkgconfig_DATA = libx52io.pc
|
||||
|
||||
if HAVE_CMOCKA
|
||||
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tap-driver.sh
|
||||
TESTS = test-axis test-parser
|
||||
check_PROGRAMS = $(TESTS)
|
||||
|
||||
test_axis_SOURCES = test_axis.c $(libx52io_la_SOURCES)
|
||||
test_axis_CFLAGS = $(libx52io_la_CFLAGS)
|
||||
test_axis_LDFLAGS = @CMOCKA_LIBS@ @HIDAPI_LIBS@ $(WARN_LDFLAGS)
|
||||
test_axis_LDADD = @LTLIBINTL@
|
||||
|
||||
test_parser_SOURCES = test_parser.c $(libx52io_la_SOURCES)
|
||||
test_parser_CFLAGS = $(libx52io_la_CFLAGS)
|
||||
test_parser_LDFLAGS = @CMOCKA_LIBS@ @HIDAPI_LIBS@ $(WARN_LDFLAGS)
|
||||
test_parser_LDADD = @LTLIBINTL@
|
||||
|
||||
# Add a dependency on test_parser_tests.c
|
||||
test_parser.c: test_parser_tests.c
|
||||
endif
|
||||
|
||||
# Extra files that need to be in the distribution
|
||||
EXTRA_DIST = libx52io.h io_common.h test_parser_tests.c
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Saitek X52 IO driver - axis ranges
|
||||
*
|
||||
* Copyright (C) 2012-2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "io_common.h"
|
||||
#include "usb-ids.h"
|
||||
|
||||
static const int32_t axis_min[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1
|
||||
};
|
||||
|
||||
static const int32_t x52_axis_max[] = {
|
||||
2047, 2047, 1023, 255, 255, 255, 255, 15, 15, 1, 1
|
||||
};
|
||||
|
||||
static const int32_t x52pro_axis_max[] = {
|
||||
1023, 1023, 1023, 255, 255, 255, 255, 15, 15, 1, 1
|
||||
};
|
||||
|
||||
void _x52io_set_axis_range(libx52io_context *ctx)
|
||||
{
|
||||
switch (ctx->pid) {
|
||||
case X52_PROD_X52_1:
|
||||
case X52_PROD_X52_2:
|
||||
memcpy(ctx->axis_min, axis_min, sizeof(ctx->axis_min));
|
||||
memcpy(ctx->axis_max, x52_axis_max, sizeof(ctx->axis_max));
|
||||
break;
|
||||
|
||||
case X52_PROD_X52PRO:
|
||||
memcpy(ctx->axis_min, axis_min, sizeof(ctx->axis_min));
|
||||
memcpy(ctx->axis_max, x52pro_axis_max, sizeof(ctx->axis_max));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int libx52io_get_axis_range(libx52io_context *ctx,
|
||||
libx52io_axis axis,
|
||||
int32_t *min,
|
||||
int32_t *max)
|
||||
{
|
||||
if (ctx == NULL || min == NULL || max == NULL) {
|
||||
return LIBX52IO_ERROR_INVALID;
|
||||
}
|
||||
|
||||
if (!(axis >= LIBX52IO_AXIS_X && axis < LIBX52IO_AXIS_MAX)) {
|
||||
return LIBX52IO_ERROR_INVALID;
|
||||
}
|
||||
|
||||
if (ctx->handle == NULL) {
|
||||
return LIBX52IO_ERROR_NO_DEVICE;
|
||||
}
|
||||
|
||||
*min = ctx->axis_min[axis];
|
||||
*max = ctx->axis_max[axis];
|
||||
|
||||
return LIBX52IO_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Saitek X52 IO driver - common definitions
|
||||
*
|
||||
* Copyright (C) 2012-2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#ifndef IO_COMMON_H
|
||||
#define IO_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "libx52io.h"
|
||||
#include "hidapi.h"
|
||||
|
||||
// Function handler for parsing reports
|
||||
typedef int (*x52_parse_report)(unsigned char *data, int length, libx52io_report *report);
|
||||
|
||||
struct libx52io_context {
|
||||
hid_device *handle;
|
||||
|
||||
int32_t axis_min[LIBX52IO_AXIS_MAX];
|
||||
int32_t axis_max[LIBX52IO_AXIS_MAX];
|
||||
|
||||
int16_t vid;
|
||||
int16_t pid;
|
||||
int16_t version;
|
||||
|
||||
char *manufacturer;
|
||||
char *product;
|
||||
char *serial_number;
|
||||
|
||||
x52_parse_report parser;
|
||||
};
|
||||
|
||||
void _x52io_set_axis_range(libx52io_context *ctx);
|
||||
void _x52io_set_report_parser(libx52io_context *ctx);
|
||||
int _x52io_parse_report(libx52io_context *ctx, libx52io_report *report,
|
||||
unsigned char *data, int length);
|
||||
|
||||
void _x52io_save_device_info(libx52io_context *ctx, struct hid_device_info *dev);
|
||||
void _x52io_release_device_info(libx52io_context *ctx);
|
||||
|
||||
#endif // !defined IO_COMMON_H
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Saitek X52 IO driver
|
||||
*
|
||||
* Copyright (C) 2012-2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "io_common.h"
|
||||
#include "usb-ids.h"
|
||||
#include "gettext.h"
|
||||
|
||||
int libx52io_init(libx52io_context **ctx)
|
||||
{
|
||||
libx52io_context *tmp;
|
||||
|
||||
if (ctx == NULL) {
|
||||
return LIBX52IO_ERROR_INVALID;
|
||||
}
|
||||
|
||||
if (hid_init()) {
|
||||
return LIBX52IO_ERROR_INIT_FAILURE;
|
||||
}
|
||||
|
||||
// Allocate a context
|
||||
tmp = calloc(1, sizeof(*tmp));
|
||||
if (tmp == NULL) {
|
||||
return LIBX52IO_ERROR_INIT_FAILURE;
|
||||
}
|
||||
|
||||
*ctx = tmp;
|
||||
|
||||
#if ENABLE_NLS
|
||||
/* Setup the gettext utilities */
|
||||
bindtextdomain(PACKAGE, LOCALEDIR);
|
||||
#endif
|
||||
|
||||
return LIBX52IO_SUCCESS;
|
||||
}
|
||||
|
||||
void libx52io_exit(libx52io_context *ctx)
|
||||
{
|
||||
// Close any open handles, free context
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
libx52io_close(ctx);
|
||||
hid_exit();
|
||||
}
|
||||
|
||||
int libx52io_close(libx52io_context *ctx)
|
||||
{
|
||||
if (ctx == NULL) {
|
||||
return LIBX52IO_ERROR_INVALID;
|
||||
}
|
||||
|
||||
if (ctx->handle != NULL) {
|
||||
hid_close(ctx->handle);
|
||||
}
|
||||
_x52io_release_device_info(ctx);
|
||||
|
||||
return LIBX52IO_SUCCESS;
|
||||
}
|
||||
|
||||
int libx52io_open(libx52io_context *ctx)
|
||||
{
|
||||
struct hid_device_info *devs, *cur_dev;
|
||||
int rc = LIBX52IO_ERROR_NO_DEVICE;
|
||||
|
||||
if (ctx == NULL) {
|
||||
return LIBX52IO_ERROR_INVALID;
|
||||
}
|
||||
|
||||
/* Close any already open handles */
|
||||
libx52io_close(ctx);
|
||||
|
||||
/* Enumerate all Saitek HID devices */
|
||||
devs = hid_enumerate(VENDOR_SAITEK, 0);
|
||||
cur_dev = devs;
|
||||
while (cur_dev) {
|
||||
switch (cur_dev->product_id) {
|
||||
case X52_PROD_X52_1:
|
||||
case X52_PROD_X52_2:
|
||||
case X52_PROD_X52PRO:
|
||||
ctx->handle = hid_open_path(cur_dev->path);
|
||||
if (ctx->handle == NULL) {
|
||||
rc = LIBX52IO_ERROR_CONN;
|
||||
goto finally;
|
||||
}
|
||||
|
||||
_x52io_save_device_info(ctx, cur_dev);
|
||||
|
||||
rc = LIBX52IO_SUCCESS;
|
||||
goto finally;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
cur_dev = cur_dev->next;
|
||||
}
|
||||
|
||||
finally:
|
||||
hid_free_enumeration(devs);
|
||||
return rc;
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Saitek X52 IO driver - device information
|
||||
*
|
||||
* Copyright (C) 2012-2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include "io_common.h"
|
||||
|
||||
static char * _save_string(wchar_t *wcs)
|
||||
{
|
||||
int n;
|
||||
char *out_str = NULL;
|
||||
|
||||
if (wcs == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the number of bytes needed to save the wide character string as
|
||||
* a multibyte string
|
||||
*/
|
||||
n = wcstombs(NULL, wcs, 0);
|
||||
if (n <= 0) {
|
||||
/* Two possibilities here:
|
||||
* n == -1; A wide character that couldn't be converted was found.
|
||||
* n == 0; No wide characters were in the input string.
|
||||
*
|
||||
* In the latter case, we don't have anything to convert, while in
|
||||
* the former case, we don't know how long the string is to convert.
|
||||
*
|
||||
* Return NULL in both cases.
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
n++;
|
||||
out_str = calloc(n, sizeof(*out_str));
|
||||
if (out_str != NULL) {
|
||||
wcstombs(out_str, wcs, n);
|
||||
}
|
||||
|
||||
return out_str;
|
||||
}
|
||||
|
||||
static void _free_string(char **str)
|
||||
{
|
||||
if (*str != NULL) {
|
||||
free(*str);
|
||||
*str = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void _x52io_save_device_info(libx52io_context *ctx, struct hid_device_info *dev)
|
||||
{
|
||||
ctx->vid = dev->vendor_id;
|
||||
ctx->pid = dev->product_id;
|
||||
ctx->version = dev->release_number;
|
||||
|
||||
ctx->manufacturer = _save_string(dev->manufacturer_string);
|
||||
ctx->product = _save_string(dev->product_string);
|
||||
ctx->serial_number = _save_string(dev->serial_number);
|
||||
|
||||
_x52io_set_axis_range(ctx);
|
||||
_x52io_set_report_parser(ctx);
|
||||
}
|
||||
|
||||
void _x52io_release_device_info(libx52io_context *ctx)
|
||||
{
|
||||
ctx->vid = 0;
|
||||
ctx->pid = 0;
|
||||
ctx->version = 0;
|
||||
|
||||
_free_string(&(ctx->manufacturer));
|
||||
_free_string(&(ctx->product));
|
||||
_free_string(&(ctx->serial_number));
|
||||
|
||||
memset(ctx->axis_min, 0, sizeof(ctx->axis_min));
|
||||
memset(ctx->axis_max, 0, sizeof(ctx->axis_max));
|
||||
ctx->parser = NULL;
|
||||
ctx->handle = NULL;
|
||||
}
|
||||
|
||||
uint16_t libx52io_get_vendor_id(libx52io_context *ctx)
|
||||
{
|
||||
return (ctx ? ctx->vid : 0);
|
||||
}
|
||||
|
||||
uint16_t libx52io_get_product_id(libx52io_context *ctx)
|
||||
{
|
||||
return (ctx ? ctx->pid : 0);
|
||||
}
|
||||
|
||||
uint16_t libx52io_get_device_version(libx52io_context *ctx)
|
||||
{
|
||||
return (ctx ? ctx->version : 0);
|
||||
}
|
||||
|
||||
const char * libx52io_get_manufacturer_string(libx52io_context *ctx)
|
||||
{
|
||||
return (ctx ? ctx->manufacturer : NULL);
|
||||
}
|
||||
|
||||
const char * libx52io_get_product_string(libx52io_context *ctx)
|
||||
{
|
||||
return (ctx ? ctx->product : NULL);
|
||||
}
|
||||
|
||||
const char * libx52io_get_serial_number_string(libx52io_context *ctx)
|
||||
{
|
||||
return (ctx ? ctx->serial_number : NULL);
|
||||
}
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
* 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 <stdint.h>
|
||||
#include "io_common.h"
|
||||
#include "usb-ids.h"
|
||||
|
||||
static void map_hat(uint8_t hat, libx52io_report *report)
|
||||
{
|
||||
/*
|
||||
* Hat reports values from 0-8, but just to account for any spurious
|
||||
* values, leave the remaining 7 entries blank.
|
||||
*
|
||||
* Pushing the hat North reports 1, and it increases to 2 for NE, 3 for
|
||||
* East, 4 for SE and so on in a clockwise fashion until it hits 8 for NW.
|
||||
*
|
||||
* According to the USB spec, Y axis increases as it is pulled from front
|
||||
* to back, i.e., further from the user to closer to the user, and X axis
|
||||
* increases left to right. Therefore NE is X=+1, Y=-1.
|
||||
*/
|
||||
static const int32_t hat_to_axis[16][2] = {
|
||||
{0, 0},
|
||||
{0, -1},
|
||||
{1, -1},
|
||||
{1, 0},
|
||||
{1, 1},
|
||||
{0, 1},
|
||||
{-1, 1},
|
||||
{-1, 0},
|
||||
{-1, -1},
|
||||
};
|
||||
|
||||
report->axis[LIBX52IO_AXIS_HATX] = hat_to_axis[hat][0];
|
||||
report->axis[LIBX52IO_AXIS_HATY] = hat_to_axis[hat][1];
|
||||
}
|
||||
|
||||
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] & 0xf;
|
||||
report->axis[LIBX52IO_AXIS_THUMBY] = data[thumb_pos] >> 4;
|
||||
|
||||
/*
|
||||
* The hat report is in the upper 4 bits of the byte preceding the
|
||||
* thumbstick report. Use that to map to the axis values
|
||||
*/
|
||||
report->hat = data[thumb_pos-1] >> 4;
|
||||
map_hat(report->hat, report);
|
||||
}
|
||||
|
||||
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 & ((uint64_t)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| MouseY| MouseX|
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
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_DN),
|
||||
B(MOUSE_SCROLL_UP),
|
||||
-1
|
||||
};
|
||||
|
||||
if (length != 14) {
|
||||
return LIBX52IO_ERROR_IO;
|
||||
}
|
||||
|
||||
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 LIBX52IO_SUCCESS;
|
||||
}
|
||||
|
||||
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 |///////| MouseY| MouseX|
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
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 != 15) {
|
||||
return LIBX52IO_ERROR_IO;
|
||||
}
|
||||
|
||||
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 LIBX52IO_SUCCESS;
|
||||
}
|
||||
|
||||
void _x52io_set_report_parser(libx52io_context *ctx)
|
||||
{
|
||||
switch (ctx->pid) {
|
||||
case X52_PROD_X52_1:
|
||||
case X52_PROD_X52_2:
|
||||
ctx->parser = parse_x52;
|
||||
break;
|
||||
|
||||
case X52_PROD_X52PRO:
|
||||
ctx->parser = parse_x52pro;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int _x52io_parse_report(libx52io_context *ctx, libx52io_report *report,
|
||||
unsigned char *data, int length)
|
||||
{
|
||||
if (ctx->parser == NULL) {
|
||||
return LIBX52IO_ERROR_NO_DEVICE;
|
||||
}
|
||||
|
||||
return (ctx->parser)(data, length, report);
|
||||
}
|
||||
|
||||
int libx52io_read(libx52io_context *ctx, libx52io_report *report)
|
||||
{
|
||||
return libx52io_read_timeout(ctx, report, -1);
|
||||
}
|
||||
|
||||
int libx52io_read_timeout(libx52io_context *ctx, libx52io_report *report, int timeout)
|
||||
{
|
||||
int rc;
|
||||
unsigned char data[16];
|
||||
|
||||
if (ctx == NULL || report == NULL) {
|
||||
return LIBX52IO_ERROR_INVALID;
|
||||
}
|
||||
|
||||
if (ctx->handle == NULL) {
|
||||
return LIBX52IO_ERROR_NO_DEVICE;
|
||||
}
|
||||
|
||||
rc = hid_read_timeout(ctx->handle, data, sizeof(data), timeout);
|
||||
if (rc == 0) {
|
||||
return LIBX52IO_ERROR_TIMEOUT;
|
||||
} else if (rc < 0) {
|
||||
return LIBX52IO_ERROR_IO;
|
||||
}
|
||||
|
||||
// rc > 0
|
||||
return _x52io_parse_report(ctx, report, data, rc);
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Saitek X52 IO driver - string representations
|
||||
*
|
||||
* Copyright (C) 2012-2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include "libx52io.h"
|
||||
#include "gettext.h"
|
||||
|
||||
/* The strings corresponding to the axis and button enumerations are
|
||||
* deliberately left untranslated, as they essentially correspond one
|
||||
* on one to the enumeration definitions.
|
||||
*/
|
||||
|
||||
/* String mapping for axis */
|
||||
static const char * _x52io_axis_str[LIBX52IO_AXIS_MAX] = {
|
||||
[LIBX52IO_AXIS_X] = "ABS_X",
|
||||
[LIBX52IO_AXIS_Y] = "ABS_Y",
|
||||
[LIBX52IO_AXIS_RZ] = "ABS_RZ",
|
||||
[LIBX52IO_AXIS_Z] = "ABS_Z",
|
||||
[LIBX52IO_AXIS_RX] = "ABS_RX",
|
||||
[LIBX52IO_AXIS_RY] = "ABS_RY",
|
||||
[LIBX52IO_AXIS_SLIDER] = "ABS_SLIDER",
|
||||
[LIBX52IO_AXIS_THUMBX] = "ABS_THUMBX",
|
||||
[LIBX52IO_AXIS_THUMBY] = "ABS_THUMBY",
|
||||
[LIBX52IO_AXIS_HATX] = "ABS_HATX",
|
||||
[LIBX52IO_AXIS_HATY] = "ABS_HATY",
|
||||
};
|
||||
|
||||
const char * libx52io_axis_to_str(libx52io_axis axis)
|
||||
{
|
||||
if (axis >= LIBX52IO_AXIS_X && axis < LIBX52IO_AXIS_MAX) {
|
||||
return _x52io_axis_str[axis];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* String mapping for button */
|
||||
static const char * _x52io_button_str[LIBX52IO_BUTTON_MAX] = {
|
||||
[LIBX52IO_BTN_TRIGGER] = "BTN_TRIGGER",
|
||||
[LIBX52IO_BTN_TRIGGER_2] = "BTN_TRIGGER_2",
|
||||
[LIBX52IO_BTN_FIRE] = "BTN_FIRE",
|
||||
[LIBX52IO_BTN_PINKY] = "BTN_PINKY",
|
||||
[LIBX52IO_BTN_A] = "BTN_A",
|
||||
[LIBX52IO_BTN_B] = "BTN_B",
|
||||
[LIBX52IO_BTN_C] = "BTN_C",
|
||||
[LIBX52IO_BTN_D] = "BTN_D",
|
||||
[LIBX52IO_BTN_E] = "BTN_E",
|
||||
[LIBX52IO_BTN_T1_UP] = "BTN_T1_UP",
|
||||
[LIBX52IO_BTN_T1_DN] = "BTN_T1_DN",
|
||||
[LIBX52IO_BTN_T2_UP] = "BTN_T2_UP",
|
||||
[LIBX52IO_BTN_T2_DN] = "BTN_T2_DN",
|
||||
[LIBX52IO_BTN_T3_UP] = "BTN_T3_UP",
|
||||
[LIBX52IO_BTN_T3_DN] = "BTN_T3_DN",
|
||||
[LIBX52IO_BTN_POV_1_N] = "BTN_POV_1_N",
|
||||
[LIBX52IO_BTN_POV_1_E] = "BTN_POV_1_E",
|
||||
[LIBX52IO_BTN_POV_1_S] = "BTN_POV_1_S",
|
||||
[LIBX52IO_BTN_POV_1_W] = "BTN_POV_1_W",
|
||||
[LIBX52IO_BTN_POV_2_N] = "BTN_POV_2_N",
|
||||
[LIBX52IO_BTN_POV_2_E] = "BTN_POV_2_E",
|
||||
[LIBX52IO_BTN_POV_2_S] = "BTN_POV_2_S",
|
||||
[LIBX52IO_BTN_POV_2_W] = "BTN_POV_2_W",
|
||||
[LIBX52IO_BTN_CLUTCH] = "BTN_CLUTCH",
|
||||
[LIBX52IO_BTN_MOUSE_PRIMARY] = "BTN_MOUSE_PRIMARY",
|
||||
[LIBX52IO_BTN_MOUSE_SECONDARY] = "BTN_MOUSE_SECONDARY",
|
||||
[LIBX52IO_BTN_MOUSE_SCROLL_UP] = "BTN_MOUSE_SCROLL_UP",
|
||||
[LIBX52IO_BTN_MOUSE_SCROLL_DN] = "BTN_MOUSE_SCROLL_DN",
|
||||
[LIBX52IO_BTN_FUNCTION] = "BTN_FUNCTION",
|
||||
[LIBX52IO_BTN_START_STOP] = "BTN_START_STOP",
|
||||
[LIBX52IO_BTN_RESET] = "BTN_RESET",
|
||||
[LIBX52IO_BTN_PG_UP] = "BTN_PG_UP",
|
||||
[LIBX52IO_BTN_PG_DN] = "BTN_PG_DN",
|
||||
[LIBX52IO_BTN_UP] = "BTN_UP",
|
||||
[LIBX52IO_BTN_DN] = "BTN_DN",
|
||||
[LIBX52IO_BTN_SELECT] = "BTN_SELECT",
|
||||
[LIBX52IO_BTN_MODE_1] = "BTN_MODE_1",
|
||||
[LIBX52IO_BTN_MODE_2] = "BTN_MODE_2",
|
||||
[LIBX52IO_BTN_MODE_3] = "BTN_MODE_3",
|
||||
};
|
||||
|
||||
const char * libx52io_button_to_str(libx52io_button button)
|
||||
{
|
||||
if (button >= LIBX52IO_BTN_TRIGGER && button < LIBX52IO_BUTTON_MAX) {
|
||||
return _x52io_button_str[button];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Error buffer used for building custom error strings */
|
||||
static char error_buffer[256];
|
||||
|
||||
#define _(str) dgettext(PACKAGE, str)
|
||||
|
||||
const char * libx52io_strerror(libx52io_error_code code)
|
||||
{
|
||||
switch (code) {
|
||||
case LIBX52IO_SUCCESS:
|
||||
return _("Success");
|
||||
|
||||
case LIBX52IO_ERROR_INIT_FAILURE:
|
||||
return _("Initialization failure");
|
||||
|
||||
case LIBX52IO_ERROR_NO_DEVICE:
|
||||
return _("No device");
|
||||
|
||||
case LIBX52IO_ERROR_INVALID:
|
||||
return _("Invalid arguments");
|
||||
|
||||
case LIBX52IO_ERROR_CONN:
|
||||
return _("Connection failure");
|
||||
|
||||
case LIBX52IO_ERROR_IO:
|
||||
return _("I/O error");
|
||||
|
||||
case LIBX52IO_ERROR_TIMEOUT:
|
||||
return _("Read timeout");
|
||||
|
||||
default:
|
||||
snprintf(error_buffer, sizeof(error_buffer), _("Unknown error %d"), code);
|
||||
break;
|
||||
}
|
||||
|
||||
(void)_x52io_axis_str;
|
||||
(void)_x52io_button_str;
|
||||
|
||||
return error_buffer;
|
||||
}
|
||||
|
|
@ -0,0 +1,500 @@
|
|||
/*
|
||||
* Saitek X52 IO driver
|
||||
*
|
||||
* Copyright (C) 2012-2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file libx52io.h
|
||||
* @brief Functions, structures and enumerations for the Saitek X52 IO driver
|
||||
* library.
|
||||
*
|
||||
* This file contains the type, enum and function prototypes for the Saitek X52
|
||||
* IO driver library. These functions allow an application to connect to a
|
||||
* supported X52/X52Pro joystick and read the state of the buttons and axes.
|
||||
*
|
||||
* @author Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
*/
|
||||
#ifndef LIBX52IO_H
|
||||
#define LIBX52IO_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup libx52io IO Library APIs
|
||||
*
|
||||
* These functions allow an application to connect to a supported X52/X52Pro
|
||||
* joystick and read the state of the buttons and axes.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Opaque structure used by libx52io
|
||||
*/
|
||||
struct libx52io_context;
|
||||
|
||||
/**
|
||||
* @brief Device context structure used by libx52io
|
||||
*
|
||||
* All libx52io API functions require the application to pass in a pointer to
|
||||
* a valid device context structure. A pointer can be obtained by calling
|
||||
* \ref libx52io_init
|
||||
*/
|
||||
typedef struct libx52io_context libx52io_context;
|
||||
|
||||
/**
|
||||
* @brief libx52 IO error codes
|
||||
*
|
||||
* Error codes returned by libx52io
|
||||
*/
|
||||
typedef enum {
|
||||
/** No error, indicates success */
|
||||
LIBX52IO_SUCCESS,
|
||||
|
||||
/** Initialization failure */
|
||||
LIBX52IO_ERROR_INIT_FAILURE,
|
||||
|
||||
/** No compatible device found */
|
||||
LIBX52IO_ERROR_NO_DEVICE,
|
||||
|
||||
/** Invalid arguments for function */
|
||||
LIBX52IO_ERROR_INVALID,
|
||||
|
||||
/** Connection error */
|
||||
LIBX52IO_ERROR_CONN,
|
||||
|
||||
/** Read error from device */
|
||||
LIBX52IO_ERROR_IO,
|
||||
|
||||
/** Timeout during read from device */
|
||||
LIBX52IO_ERROR_TIMEOUT,
|
||||
} libx52io_error_code;
|
||||
|
||||
/**
|
||||
* @brief X52 Axis definitions
|
||||
*/
|
||||
typedef enum {
|
||||
/** Stick X axis */
|
||||
LIBX52IO_AXIS_X,
|
||||
|
||||
/** Stick Y axis */
|
||||
LIBX52IO_AXIS_Y,
|
||||
|
||||
/** Stick twist axis */
|
||||
LIBX52IO_AXIS_RZ,
|
||||
|
||||
/** Throttle axis */
|
||||
LIBX52IO_AXIS_Z,
|
||||
|
||||
/** Throttle Rotary X */
|
||||
LIBX52IO_AXIS_RX,
|
||||
|
||||
/** Throttle Rotary Y */
|
||||
LIBX52IO_AXIS_RY,
|
||||
|
||||
/** Throttle Slider */
|
||||
LIBX52IO_AXIS_SLIDER,
|
||||
|
||||
/** Thumbstick X */
|
||||
LIBX52IO_AXIS_THUMBX,
|
||||
|
||||
/** Thumbstick Y */
|
||||
LIBX52IO_AXIS_THUMBY,
|
||||
|
||||
/** Hat X */
|
||||
LIBX52IO_AXIS_HATX,
|
||||
|
||||
/** Hat Y */
|
||||
LIBX52IO_AXIS_HATY,
|
||||
|
||||
LIBX52IO_AXIS_MAX
|
||||
} libx52io_axis;
|
||||
|
||||
/**
|
||||
* @brief X52 Button definitions
|
||||
*/
|
||||
typedef enum {
|
||||
/** Primary trigger */
|
||||
LIBX52IO_BTN_TRIGGER,
|
||||
|
||||
/** Secondary trigger */
|
||||
LIBX52IO_BTN_TRIGGER_2,
|
||||
|
||||
/** Fire button */
|
||||
LIBX52IO_BTN_FIRE,
|
||||
|
||||
/** Pinky trigger */
|
||||
LIBX52IO_BTN_PINKY,
|
||||
|
||||
/** A button, on stick */
|
||||
LIBX52IO_BTN_A,
|
||||
|
||||
/** B button, on stick */
|
||||
LIBX52IO_BTN_B,
|
||||
|
||||
/** C button, on stick */
|
||||
LIBX52IO_BTN_C,
|
||||
|
||||
/** D button, on throttle */
|
||||
LIBX52IO_BTN_D,
|
||||
|
||||
/** E button, on throttle */
|
||||
LIBX52IO_BTN_E,
|
||||
|
||||
/** Toggle 1 up */
|
||||
LIBX52IO_BTN_T1_UP,
|
||||
|
||||
/** Toggle 1 down */
|
||||
LIBX52IO_BTN_T1_DN,
|
||||
|
||||
/** Toggle 2 up */
|
||||
LIBX52IO_BTN_T2_UP,
|
||||
|
||||
/** Toggle 2 down */
|
||||
LIBX52IO_BTN_T2_DN,
|
||||
|
||||
/** Toggle 3 up */
|
||||
LIBX52IO_BTN_T3_UP,
|
||||
|
||||
/** Toggle 3 down */
|
||||
LIBX52IO_BTN_T3_DN,
|
||||
|
||||
/** POV 1 Up, on stick */
|
||||
LIBX52IO_BTN_POV_1_N,
|
||||
|
||||
/** POV 1 Right, on stick */
|
||||
LIBX52IO_BTN_POV_1_E,
|
||||
|
||||
/** POV 1 Down, on stick */
|
||||
LIBX52IO_BTN_POV_1_S,
|
||||
|
||||
/** POV 1 Left, on stick */
|
||||
LIBX52IO_BTN_POV_1_W,
|
||||
|
||||
/** POV 2 Up, on throttle */
|
||||
LIBX52IO_BTN_POV_2_N,
|
||||
|
||||
/** POV 2 Right, on throttle */
|
||||
LIBX52IO_BTN_POV_2_E,
|
||||
|
||||
/** POV 2 Down, on throttle */
|
||||
LIBX52IO_BTN_POV_2_S,
|
||||
|
||||
/** POV 2 Left, on throttle */
|
||||
LIBX52IO_BTN_POV_2_W,
|
||||
|
||||
/** Clutch button, on throttle */
|
||||
LIBX52IO_BTN_CLUTCH,
|
||||
|
||||
/** Primary mouse button, next to thumbstick */
|
||||
LIBX52IO_BTN_MOUSE_PRIMARY,
|
||||
|
||||
/** Secondary mouse button, press scroll wheel on throttle */
|
||||
LIBX52IO_BTN_MOUSE_SECONDARY,
|
||||
|
||||
/** Scroll wheel up, on throttle */
|
||||
LIBX52IO_BTN_MOUSE_SCROLL_UP,
|
||||
|
||||
/** Scroll wheel down, on throttle */
|
||||
LIBX52IO_BTN_MOUSE_SCROLL_DN,
|
||||
|
||||
/** Function button */
|
||||
LIBX52IO_BTN_FUNCTION,
|
||||
|
||||
/** Start/Stop button */
|
||||
LIBX52IO_BTN_START_STOP,
|
||||
|
||||
/** Reset button */
|
||||
LIBX52IO_BTN_RESET,
|
||||
|
||||
/** Page Up button, X52 Pro only */
|
||||
LIBX52IO_BTN_PG_UP,
|
||||
|
||||
/** Page Down button, X52 Pro only */
|
||||
LIBX52IO_BTN_PG_DN,
|
||||
|
||||
/** Up button, X52 Pro only */
|
||||
LIBX52IO_BTN_UP,
|
||||
|
||||
/** Down button, X52 Pro only */
|
||||
LIBX52IO_BTN_DN,
|
||||
|
||||
/** 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;
|
||||
|
||||
/**
|
||||
* @brief X52 HID Report
|
||||
*
|
||||
* This structure holds a parsed HID report
|
||||
*/
|
||||
struct libx52io_report {
|
||||
/** Axis values */
|
||||
int32_t axis[LIBX52IO_AXIS_MAX];
|
||||
|
||||
/** Button values, true is pressed */
|
||||
bool button[LIBX52IO_BUTTON_MAX];
|
||||
|
||||
/** Current mode - 1, 2 or 3 */
|
||||
uint8_t mode;
|
||||
|
||||
/** Hat position 0-8 */
|
||||
uint8_t hat;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief X52 HID Report
|
||||
*
|
||||
* This structure holds a parsed HID report
|
||||
*/
|
||||
typedef struct libx52io_report libx52io_report;
|
||||
|
||||
/**
|
||||
* @brief Initialize the IO library
|
||||
*
|
||||
* This function initializes the libx52io library, sets up any internal data
|
||||
* structures to access the joystick, and returns a \ref libx52io_context
|
||||
* pointer in the output parameter. All calls to libx52io use the returned
|
||||
* pointer to control the device.
|
||||
*
|
||||
* @par Example
|
||||
* @code
|
||||
* int rc;
|
||||
* libx52io_context *ctx;
|
||||
* rc = libx52io_init(&ctx);
|
||||
* if (rc != LIBX52IO_SUCCESS) {
|
||||
* // Error handling omitted for brevity
|
||||
* }
|
||||
*
|
||||
* // Save ctx for use later
|
||||
* @endcode
|
||||
*
|
||||
* @param[out] ctx Pointer to a \ref libx52io_context *. This function will
|
||||
* allocate a device context and return the pointer to the context in this variable.
|
||||
*
|
||||
* @returns \c libx52io_error_code indicating status
|
||||
*/
|
||||
int libx52io_init(libx52io_context **ctx);
|
||||
|
||||
/**
|
||||
* @brief Exit the library and free up any resources used
|
||||
*
|
||||
* This function releases any resources allocated by \ref libx52io_init and
|
||||
* terminates the library. Using the freed device now is invalid and can
|
||||
* cause errors.
|
||||
*
|
||||
* @param[in] ctx Pointer to the device context
|
||||
* @returns None
|
||||
*/
|
||||
void libx52io_exit(libx52io_context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Open a connection to a supported joystick
|
||||
*
|
||||
* This function scans for and opens a connection to a supported X52/X52Pro
|
||||
* joystick. If no supported joystick is found, it will return \ref
|
||||
* LIBX52IO_ERROR_NO_DEVICE.
|
||||
*
|
||||
* @param[in] ctx Pointer to the device context
|
||||
*
|
||||
* @returns
|
||||
* - \ref LIBX52IO_SUCCESS on successful opening
|
||||
* - \ref LIBX52IO_ERROR_INVALID if the context pointer is not valid
|
||||
* - \ref LIBX52IO_ERROR_NO_DEVICE if no supported joystick is found
|
||||
* - \ref LIBX52IO_ERROR_CONN if the connection fails
|
||||
*/
|
||||
int libx52io_open(libx52io_context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Close an existing connection to a supported joystick
|
||||
*
|
||||
* This function closes any existing connection to a joystick. It is acceptable
|
||||
* to call this function if no connection exists.
|
||||
*
|
||||
* @param[in] ctx Pointer to the device context
|
||||
*
|
||||
* @returns
|
||||
* - \ref LIBX52IO_SUCCESS on closing, or if the connection is already closed.
|
||||
* - \ref LIBX52IO_ERROR_INVALID if the context pointer is not valid
|
||||
*/
|
||||
int libx52io_close(libx52io_context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Read and parse a HID report
|
||||
*
|
||||
* This function reads and parses a HID report from a connected joystick. This
|
||||
* function will block until some data is available from the joystick, or the
|
||||
* timeout is hit, whichever is first.
|
||||
*
|
||||
* @param[in] ctx Pointer to the device context
|
||||
* @param[out] report Pointer to save the decoded HID report
|
||||
* @param[in] timeout Timeout value in milliseconds
|
||||
*
|
||||
* @returns
|
||||
* - \ref LIBX52IO_SUCCESS on read and parse success
|
||||
* - \ref LIBX52IO_ERROR_INVALID if the context or report pointers are not valid
|
||||
* - \ref LIBX52IO_ERROR_NO_DEVICE if the device is disconnected
|
||||
* - \ref LIBX52IO_ERROR_IO if there was an error reading from the device,
|
||||
* including if the device was disconnected during the read.
|
||||
* - \ref LIBX52IO_ERROR_TIMEOUT if no report was read before timeout.
|
||||
*/
|
||||
int libx52io_read_timeout(libx52io_context *ctx, libx52io_report *report, int timeout);
|
||||
|
||||
/**
|
||||
* @brief Read and parse a HID report
|
||||
*
|
||||
* This behaves the same as \ref libx52io_read_timeout with a timeout of \c -1.
|
||||
* This function will block until some data is available from the joystick.
|
||||
*
|
||||
* @param[in] ctx Pointer to the device context
|
||||
* @param[out] report Pointer to save the decoded HID report
|
||||
*
|
||||
* @returns
|
||||
* - \ref LIBX52IO_SUCCESS on read and parse success
|
||||
* - \ref LIBX52IO_ERROR_INVALID if the context or report pointers are not valid
|
||||
* - \ref LIBX52IO_ERROR_NO_DEVICE if the device is disconnected
|
||||
* - \ref LIBX52IO_ERROR_IO if there was an error reading from the device,
|
||||
* including if the device was disconnected during the read.
|
||||
*/
|
||||
int libx52io_read(libx52io_context *ctx, libx52io_report *report);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the range of an axis
|
||||
*
|
||||
* This saves the minimum and maximum values of the requested axis in the output
|
||||
* parameters. This will only be valid if the device is connected.
|
||||
*
|
||||
* @param[in] ctx Pointer to the device context
|
||||
* @param[in] axis Axis identifier - see \ref libx52io_axis
|
||||
* @param[out] min Pointer to save the axis minimum value
|
||||
* @param[out] max Pointer to save the axis maximum value
|
||||
*
|
||||
* @returns
|
||||
* - \ref LIBX52IO_SUCCESS on read and parse success
|
||||
* - \ref LIBX52IO_ERROR_INVALID if the context or output pointers are not
|
||||
* valid, or the requested axis is not a valid axis identifier
|
||||
* - \ref LIBX52IO_ERROR_NO_DEVICE if the device is disconnected
|
||||
*/
|
||||
int libx52io_get_axis_range(libx52io_context *ctx, libx52io_axis axis, int32_t *min, int32_t *max);
|
||||
|
||||
/**
|
||||
* @brief Get the string representation of an error code
|
||||
*
|
||||
* @param[in] code Return code from one of the libx52io APIs
|
||||
*
|
||||
* @returns String representation of the error. This pointer must not be freed.
|
||||
*/
|
||||
const char * libx52io_strerror(libx52io_error_code code);
|
||||
|
||||
/**
|
||||
* @brief Get the string representation of an axis.
|
||||
*
|
||||
* @param[in] axis Axis ID - see \ref libx52io_axis
|
||||
*
|
||||
* @returns String representation of the axis. This pointer must not be freed.
|
||||
* If axis is outside the defined range, then this returns NULL.
|
||||
*/
|
||||
const char * libx52io_axis_to_str(libx52io_axis axis);
|
||||
|
||||
/**
|
||||
* @brief Get the string representation of a button.
|
||||
*
|
||||
* @param[in] button Button ID - see \ref libx52io_button
|
||||
*
|
||||
* @returns String representation of the button. This pointer must not be freed.
|
||||
* If button is outside the defined range, then this returns NULL.
|
||||
*/
|
||||
const char * libx52io_button_to_str(libx52io_button button);
|
||||
|
||||
/**
|
||||
* @brief Get the vendor ID of the connected X52 device.
|
||||
*
|
||||
* @param[in] ctx Pointer to the device context
|
||||
*
|
||||
* @returns Vendor ID of the connected device. Returns 0 if no device is connected.
|
||||
*/
|
||||
uint16_t libx52io_get_vendor_id(libx52io_context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Get the product ID of the connected X52 device.
|
||||
*
|
||||
* @param[in] ctx Pointer to the device context
|
||||
*
|
||||
* @returns Product ID of the connected device. Returns 0 if no device is connected.
|
||||
*/
|
||||
uint16_t libx52io_get_product_id(libx52io_context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Get the device version of the connected X52 device.
|
||||
*
|
||||
* @param[in] ctx Pointer to the device context
|
||||
*
|
||||
* @returns Device version of the connected device. Returns 0 if no device is connected.
|
||||
*/
|
||||
uint16_t libx52io_get_device_version(libx52io_context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Get the manufacturer string of the connected X52 device.
|
||||
*
|
||||
* Returns a pointer to a string which can be passed to \c printf or \c puts.
|
||||
* This pointer must not be freed.
|
||||
*
|
||||
* @param[in] ctx Pointer to the device context
|
||||
*
|
||||
* @returns Pointer to the manufacturer string, which may be NULL. Return value
|
||||
* is always NULL if no device is connected.
|
||||
*/
|
||||
const char * libx52io_get_manufacturer_string(libx52io_context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Get the product string of the connected X52 device.
|
||||
*
|
||||
* Returns a pointer to a string which can be passed to \c printf or \c puts.
|
||||
* This pointer must not be freed.
|
||||
*
|
||||
* @param[in] ctx Pointer to the device context
|
||||
*
|
||||
* @returns Pointer to the product string, which may be NULL. Return value
|
||||
* is always NULL if no device is connected.
|
||||
*/
|
||||
const char * libx52io_get_product_string(libx52io_context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Get the serial number of the connected X52 device.
|
||||
*
|
||||
* Returns a pointer to a string which can be passed to \c printf or \c puts.
|
||||
* This pointer must not be freed.
|
||||
*
|
||||
* @param[in] ctx Pointer to the device context
|
||||
*
|
||||
* @returns Pointer to the serial number string, which may be NULL. Return value
|
||||
* is always NULL if no device is connected.
|
||||
*/
|
||||
const char * libx52io_get_serial_number_string(libx52io_context *ctx);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // !defined LIBX52IO_H
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Saitek X52 IO driver - Axis test suite
|
||||
*
|
||||
* Copyright (C) 2012-2020 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 "io_common.h"
|
||||
#include "usb-ids.h"
|
||||
|
||||
static int group_setup(void **state)
|
||||
{
|
||||
libx52io_context *ctx;
|
||||
int rc;
|
||||
|
||||
rc = libx52io_init(&ctx);
|
||||
if (rc != LIBX52IO_SUCCESS) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
*state = ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_setup(void **state)
|
||||
{
|
||||
libx52io_context *ctx = *state;
|
||||
|
||||
/* Create a dummy handle so that the test cases don't abort early */
|
||||
ctx->handle = (void *)(uintptr_t)(-1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_teardown(void **state)
|
||||
{
|
||||
libx52io_context *ctx = *state;
|
||||
|
||||
ctx->handle = NULL;
|
||||
libx52io_close(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int group_teardown(void **state)
|
||||
{
|
||||
libx52io_context *ctx = *state;
|
||||
|
||||
ctx->handle = NULL;
|
||||
libx52io_exit(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* List of test cases - list the axis and the corresponding maximum for that axis */
|
||||
#define TEST_CASES \
|
||||
TEST_X52(X, 0, 2047) \
|
||||
TEST_X52(Y, 0, 2047) \
|
||||
TEST_X52(RZ, 0, 1023) \
|
||||
TEST_X52(Z, 0, 255) \
|
||||
TEST_X52(RX, 0, 255) \
|
||||
TEST_X52(RY, 0, 255) \
|
||||
TEST_X52(SLIDER, 0, 255) \
|
||||
TEST_X52(THUMBX, 0, 15) \
|
||||
TEST_X52(THUMBY, 0, 15) \
|
||||
TEST_X52(HATX, -1, 1) \
|
||||
TEST_X52(HATY, -1, 1) \
|
||||
TEST_PRO(X, 0, 1023) \
|
||||
TEST_PRO(Y, 0, 1023) \
|
||||
TEST_PRO(RZ, 0, 1023) \
|
||||
TEST_PRO(Z, 0, 255) \
|
||||
TEST_PRO(RX, 0, 255) \
|
||||
TEST_PRO(RY, 0, 255) \
|
||||
TEST_PRO(SLIDER, 0, 255) \
|
||||
TEST_PRO(THUMBX, 0, 15) \
|
||||
TEST_PRO(THUMBY, 0, 15) \
|
||||
TEST_PRO(HATX, -1, 1) \
|
||||
TEST_PRO(HATY, -1, 1)
|
||||
|
||||
#define TEST_X52(axis, min, max) TEST(_1, axis, min, max) TEST(_2, axis, min, max)
|
||||
#define TEST_PRO(axis, min, max) TEST(PRO, axis, min, max)
|
||||
|
||||
#define TEST(prodid, axis, minval, maxval) \
|
||||
static void axis ## _ ## prodid (void **state) \
|
||||
{ \
|
||||
libx52io_context *ctx = *state; \
|
||||
int rc; \
|
||||
int32_t min, max; \
|
||||
ctx->pid = X52_PROD_X52 ## prodid; \
|
||||
_x52io_set_axis_range(ctx); \
|
||||
rc = libx52io_get_axis_range(ctx, LIBX52IO_AXIS_ ## axis, &min, &max); \
|
||||
assert_int_equal(rc, LIBX52IO_SUCCESS); \
|
||||
assert_int_equal(min, minval); \
|
||||
assert_int_equal(max, maxval); \
|
||||
}
|
||||
|
||||
TEST_CASES
|
||||
#undef TEST
|
||||
|
||||
static void test_error_case(void **state)
|
||||
{
|
||||
libx52io_context *ctx = *state;
|
||||
int rc;
|
||||
int32_t min, max;
|
||||
|
||||
rc = libx52io_get_axis_range(NULL, 0, NULL, NULL);
|
||||
assert_int_equal(rc, LIBX52IO_ERROR_INVALID);
|
||||
|
||||
rc = libx52io_get_axis_range(ctx, 0, NULL, NULL);
|
||||
assert_int_equal(rc, LIBX52IO_ERROR_INVALID);
|
||||
|
||||
rc = libx52io_get_axis_range(ctx, 0, &min, NULL);
|
||||
assert_int_equal(rc, LIBX52IO_ERROR_INVALID);
|
||||
|
||||
rc = libx52io_get_axis_range(ctx, -1, &min, &max);
|
||||
assert_int_equal(rc, LIBX52IO_ERROR_INVALID);
|
||||
|
||||
rc = libx52io_get_axis_range(ctx, LIBX52IO_AXIS_MAX, &min, &max);
|
||||
assert_int_equal(rc, LIBX52IO_ERROR_INVALID);
|
||||
|
||||
ctx->handle = NULL;
|
||||
rc = libx52io_get_axis_range(ctx, LIBX52IO_AXIS_X, &min, &max);
|
||||
assert_int_equal(rc, LIBX52IO_ERROR_NO_DEVICE);
|
||||
}
|
||||
|
||||
#define TEST(prodid, axis, minval, maxval) cmocka_unit_test_setup_teardown(axis ## _ ## prodid, test_setup, test_teardown),
|
||||
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(test_error_case, test_setup, test_teardown),
|
||||
TEST_CASES
|
||||
};
|
||||
#undef TEST
|
||||
|
||||
int main(void)
|
||||
{
|
||||
cmocka_set_message_output(CM_OUTPUT_TAP);
|
||||
cmocka_run_group_tests(tests, group_setup, group_teardown);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Saitek X52 IO driver - Parser test suite
|
||||
*
|
||||
* Copyright (C) 2012-2020 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 "io_common.h"
|
||||
#include "usb-ids.h"
|
||||
|
||||
static int group_setup(void **state)
|
||||
{
|
||||
libx52io_context *ctx;
|
||||
int rc;
|
||||
|
||||
rc = libx52io_init(&ctx);
|
||||
if (rc != LIBX52IO_SUCCESS) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
*state = ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TEST_SETUP(prodid) test_setup_ ## prodid
|
||||
|
||||
#define TEST_SETUP_FUNCTION(prodid) static int TEST_SETUP(prodid)(void **state) { \
|
||||
libx52io_context *ctx = *state; \
|
||||
ctx->pid = X52_PROD_X52 ## prodid; \
|
||||
_x52io_set_report_parser(ctx); \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
TEST_SETUP_FUNCTION(_1);
|
||||
TEST_SETUP_FUNCTION(_2);
|
||||
TEST_SETUP_FUNCTION(PRO);
|
||||
|
||||
#undef TEST_SETUP_FUNCTION
|
||||
|
||||
static int test_teardown(void **state)
|
||||
{
|
||||
libx52io_context *ctx = *state;
|
||||
|
||||
libx52io_close(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int group_teardown(void **state)
|
||||
{
|
||||
libx52io_context *ctx = *state;
|
||||
|
||||
libx52io_exit(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_error_x52(void **state) {
|
||||
/* Verify that passing a buffer of the wrong size returns IO error */
|
||||
libx52io_context *ctx = *state;
|
||||
unsigned char data[15];
|
||||
int rc;
|
||||
|
||||
rc = _x52io_parse_report(ctx, NULL, data, sizeof(data));
|
||||
assert_int_equal(rc, LIBX52IO_ERROR_IO);
|
||||
}
|
||||
|
||||
static void test_error_pro(void **state) {
|
||||
/* Verify that passing a buffer of the wrong size returns IO error */
|
||||
libx52io_context *ctx = *state;
|
||||
unsigned char data[14];
|
||||
int rc;
|
||||
|
||||
rc = _x52io_parse_report(ctx, NULL, data, sizeof(data));
|
||||
assert_int_equal(rc, LIBX52IO_ERROR_IO);
|
||||
}
|
||||
|
||||
#include "test_parser_tests.c"
|
||||
|
||||
#define TEST_LIST
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(test_error_x52, TEST_SETUP(_1), test_teardown),
|
||||
cmocka_unit_test_setup_teardown(test_error_x52, TEST_SETUP(_2), test_teardown),
|
||||
cmocka_unit_test_setup_teardown(test_error_pro, TEST_SETUP(PRO), test_teardown),
|
||||
#include "test_parser_tests.c"
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
cmocka_set_message_output(CM_OUTPUT_TAP);
|
||||
cmocka_run_group_tests(tests, group_setup, group_teardown);
|
||||
return 0;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,10 @@
|
|||
# List of source files which contain translatable strings.
|
||||
lib/libx52/x52_strerror.c
|
||||
|
||||
lib/libx52io/io_strings.c
|
||||
|
||||
utils/evtest/ev_test.c
|
||||
|
||||
utils/test/x52_test.c
|
||||
utils/test/x52_test_clock.c
|
||||
utils/test/x52_test_common.h
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: x52pro-linux 0.2.1\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/nirenjan/x52pro-linux/issues\n"
|
||||
"POT-Creation-Date: 2020-06-12 23:15-0700\n"
|
||||
"POT-Creation-Date: 2020-07-16 04:24-0700\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -17,11 +17,11 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: lib/libx52/x52_strerror.c:25
|
||||
#: lib/libx52/x52_strerror.c:25 lib/libx52io/io_strings.c:104
|
||||
msgid "Success"
|
||||
msgstr ""
|
||||
|
||||
#: lib/libx52/x52_strerror.c:28
|
||||
#: lib/libx52/x52_strerror.c:28 lib/libx52io/io_strings.c:107
|
||||
msgid "Initialization failure"
|
||||
msgstr ""
|
||||
|
||||
|
@ -85,11 +85,55 @@ msgstr ""
|
|||
msgid "System call interrupted"
|
||||
msgstr ""
|
||||
|
||||
#: lib/libx52/x52_strerror.c:77
|
||||
#: lib/libx52/x52_strerror.c:77 lib/libx52io/io_strings.c:125
|
||||
#, c-format
|
||||
msgid "Unknown error %d"
|
||||
msgstr ""
|
||||
|
||||
#: lib/libx52io/io_strings.c:110
|
||||
msgid "No device"
|
||||
msgstr ""
|
||||
|
||||
#: lib/libx52io/io_strings.c:113
|
||||
msgid "Invalid arguments"
|
||||
msgstr ""
|
||||
|
||||
#: lib/libx52io/io_strings.c:116
|
||||
msgid "Connection failure"
|
||||
msgstr ""
|
||||
|
||||
#: lib/libx52io/io_strings.c:119
|
||||
msgid "I/O error"
|
||||
msgstr ""
|
||||
|
||||
#: lib/libx52io/io_strings.c:122
|
||||
msgid "Read timeout"
|
||||
msgstr ""
|
||||
|
||||
#: utils/evtest/ev_test.c:109
|
||||
#, c-format
|
||||
msgid "Device ID: vendor 0x%04x product 0x%04x version 0x%04x\n"
|
||||
msgstr ""
|
||||
|
||||
#: utils/evtest/ev_test.c:113
|
||||
#, c-format
|
||||
msgid "Device name: \"%s %s\"\n"
|
||||
msgstr ""
|
||||
|
||||
#: utils/evtest/ev_test.c:116
|
||||
#, c-format
|
||||
msgid "Serial number: \"%s\"\n"
|
||||
msgstr ""
|
||||
|
||||
#: utils/evtest/ev_test.c:117
|
||||
msgid "Testing (interrupt to exit)\n"
|
||||
msgstr ""
|
||||
|
||||
#: utils/evtest/ev_test.c:157 utils/evtest/ev_test.c:165
|
||||
#, c-format
|
||||
msgid "Event @ %ld.%06ld: %s, value %d\n"
|
||||
msgstr ""
|
||||
|
||||
#: utils/test/x52_test.c:97
|
||||
msgid "Test brightness scale (~ 1m)"
|
||||
msgstr ""
|
||||
|
|
54
po/xx_PL.po
54
po/xx_PL.po
|
@ -7,8 +7,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: x52pro-linux 0.2.1\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/nirenjan/x52pro-linux/issues\n"
|
||||
"POT-Creation-Date: 2020-06-12 23:15-0700\n"
|
||||
"PO-Revision-Date: 2020-05-22 13:25-0700\n"
|
||||
"POT-Creation-Date: 2020-07-16 04:24-0700\n"
|
||||
"PO-Revision-Date: 2020-07-13 18:05-0700\n"
|
||||
"Last-Translator: Nirenjan Krishnan <nirenjan@gmail.com>\n"
|
||||
"Language-Team: Dummy Language for testing i18n\n"
|
||||
"Language: xx_PL\n"
|
||||
|
@ -17,11 +17,11 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.3.1\n"
|
||||
|
||||
#: lib/libx52/x52_strerror.c:25
|
||||
#: lib/libx52/x52_strerror.c:25 lib/libx52io/io_strings.c:104
|
||||
msgid "Success"
|
||||
msgstr "Uccesssay"
|
||||
|
||||
#: lib/libx52/x52_strerror.c:28
|
||||
#: lib/libx52/x52_strerror.c:28 lib/libx52io/io_strings.c:107
|
||||
msgid "Initialization failure"
|
||||
msgstr "Initializationay ailurefay"
|
||||
|
||||
|
@ -85,11 +85,55 @@ msgstr "Ipepay erroray"
|
|||
msgid "System call interrupted"
|
||||
msgstr "Ystemsay allcay interrupteday"
|
||||
|
||||
#: lib/libx52/x52_strerror.c:77
|
||||
#: lib/libx52/x52_strerror.c:77 lib/libx52io/io_strings.c:125
|
||||
#, c-format
|
||||
msgid "Unknown error %d"
|
||||
msgstr "Unknownay erroray %d"
|
||||
|
||||
#: lib/libx52io/io_strings.c:110
|
||||
msgid "No device"
|
||||
msgstr "Onay eviceday"
|
||||
|
||||
#: lib/libx52io/io_strings.c:113
|
||||
msgid "Invalid arguments"
|
||||
msgstr "Invaliday argumentsay"
|
||||
|
||||
#: lib/libx52io/io_strings.c:116
|
||||
msgid "Connection failure"
|
||||
msgstr "Onnectioncay ailurefay"
|
||||
|
||||
#: lib/libx52io/io_strings.c:119
|
||||
msgid "I/O error"
|
||||
msgstr "I/O erroray"
|
||||
|
||||
#: lib/libx52io/io_strings.c:122
|
||||
msgid "Read timeout"
|
||||
msgstr "Eadray imeouttay"
|
||||
|
||||
#: utils/evtest/ev_test.c:109
|
||||
#, c-format
|
||||
msgid "Device ID: vendor 0x%04x product 0x%04x version 0x%04x\n"
|
||||
msgstr "Eviceday IDay: endorvay 0x%04x oductpray 0x%04x ersionvay 0x%04x\n"
|
||||
|
||||
#: utils/evtest/ev_test.c:113
|
||||
#, c-format
|
||||
msgid "Device name: \"%s %s\"\n"
|
||||
msgstr "Eviceday amenay: \"%s %s\"\n"
|
||||
|
||||
#: utils/evtest/ev_test.c:116
|
||||
#, c-format
|
||||
msgid "Serial number: \"%s\"\n"
|
||||
msgstr "Erialsay umbernay: \"%s\"\n"
|
||||
|
||||
#: utils/evtest/ev_test.c:117
|
||||
msgid "Testing (interrupt to exit)\n"
|
||||
msgstr "Estingtay (interruptay otay exitay)\n"
|
||||
|
||||
#: utils/evtest/ev_test.c:157 utils/evtest/ev_test.c:165
|
||||
#, c-format
|
||||
msgid "Event @ %ld.%06ld: %s, value %d\n"
|
||||
msgstr "Eventay @ %ld.%06ld: %s, aluevay %d\n"
|
||||
|
||||
#: utils/test/x52_test.c:97
|
||||
msgid "Test brightness scale (~ 1m)"
|
||||
msgstr "Esttay ightnessbray alescay (~ 1m)"
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
/* USB IDs for Saitek X52 and X52Pro */
|
||||
#ifndef USB_IDS_H
|
||||
#define USB_IDS_H
|
||||
|
||||
#define VENDOR_SAITEK 0x06a3
|
||||
#define X52_PROD_X52PRO 0x0762
|
||||
#define X52_PROD_X52_1 0x0255
|
||||
#define X52_PROD_X52_2 0x075C
|
||||
|
||||
#endif // !defined USB_IDS_H
|
|
@ -4,5 +4,5 @@
|
|||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
|
||||
SUBDIRS = cli test
|
||||
SUBDIRS = cli test evtest
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# Automake for x52evtest
|
||||
#
|
||||
# Copyright (C) 2012-2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
bin_PROGRAMS = x52evtest
|
||||
|
||||
# Event test utility that works similarly to the Linux evtest
|
||||
x52evtest_SOURCES = ev_test.c
|
||||
x52evtest_CFLAGS = -I $(top_srcdir)/lib/libx52io -I $(top_srcdir) -DLOCALEDIR=\"$(localedir)\" $(WARN_CFLAGS)
|
||||
x52evtest_LDFLAGS = $(WARN_LDFLAGS)
|
||||
x52evtest_LDADD = ../../lib/libx52io/libx52io.la
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* Saitek X52 Pro MFD & LED driver - Event test utility
|
||||
*
|
||||
* Copyright (C) 2012-2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "libx52io.h"
|
||||
#include "gettext.h"
|
||||
|
||||
/*
|
||||
Output Format
|
||||
=============
|
||||
|
||||
Driver version is 0.0.0
|
||||
Device ID: bus 0x3 vendor 0x06a3 product 0x0762 version 0x110
|
||||
Device name: "Saitek X52 Pro Flight Control System"
|
||||
Testing ... (interrupt to exit)
|
||||
|
||||
Event @ 1594431236.817842, ABS_X, value 512
|
||||
Event @ 1594431236.817842, ABS_Y, value 511
|
||||
Event @ 1594431236.817842, BTN_TRIGGER, value 1
|
||||
Event @ 1594431236.817842, BTN_MODE_1, value 1
|
||||
|
||||
Event @ 1594431236.847810, BTN_MODE_1, value 0
|
||||
|
||||
Event @ 1594431236.877802, BTN_MODE_2, value 1
|
||||
|
||||
*/
|
||||
|
||||
static bool exit_loop = false;
|
||||
|
||||
static void signal_handler(int sig)
|
||||
{
|
||||
exit_loop = true;
|
||||
}
|
||||
|
||||
/* Denoising - reduce event noise due to adjacent values being reported */
|
||||
static bool denoise = true;
|
||||
|
||||
/* For i18n */
|
||||
#define _(x) gettext(x)
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
libx52io_context *ctx;
|
||||
libx52io_report last, curr;
|
||||
int32_t denoise_mask[LIBX52IO_AXIS_MAX] = { 0 };
|
||||
int rc;
|
||||
#define CHECK_RC() do { \
|
||||
if (rc != LIBX52IO_SUCCESS) { \
|
||||
fprintf(stderr, "%s\n", libx52io_strerror(rc)); \
|
||||
return rc; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* Initialize gettext */
|
||||
#if ENABLE_NLS
|
||||
setlocale(LC_ALL, "");
|
||||
bindtextdomain(PACKAGE, LOCALEDIR);
|
||||
textdomain(LOCALEDIR);
|
||||
#endif
|
||||
|
||||
memset(&last, 0, sizeof(last));
|
||||
memset(&curr, 0, sizeof(curr));
|
||||
|
||||
/* Initialize libx52io */
|
||||
rc = libx52io_init(&ctx);
|
||||
CHECK_RC();
|
||||
|
||||
/* Make sure that we have a device to connect to */
|
||||
rc = libx52io_open(ctx);
|
||||
CHECK_RC();
|
||||
|
||||
/* Initialize denoising */
|
||||
if (denoise) {
|
||||
for (int i = LIBX52IO_AXIS_X; i < LIBX52IO_AXIS_MAX; i++) {
|
||||
int32_t min, max;
|
||||
rc = libx52io_get_axis_range(ctx, i, &min, &max);
|
||||
CHECK_RC();
|
||||
|
||||
/*
|
||||
* Denoising algorithm ignores the last few bits of the axis,
|
||||
* and is based on the maximum value of the axis. The mask is
|
||||
* ~(max >> 6) which will do nothing for the axis with a small
|
||||
* range, but reduce the noise on those with a larger range.
|
||||
*/
|
||||
denoise_mask[i] = ~(max >> 6);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up the signal handler to terminate the loop on SIGTERM or SIGINT */
|
||||
exit_loop = false;
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
|
||||
/* Print the driver version and the connected device */
|
||||
printf(_("Device ID: vendor 0x%04x product 0x%04x version 0x%04x\n"),
|
||||
libx52io_get_vendor_id(ctx),
|
||||
libx52io_get_product_id(ctx),
|
||||
libx52io_get_device_version(ctx));
|
||||
printf(_("Device name: \"%s %s\"\n"),
|
||||
libx52io_get_manufacturer_string(ctx),
|
||||
libx52io_get_product_string(ctx));
|
||||
printf(_("Serial number: \"%s\"\n"), libx52io_get_serial_number_string(ctx));
|
||||
puts(_("Testing (interrupt to exit)\n"));
|
||||
|
||||
/* Wait until we get an event */
|
||||
while (!exit_loop) {
|
||||
struct timeval tv;
|
||||
bool printed = false;
|
||||
|
||||
/* Wait for 1 second before timing out */
|
||||
rc = libx52io_read_timeout(ctx, &curr, 1000);
|
||||
if (rc == LIBX52IO_ERROR_TIMEOUT) {
|
||||
continue;
|
||||
} else if (rc != LIBX52IO_SUCCESS) {
|
||||
/* Some other error while reading. Abort the loop */
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Successful read, compare the current report against the previous
|
||||
* one and display the result
|
||||
*/
|
||||
if (memcmp(&last, &curr, sizeof(curr)) == 0) {
|
||||
/* No change, ignore the output */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get the current timeval - we don't need a timezone */
|
||||
gettimeofday(&tv, NULL);
|
||||
for (int axis = 0; axis < LIBX52IO_AXIS_MAX; axis++) {
|
||||
if (last.axis[axis] != curr.axis[axis]) {
|
||||
/* Account for denoising */
|
||||
if (denoise) {
|
||||
int32_t last_v = last.axis[axis] & denoise_mask[axis];
|
||||
int32_t curr_v = curr.axis[axis] & denoise_mask[axis];
|
||||
|
||||
if (last_v == curr_v) {
|
||||
/* Within the noise threshold */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
printf(_("Event @ %ld.%06ld: %s, value %d\n"),
|
||||
(long int)tv.tv_sec, (long int)tv.tv_usec,
|
||||
libx52io_axis_to_str(axis), curr.axis[axis]);
|
||||
printed = true;
|
||||
}
|
||||
}
|
||||
for (int btn = 0; btn < LIBX52IO_BUTTON_MAX; btn++) {
|
||||
if (last.button[btn] != curr.button[btn]) {
|
||||
printf(_("Event @ %ld.%06ld: %s, value %d\n"),
|
||||
(long int)tv.tv_sec, (long int)tv.tv_usec,
|
||||
libx52io_button_to_str(btn), curr.button[btn]);
|
||||
printed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (printed) {
|
||||
puts("");
|
||||
}
|
||||
|
||||
memcpy(&last, &curr, sizeof(curr));
|
||||
}
|
||||
|
||||
/* Close and exit the libx52io library */
|
||||
libx52io_close(ctx);
|
||||
libx52io_exit(ctx);
|
||||
}
|
Loading…
Reference in New Issue