diff --git a/ChangeLog.md b/ChangeLog.md index 36a51ac..c50c701 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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). diff --git a/lib/libx52/libx52.h b/lib/libx52/libx52.h index 7254f1c..41e3ff7 100644 --- a/lib/libx52/libx52.h +++ b/lib/libx52/libx52.h @@ -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); + /** @} */ /** diff --git a/lib/libx52/x52_control.c b/lib/libx52/x52_control.c index 1d5ddde..dd37563 100644 --- a/lib/libx52/x52_control.c +++ b/lib/libx52/x52_control.c @@ -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 */ diff --git a/lib/libx52/x52_core.c b/lib/libx52/x52_core.c index 5a18c79..387fe4e 100644 --- a/lib/libx52/x52_core.c +++ b/lib/libx52/x52_core.c @@ -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 */ diff --git a/utils/cli/x52_cli.c b/utils/cli/x52_cli.c index 09542e4..bfed4a9 100644 --- a/utils/cli/x52_cli.c +++ b/utils/cli/x52_cli.c @@ -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; }