diff --git a/app/src/usb/aoa_hid.c b/app/src/usb/aoa_hid.c index 57296bfc..0007169d 100644 --- a/app/src/usb/aoa_hid.c +++ b/app/src/usb/aoa_hid.c @@ -95,6 +95,7 @@ sc_aoa_register_hid(struct sc_aoa *aoa, uint16_t accessory_id, DEFAULT_TIMEOUT); if (result < 0) { LOGE("REGISTER_HID: libusb error: %s", libusb_strerror(result)); + sc_usb_check_disconnected(aoa->usb, result); return false; } @@ -131,6 +132,7 @@ sc_aoa_set_hid_report_desc(struct sc_aoa *aoa, uint16_t accessory_id, DEFAULT_TIMEOUT); if (result < 0) { LOGE("SET_HID_REPORT_DESC: libusb error: %s", libusb_strerror(result)); + sc_usb_check_disconnected(aoa->usb, result); return false; } @@ -173,6 +175,7 @@ sc_aoa_send_hid_event(struct sc_aoa *aoa, const struct sc_hid_event *event) { DEFAULT_TIMEOUT); if (result < 0) { LOGE("SEND_HID_EVENT: libusb error: %s", libusb_strerror(result)); + sc_usb_check_disconnected(aoa->usb, result); return false; } @@ -195,6 +198,7 @@ sc_aoa_unregister_hid(struct sc_aoa *aoa, const uint16_t accessory_id) { DEFAULT_TIMEOUT); if (result < 0) { LOGE("UNREGISTER_HID: libusb error: %s", libusb_strerror(result)); + sc_usb_check_disconnected(aoa->usb, result); return false; } diff --git a/app/src/usb/usb.c b/app/src/usb/usb.c index 07fb9619..7a0e4cfd 100644 --- a/app/src/usb/usb.c +++ b/app/src/usb/usb.c @@ -216,6 +216,24 @@ sc_usb_destroy(struct sc_usb *usb) { libusb_exit(usb->context); } +static void +sc_usb_report_disconnected(struct sc_usb *usb) { + if (usb->cbs && !atomic_flag_test_and_set(&usb->disconnection_notified)) { + assert(usb->cbs && usb->cbs->on_disconnected); + usb->cbs->on_disconnected(usb, usb->cbs_userdata); + } +} + +bool +sc_usb_check_disconnected(struct sc_usb *usb, int result) { + if (result == LIBUSB_ERROR_NO_DEVICE || result == LIBUSB_ERROR_NOT_FOUND) { + sc_usb_report_disconnected(usb); + return false; + } + + return true; +} + static int sc_usb_libusb_callback(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *userdata) { @@ -232,8 +250,7 @@ sc_usb_libusb_callback(libusb_context *ctx, libusb_device *device, return 0; } - assert(usb->cbs && usb->cbs->on_disconnected); - usb->cbs->on_disconnected(usb, usb->cbs_userdata); + sc_usb_report_disconnected(usb); // Do not automatically deregister the callback by returning 1. Instead, // manually deregister to interrupt libusb_handle_events() from the libusb @@ -307,6 +324,7 @@ sc_usb_connect(struct sc_usb *usb, libusb_device *device, if (cbs) { atomic_init(&usb->stopped, false); + usb->disconnection_notified = (atomic_flag) ATOMIC_FLAG_INIT; if (sc_usb_register_callback(usb)) { // Create a thread to process libusb events, so that device // disconnection could be detected immediately diff --git a/app/src/usb/usb.h b/app/src/usb/usb.h index d264a536..f0ebbd96 100644 --- a/app/src/usb/usb.h +++ b/app/src/usb/usb.h @@ -22,6 +22,7 @@ struct sc_usb { sc_thread libusb_event_thread; atomic_bool stopped; // only used if cbs != NULL + atomic_flag disconnection_notified; }; struct sc_usb_callbacks { @@ -73,6 +74,11 @@ sc_usb_connect(struct sc_usb *usb, libusb_device *device, void sc_usb_disconnect(struct sc_usb *usb); +// A client should call this function with the return value of a libusb call +// to detect disconnection immediately +bool +sc_usb_check_disconnected(struct sc_usb *usb, int result); + void sc_usb_stop(struct sc_usb *usb);