From 5ae2e71abd476e84d8d2f5b14feb94e1489b66d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20=C5=81opaciuk?= Date: Wed, 10 Feb 2021 01:01:02 +0000 Subject: [PATCH] first working prototype MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Szymon Łopaciuk --- README.md | 19 +- platformio.ini | 5 +- src/adb.h | 45 ++-- src/adb_devices.h | 72 +++++++ src/adb_structures.h | 63 ++++++ src/hid_keyboard.cpp | 129 +++++++++++ src/hid_keyboard.h | 24 +++ src/hid_tables.h | 274 ++++++++++++++++++++++++ src/keymap.h | 147 ++++++++++++- src/main.cpp | 142 ++++++++---- src/raw_keyboard.cpp | 94 -------- src/raw_keyboard.h | 38 ---- test/test_desktop/test_raw_keyboard.cpp | 69 ------ test/test_desktop/tests.cpp | 116 ++++++++++ 14 files changed, 965 insertions(+), 272 deletions(-) create mode 100644 src/adb_devices.h create mode 100644 src/adb_structures.h create mode 100644 src/hid_keyboard.cpp create mode 100644 src/hid_keyboard.h create mode 100644 src/hid_tables.h delete mode 100644 src/raw_keyboard.cpp delete mode 100644 src/raw_keyboard.h delete mode 100644 test/test_desktop/test_raw_keyboard.cpp create mode 100644 test/test_desktop/tests.cpp diff --git a/README.md b/README.md index f148912..d49fe58 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,25 @@ -# stm32-adb2usb [WIP] +# stm32-adb2usb A simple implementation of an ADB to USB converter for the STM32F103 'Blue Pill'. -This is work in progress and is not yet functional. +It is currently functional, but there are many ways in which it could be improved upon (see TODO). Testing done on an STM32F103 (or rather actually a Chinese clone), but in the end should be compatible with other STM32 microcontrollers/boards. +This code is a Platform IO project reliant on ST's stm32duino, although I am sure it could be easily converted to something else. ## Useful resources I aim to make the code as easy to follow as it is possible in this case, especially the part relating to the ADB protocol. -However, there are some resources that are very useful in understanding how the protocol works, and which I used: +By far the best and most comprehensive description of the ADB protocol and devices is Apple's [Guide to the Macintosh© Family Hardware (2nd ed.)](https://archive.org/details/apple-guide-macintosh-family-hardware) (chapter 8, p. 287). +Some other resources that were useful to me as well: -- [ADB - The Untold Story: Space Aliens Ate My Mouse'](https://developer.apple.com/library/archive/technotes/hw/hw_01.html#//apple_ref/doc/uid/DTS10002470) by Apple, the most 'authoritative' source, but lacks some key spec features +- [ADB - The Untold Story: Space Aliens Ate My Mouse'](https://developer.apple.com/library/archive/technotes/hw/hw_01.html#//apple_ref/doc/uid/DTS10002470) by Apple, quick introduction to what ADB is about, but lacks some key spec features - https://web.archive.org/save/http://85.226.187.247/_pdf/Processor_Memory/PIC1617/MIDRANGE/00591A.PDF -- Inside Macintosh volume V, pages 191-192, for the keycodes +- Inside Macintosh volume V, pages 191-192, for keycodes - [This blog post](https://www.bigmessowires.com/2016/03/30/understanding-the-adb-service-request-signal/) which explains `Srq` and `Tlt` +## TODO + +- [x] Make the keyboard work +- [ ] Make the mouse work +- [ ] Ditch bitfields as the ordering of bytes/bits is confusing and not super portable (see tests failing; passing around uint16_t and then casting to bitfield structs is really dodgy and I shouldn't be doing that) +- [ ] Handle SRQs and multiple devices of the same type properly +- [ ] Make the layout easier to customise (swap a control, or num lock, for fn?) diff --git a/platformio.ini b/platformio.ini index d117a30..63d1f5d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -19,8 +19,10 @@ build_flags = -D USBD_VID=0x0483 -D USBD_PID=0x5711 -D USB_MANUFACTURER="STMicroelectronics" - -D USB_PRODUCT="Apple Desktop Bus Device" + -D USB_PRODUCT="Apple Desktop Bus Device" -D HAL_PCD_MODULE_ENABLED + ;-D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC + ;-D PIO_FRAMEWORK_ARDUINO_USB_FULLSPEED_FULLMODE upload_flags = -c set CPUTAPID 0x2ba01477 ; Chinese clone, genuine is 0x1ba01477 debug_tool = stlink upload_protocol = stlink @@ -39,5 +41,6 @@ platform = native build_flags = -D USBCON -D USBD_USE_HID_COMPOSITE + -D PIO_FRAMEWORK_ARDUINO_ENABLE_HID -I $PROJECT_DIR/test/test_desktop/include test_build_project_src = true \ No newline at end of file diff --git a/src/adb.h b/src/adb.h index f3641be..2633d9e 100644 --- a/src/adb.h +++ b/src/adb.h @@ -2,21 +2,20 @@ #define ADB_h #include -#include #define ADB_DATA_PIN PB4 #define ADB_DATA_PORT GPIOB #define ADB_DATA_PIN_NO 4 #define ADB_WRITE(bit) digitalWrite(ADB_DATA_PIN, bit) -//#define ADB_WRITE(bit) HAL_GPIO_WritePin(ADB_DATA_PORT, ADB_DATA_PIN_NO, (GPIO_PinState)(bit)); #define ADB_READ() (digitalRead(ADB_DATA_PIN)) -//#define ADB_READ() HAL_GPIO_ReadPin(ADB_DATA_PORT, ADB_DATA_PIN_NO) #define ADB_ADDRESS(addr) (addr << 4) #define ADB_REGISTER(reg) (reg) -#define ADB_CMD_TALK 0b1100 -#define ADB_CMD_LISTEN 0b1000 -#define ADB_CMD_FLUSH 0b0001 +#define ADB_CMD_TALK (0b11 << 2) +#define ADB_CMD_LISTEN (0b10 << 2) +#define ADB_CMD_FLUSH (0b01 << 2) + +#define ADB_BIT_ERROR 0xFF // Reset: signal low for 3 ms. static void adb_reset() { @@ -69,6 +68,13 @@ static void adb_write_bits(uint16_t bits, uint8_t length) { } } +// Like adb_write_bits, but add start and stop bits +static void adb_write_data_packet(uint16_t bits, uint8_t length) { + adb_write_bit(1); + adb_write_bits(bits, length); + adb_write_bit(0); +} + // Send the '0' stop bit, and listen for an SRQ. // ``If a device in need of service issues a service request, // it must do so within the 65 μs of the Stop Bit’s low time @@ -92,13 +98,17 @@ void adb_write_command(uint8_t command_byte) { adb_stop_bit_srq_listen(); // TODO: Handle the SRQ } -// Stop-to-start time: period of 160 - 240 μs before device's +// Stop-to-start time: period of 140 - 260 μs before device's // response when the bus is held high. // Returns: true if the response is starting, false if timeout -static bool adb_wait_tlt() { - // spec says 160 μs, but it seems that Macintoshes allowed 140 μs, - // (see `ADB - The Untold Story: Space Aliens Ate My Mouse'): +static bool adb_wait_tlt(bool response_expected) { + ADB_WRITE(HIGH); delayMicroseconds(140); + uint8_t i = 0; + while (ADB_READ() == HIGH && i < 240 && response_expected) { + delayMicroseconds(1); + i++; + } return true; } @@ -111,7 +121,7 @@ static uint8_t adb_read_bit() { // devices need to stick to 30% precision, 65 * 1.3 = 85 μs // if this time is exceeded assume timeout if (micros() - time_start > 85) - return 0xFF; + return ADB_BIT_ERROR; } auto low_time = micros() - time_start; @@ -119,7 +129,7 @@ static uint8_t adb_read_bit() { // devices need to stick to 30% precision, 65 * 1.3 = 85 μs // if this time is exceeded assume timeout if (micros() - time_start - low_time > 85) - return 0xFF; + return ADB_BIT_ERROR; } auto high_time = micros() - time_start - low_time; @@ -129,14 +139,17 @@ static uint8_t adb_read_bit() { // Read `length` bits from the bus into `buffer`. bool adb_read_data_packet(uint16_t* buffer, uint8_t length) { - uint8_t start_bit = adb_read_bit(); // should equal to '1' - if (start_bit != 0x1) {return false;} + if (adb_read_bit() != 0x1) { // start bit should equal to '1' + return false; + } *buffer = 0; for (uint8_t i = 0; i < length; i++) { uint8_t current_bit = adb_read_bit(); - if (current_bit == 0xFF) return false; + if (current_bit == ADB_BIT_ERROR) { + return false; + } *buffer <<= 1; *buffer |= current_bit; } @@ -147,7 +160,7 @@ bool adb_read_data_packet(uint16_t* buffer, uint8_t length) void adb_init() { pinMode(ADB_DATA_PIN, OUTPUT_OPEN_DRAIN); - digitalWrite(ADB_DATA_PIN, HIGH); + ADB_WRITE(HIGH); while (ADB_READ() == LOW); // wait for the bus diff --git a/src/adb_devices.h b/src/adb_devices.h new file mode 100644 index 0000000..2bd6632 --- /dev/null +++ b/src/adb_devices.h @@ -0,0 +1,72 @@ +#ifndef ADB_DEVICES_h +#define ADB_DEVICES_h + +#include + +#include "adb.h" +#include "hid_keyboard.h" +#include "adb_structures.h" + +adb_kb_data adb_keyboard_read_modifiers(bool* error) { + adb_kb_data modifiers = {0}; + adb_write_command(ADB_CMD_TALK | ADB_ADDRESS(2) | ADB_REGISTER(2)); + adb_wait_tlt(true); + *error = !adb_read_data_packet(&modifiers.raw, 16); + return modifiers; +} + +adb_kb_data adb_keyboard_read_key_press(bool* error) { + adb_kb_data key_press = {0}; + adb_write_command(ADB_CMD_TALK | ADB_ADDRESS(2) | ADB_REGISTER(0)); + adb_wait_tlt(true); + *error = !adb_read_data_packet(&key_press.raw, 16); + return key_press; +} + +adb_kb_data adb_keyboard_read_register3(bool* error) { + adb_kb_data reg3 = {0}; + adb_write_command(ADB_CMD_TALK | ADB_ADDRESS(2) | ADB_REGISTER(3)); + adb_wait_tlt(true); + *error = !adb_read_data_packet(®3.raw, 16); + return reg3; +} + + +bool adb_keyboard_update_register3(adb_kb_data new_reg3, uint16_t mask, bool* error) { + // Read the initial value + adb_kb_data reg3 = adb_keyboard_read_register3(error); + if (*error) return false; + delay(ADB_POLL_DELAY); + + // Apply the change + reg3.raw = (reg3.raw & ~mask) | (new_reg3.raw & mask); + + // Write the new value + adb_write_command(ADB_CMD_LISTEN | ADB_ADDRESS(2) | ADB_REGISTER(3)); + adb_wait_tlt(false); + adb_write_data_packet(reg3.raw, 16); + delay(ADB_POLL_DELAY); + + // Read the current value + reg3 = adb_keyboard_read_register3(error); + if (*error) return false; + + // Check if update worked, return true + if ((reg3.raw & mask) == (new_reg3.raw & mask)) + return true; + + return false; +} + +void adb_keyboard_write_leds(bool scroll, bool caps, bool num) { + adb_kb_data modifiers = {0}; + modifiers.data.led_scroll = !scroll; + modifiers.data.led_caps = !caps; + modifiers.data.led_num = !num; + + adb_write_command(ADB_CMD_LISTEN | ADB_ADDRESS(2) | ADB_REGISTER(2)); + adb_wait_tlt(false); + adb_write_data_packet(modifiers.raw, 16); +} + +#endif \ No newline at end of file diff --git a/src/adb_structures.h b/src/adb_structures.h new file mode 100644 index 0000000..22e64eb --- /dev/null +++ b/src/adb_structures.h @@ -0,0 +1,63 @@ +#ifndef ADB_STRUCTURES_h +#define ADB_STRUCTURES_h + +#include + +#define KEY_POWER 0x66 + +#define ADB_KEY_POWER_DOWN 0x7F7F +#define ADB_KEY_POWER_UP 0xFFFF + +#define ADB_KEY_LEFT_SHIFT 0x38 +#define ADB_KEY_RIGHT_SHIFT 0x7B +#define ADB_KEY_LEFT_CONTROL 0x36 +#define ADB_KEY_RIGHT_CONTROL 0x7D +#define ADB_KEY_LEFT_OPTION 0x3A +#define ADB_KEY_RIGHT_OPTION 0x7C +#define ADB_KEY_LEFT_COMMAND 0x37 +#define ADB_KEY_RIGHT_COMMAND 0x37 +#define ADB_KEY_CAPS_LOCK 0x39 + +struct adb_kb_keypress { + uint8_t key1 : 7; + bool released1 : 1; + uint8_t key0 : 7; + bool released0 : 1; +}; + +struct adb_kb_modifiers { + // Only on Apple Extended Keyboard: + bool led_num : 1; // 0, ditto + bool led_caps : 1; // 1, ditto + bool led_scroll : 1; // 2, changeable with `listen' command + uint8_t reserved1 : 3; // 5–3 + bool scroll_lock : 1; // 6 + bool num_lock : 1; // 7 + // Apple Standard Keyboard: + bool command : 1; // 8 + bool option : 1; // 9 + bool shift : 1; // 10 + bool control : 1; // 11 + bool reset : 1; // 12 + bool caps_lock : 1; // 13 + bool backspace : 1; // 14 + uint8_t reserved0 : 1; // 15 +}; + +// Contains device info and params +struct adb_register3 { + uint8_t device_handler_id : 8; + uint8_t device_address : 4; + uint8_t reserved1 : 1; // must be 0 + uint8_t srq_enable : 1; + uint8_t exceptional_event : 1; // device specific, always 1 if not used + uint8_t reserved0 : 1; // must be 0 +}; + +template +union adb_kb_data { + uint16_t raw; + T data; +}; + +#endif \ No newline at end of file diff --git a/src/hid_keyboard.cpp b/src/hid_keyboard.cpp new file mode 100644 index 0000000..6f26d83 --- /dev/null +++ b/src/hid_keyboard.cpp @@ -0,0 +1,129 @@ +#include "hid_keyboard.h" +#include "usbd_hid_composite_if.h" +#include +#include "keymap.h" + +void hid_keyboard_init() +{ + HID_Composite_Init(HID_KEYBOARD); +} + +void hid_keyboard_close() +{ + HID_Composite_DeInit(HID_KEYBOARD); +} + +void hid_keyboard_send_report(hid_key_report* report) +{ + uint8_t buf[8] = {report->modifiers, 0, report->keys[0], + report->keys[1], report->keys[2], report->keys[3], report->keys[4], + report->keys[5] + }; + HID_Composite_keyboard_sendReport(buf, 8); +} + +bool hid_keyboard_set_keys_from_adb_register( + hid_key_report* report, adb_kb_data key_press) { + // Power button is a special case, residing in both octets: + if (key_press.raw == ADB_KEY_POWER_DOWN) + return hid_keyboard_update_key_in_report(report, KEY_POWER, false); + else if (key_press.raw == ADB_KEY_POWER_UP) + return hid_keyboard_update_key_in_report(report, KEY_POWER, true); + + // Other keys: + bool report_changed = false; + // Higher octet key: + uint8_t key0 = key_press.data.key0; + if (adb_keymap_is_modifier(key0)) + report_changed = hid_keyboard_update_modifier_in_report( + report, key0, key_press.data.released0); + else + report_changed = hid_keyboard_update_key_in_report( + report, adb_keycode_to_hid[key0], key_press.data.released0); + + // Lower octet key: + uint8_t key1 = key_press.data.key1; + if (adb_keymap_is_modifier(key1)) + report_changed = hid_keyboard_update_modifier_in_report( + report, key1, key_press.data.released1) || report_changed; + else + report_changed = hid_keyboard_update_key_in_report( + report, adb_keycode_to_hid[key1], key_press.data.released1) || report_changed; + + return report_changed; +} + +bool hid_keyboard_set_modifiers_from_adb_register( + hid_key_report* report, adb_kb_data reg) { + // TODO: don't seem necessary, as modifiers still register normal keypresses + return false; +} + +bool hid_keyboard_update_key_in_report(hid_key_report* report, uint8_t hid_keycode, bool released) { + // Sometimes the response from the ADB keyboard is rubbish: + // either an invalid key, or lifting a key that's not been pressed. + // This detects the first case, so we don't waste time processing it. + if (hid_keycode == KEY_NONE) return false; + + if (released) + return hid_keyboard_remove_key_from_report(report, hid_keycode); + else + return hid_keyboard_add_key_to_report(report, hid_keycode); +} + +bool hid_keyboard_update_modifier_in_report(hid_key_report* report, uint8_t adb_keycode, bool released) { + auto put_key = [released, report](uint8_t mask) mutable { + // no change if the modifier is already set + if (released == !(report->modifiers & mask)) return false; + + // flip the modifier + if (!released) report->modifiers |= mask; + else report->modifiers &= ~mask; + + return true; + }; + + if (adb_keycode == ADB_KEY_LEFT_SHIFT) return put_key(KEY_MOD_LSHIFT); + if (adb_keycode == ADB_KEY_RIGHT_SHIFT) return put_key(KEY_MOD_RSHIFT); + if (adb_keycode == ADB_KEY_LEFT_CONTROL) return put_key(KEY_MOD_LCTRL); + if (adb_keycode == ADB_KEY_RIGHT_CONTROL) return put_key(KEY_MOD_RCTRL); + if (adb_keycode == ADB_KEY_LEFT_OPTION) return put_key(KEY_MOD_LALT); + if (adb_keycode == ADB_KEY_RIGHT_OPTION) return put_key(KEY_MOD_RALT); + if (adb_keycode == ADB_KEY_LEFT_COMMAND) return put_key(KEY_MOD_LMETA); + if (adb_keycode == ADB_KEY_RIGHT_COMMAND) return put_key(KEY_MOD_RMETA); + + return false; // unreachable +} + +// Returns true if after execution the key is in the report +// in other words, returns false if insertion unsuccessful (report unchanged) +bool hid_keyboard_add_key_to_report(hid_key_report* report, uint8_t hid_keycode) { + int8_t free_slot = -1; + + for (uint8_t i = 0; i < KEY_REPORT_KEYS_COUNT; i++) { + if (report->keys[i] == hid_keycode) + return true; // key is already in the report + + if (report->keys[i] == 0 && free_slot == -1) + free_slot = i; // memorise empty entry idx + } + + if (free_slot == -1) + return false; // key report is full + + report->keys[free_slot] = hid_keycode; + return true; +} + +bool hid_keyboard_remove_key_from_report(hid_key_report* report, uint8_t hid_keycode) { + bool report_changed = false; + for (uint8_t i = 0; i < KEY_REPORT_KEYS_COUNT; i++) { + if (report->keys[i] == hid_keycode) { + report->keys[i] = 0; + report_changed = true; + } + } + return report_changed; +} + +//#endif \ No newline at end of file diff --git a/src/hid_keyboard.h b/src/hid_keyboard.h new file mode 100644 index 0000000..357a0de --- /dev/null +++ b/src/hid_keyboard.h @@ -0,0 +1,24 @@ +#ifndef HID_KEYBOARD_h +#define HID_KEYBOARD_h + +#include +#include "adb_structures.h" + +#define KEY_REPORT_KEYS_COUNT 6 + +struct hid_key_report { + uint8_t modifiers; + uint8_t keys[KEY_REPORT_KEYS_COUNT]; +}; + +void hid_keyboard_init(); +void hid_keyboard_close(); +void hid_keyboard_send_report(hid_key_report* report); +bool hid_keyboard_set_keys_from_adb_register(hid_key_report* report, adb_kb_data reg); +bool hid_keyboard_set_modifiers_from_adb_register(hid_key_report* report, adb_kb_data reg); +bool hid_keyboard_update_key_in_report(hid_key_report* report, uint8_t hid_keycode, bool released); +bool hid_keyboard_update_modifier_in_report(hid_key_report* report, uint8_t adb_keycode, bool released); +bool hid_keyboard_add_key_to_report(hid_key_report* report, uint8_t hid_keycode); +bool hid_keyboard_remove_key_from_report(hid_key_report* report, uint8_t hid_keycode); + +#endif \ No newline at end of file diff --git a/src/hid_tables.h b/src/hid_tables.h new file mode 100644 index 0000000..0b5e915 --- /dev/null +++ b/src/hid_tables.h @@ -0,0 +1,274 @@ +// https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf, pp. 53 +// adapted from https://gist.github.com/MightyPork/6da26e382a7ad91b5496ee55fdc73db2 + +#ifndef HID_TABLES_h +#define HID_TABLES_h + +#define KEY_MOD_LCTRL 0x01 +#define KEY_MOD_LSHIFT 0x02 +#define KEY_MOD_LALT 0x04 +#define KEY_MOD_LMETA 0x08 +#define KEY_MOD_RCTRL 0x10 +#define KEY_MOD_RSHIFT 0x20 +#define KEY_MOD_RALT 0x40 +#define KEY_MOD_RMETA 0x80 + +/** + * 0x00 if no key pressed. + * + * If more than N keys are pressed, the HID reports + * KEY_ERR_OVF in all slots to indicate this condition. + */ + +#define KEY_NONE 0x00 // No key pressed +#define KEY_ERR_OVF 0x01 // Keyboard Error Roll Over - used for all slots if too many keys are pressed ("Phantom key") +// 0x02 // Keyboard POST Fail +// 0x03 // Keyboard Error Undefined +#define KEY_A 0x04 // Keyboard a and A +#define KEY_B 0x05 // Keyboard b and B +#define KEY_C 0x06 // Keyboard c and C +#define KEY_D 0x07 // Keyboard d and D +#define KEY_E 0x08 // Keyboard e and E +#define KEY_F 0x09 // Keyboard f and F +#define KEY_G 0x0a // Keyboard g and G +#define KEY_H 0x0b // Keyboard h and H +#define KEY_I 0x0c // Keyboard i and I +#define KEY_J 0x0d // Keyboard j and J +#define KEY_K 0x0e // Keyboard k and K +#define KEY_L 0x0f // Keyboard l and L +#define KEY_M 0x10 // Keyboard m and M +#define KEY_N 0x11 // Keyboard n and N +#define KEY_O 0x12 // Keyboard o and O +#define KEY_P 0x13 // Keyboard p and P +#define KEY_Q 0x14 // Keyboard q and Q +#define KEY_R 0x15 // Keyboard r and R +#define KEY_S 0x16 // Keyboard s and S +#define KEY_T 0x17 // Keyboard t and T +#define KEY_U 0x18 // Keyboard u and U +#define KEY_V 0x19 // Keyboard v and V +#define KEY_W 0x1a // Keyboard w and W +#define KEY_X 0x1b // Keyboard x and X +#define KEY_Y 0x1c // Keyboard y and Y +#define KEY_Z 0x1d // Keyboard z and Z + +#define KEY_1 0x1e // Keyboard 1 and ! +#define KEY_2 0x1f // Keyboard 2 and @ +#define KEY_3 0x20 // Keyboard 3 and # +#define KEY_4 0x21 // Keyboard 4 and $ +#define KEY_5 0x22 // Keyboard 5 and % +#define KEY_6 0x23 // Keyboard 6 and ^ +#define KEY_7 0x24 // Keyboard 7 and & +#define KEY_8 0x25 // Keyboard 8 and * +#define KEY_9 0x26 // Keyboard 9 and ( +#define KEY_0 0x27 // Keyboard 0 and ) + +#define KEY_ENTER 0x28 // Keyboard Return (ENTER) +#define KEY_ESC 0x29 // Keyboard ESCAPE +#define KEY_BACKSPACE 0x2a // Keyboard DELETE (Backspace) +#define KEY_TAB 0x2b // Keyboard Tab +#define KEY_SPACE 0x2c // Keyboard Spacebar +#define KEY_MINUS 0x2d // Keyboard - and _ +#define KEY_EQUAL 0x2e // Keyboard = and + +#define KEY_LEFTBRACE 0x2f // Keyboard [ and { +#define KEY_RIGHTBRACE 0x30 // Keyboard ] and } +#define KEY_BACKSLASH 0x31 // Keyboard \ and | +#define KEY_HASHTILDE 0x32 // Keyboard Non-US # and ~ +#define KEY_SEMICOLON 0x33 // Keyboard ; and : +#define KEY_APOSTROPHE 0x34 // Keyboard ' and " +#define KEY_GRAVE 0x35 // Keyboard ` and ~ +#define KEY_COMMA 0x36 // Keyboard , and < +#define KEY_DOT 0x37 // Keyboard . and > +#define KEY_SLASH 0x38 // Keyboard / and ? +#define KEY_CAPSLOCK 0x39 // Keyboard Caps Lock + +#define KEY_F1 0x3a // Keyboard F1 +#define KEY_F2 0x3b // Keyboard F2 +#define KEY_F3 0x3c // Keyboard F3 +#define KEY_F4 0x3d // Keyboard F4 +#define KEY_F5 0x3e // Keyboard F5 +#define KEY_F6 0x3f // Keyboard F6 +#define KEY_F7 0x40 // Keyboard F7 +#define KEY_F8 0x41 // Keyboard F8 +#define KEY_F9 0x42 // Keyboard F9 +#define KEY_F10 0x43 // Keyboard F10 +#define KEY_F11 0x44 // Keyboard F11 +#define KEY_F12 0x45 // Keyboard F12 + +#define KEY_SYSRQ 0x46 // Keyboard Print Screen +#define KEY_SCROLLLOCK 0x47 // Keyboard Scroll Lock +#define KEY_PAUSE 0x48 // Keyboard Pause +#define KEY_INSERT 0x49 // Keyboard Insert +#define KEY_HOME 0x4a // Keyboard Home +#define KEY_PAGEUP 0x4b // Keyboard Page Up +#define KEY_DELETE 0x4c // Keyboard Delete Forward +#define KEY_END 0x4d // Keyboard End +#define KEY_PAGEDOWN 0x4e // Keyboard Page Down +#define KEY_RIGHT 0x4f // Keyboard Right Arrow +#define KEY_LEFT 0x50 // Keyboard Left Arrow +#define KEY_DOWN 0x51 // Keyboard Down Arrow +#define KEY_UP 0x52 // Keyboard Up Arrow + +#define KEY_NUMLOCK 0x53 // Keyboard Num Lock and Clear +#define KEY_KPSLASH 0x54 // Keypad / +#define KEY_KPASTERISK 0x55 // Keypad * +#define KEY_KPMINUS 0x56 // Keypad - +#define KEY_KPPLUS 0x57 // Keypad + +#define KEY_KPENTER 0x58 // Keypad ENTER +#define KEY_KP1 0x59 // Keypad 1 and End +#define KEY_KP2 0x5a // Keypad 2 and Down Arrow +#define KEY_KP3 0x5b // Keypad 3 and PageDn +#define KEY_KP4 0x5c // Keypad 4 and Left Arrow +#define KEY_KP5 0x5d // Keypad 5 +#define KEY_KP6 0x5e // Keypad 6 and Right Arrow +#define KEY_KP7 0x5f // Keypad 7 and Home +#define KEY_KP8 0x60 // Keypad 8 and Up Arrow +#define KEY_KP9 0x61 // Keypad 9 and Page Up +#define KEY_KP0 0x62 // Keypad 0 and Insert +#define KEY_KPDOT 0x63 // Keypad . and Delete + +#define KEY_102ND 0x64 // Keyboard Non-US \ and | +#define KEY_COMPOSE 0x65 // Keyboard Application +#define KEY_POWER 0x66 // Keyboard Power +#define KEY_KPEQUAL 0x67 // Keypad = + +#define KEY_F13 0x68 // Keyboard F13 +#define KEY_F14 0x69 // Keyboard F14 +#define KEY_F15 0x6a // Keyboard F15 +#define KEY_F16 0x6b // Keyboard F16 +#define KEY_F17 0x6c // Keyboard F17 +#define KEY_F18 0x6d // Keyboard F18 +#define KEY_F19 0x6e // Keyboard F19 +#define KEY_F20 0x6f // Keyboard F20 +#define KEY_F21 0x70 // Keyboard F21 +#define KEY_F22 0x71 // Keyboard F22 +#define KEY_F23 0x72 // Keyboard F23 +#define KEY_F24 0x73 // Keyboard F24 + +#define KEY_OPEN 0x74 // Keyboard Execute +#define KEY_HELP 0x75 // Keyboard Help +#define KEY_PROPS 0x76 // Keyboard Menu +#define KEY_FRONT 0x77 // Keyboard Select +#define KEY_STOP 0x78 // Keyboard Stop +#define KEY_AGAIN 0x79 // Keyboard Again +#define KEY_UNDO 0x7a // Keyboard Undo +#define KEY_CUT 0x7b // Keyboard Cut +#define KEY_COPY 0x7c // Keyboard Copy +#define KEY_PASTE 0x7d // Keyboard Paste +#define KEY_FIND 0x7e // Keyboard Find +#define KEY_MUTE 0x7f // Keyboard Mute +#define KEY_VOLUMEUP 0x80 // Keyboard Volume Up +#define KEY_VOLUMEDOWN 0x81 // Keyboard Volume Down +// 0x82 Keyboard Locking Caps Lock +// 0x83 Keyboard Locking Num Lock +// 0x84 Keyboard Locking Scroll Lock +#define KEY_KPCOMMA 0x85 // Keypad Comma +// 0x86 Keypad Equal Sign +#define KEY_RO 0x87 // Keyboard International1 +#define KEY_KATAKANAHIRAGANA 0x88 // Keyboard International2 +#define KEY_YEN 0x89 // Keyboard International3 +#define KEY_HENKAN 0x8a // Keyboard International4 +#define KEY_MUHENKAN 0x8b // Keyboard International5 +#define KEY_KPJPCOMMA 0x8c // Keyboard International6 +// 0x8d Keyboard International7 +// 0x8e Keyboard International8 +// 0x8f Keyboard International9 +#define KEY_HANGEUL 0x90 // Keyboard LANG1 +#define KEY_HANJA 0x91 // Keyboard LANG2 +#define KEY_KATAKANA 0x92 // Keyboard LANG3 +#define KEY_HIRAGANA 0x93 // Keyboard LANG4 +#define KEY_ZENKAKUHANKAKU 0x94 // Keyboard LANG5 +// 0x95 Keyboard LANG6 +// 0x96 Keyboard LANG7 +// 0x97 Keyboard LANG8 +// 0x98 Keyboard LANG9 +// 0x99 Keyboard Alternate Erase +// 0x9a Keyboard SysReq/Attention +// 0x9b Keyboard Cancel +// 0x9c Keyboard Clear +// 0x9d Keyboard Prior +// 0x9e Keyboard Return +// 0x9f Keyboard Separator +// 0xa0 Keyboard Out +// 0xa1 Keyboard Oper +// 0xa2 Keyboard Clear/Again +// 0xa3 Keyboard CrSel/Props +// 0xa4 Keyboard ExSel + +// 0xb0 Keypad 00 +// 0xb1 Keypad 000 +// 0xb2 Thousands Separator +// 0xb3 Decimal Separator +// 0xb4 Currency Unit +// 0xb5 Currency Sub-unit +#define KEY_KPLEFTPAREN 0xb6 // Keypad ( +#define KEY_KPRIGHTPAREN 0xb7 // Keypad ) +// 0xb8 Keypad { +// 0xb9 Keypad } +// 0xba Keypad Tab +// 0xbb Keypad Backspace +// 0xbc Keypad A +// 0xbd Keypad B +// 0xbe Keypad C +// 0xbf Keypad D +// 0xc0 Keypad E +// 0xc1 Keypad F +// 0xc2 Keypad XOR +// 0xc3 Keypad ^ +// 0xc4 Keypad % +// 0xc5 Keypad < +// 0xc6 Keypad > +// 0xc7 Keypad & +// 0xc8 Keypad && +// 0xc9 Keypad | +// 0xca Keypad || +// 0xcb Keypad : +// 0xcc Keypad # +// 0xcd Keypad Space +// 0xce Keypad @ +// 0xcf Keypad ! +// 0xd0 Keypad Memory Store +// 0xd1 Keypad Memory Recall +// 0xd2 Keypad Memory Clear +// 0xd3 Keypad Memory Add +// 0xd4 Keypad Memory Subtract +// 0xd5 Keypad Memory Multiply +// 0xd6 Keypad Memory Divide +// 0xd7 Keypad +/- +// 0xd8 Keypad Clear +// 0xd9 Keypad Clear Entry +// 0xda Keypad Binary +// 0xdb Keypad Octal +// 0xdc Keypad Decimal +// 0xdd Keypad Hexadecimal + +#define KEY_LEFTCTRL 0xe0 // Keyboard Left Control +#define KEY_LEFTSHIFT 0xe1 // Keyboard Left Shift +#define KEY_LEFTALT 0xe2 // Keyboard Left Alt +#define KEY_LEFTMETA 0xe3 // Keyboard Left GUI +#define KEY_RIGHTCTRL 0xe4 // Keyboard Right Control +#define KEY_RIGHTSHIFT 0xe5 // Keyboard Right Shift +#define KEY_RIGHTALT 0xe6 // Keyboard Right Alt +#define KEY_RIGHTMETA 0xe7 // Keyboard Right GUI + +#define KEY_MEDIA_PLAYPAUSE 0xe8 +#define KEY_MEDIA_STOPCD 0xe9 +#define KEY_MEDIA_PREVIOUSSONG 0xea +#define KEY_MEDIA_NEXTSONG 0xeb +#define KEY_MEDIA_EJECTCD 0xec +#define KEY_MEDIA_VOLUMEUP 0xed +#define KEY_MEDIA_VOLUMEDOWN 0xee +#define KEY_MEDIA_MUTE 0xef +#define KEY_MEDIA_WWW 0xf0 +#define KEY_MEDIA_BACK 0xf1 +#define KEY_MEDIA_FORWARD 0xf2 +#define KEY_MEDIA_STOP 0xf3 +#define KEY_MEDIA_FIND 0xf4 +#define KEY_MEDIA_SCROLLUP 0xf5 +#define KEY_MEDIA_SCROLLDOWN 0xf6 +#define KEY_MEDIA_EDIT 0xf7 +#define KEY_MEDIA_SLEEP 0xf8 +#define KEY_MEDIA_COFFEE 0xf9 +#define KEY_MEDIA_REFRESH 0xfa +#define KEY_MEDIA_CALC 0xfb + +#endif \ No newline at end of file diff --git a/src/keymap.h b/src/keymap.h index 0144b14..70f2c96 100644 --- a/src/keymap.h +++ b/src/keymap.h @@ -1,12 +1,149 @@ #ifndef KEYMAP_h #define KEYMAP_h #include +#include "hid_tables.h" -#define KEY_POWER 0x66 +bool adb_keymap_is_modifier(uint8_t key) { + return \ + (key == ADB_KEY_LEFT_SHIFT) || + (key == ADB_KEY_RIGHT_SHIFT) || + (key == ADB_KEY_LEFT_OPTION) || + (key == ADB_KEY_RIGHT_OPTION) || + (key == ADB_KEY_LEFT_COMMAND) || + (key == ADB_KEY_RIGHT_COMMAND) || + (key == ADB_KEY_LEFT_CONTROL) || + (key == ADB_KEY_RIGHT_CONTROL); +} -#define ADB_KEY_POWER_DOWN 0x7F7F -#define ADB_KEY_POWER_UP 0xFFFF - -uint8_t* adb_keycode_to_hid = {0x0}; // TODO +uint8_t adb_keycode_to_hid[128] = { + /* 0x00 = */ KEY_A, + /* 0x01 = */ KEY_S, + /* 0x02 = */ KEY_D, + /* 0x03 = */ KEY_F, + /* 0x04 = */ KEY_H, + /* 0x05 = */ KEY_G, + /* 0x06 = */ KEY_Z, + /* 0x07 = */ KEY_X, + /* 0x08 = */ KEY_C, + /* 0x09 = */ KEY_V, + /* 0x0a = */ KEY_102ND, + /* 0x0b = */ KEY_B, + /* 0x0c = */ KEY_Q, + /* 0x0d = */ KEY_W, + /* 0x0e = */ KEY_E, + /* 0x0f = */ KEY_R, + /* 0x10 = */ KEY_Y, + /* 0x11 = */ KEY_T, + /* 0x12 = */ KEY_1, + /* 0x13 = */ KEY_2, + /* 0x14 = */ KEY_3, + /* 0x15 = */ KEY_4, + /* 0x16 = */ KEY_6, + /* 0x17 = */ KEY_5, + /* 0x18 = */ KEY_EQUAL, + /* 0x19 = */ KEY_9, + /* 0x1a = */ KEY_7, + /* 0x1b = */ KEY_MINUS, + /* 0x1c = */ KEY_8, + /* 0x1d = */ KEY_0, + /* 0x1e = */ KEY_RIGHTBRACE, + /* 0x1f = */ KEY_O, + /* 0x20 = */ KEY_U, + /* 0x21 = */ KEY_LEFTBRACE, + /* 0x22 = */ KEY_I, + /* 0x23 = */ KEY_P, + /* 0x24 = */ KEY_ENTER, + /* 0x25 = */ KEY_L, + /* 0x26 = */ KEY_J, + /* 0x27 = */ KEY_APOSTROPHE, + /* 0x28 = */ KEY_K, + /* 0x29 = */ KEY_SEMICOLON, + /* 0x2a = */ KEY_HASHTILDE, + /* 0x2b = */ KEY_COMMA, + /* 0x2c = */ KEY_SLASH, + /* 0x2d = */ KEY_N, + /* 0x2e = */ KEY_M, + /* 0x2f = */ KEY_DOT, + /* 0x30 = */ KEY_TAB, + /* 0x31 = */ KEY_SPACE, + /* 0x32 = */ KEY_GRAVE, + /* 0x33 = */ KEY_BACKSPACE, + /* 0x34 = */ 0, + /* 0x35 = */ KEY_ESC, + /* 0x36 = */ KEY_LEFTCTRL, + /* 0x37 = */ KEY_LEFTMETA, + /* 0x38 = */ KEY_LEFTSHIFT, + /* 0x39 = */ KEY_CAPSLOCK, + /* 0x3a = */ KEY_LEFTALT, + /* 0x3b = */ KEY_LEFT, + /* 0x3c = */ KEY_RIGHT, + /* 0x3d = */ KEY_DOWN, + /* 0x3e = */ KEY_UP, + /* 0x3f = */ 0, + /* 0x40 = */ 0, + /* 0x41 = */ KEY_KPDOT, + /* 0x42 = */ 0, + /* 0x43 = */ KEY_KPASTERISK, + /* 0x44 = */ 0, + /* 0x45 = */ KEY_KPPLUS, + /* 0x46 = */ 0, + /* 0x47 = */ KEY_NUMLOCK, + /* 0x48 = */ 0, + /* 0x49 = */ 0, + /* 0x4a = */ 0, + /* 0x4b = */ KEY_KPSLASH, + /* 0x4c = */ KEY_KPENTER, + /* 0x4d = */ 0, + /* 0x4e = */ KEY_KPMINUS, + /* 0x4f = */ 0, + /* 0x50 = */ 0, + /* 0x51 = */ KEY_KPEQUAL, + /* 0x52 = */ KEY_KP0, + /* 0x53 = */ KEY_KP1, + /* 0x54 = */ KEY_KP2, + /* 0x55 = */ KEY_KP3, + /* 0x56 = */ KEY_KP4, + /* 0x57 = */ KEY_KP5, + /* 0x58 = */ KEY_KP6, + /* 0x59 = */ KEY_KP7, + /* 0x5a = */ 0, + /* 0x5b = */ KEY_KP8, + /* 0x5c = */ KEY_KP9, + /* 0x5d = */ 0, + /* 0x5e = */ 0, + /* 0x5f = */ 0, + /* 0x60 = */ KEY_F5, + /* 0x61 = */ KEY_F6, + /* 0x62 = */ KEY_F7, + /* 0x63 = */ KEY_F3, + /* 0x64 = */ KEY_F8, + /* 0x65 = */ KEY_F9, + /* 0x66 = */ 0, + /* 0x67 = */ KEY_VOLUMEDOWN, //KEY_F11, + /* 0x68 = */ 0, + /* 0x69 = */ KEY_F13, + /* 0x6a = */ 0, + /* 0x6b = */ KEY_F14, + /* 0x6c = */ 0, + /* 0x6d = */ KEY_MUTE, //KEY_F10, + /* 0x6e = */ 0, + /* 0x6f = */ KEY_VOLUMEUP, //KEY_F12, + /* 0x70 = */ 0, + /* 0x71 = */ KEY_F15, + /* 0x72 = */ KEY_HELP, // or KEY_INSERT + /* 0x73 = */ KEY_HOME, + /* 0x74 = */ KEY_PAGEUP, + /* 0x75 = */ KEY_DELETE, + /* 0x76 = */ KEY_F4, + /* 0x77 = */ KEY_END, + /* 0x78 = */ KEY_F2, + /* 0x79 = */ KEY_PAGEDOWN, + /* 0x7a = */ KEY_F1, + /* 0x7b = */ KEY_RIGHTSHIFT, + /* 0x7c = */ KEY_RIGHTALT, + /* 0x7d = */ KEY_RIGHTCTRL, + /* 0x7e = */ 0, + /* 0x7f = */ KEY_POWER, // Special key, repeated in both bytes of the register +}; #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index cbfd7e0..e0cc4f2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,66 +2,120 @@ #include #include -#include -#include "adb.h" +#include "hid_tables.h" +#include "adb_structures.h" +#include "hid_keyboard.h" +#include "adb_devices.h" -void setup() { - Keyboard.begin(); - pinMode(PC13, OUTPUT); - digitalWrite(PC13, LOW); +#define POLL_DELAY 5 - delay(5000); - digitalWrite(PC13, HIGH); - - Keyboard.print("Setting up IO...\n"); - pinMode(ADB_DATA_PIN, OUTPUT_OPEN_DRAIN); - ADB_WRITE(HIGH); - Keyboard.print("Waiting for ADB high... "); - - while(LOW == ADB_READ()); - Keyboard.print("Ready.\n"); - - Keyboard.print("Resetting... "); - adb_reset(); - delay(500); // A wait for good measure, apparently AEKII can take a moment to reset - Keyboard.print("Ready.\n"); -} +bool apple_extended_detected = false; +#ifdef PIO_FRAMEWORK_ARDUINO_ENABLE_CDC void print16b(uint16_t buff) { uint16_t mask = (1 << 15); for (uint8_t i = 0; i < 16; i++) { - Keyboard.write(buff & mask ? '1' : '0'); + Serial.write(buff & mask ? '1' : '0'); mask >>= 1; } + Serial.write('\n'); + Serial.flush(); } -void printkbresp(uint16_t buff) { +void printkbresp(adb_kb_keypress buff) { //Keyboard.print(" key1 "); - Keyboard.print(buff & (1 << 15) ? "u" : "d"); - Keyboard.print((buff & (0x7F << 8)) >> 8, 16); - //Keyboard.print(" key2 "); - //Keyboard.print(buff & (1 << 7) ? "up \t" : "down\t"); - //Keyboard.print(buff & 0x7F, 16); - Keyboard.print("\n"); + Serial.print(buff.released0 ? "u" : "d"); + Serial.print(buff.key0, 16); + Serial.print(", "); + Serial.print(buff.released1 ? "u" : "d"); + Serial.print(buff.key1, 16); + Serial.print("\n"); + Serial.flush(); +} +#endif + +void setup() { + // Turn the led on at the beginning of setup + pinMode(PC13, OUTPUT); + digitalWrite(PC13, LOW); + +#ifdef PIO_FRAMEWORK_ARDUINO_ENABLE_CDC + Serial.begin(115200); +#endif + + // Set up HID + hid_keyboard_init(); + + // Set up the ADB bus + adb_init(); + + delay(1000); // A wait for good measure, apparently AEKII can take a moment to reset + + // Initialise the ADB devices + // Switch the keyboard to Apple Extended if available + bool error = false; + adb_kb_data reg3 = {0}, mask = {0}; + reg3.data.device_handler_id = 0x03; + mask.data.device_handler_id = 0xFF; + apple_extended_detected = adb_keyboard_update_register3(reg3, mask.raw, &error); + + // Set-up successful, turn of the LED + digitalWrite(PC13, HIGH); } void loop() { - //Keyboard.print("Talk addr 3 reg 0... "); - //noInterrupts(); - uint16_t buffer = 0; - adb_write_command(ADB_CMD_TALK | ADB_ADDRESS(2) | ADB_REGISTER(0)); - adb_wait_tlt(); - bool success = adb_read_data_packet(&buffer, 17); - - //interrupts(); - if (success) { - //print16b(buffer); - printkbresp(buffer); + // TODO: Add proper polling based on SRQ + static bool led_caps = false; + static hid_key_report key_report = {0}; + bool error = false; + + auto key_press = adb_keyboard_read_key_press(&error); + + if (error) return; // don't continue changing the hid report if there was + // an error reading from ADB – most often it's a timeout + + bool report_changed = hid_keyboard_set_keys_from_adb_register(&key_report, key_press); + + // Handle the `toggle' caps lock key. + // Every action reported by an ADB keyboard on the caps lock key + // should be interpreted as key down, followed by key released, + // unless the keypress was so fast, that it was pressed and depressed + // within the same `register'. Then ignore. + bool caps_lock_action = key_press.data.key0 == ADB_KEY_CAPS_LOCK || + key_press.data.key1 == ADB_KEY_CAPS_LOCK; + + bool caps_lock_twice_in_register = key_press.data.key0 == ADB_KEY_CAPS_LOCK && + key_press.data.key1 == ADB_KEY_CAPS_LOCK && + key_press.data.released0 != key_press.data.released1; + + if (caps_lock_action && !caps_lock_twice_in_register) + { + hid_keyboard_add_key_to_report(&key_report, KEY_CAPSLOCK); + + // Send the preliminary report, with caps lock down + hid_keyboard_send_report(&key_report); + + led_caps = !led_caps; + adb_keyboard_write_leds(0, led_caps, 0); + + // Wait a little bit + delay(80); + + // Now just release the caps lock key, and continue as before + hid_keyboard_remove_key_from_report(&key_report, KEY_CAPSLOCK); + + report_changed = true; } - //else - // Keyboard.print("timeout.\n"); - delay(5); + + // Send the finished report + if (report_changed) + hid_keyboard_send_report(&key_report); + + // Wait a tiny bit before polling again, + // while ADB seems fairly tolarent of quick requests + // we don't want to overwhelm USB either + delay(POLL_DELAY); } #endif \ No newline at end of file diff --git a/src/raw_keyboard.cpp b/src/raw_keyboard.cpp deleted file mode 100644 index e8f4274..0000000 --- a/src/raw_keyboard.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include -#include "raw_keyboard.h" -#include "keymap.h" - -#if defined(USBCON) -#include "usbd_hid_composite_if.h" - -RawKeyboard::RawKeyboard() { - keyReport = (KeyReport) {0}; -} - -void RawKeyboard::begin(void) -{ - HID_Composite_Init(HID_KEYBOARD); -} - -void RawKeyboard::end(void) -{ - HID_Composite_DeInit(HID_KEYBOARD); -} - -void RawKeyboard::sendReport() -{ - uint8_t buf[8] = {keyReport.modifiers, keyReport.reserved, keyReport.keys[0], - keyReport.keys[1], keyReport.keys[2], keyReport.keys[3], keyReport.keys[4], - keyReport.keys[5] - }; - - HID_Composite_keyboard_sendReport(buf, 8); - - //delay required to prevent persistent key when call print - delay(20); -} - -void RawKeyboard::setKeysFromADBRegister(uint16_t reg) { - if (reg == ADB_KEY_POWER_DOWN) // power button is a special case - { // residing in both octets - updateKeyInReport(KEY_POWER, false); - } - else if (reg == ADB_KEY_POWER_UP) { - updateKeyInReport(KEY_POWER, true); - } - else { - // Higher octet key: - uint8_t key0 = (reg >> 8) & 0x7F; - bool released0 = (reg >> 8) & 0x80; - updateKeyInReport(adb_keycode_to_hid[key0], released0); - - // Lower octet key: - uint8_t key1 = reg & 0x7F; - bool released1 = reg & 0x80; - updateKeyInReport(adb_keycode_to_hid[key1], released1); - } -} - -void RawKeyboard::setModifiersFromADBRegister(uint16_t reg) { - -} - -void RawKeyboard::updateKeyInReport(uint8_t hid_keycode, bool released) { - if (released) - removeKeyFromReport(hid_keycode); - else - addKeyToReport(hid_keycode); -} - -// Returns true if after execution the key is in the report -bool RawKeyboard::addKeyToReport(uint8_t hid_keycode) { - int8_t free_slot = -1; - - for (uint8_t i = 0; i < KEY_REPORT_KEYS_COUNT; i++) { - if (keyReport.keys[i] == hid_keycode) - return true; // key is already in the report - - if (keyReport.keys[i] == 0 && free_slot == -1) - free_slot = i; // memorise empty entry idx - } - - if (free_slot == -1) - return false; // key report is full - - keyReport.keys[free_slot] = hid_keycode; - return true; -} - -void RawKeyboard::removeKeyFromReport(uint8_t hid_keycode) { - for (uint8_t i = 0; i < KEY_REPORT_KEYS_COUNT; i++) { - if (keyReport.keys[i] == hid_keycode) { - keyReport.keys[i] = 0; - } - } -} - -#endif \ No newline at end of file diff --git a/src/raw_keyboard.h b/src/raw_keyboard.h deleted file mode 100644 index ad5c1b7..0000000 --- a/src/raw_keyboard.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef RAWKEYBOARD_h -#define RAWKEYBOARD_h - -#if !defined(USBCON) || !defined(USBD_USE_HID_COMPOSITE) - -#error "USB HID not enabled! Select 'HID' in the 'Tools->USB interface' menu." - -#else - -#include - -#define KEY_REPORT_KEYS_COUNT 6 - -typedef struct { - uint8_t modifiers; - uint8_t reserved; - uint8_t keys[6]; -} KeyReport; - - -class RawKeyboard { - private: - RawKeyboard(); - KeyReport keyReport; - void updateKeyInReport(uint8_t hid_keycode, bool released); - bool addKeyToReport(uint8_t hid_keycode); - void removeKeyFromReport(uint8_t hid_keycode); - public: - void begin(); - void end(); - void setKeysFromADBRegister(uint16_t); - void setModifiersFromADBRegister(uint16_t); - void sendReport(); -}; - -#endif - -#endif \ No newline at end of file diff --git a/test/test_desktop/test_raw_keyboard.cpp b/test/test_desktop/test_raw_keyboard.cpp deleted file mode 100644 index 1d5da87..0000000 --- a/test/test_desktop/test_raw_keyboard.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#define private public -#pragma GCC diagnostic ignored "-Wc++11-extensions" - -#include -#include "raw_keyboard.h" - -// void setUp(void) { -// // set stuff up here -// } - -// void tearDown(void) { -// // clean stuff up here -// } - -void test_keyReport_empty(void) { - RawKeyboard k; - - for (uint8_t i = 0; i < 6; i++) - TEST_ASSERT_EQUAL(0, k.keyReport.keys[i]); - - TEST_ASSERT_EQUAL(0, k.keyReport.reserved); - TEST_ASSERT_EQUAL(0, k.keyReport.modifiers); -} - -void test_addKeyToReport(void) { - RawKeyboard k; - - TEST_ASSERT_TRUE(k.addKeyToReport(1)); - TEST_ASSERT_TRUE(k.addKeyToReport(2)); - TEST_ASSERT_TRUE(k.addKeyToReport(3)); - TEST_ASSERT_TRUE(k.addKeyToReport(2)); - - TEST_ASSERT_EQUAL(1, k.keyReport.keys[0]); - TEST_ASSERT_EQUAL(2, k.keyReport.keys[1]); - TEST_ASSERT_EQUAL(3, k.keyReport.keys[2]); - TEST_ASSERT_EQUAL(0, k.keyReport.keys[3]); - TEST_ASSERT_EQUAL(0, k.keyReport.keys[4]); - TEST_ASSERT_EQUAL(0, k.keyReport.keys[5]); -} - -void test_removeKeyFromReport(void) { - RawKeyboard k; - - uint8_t initial_keys[] = {7, 8, 9, 0, 0, 0}; - for (uint8_t i = 0; i < 6; i++) { - k.keyReport.keys[i] = initial_keys[i]; - } - - k.removeKeyFromReport(7); - k.removeKeyFromReport(9); - k.removeKeyFromReport(7); - - TEST_ASSERT_EQUAL(0, k.keyReport.keys[0]); - TEST_ASSERT_EQUAL(8, k.keyReport.keys[1]); - TEST_ASSERT_EQUAL(0, k.keyReport.keys[2]); - TEST_ASSERT_EQUAL(0, k.keyReport.keys[3]); - TEST_ASSERT_EQUAL(0, k.keyReport.keys[4]); - TEST_ASSERT_EQUAL(0, k.keyReport.keys[5]); -} - -int main(int argc, char **argv) { - UNITY_BEGIN(); - RUN_TEST(test_keyReport_empty); - RUN_TEST(test_addKeyToReport); - RUN_TEST(test_removeKeyFromReport); - UNITY_END(); - - return 0; -} diff --git a/test/test_desktop/tests.cpp b/test/test_desktop/tests.cpp new file mode 100644 index 0000000..37e0e90 --- /dev/null +++ b/test/test_desktop/tests.cpp @@ -0,0 +1,116 @@ +#define private public +#pragma GCC diagnostic ignored "-Wc++11-extensions" + +#include +#include "adb_devices.h" +#include "hid_keyboard.h" + +// void setUp(void) { +// // set stuff up here +// } + +// void tearDown(void) { +// // clean stuff up here +// } + +void test_key_report_empty(void) { + hid_key_report k = {0}; + + for (uint8_t i = 0; i < 6; i++) + TEST_ASSERT_EQUAL(0, k.keys[i]); + + TEST_ASSERT_EQUAL(0, k.modifiers); +} + +void test_hid_keyboard_add_key_to_report(void) { + hid_key_report k = {0}; + + TEST_ASSERT_TRUE(hid_keyboard_add_key_to_report(&k, 1)); + TEST_ASSERT_TRUE(hid_keyboard_add_key_to_report(&k, 2)); + TEST_ASSERT_TRUE(hid_keyboard_add_key_to_report(&k, 3)); + TEST_ASSERT_TRUE(hid_keyboard_add_key_to_report(&k, 2)); + + TEST_ASSERT_EQUAL(1, k.keys[0]); + TEST_ASSERT_EQUAL(2, k.keys[1]); + TEST_ASSERT_EQUAL(3, k.keys[2]); + TEST_ASSERT_EQUAL(0, k.keys[3]); + TEST_ASSERT_EQUAL(0, k.keys[4]); + TEST_ASSERT_EQUAL(0, k.keys[5]); +} + +void test_hid_keyboard_remove_key_from_report(void) { + hid_key_report k = {0}; + + uint8_t initial_keys[] = {7, 8, 9, 0, 0, 0}; + for (uint8_t i = 0; i < 6; i++) { + k.keys[i] = initial_keys[i]; + } + + hid_keyboard_remove_key_from_report(&k, 7); + hid_keyboard_remove_key_from_report(&k, 9); + hid_keyboard_remove_key_from_report(&k, 7); + + TEST_ASSERT_EQUAL(0, k.keys[0]); + TEST_ASSERT_EQUAL(8, k.keys[1]); + TEST_ASSERT_EQUAL(0, k.keys[2]); + TEST_ASSERT_EQUAL(0, k.keys[3]); + TEST_ASSERT_EQUAL(0, k.keys[4]); + TEST_ASSERT_EQUAL(0, k.keys[5]); +} + +void test_adb_kb_keypress(void) { + uint8_t kp_bin[2] = {0b10000001, 0b00001011}; + + adb_kb_keypress kp_stru; + kp_stru.released0 = true; + kp_stru.key0 = 1; + kp_stru.released1 = false; + kp_stru.key1 = 11; + + uint16_t kp_stru_bin = *((uint16_t*)&kp_stru); + + TEST_ASSERT_EQUAL(kp_bin, kp_stru_bin); +} + +void test_adb_kb_modifiers() { + uint8_t mod_bin[2] = {0b00110101, 0b11000010}; + adb_kb_modifiers mod_stru = *((adb_kb_modifiers*)&mod_bin); + + TEST_ASSERT_EQUAL(0, mod_stru.reserved0); + TEST_ASSERT_EQUAL(0, mod_stru.backspace); + TEST_ASSERT_EQUAL(1, mod_stru.caps_lock); + TEST_ASSERT_EQUAL(1, mod_stru.reset); + TEST_ASSERT_EQUAL(0, mod_stru.control); + TEST_ASSERT_EQUAL(1, mod_stru.shift); + TEST_ASSERT_EQUAL(0, mod_stru.option); + TEST_ASSERT_EQUAL(1, mod_stru.command); + TEST_ASSERT_EQUAL(1, mod_stru.num_lock); + TEST_ASSERT_EQUAL(1, mod_stru.scroll_lock); + TEST_ASSERT_EQUAL(0, mod_stru.reserved1); + TEST_ASSERT_EQUAL(0, mod_stru.led_scroll); + TEST_ASSERT_EQUAL(1, mod_stru.led_caps); + TEST_ASSERT_EQUAL(0, mod_stru.led_num); +} + +void test_adb_command() { + uint8_t cmd_bin = 0b01101001; + adb_command cmd_stru = *((adb_command*)&cmd_bin); + + TEST_ASSERT_EQUAL(6, cmd_stru.addr); + TEST_ASSERT_EQUAL(2, cmd_stru.cmd); + TEST_ASSERT_EQUAL(1, cmd_stru.reg); +} + +int main(int argc, char **argv) { + UNITY_BEGIN(); + RUN_TEST(test_key_report_empty); + RUN_TEST(test_hid_keyboard_add_key_to_report); + RUN_TEST(test_hid_keyboard_remove_key_from_report); + + RUN_TEST(test_adb_kb_keypress); + RUN_TEST(test_adb_kb_modifiers); + RUN_TEST(test_adb_command); + UNITY_END(); + + return 0; +}