Skip to content

Commit

Permalink
Support to send media events from hid_keyboard
Browse files Browse the repository at this point in the history
  • Loading branch information
AlynxZhou committed Sep 11, 2021
1 parent 8a98bdb commit 1290712
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 83 deletions.
21 changes: 10 additions & 11 deletions app/src/aoa_hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,15 @@ int aoa_unregister_hid(libusb_device_handle *handle) {

int
aoa_set_hid_report_desc(libusb_device_handle *handle,
const unsigned char *report_desc, uint16_t report_desc_size,
uint8_t max_packet_size_0) {
const struct report_desc *report_desc, uint8_t max_packet_size_0) {
const uint8_t request_type = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR;
const uint8_t request = ACCESSORY_SET_HID_REPORT_DESC;
// 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;
// libusb_control_transfer expects non-const but should not modify it.
unsigned char *buffer = (unsigned char *)report_desc;
const uint16_t size = report_desc_size;
unsigned char *buffer = (unsigned char *)report_desc->buffer;
const uint16_t size = report_desc->size;
/**
* If the HID descriptor is longer than the endpoint zero max packet size,
* the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC
Expand All @@ -108,8 +107,8 @@ 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(handle, request_type, request,
value, offset, buffer + offset, packet_length, DEFAULT_TIMEOUT);
offset += packet_length;
if (result < 0) {
print_libusb_error((enum libusb_error)result);
Expand All @@ -121,7 +120,7 @@ aoa_set_hid_report_desc(libusb_device_handle *handle,

int
aoa_send_hid_event(libusb_device_handle *handle,
const unsigned char *event, uint16_t size) {
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>.
Expand All @@ -130,10 +129,10 @@ aoa_send_hid_event(libusb_device_handle *handle,
const uint16_t value = 0;
const uint16_t index = 0;
// libusb_control_transfer expects non-const but should not modify it.
unsigned char *buffer = (unsigned char *)event;
const uint16_t length = size;
int result = libusb_control_transfer(
handle, request_type, request, value, index, buffer, length, DEFAULT_TIMEOUT);
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);
if (result < 0) {
print_libusb_error((enum libusb_error)result);
return result;
Expand Down
15 changes: 12 additions & 3 deletions app/src/aoa_hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,25 @@

#include <libusb-1.0/libusb.h>

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

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

libusb_device *aoa_find_usb_device(uint16_t vid, uint16_t pid);
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);
int
aoa_set_hid_report_desc(libusb_device_handle *handle,
const unsigned char *report_desc, uint16_t report_desc_size,
uint8_t max_packet_size_0);
const struct report_desc *report_desc, uint8_t max_packet_size_0);
int
aoa_send_hid_event(libusb_device_handle *handle,
const unsigned char *event, uint16_t size);
const struct hid_event *event);

#endif
180 changes: 127 additions & 53 deletions app/src/hid_keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@
* <https://www.usb.org/sites/default/files/hid1_11.pdf>.
* For Consumer Page tags, see 15 Consumer Page (0x0C) in
* <https://usb.org/sites/default/files/hut1_2.pdf>.
* If you changed REPORT_DESC, please also update REPORT_DESC_SIZE defined in
* hid_keyboard.h.
*/
const unsigned char REPORT_DESC[REPORT_DESC_SIZE] = {
const unsigned char REPORT_DESC_BUFFER[] = {
// Usage Page (Generic Desktop)
0x05, 0x01,
// Usage (Keyboard)
Expand Down Expand Up @@ -141,6 +139,10 @@ const unsigned char REPORT_DESC[REPORT_DESC_SIZE] = {
// End Collection
0xC0
};
const struct report_desc REPORT_DESC = {
REPORT_DESC_BUFFER,
sizeof(REPORT_DESC_BUFFER) / sizeof(REPORT_DESC_BUFFER[0])
};

/**
* HID keyboard events are sequence-based, every time keyboard state changes
Expand All @@ -162,40 +164,8 @@ const unsigned char REPORT_DESC[REPORT_DESC_SIZE] = {
// 0xE0 is LeftControl, the start of modifiers and the end of keys.
#define KEYBOARD_KEYS 0xE0
static bool keys[KEYBOARD_KEYS] = { false };
static unsigned char modifiers = HID_KEYBOARD_MODIFIER_NONE;

inline static unsigned char sdl_keymod_to_hid_modifiers(SDL_Keymod mod) {
unsigned char modifiers = HID_KEYBOARD_MODIFIER_NONE;
// Not so cool, but more readable, and does not rely on actual value.
if (mod & KMOD_LCTRL) {
modifiers |= HID_KEYBOARD_MODIFIER_LEFT_CONTROL;
}
if (mod & KMOD_LSHIFT) {
modifiers |= HID_KEYBOARD_MODIFIER_LEFT_SHIFT;
}
if (mod & KMOD_LALT) {
modifiers |= HID_KEYBOARD_MODIFIER_LEFT_ALT;
}
if (mod & KMOD_LGUI) {
modifiers |= HID_KEYBOARD_MODIFIER_LEFT_GUI;
}
if (mod & KMOD_RCTRL) {
modifiers |= HID_KEYBOARD_MODIFIER_RIGHT_CONTROL;
}
if (mod & KMOD_RSHIFT) {
modifiers |= HID_KEYBOARD_MODIFIER_RIGHT_SHIFT;
}
if (mod & KMOD_RALT) {
modifiers |= HID_KEYBOARD_MODIFIER_RIGHT_ALT;
}
if (mod & KMOD_RGUI) {
modifiers |= HID_KEYBOARD_MODIFIER_RIGHT_GUI;
}
return modifiers;
}

// Pantom state is made of `ReportID, Modifiers, Reserved, ErrorRollOver, ErrorRollOver, ErrorRollOver, ErrorRollOver, ErrorRollOver, ErrorRollOver`.
static unsigned char phantom_state[HID_KEYBOARD_KEY_LENGTH] = {
static unsigned char phantom_state[HID_KEYBOARD_EVENT_SIZE] = {
HID_KEYBOARD_REPORT_ID,
HID_KEYBOARD_MODIFIER_NONE,
HID_KEYBOARD_RESERVED,
Expand All @@ -206,7 +176,7 @@ static unsigned char phantom_state[HID_KEYBOARD_KEY_LENGTH] = {
HID_KEYBOARD_ERROR_ROLL_OVER,
HID_KEYBOARD_ERROR_ROLL_OVER
};
static unsigned char hid_keyboard_event[HID_KEYBOARD_KEY_LENGTH] = {
static unsigned char hid_keyboard_event[HID_KEYBOARD_EVENT_SIZE] = {
HID_KEYBOARD_REPORT_ID,
HID_KEYBOARD_MODIFIER_NONE,
HID_KEYBOARD_RESERVED,
Expand All @@ -217,6 +187,15 @@ static unsigned char hid_keyboard_event[HID_KEYBOARD_KEY_LENGTH] = {
HID_KEYBOARD_RESERVED,
HID_KEYBOARD_RESERVED
};
static unsigned char hid_media_event[HID_MEDIA_EVENT_SIZE] = {
HID_MEDIA_REPORT_ID,
HID_MEDIA_KEY_UNDEFINED
};
// The real place that returned pointers point to.
static struct hid_event hid_event = {
NULL,
0
};

inline static void reset_hid_keyboard_event(void) {
hid_keyboard_event[HID_KEYBOARD_MODIFIER_INDEX] = HID_KEYBOARD_MODIFIER_NONE;
Expand All @@ -227,33 +206,57 @@ inline static void reset_hid_keyboard_event(void) {
void hid_keyboard_init(void) {
// Reset all states.
memset(keys, false, KEYBOARD_KEYS);
modifiers = HID_KEYBOARD_MODIFIER_NONE;
phantom_state[HID_KEYBOARD_MODIFIER_INDEX] = HID_KEYBOARD_MODIFIER_NONE;
reset_hid_keyboard_event();
hid_media_event[HID_MEDIA_KEY_INDEX] = HID_MEDIA_KEY_UNDEFINED;
hid_event.buffer = NULL;
hid_event.size = 0;
}

void hid_keyboard_update_state(const SDL_KeyboardEvent *event) {
LOGD("Type: %s, Repeat: %s, Modifiers: %02x, Key: %02x",
event->type == SDL_KEYDOWN ? "down" : "up",
event->repeat != 0 ? "true" : "false",
sdl_keymod_to_hid_modifiers(event->keysym.mod),
event->keysym.scancode
);
inline static unsigned char sdl_keymod_to_hid_modifiers(SDL_Keymod mod) {
unsigned char modifiers = HID_KEYBOARD_MODIFIER_NONE;
// Not so cool, but more readable, and does not rely on actual value.
if (mod & KMOD_LCTRL) {
modifiers |= HID_KEYBOARD_MODIFIER_LEFT_CONTROL;
}
if (mod & KMOD_LSHIFT) {
modifiers |= HID_KEYBOARD_MODIFIER_LEFT_SHIFT;
}
if (mod & KMOD_LALT) {
modifiers |= HID_KEYBOARD_MODIFIER_LEFT_ALT;
}
if (mod & KMOD_LGUI) {
modifiers |= HID_KEYBOARD_MODIFIER_LEFT_GUI;
}
if (mod & KMOD_RCTRL) {
modifiers |= HID_KEYBOARD_MODIFIER_RIGHT_CONTROL;
}
if (mod & KMOD_RSHIFT) {
modifiers |= HID_KEYBOARD_MODIFIER_RIGHT_SHIFT;
}
if (mod & KMOD_RALT) {
modifiers |= HID_KEYBOARD_MODIFIER_RIGHT_ALT;
}
if (mod & KMOD_RGUI) {
modifiers |= HID_KEYBOARD_MODIFIER_RIGHT_GUI;
}
return modifiers;
}

inline static const struct hid_event *
make_hid_keyboard_event(const SDL_KeyboardEvent *event) {
unsigned char modifiers = sdl_keymod_to_hid_modifiers(event->keysym.mod);

SDL_Scancode scancode = event->keysym.scancode;
// SDL also generates event when only modifiers are pressed,
// we cannot ignore them totally, for example press `a` first then
// press `Control`, if we ignore `Control` event, only `a` is sent.
if (scancode < KEYBOARD_KEYS) {
if (scancode >= 0 && scancode < KEYBOARD_KEYS) {
// Pressed is true and released is false.
keys[scancode] = (event->type == SDL_KEYDOWN);
LOGD("keys[%02x] = %s", scancode, keys[scancode] ? "true" : "false");
}
// TODO: Maybe also handle media keys here?
modifiers = sdl_keymod_to_hid_modifiers(event->keysym.mod);
return;
}

const unsigned char *hid_keyboard_get_hid_event(void) {
// Re-calculate pressed keys every time.
reset_hid_keyboard_event();
int keys_pressed_count = 0;
Expand All @@ -263,7 +266,10 @@ const unsigned char *hid_keyboard_get_hid_event(void) {
if (keys_pressed_count > HID_KEYBOARD_MAX_KEYS) {
// But the modifiers should be report normally for phantom state.
phantom_state[HID_KEYBOARD_MODIFIER_INDEX] = modifiers;
return phantom_state;
LOGD("HID Keyboard Phantom State");
hid_event.buffer = phantom_state;
hid_event.size = HID_KEYBOARD_EVENT_SIZE;
return &hid_event;
}
if (keys[i]) {
hid_keyboard_event[
Expand All @@ -278,5 +284,73 @@ const unsigned char *hid_keyboard_get_hid_event(void) {
hid_keyboard_event[3], hid_keyboard_event[4], hid_keyboard_event[5],
hid_keyboard_event[6], hid_keyboard_event[7], hid_keyboard_event[8]
);
return hid_keyboard_event;
hid_event.buffer = hid_keyboard_event;
hid_event.size = HID_KEYBOARD_EVENT_SIZE;
return &hid_event;
}

inline static const struct hid_event *
make_hid_media_event(const SDL_KeyboardEvent *event) {
// Re-calculate pressed keys every time.
unsigned char media_key = HID_MEDIA_KEY_UNDEFINED;

SDL_Scancode scancode = event->keysym.scancode;
if (scancode == SDL_SCANCODE_AUDIONEXT) {
media_key |= HID_MEDIA_KEY_NEXT;
}
if (scancode == SDL_SCANCODE_AUDIOPREV) {
media_key |= HID_MEDIA_KEY_PREVIOUS;
}
if (scancode == SDL_SCANCODE_AUDIOSTOP) {
media_key |= HID_MEDIA_KEY_STOP;
}
if (scancode == SDL_SCANCODE_EJECT) {
media_key |= HID_MEDIA_KEY_EJECT;
}
if (scancode == SDL_SCANCODE_AUDIOPLAY) {
media_key |= HID_MEDIA_KEY_PLAY_PAUSE;
}
if (scancode == SDL_SCANCODE_AUDIOMUTE) {
media_key |= HID_MEDIA_KEY_MUTE;
}
if (scancode == SDL_SCANCODE_AUDIOSTOP) {
media_key |= HID_MEDIA_KEY_STOP;
}
// SDL has no equivalence for HID_MEDIA_KEY_VOLUME_UP and
// HID_MEDIA_KEY_VOLUME_DOWN, it does have SDL_SCANCODE_VOLUMEUP and
// SDL_SCANCODE_VOLUMEDOWN, but they are under Usage Page (0x07),
// which should be a keyboard event.

// Not all other keys are Usage Page 0x0C,
// we return NULL for unsupported keys.
if (media_key == HID_MEDIA_KEY_UNDEFINED) {
return NULL;
}

hid_media_event[HID_MEDIA_KEY_INDEX] = media_key;
LOGD("HID Media Event: %02x %02x", hid_media_event[0], hid_media_event[1]);

hid_event.buffer = hid_media_event;
hid_event.size = HID_MEDIA_EVENT_SIZE;
return &hid_event;
}

const struct hid_event *
hid_keyboard_get_hid_event(const SDL_KeyboardEvent *event) {
LOGD(
"Type: %s, Repeat: %s, Modifiers: %02x, Key: %02x",
event->type == SDL_KEYDOWN ? "down" : "up",
event->repeat != 0 ? "true" : "false",
sdl_keymod_to_hid_modifiers(event->keysym.mod),
event->keysym.scancode
);

if (event->keysym.scancode >= 0 &&
event->keysym.scancode <= SDL_SCANCODE_MODE) {
// Usage Page 0x07 (Keyboard).
return make_hid_keyboard_event(event);
} else {
// Others.
return make_hid_media_event(event);
}
}
19 changes: 13 additions & 6 deletions app/src/hid_keyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

#include <SDL2/SDL.h>

#define REPORT_DESC_SIZE 102
extern const unsigned char REPORT_DESC[REPORT_DESC_SIZE];
#include "aoa_hid.h"

extern const struct report_desc REPORT_DESC;

/**
* Because of dual-report, when we send hid events, we need to add report id
Expand All @@ -27,12 +28,13 @@ extern const unsigned char REPORT_DESC[REPORT_DESC_SIZE];
// keyboard support, though OS could support more keys via modifying the report
// desc, I think 6 is enough for us.
#define HID_KEYBOARD_MAX_KEYS 6
#define HID_KEYBOARD_KEY_LENGTH (3 + HID_KEYBOARD_MAX_KEYS)
#define HID_KEYBOARD_EVENT_SIZE (3 + HID_KEYBOARD_MAX_KEYS)
#define HID_KEYBOARD_REPORT_ID 0x01
#define HID_KEYBOARD_RESERVED 0x00
#define HID_KEYBOARD_ERROR_ROLL_OVER 0x01
#define HID_MEDIA_KEY_LENGTH 2
#define HID_MEDIA_EVENT_SIZE 2
#define HID_MEDIA_REPORT_ID 0x02
#define HID_MEDIA_KEY_INDEX (HID_REPORT_ID_INDEX + 1)
/**
* Media keys handle as mask so we define them here.
* Currently not used because desktop environment catches them before window.
Expand All @@ -48,7 +50,12 @@ extern const unsigned char REPORT_DESC[REPORT_DESC_SIZE];
#define HID_MEDIA_KEY_VOLUME_DOWN (1 << 7)

void hid_keyboard_init(void);
void hid_keyboard_update_state(const SDL_KeyboardEvent *event);
const unsigned char *hid_keyboard_get_hid_event(void);
/**
* Return pointer to keyboard or media event,
* or NULL if unsupported keys is received.
* The returned event should be NEVER modified!
*/
const struct hid_event *
hid_keyboard_get_hid_event(const SDL_KeyboardEvent *event);

#endif
Loading

0 comments on commit 1290712

Please sign in to comment.