Skip to content

Commit

Permalink
Send HID events in separated thread
Browse files Browse the repository at this point in the history
  • Loading branch information
AlynxZhou committed Sep 14, 2021
1 parent 5140be9 commit e4ca327
Show file tree
Hide file tree
Showing 7 changed files with 351 additions and 200 deletions.
219 changes: 183 additions & 36 deletions app/src/aoa_hid.c
Original file line number Diff line number Diff line change
@@ -1,17 +1,41 @@
#include "util/log.h"

#include <assert.h>

#include "aoa_hid.h"

// See <https://source.android.com/devices/accessories/aoa2#hid-support>.
#define ACCESSORY_REGISTER_HID 54
#define ACCESSORY_UNREGISTER_HID 55
#define ACCESSORY_SET_HID_REPORT_DESC 56
#define ACCESSORY_SEND_HID_EVENT 57
#define ACCESSORY_UNREGISTER_HID 55

#define DEFAULT_TIMEOUT 1000

// 128 seems to be enough for serial.
#define SERIAL_BUFFER_SIZE 128

void hid_event_log(const struct hid_event *event) {
// HID Event: [00] FF FF FF FF...
unsigned int buffer_size = event->size * 3 + 1;
char *buffer = malloc(sizeof(*buffer) * buffer_size);
if (!buffer) {
return;
}
buffer[0] = '\0';
for (unsigned int i = 0; i < event->size; ++i) {
snprintf(buffer + i * 3, buffer_size - i * 3, " %02x",
event->buffer[i]);
}
LOGV("HID Event: [%d]%s", event->from_accessory_id, buffer);
free(buffer);
return;
}

void hid_event_destroy(struct hid_event *event) {
free(event->buffer);
}

inline static void log_libusb_error(enum libusb_error errcode) {
LOGW("libusb error: %s", libusb_strerror(errcode));
}
Expand Down Expand Up @@ -47,7 +71,7 @@ get_usb_serial(libusb_device *device, char *buffer, int size) {
return 0;
}

