Make AOA open and close asynchronous

For AOA keyboard and mouse, only input events were asynchronous.
Register/unregister were called from the main thread.

This had the benefit to fail immediately if the AOA registration failed,
but to support gamepads we want to open/close AOA devices dynamically.

Also, it is better to avoid USB I/O from the main thread.
This commit is contained in:
Romain Vimont 2024-09-06 23:08:08 +02:00
parent 9c382e8169
commit 5841650aa0
4 changed files with 160 additions and 45 deletions

View file

@ -16,7 +16,8 @@
#define DEFAULT_TIMEOUT 1000
#define SC_AOA_EVENT_QUEUE_MAX 64
// Drop droppable events above this limit
#define SC_AOA_EVENT_QUEUE_LIMIT 60
static void
sc_hid_event_log(const struct sc_hid_event *event) {
@ -35,7 +36,8 @@ sc_aoa_init(struct sc_aoa *aoa, struct sc_usb *usb,
struct sc_acksync *acksync) {
sc_vecdeque_init(&aoa->queue);
if (!sc_vecdeque_reserve(&aoa->queue, SC_AOA_EVENT_QUEUE_MAX)) {
// Add 4 to support 4 non-droppable events without re-allocation
if (!sc_vecdeque_reserve(&aoa->queue, SC_AOA_EVENT_QUEUE_LIMIT + 4)) {
return false;
}
@ -149,7 +151,7 @@ sc_aoa_send_hid_event(struct sc_aoa *aoa, const struct sc_hid_event *event) {
return true;
}
bool
static bool
sc_aoa_unregister_hid(struct sc_aoa *aoa, uint16_t accessory_id) {
uint8_t request_type = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR;
uint8_t request = ACCESSORY_UNREGISTER_HID;
@ -172,7 +174,7 @@ sc_aoa_unregister_hid(struct sc_aoa *aoa, uint16_t accessory_id) {
return true;
}
bool
static bool
sc_aoa_setup_hid(struct sc_aoa *aoa, uint16_t accessory_id,
const uint8_t *report_desc, uint16_t report_desc_size) {
bool ok = sc_aoa_register_hid(aoa, accessory_id, report_desc_size);
@ -207,8 +209,9 @@ sc_aoa_push_hid_event_with_ack_to_wait(struct sc_aoa *aoa,
struct sc_aoa_event *aoa_event =
sc_vecdeque_push_hole_noresize(&aoa->queue);
aoa_event->hid = *event;
aoa_event->ack_to_wait = ack_to_wait;
aoa_event->type = SC_AOA_EVENT_TYPE_INPUT;
aoa_event->input.hid = *event;
aoa_event->input.ack_to_wait = ack_to_wait;
if (was_empty) {
sc_cond_signal(&aoa->event_cond);
@ -221,14 +224,75 @@ sc_aoa_push_hid_event_with_ack_to_wait(struct sc_aoa *aoa,
return !full;
}
bool
sc_aoa_push_open(struct sc_aoa *aoa, uint16_t accessory_id,
const uint8_t *report_desc, uint16_t report_desc_size) {
// TODO log verbose
sc_mutex_lock(&aoa->mutex);
bool was_empty = sc_vecdeque_is_empty(&aoa->queue);
// an OPEN event is non-droppable, so push it to the queue even above the
// SC_AOA_EVENT_QUEUE_LIMIT
struct sc_aoa_event *aoa_event = sc_vecdeque_push_hole(&aoa->queue);
if (!aoa_event) {
LOG_OOM();
sc_mutex_unlock(&aoa->mutex);
return false;
}
aoa_event->type = SC_AOA_EVENT_TYPE_OPEN;
aoa_event->open.hid_id = accessory_id;
aoa_event->open.report_desc = report_desc;
aoa_event->open.report_desc_size = report_desc_size;
if (was_empty) {
sc_cond_signal(&aoa->event_cond);
}
sc_mutex_unlock(&aoa->mutex);
return true;
}
bool
sc_aoa_push_close(struct sc_aoa *aoa, uint16_t accessory_id) {
// TODO log verbose
sc_mutex_lock(&aoa->mutex);
bool was_empty = sc_vecdeque_is_empty(&aoa->queue);
// an OPEN event is non-droppable, so push it to the queue even above the
// SC_AOA_EVENT_QUEUE_LIMIT
struct sc_aoa_event *aoa_event = sc_vecdeque_push_hole(&aoa->queue);
if (!aoa_event) {
LOG_OOM();
sc_mutex_unlock(&aoa->mutex);
return false;
}
aoa_event->type = SC_AOA_EVENT_TYPE_CLOSE;
aoa_event->close.hid_id = accessory_id;
if (was_empty) {
sc_cond_signal(&aoa->event_cond);
}
sc_mutex_unlock(&aoa->mutex);
return true;
}
static bool
sc_aoa_process_event(struct sc_aoa *aoa, struct sc_aoa_event *event) {
uint64_t ack_to_wait = event->ack_to_wait;
switch (event->type) {
case SC_AOA_EVENT_TYPE_INPUT: {
uint64_t ack_to_wait = event->input.ack_to_wait;
if (ack_to_wait != SC_SEQUENCE_INVALID) {
LOGD("Waiting ack from server sequence=%" PRIu64_, ack_to_wait);
// If some events have ack_to_wait set, then sc_aoa must have been
// initialized with a non NULL acksync
// If some events have ack_to_wait set, then sc_aoa must have
// been initialized with a non NULL acksync
assert(aoa->acksync);
// Do not block the loop indefinitely if the ack never comes (it
@ -247,9 +311,33 @@ sc_aoa_process_event(struct sc_aoa *aoa, struct sc_aoa_event *event) {
}
}
bool ok = sc_aoa_send_hid_event(aoa, &event->hid);
bool ok = sc_aoa_send_hid_event(aoa, &event->input.hid);
if (!ok) {
LOGW("Could not send HID event to USB device");
LOGW("Could not send HID event to USB device: %" PRIu16,
event->input.hid.hid_id);
}
break;
}
case SC_AOA_EVENT_TYPE_OPEN: {
bool ok = sc_aoa_setup_hid(aoa, event->open.hid_id,
event->open.report_desc,
event->open.report_desc_size);
if (!ok) {
LOGW("Could not open AOA device: %" PRIu16, event->open.hid_id);
}
break;
}
case SC_AOA_EVENT_TYPE_CLOSE: {
bool ok = sc_aoa_unregister_hid(aoa, event->close.hid_id);
if (!ok) {
LOGW("Could not close AOA device: %" PRIu16,
event->close.hid_id);
}
break;
}
}
// continue to process events

View file

@ -13,9 +13,28 @@
#include "util/tick.h"
#include "util/vecdeque.h"
enum sc_aoa_event_type {
SC_AOA_EVENT_TYPE_OPEN,
SC_AOA_EVENT_TYPE_INPUT,
SC_AOA_EVENT_TYPE_CLOSE,
};
struct sc_aoa_event {
enum sc_aoa_event_type type;
union {
struct {
uint16_t hid_id;
const uint8_t *report_desc; // pointer to static memory
uint16_t report_desc_size;
} open;
struct {
uint16_t hid_id;
} close;
struct {
struct sc_hid_event hid;
uint64_t ack_to_wait;
} input;
};
};
struct sc_aoa_event_queue SC_VECDEQUE(struct sc_aoa_event);
@ -46,12 +65,21 @@ sc_aoa_stop(struct sc_aoa *aoa);
void
sc_aoa_join(struct sc_aoa *aoa);
//bool
//sc_aoa_setup_hid(struct sc_aoa *aoa, uint16_t accessory_id,
// const uint8_t *report_desc, uint16_t report_desc_size);
//
//bool
//sc_aoa_unregister_hid(struct sc_aoa *aoa, uint16_t accessory_id);
// report_desc must be a pointer to static memory, accessed at any time from
// another thread
bool
sc_aoa_setup_hid(struct sc_aoa *aoa, uint16_t accessory_id,
sc_aoa_push_open(struct sc_aoa *aoa, uint16_t accessory_id,
const uint8_t *report_desc, uint16_t report_desc_size);
bool
sc_aoa_unregister_hid(struct sc_aoa *aoa, uint16_t accessory_id);
sc_aoa_push_close(struct sc_aoa *aoa, uint16_t accessory_id);
bool
sc_aoa_push_hid_event_with_ack_to_wait(struct sc_aoa *aoa,

View file

@ -66,11 +66,11 @@ bool
sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb, struct sc_aoa *aoa) {
kb->aoa = aoa;
bool ok = sc_aoa_setup_hid(aoa, SC_HID_ID_KEYBOARD,
bool ok = sc_aoa_push_open(aoa, SC_HID_ID_KEYBOARD,
SC_HID_KEYBOARD_REPORT_DESC,
SC_HID_KEYBOARD_REPORT_DESC_LEN);
if (!ok) {
LOGW("Register HID keyboard failed");
LOGW("Could not push AOA keyboard open request");
return false;
}
@ -97,9 +97,8 @@ sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb, struct sc_aoa *aoa) {
void
sc_keyboard_aoa_destroy(struct sc_keyboard_aoa *kb) {
// Unregister HID keyboard so the soft keyboard shows again on Android
bool ok = sc_aoa_unregister_hid(kb->aoa, SC_HID_ID_KEYBOARD);
bool ok = sc_aoa_push_close(kb->aoa, SC_HID_ID_KEYBOARD);
if (!ok) {
LOGW("Could not unregister HID keyboard");
LOGW("Could not push AOA keyboard close request");
}
}

View file

@ -52,11 +52,11 @@ bool
sc_mouse_aoa_init(struct sc_mouse_aoa *mouse, struct sc_aoa *aoa) {
mouse->aoa = aoa;
bool ok = sc_aoa_setup_hid(aoa, SC_HID_ID_MOUSE,
bool ok = sc_aoa_push_open(aoa, SC_HID_ID_MOUSE,
SC_HID_MOUSE_REPORT_DESC,
SC_HID_MOUSE_REPORT_DESC_LEN);
if (!ok) {
LOGW("Register HID mouse failed");
LOGW("Could not push AOA mouse open request");
return false;
}
@ -77,8 +77,8 @@ sc_mouse_aoa_init(struct sc_mouse_aoa *mouse, struct sc_aoa *aoa) {
void
sc_mouse_aoa_destroy(struct sc_mouse_aoa *mouse) {
bool ok = sc_aoa_unregister_hid(mouse->aoa, SC_HID_ID_MOUSE);
bool ok = sc_aoa_push_close(mouse->aoa, SC_HID_ID_MOUSE);
if (!ok) {
LOGW("Could not unregister HID mouse");
LOGW("Could not push AOA mouse close request");
}
}