Add connect/disconnect methods to libx52

Prior to this change, libx52_init needed a supported joystick to be
plugged in, otherwise it would fail with LIBX52_ERROR_NO_DEVICE. This
change modifies the behavior so that libx52_init would still succeed,
but the application should call libx52_connect to make sure that the
device handle is valid. libx52_init still tries to connect to the
joystick, but absence is no longer treated as a failure.

This change also modifies x52cli to check that the joystick is actually
connected before calling the individual handlers. Because libx52_init no
longer fails if the joystick is absent, we need to rely on the return
code from libx52_connect.
debian-packaging
nirenjan 2020-05-23 02:16:53 -07:00
parent 93f1091b95
commit d9ae8d4b79
5 changed files with 129 additions and 42 deletions

View File

@ -6,11 +6,15 @@ The format is based upon [Keep a Changelog].
## [Unreleased]
### Added
- Connect/Disconnect methods in libx52. These allow for dynamically connecting
or disconnecting from a supported joystick without having to reinitialize the
library.
- Internationalization for the following:
* libx52
* x52test
### Changed
- libx52_init no longer fails when a supported joystick is not connected.
- x52test moves the LED tests to execute after all other tests. See
[#19](https://github.com/nirenjan/x52pro-linux/issues/19).

View File

@ -227,11 +227,10 @@ typedef enum {
* structures to access the joystick, and returns a \ref libx52_device pointer.
* All calls to libx52 use the returned pointer to control the device.
*
* If no joystick is found `libx52_init()` returns _NULL_.
*
* @par Limitations
* This function does not support hotplugging. The joystick must be plugged in
* before calling this function.
* This function attempts to connect to the joystick upon initialization.
* However, if no device is connected, then the library initialization does
* not fail, but the application must call \ref libx52_connect prior to any
* calls to \ref libx52_update
*
* @param[out] dev Pointer to a \ref libx52_device *. This function will
* allocate a device context and return the pointer to that in this variable.
@ -252,6 +251,33 @@ int libx52_init(libx52_device ** dev);
*/
void libx52_exit(libx52_device *dev);
/**
* @brief Connect to the X52 device
*
* Attempt to connect to a supported X52/X52Pro joystick. If no supported
* joysticks are found, it will return \ref LIBX52_ERROR_NO_DEVICE. If any
* errors are encountered during device enumeration, it will return an
* appropriate \ref libx52_error_code.
*
* @param[in] dev Pointer to the device
*
* @returns \ref libx52_error_code indicating status
*/
int libx52_connect(libx52_device *dev);
/**
* @brief Disconnect from the X52 device
*
* This function disconnects any active connections to supported joysticks.
* Applications must reconnect to the joystick using \ref libx52_connect prior
* to calling \ref libx52_update.
*
* @param[in] dev Pointer to the device
*
* @returns \ref libx52_error_code indicating status
*/
int libx52_disconnect(libx52_device *dev);
/** @} */
/**

View File

@ -70,6 +70,13 @@ int libx52_vendor_command(libx52_device *x52, uint16_t index, uint16_t value)
int j;
int rc = 0;
/* It is possible for the vendor command to be called when the joystick
* is not connected. Check for this and return an appropriate error.
*/
if (!x52->hdl) {
return LIBX52_ERROR_NO_DEVICE;
}
/* Allow retry in case of failure */
for (j = 0; j < 3; j++) {
rc = libusb_control_transfer(x52->hdl,
@ -226,6 +233,13 @@ int libx52_update(libx52_device *x52)
uint16_t value;
int rc = LIBX52_SUCCESS;
/* It is possible for the update command to be called when the joystick
* is not connected. Check for this and return an appropriate error.
*/
if (!x52->hdl) {
return LIBX52_ERROR_NO_DEVICE;
}
/* Save the update mask */
update_mask = x52->update_mask;
/* Reset the device update mask to 0 */

View File

@ -43,7 +43,22 @@ static int libx52_device_is_x52pro(uint16_t idProduct)
return (idProduct == X52_PROD_X52PRO);
}
int libx52_init(libx52_device **dev)
int libx52_disconnect(libx52_device *dev)
{
if (!dev) {
return LIBX52_ERROR_INVALID_PARAM;
}
if (dev->hdl) {
libusb_close(dev->hdl);
dev->hdl = NULL;
dev->flags = 0;
}
return LIBX52_SUCCESS;
}
int libx52_connect(libx52_device *dev)
{
int rc;
ssize_t count;
@ -52,6 +67,52 @@ int libx52_init(libx52_device **dev)
libusb_device *device;
libusb_device_handle *hdl;
struct libusb_device_descriptor desc;
/* Make sure that we have a valid pointer */
if (!dev) {
return LIBX52_ERROR_INVALID_PARAM;
}
/* Disconnect any existing handles. This will force libx52 to rescan the
* device list and bind to the first supported joystick, if any. If the
* joystick was unplugged between subsequent calls to this function, then
* it will return a No device error. This also means that a new device
* handle is cached in the device structure.
*/
(void)libx52_disconnect(dev);
count = libusb_get_device_list(dev->ctx, &list);
for (i = 0; i < count; i++) {
device = list[i];
if (!libusb_get_device_descriptor(device, &desc)) {
if (libx52_check_product(desc.idVendor, desc.idProduct)) {
rc = libusb_open(device, &hdl);
if (rc) {
return libx52internal_translate_libusb_error(rc);
}
dev->hdl = hdl;
if (libx52_device_is_x52pro(desc.idProduct)) {
set_bit(&(dev->flags), X52_FLAG_IS_PRO);
}
break;
}
}
}
libusb_free_device_list(list, 1);
/* Make sure we actually have an X52 device detected */
if (!dev->hdl) {
return LIBX52_ERROR_NO_DEVICE;
}
return LIBX52_SUCCESS;
}
int libx52_init(libx52_device **dev)
{
int rc;
libx52_device *x52_dev;
/* Make sure that we have a valid return pointer */
@ -67,9 +128,10 @@ int libx52_init(libx52_device **dev)
rc = libusb_init(&(x52_dev->ctx));
if (rc) {
rc = LIBX52_ERROR_INIT_FAILURE;
goto err_recovery;
free(x52_dev);
return LIBX52_ERROR_INIT_FAILURE;
}
#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000106)
/*
* Use the libusb_set_option flag instead of libusb_set_debug. This
@ -81,33 +143,11 @@ int libx52_init(libx52_device **dev)
libusb_set_debug(x52_dev->ctx, LIBUSB_LOG_LEVEL_WARNING);
#endif
count = libusb_get_device_list(x52_dev->ctx, &list);
for (i = 0; i < count; i++) {
device = list[i];
if (!libusb_get_device_descriptor(device, &desc)) {
if (libx52_check_product(desc.idVendor, desc.idProduct)) {
rc = libusb_open(device, &hdl);
if (rc) {
rc = libx52internal_translate_libusb_error(rc);
goto err_recovery;
}
x52_dev->hdl = hdl;
if (libx52_device_is_x52pro(desc.idProduct)) {
set_bit(&(x52_dev->flags), X52_FLAG_IS_PRO);
}
break;
}
}
}
libusb_free_device_list(list, 1);
/* Make sure we actually have an X52 device detected */
if (!x52_dev->hdl) {
rc = LIBX52_ERROR_NO_DEVICE;
goto err_recovery;
}
/* Try to connect to any supported joystick. It's OK if there aren't
* any available to connect to, subsequent calls to libx52_connect will
* be used to open the device handle
*/
(void)libx52_connect(x52_dev);
*dev = x52_dev;
@ -115,15 +155,11 @@ int libx52_init(libx52_device **dev)
bindtextdomain(PACKAGE, LOCALEDIR);
return LIBX52_SUCCESS;
err_recovery:
free(x52_dev);
return rc;
}
void libx52_exit(libx52_device *dev)
{
libusb_close(dev->hdl);
libx52_disconnect(dev);
libusb_exit(dev->ctx);
/* Clear the memory to prevent reuse */

View File

@ -374,11 +374,18 @@ int main(int argc, char **argv)
}
}
/* Lookup the X52 device */
/* Initialize libx52 */
rc = libx52_init(&x52);
if (rc != LIBX52_SUCCESS) {
fprintf(stderr, "Error initializing X52 joystick: %s\n", libx52_strerror(rc));
fprintf(stderr, "Error initializing X52 library: %s\n", libx52_strerror(rc));
return 1;
}
/* Make sure we are connected to the joystick */
rc = libx52_connect(x52);
if (rc != LIBX52_SUCCESS) {
fprintf(stderr, "Error connecting to joystick: %s\n", libx52_strerror(rc));
return 1;
}