libusb_device *aoa_find_usb_device(const char *serial) {
inline static libusb_device *aoa_find_usb_device(const char *serial) {
if (!serial) {
return NULL;
}
Expand All @@ -73,7 +97,8 @@ libusb_device *aoa_find_usb_device(const char *serial) {
return result;
}

int aoa_open_usb_handle(libusb_device *device, libusb_device_handle **handle) {
inline static int
aoa_open_usb_handle(libusb_device *device, libusb_device_handle **handle) {
int result = libusb_open(device, handle);
if (result < 0) {
log_libusb_error((enum libusb_error)result);
Expand All @@ -82,37 +107,65 @@ int aoa_open_usb_handle(libusb_device *device, libusb_device_handle **handle) {
return 0;
}

int aoa_register_hid(libusb_device_handle *handle, uint16_t report_desc_size) {
const uint8_t request_type = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR;
const uint8_t request = ACCESSORY_REGISTER_HID;
// See <https://source.android.com/devices/accessories/aoa2.html#hid-support>.
// value (arg0): accessory assigned ID for the HID device
// index (arg1): total length of the HID report descriptor
const uint16_t value = 0;
const uint16_t index = report_desc_size;
unsigned char *buffer = NULL;
const uint16_t length = 0;
int result = libusb_control_transfer(handle, request_type, request, value,
index, buffer, length, DEFAULT_TIMEOUT);
if (result < 0) {
log_libusb_error((enum libusb_error)result);
return result;
bool aoa_init(struct aoa *aoa, const struct scrcpy_options *options) {
aoa->usb_device = NULL;
aoa->usb_handle = NULL;
aoa->next_accessories_id = 0;

cbuf_init(&aoa->queue);

if (!sc_mutex_init(&aoa->mutex)) {
return false;
}
return 0;

if (!sc_cond_init(&aoa->event_cond)) {
sc_mutex_destroy(&aoa->mutex);
return false;
}

libusb_init(NULL);

aoa->usb_device = aoa_find_usb_device(options->serial);
if (!aoa->usb_device) {
LOGW("USB device of serial %s not found", options->serial);
sc_mutex_destroy(&aoa->mutex);
sc_cond_destroy(&aoa->event_cond);
return false;
}

if (aoa_open_usb_handle(aoa->usb_device, &aoa->usb_handle) < 0) {
LOGW("Open USB handle failed");
sc_cond_destroy(&aoa->event_cond);
sc_mutex_destroy(&aoa->mutex);
libusb_unref_device(aoa->usb_device);
return false;
}

libusb_get_device_descriptor(aoa->usb_device, &aoa->desc);

aoa->stopped = false;

return true;
}

uint16_t aoa_get_new_accessory_id(struct aoa *aoa) {
return aoa->next_accessories_id++;
}

int aoa_unregister_hid(libusb_device_handle *handle) {
int
aoa_register_hid(struct aoa *aoa, const uint16_t accessory_id,
uint16_t report_desc_size) {
const uint8_t request_type = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR;
const uint8_t request = ACCESSORY_UNREGISTER_HID;
const uint8_t request = ACCESSORY_REGISTER_HID;
// See <https://source.android.com/devices/accessories/aoa2.html#hid-support>.
// value (arg0): accessory assigned ID for the HID device
// index (arg1): 0
const uint16_t value = 0;
const uint16_t index = 0;
// index (arg1): total length of the HID report descriptor
const uint16_t value = accessory_id;
const uint16_t index = report_desc_size;
unsigned char *buffer = NULL;
const uint16_t length = 0;
int result = libusb_control_transfer(handle, request_type, request, value,
index, buffer, length, DEFAULT_TIMEOUT);
int result = libusb_control_transfer(aoa->usb_handle, request_type, request,
value, index, buffer, length, DEFAULT_TIMEOUT);
if (result < 0) {
log_libusb_error((enum libusb_error)result);
return result;
Expand All @@ -121,13 +174,14 @@ int aoa_unregister_hid(libusb_device_handle *handle) {
}

int
aoa_set_hid_report_desc(libusb_device_handle *handle,
const struct report_desc *report_desc, uint8_t max_packet_size_0) {
aoa_set_hid_report_desc(struct aoa *aoa,
const struct report_desc *report_desc) {
const uint8_t request_type = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR;
const uint8_t request = ACCESSORY_SET_HID_REPORT_DESC;
const uint8_t max_packet_size_0 = aoa->desc.bMaxPacketSize0;
// See <https://source.android.com/devices/accessories/aoa2.html#hid-support>.
// value (arg0): accessory assigned ID for the HID device
const uint16_t value = 0;
const uint16_t value = report_desc->from_accessory_id;
// libusb_control_transfer expects non-const but should not modify it.
unsigned char *buffer = (unsigned char *)report_desc->buffer;
const uint16_t size = report_desc->size;
Expand All @@ -146,8 +200,9 @@ aoa_set_hid_report_desc(libusb_device_handle *handle,
if (packet_length > max_packet_size_0) {
packet_length = max_packet_size_0;
}
int result = libusb_control_transfer(handle, request_type, request,
value, offset, buffer + offset, packet_length, DEFAULT_TIMEOUT);
int result = libusb_control_transfer(aoa->usb_handle, request_type,
request, value, offset, buffer + offset, packet_length,
DEFAULT_TIMEOUT);
offset += packet_length;
if (result < 0) {
log_libusb_error((enum libusb_error)result);
Expand All @@ -158,23 +213,115 @@ aoa_set_hid_report_desc(libusb_device_handle *handle,
}

int
aoa_send_hid_event(libusb_device_handle *handle,
const struct hid_event *event) {
aoa_send_hid_event(struct aoa *aoa, const struct hid_event *event) {
const uint8_t request_type = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR;
const uint8_t request = ACCESSORY_SEND_HID_EVENT;
// See <https://source.android.com/devices/accessories/aoa2.html#hid-support>.
// value (arg0): accessory assigned ID for the HID device
// index (arg1): 0 (unused)
const uint16_t value = 0;
const uint16_t value = event->from_accessory_id;
const uint16_t index = 0;
// libusb_control_transfer expects non-const but should not modify it.
unsigned char *buffer = (unsigned char *)event->buffer;
const uint16_t length = event->size;
int result = libusb_control_transfer(handle, request_type, request, value,
index, buffer, length, DEFAULT_TIMEOUT);
int result = libusb_control_transfer(aoa->usb_handle, request_type, request,
value, index, buffer, length, DEFAULT_TIMEOUT);
if (result < 0) {
log_libusb_error((enum libusb_error)result);
return result;
}
return 0;
}

int aoa_unregister_hid(struct aoa *aoa, const uint16_t accessory_id) {
const uint8_t request_type = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR;
const uint8_t request = ACCESSORY_UNREGISTER_HID;
// See <https://source.android.com/devices/accessories/aoa2.html#hid-support>.
// value (arg0): accessory assigned ID for the HID device
// index (arg1): 0
const uint16_t value = accessory_id;
const uint16_t index = 0;
unsigned char *buffer = NULL;
const uint16_t length = 0;
int result = libusb_control_transfer(aoa->usb_handle, request_type, request,
value, index, buffer, length, DEFAULT_TIMEOUT);
if (result < 0) {
log_libusb_error((enum libusb_error)result);
return result;
}
return 0;
}

bool aoa_push_hid_event(struct aoa *aoa, const struct hid_event *event) {
hid_event_log(event);
sc_mutex_lock(&aoa->mutex);
bool was_empty = cbuf_is_empty(&aoa->queue);
bool res = cbuf_push(&aoa->queue, *event);
if (was_empty) {
sc_cond_signal(&aoa->event_cond);
}
sc_mutex_unlock(&aoa->mutex);
return res;
}

inline static bool
process_hid_event(struct aoa *aoa, const struct hid_event *event) {
return aoa_send_hid_event(aoa, event) == 0;
}

inline static int run_aoa_thread(void *data) {
struct aoa *aoa = data;
while (true) {
sc_mutex_lock(&aoa->mutex);
while (!aoa->stopped && cbuf_is_empty(&aoa->queue)) {
sc_cond_wait(&aoa->event_cond, &aoa->mutex);
}
if (aoa->stopped) {
// Stop immediately, do not process further event.
sc_mutex_unlock(&aoa->mutex);
break;
}
struct hid_event event;
bool non_empty = cbuf_take(&aoa->queue, &event);
assert(non_empty);
(void) non_empty;
sc_mutex_unlock(&aoa->mutex);
bool ok = process_hid_event(aoa, &event);
hid_event_destroy(&event);
if (!ok) {
LOGW("Could not send HID event to USB device");
}
}
return 0;
}

bool aoa_thread_start(struct aoa *aoa) {
LOGD("Starting aoa thread");

bool ok = sc_thread_create(&aoa->thread, run_aoa_thread, "aoa_thread", aoa);

if (!ok) {
LOGC("Could not start aoa thread");
return false;
}

return true;
}

void aoa_thread_stop(struct aoa *aoa) {
sc_mutex_lock(&aoa->mutex);
aoa->stopped = true;
sc_cond_signal(&aoa->event_cond);
sc_mutex_unlock(&aoa->mutex);
}

void aoa_thread_join(struct aoa *aoa) {
sc_thread_join(&aoa->thread, NULL);
}

void aoa_destroy(struct aoa *aoa) {
libusb_close(aoa->usb_handle);
libusb_unref_device(aoa->usb_device);
sc_cond_destroy(&aoa->event_cond);
sc_mutex_destroy(&aoa->mutex);
}
48 changes: 38 additions & 10 deletions app/src/aoa_hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,56 @@
#define AOA_HID_H

#include <stdint.h>
#include <stdbool.h>

#include <libusb-1.0/libusb.h>

#include "util/cbuf.h"
#include "util/thread.h"

struct report_desc {
const unsigned char *buffer;
uint16_t from_accessory_id;
unsigned char *buffer;
uint16_t size;
};

struct hid_event {
const unsigned char *buffer;
uint16_t from_accessory_id;
unsigned char *buffer;
uint16_t size;
};

libusb_device *aoa_find_usb_device(const char *serial);
int aoa_open_usb_handle(libusb_device *device, libusb_device_handle **handle);
int aoa_register_hid(libusb_device_handle *handle, uint16_t report_desc_size);
int aoa_unregister_hid(libusb_device_handle *handle);
struct hid_event_queue CBUF(struct hid_event, 64);

struct aoa {
libusb_device *usb_device;
libusb_device_handle *usb_handle;
struct libusb_device_descriptor desc;
sc_thread thread;
sc_mutex mutex;
sc_cond event_cond;
bool stopped;
uint16_t next_accessories_id;
struct hid_event_queue queue;
};

void hid_event_log(const struct hid_event *event);
void hid_event_destroy(struct hid_event *event);
bool aoa_init(struct aoa *aoa, const struct scrcpy_options *options);
// Generate a different accessory ID.
uint16_t aoa_get_new_accessory_id(struct aoa *aoa);
int
aoa_register_hid(struct aoa *aoa, const uint16_t accessory_id,
uint16_t report_desc_size);
int
aoa_set_hid_report_desc(libusb_device_handle *handle,
const struct report_desc *report_desc, uint8_t max_packet_size_0);
aoa_set_hid_report_desc(struct aoa *aoa, const struct report_desc *report_desc);
int
aoa_send_hid_event(libusb_device_handle *handle,
const struct hid_event *event);
aoa_send_hid_event(struct aoa *aoa, const struct hid_event *event);
int aoa_unregister_hid(struct aoa *aoa, const uint16_t accessory_id);
bool aoa_push_hid_event(struct aoa *aoa, const struct hid_event *event);
bool aoa_thread_start(struct aoa *aoa);
void aoa_thread_stop(struct aoa *aoa);
void aoa_thread_join(struct aoa *aoa);
void aoa_destroy(struct aoa *aoa);

#endif
Loading

0 comments on commit e4ca327

Please sign in to comment.