/* USB EHCI Host for Teensy 3.6 * Copyright 2017 Paul Stoffregen (paul@pjrc.com) * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "USBHost_t36.h" // Read this header first for key info #include "keylayouts.h" // from Teensyduino core library typedef struct { KEYCODE_TYPE code; uint8_t ascii; } keycode_extra_t; typedef struct { KEYCODE_TYPE code; KEYCODE_TYPE codeNumlockOff; uint8_t charNumlockOn; // We will assume when num lock is on we have all characters... } keycode_numlock_t; #ifdef M #undef M #endif #define M(n) ((n) & KEYCODE_MASK) keycode_extra_t keycode_extras[] = { {M(KEY_ENTER), '\n'}, {M(KEY_ESC), 0x1b}, {M(KEY_TAB), 0x9 }, {M(KEY_UP), KEYD_UP }, {M(KEY_DOWN), KEYD_DOWN }, {M(KEY_LEFT), KEYD_LEFT }, {M(KEY_RIGHT), KEYD_RIGHT }, {M(KEY_INSERT), KEYD_INSERT }, {M(KEY_DELETE), KEYD_DELETE }, {M(KEY_PAGE_UP), KEYD_PAGE_UP }, {M(KEY_PAGE_DOWN), KEYD_PAGE_DOWN }, {M(KEY_HOME), KEYD_HOME }, {M(KEY_END), KEYD_END }, {M(KEY_F1), KEYD_F1 }, {M(KEY_F2), KEYD_F2 }, {M(KEY_F3), KEYD_F3 }, {M(KEY_F4), KEYD_F4 }, {M(KEY_F5), KEYD_F5 }, {M(KEY_F6), KEYD_F6 }, {M(KEY_F7), KEYD_F7 }, {M(KEY_F8), KEYD_F8 }, {M(KEY_F9), KEYD_F9 }, {M(KEY_F10), KEYD_F10 }, {M(KEY_F11), KEYD_F11 }, {M(KEY_F12), KEYD_F12 } }; // Some of these mapped to key + shift. keycode_numlock_t keycode_numlock[] = { {M(KEYPAD_SLASH), '/', '/'}, {M(KEYPAD_ASTERIX), '*', '*'}, {M(KEYPAD_MINUS), '-', '-'}, {M(KEYPAD_PLUS), '+', '+'}, {M(KEYPAD_ENTER), '\n', '\n'}, {M(KEYPAD_1), 0x80 | M(KEY_END), '1'}, {M(KEYPAD_2), 0x80 | M(KEY_DOWN), '2'}, {M(KEYPAD_3), 0x80 | M(KEY_PAGE_DOWN), '3'}, {M(KEYPAD_4), 0x80 | M(KEY_LEFT), '4'}, {M(KEYPAD_5), 0x00, '5'}, {M(KEYPAD_6), 0x80 | M(KEY_RIGHT), '6'}, {M(KEYPAD_7), 0x80 | M(KEY_HOME), '7'}, {M(KEYPAD_8), 0x80 | M(KEY_UP), '8'}, {M(KEYPAD_9), 0x80 | M(KEY_PAGE_UP), '9'}, {M(KEYPAD_0), 0x80 | M(KEY_INSERT), '0'}, {M(KEYPAD_PERIOD), 0x80 | M(KEY_DELETE), '.'} }; #define print USBHost::print_ #define println USBHost::println_ void KeyboardController::init() { contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t)); contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); driver_ready_for_device(this); } bool KeyboardController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) { println("KeyboardController claim this=", (uint32_t)this, HEX); // only claim at interface level if (type != 1) return false; if (len < 9+9+7) return false; uint32_t numendpoint = descriptors[4]; if (numendpoint < 1) return false; if (descriptors[5] != 3) return false; // bInterfaceClass, 3 = HID if (descriptors[6] != 1) return false; // bInterfaceSubClass, 1 = Boot Device if (descriptors[7] != 1) return false; // bInterfaceProtocol, 1 = Keyboard if (descriptors[9] != 9) return false; if (descriptors[10] != 33) return false; // HID descriptor (ignored, Boot Protocol) if (descriptors[18] != 7) return false; if (descriptors[19] != 5) return false; // endpoint descriptor uint32_t endpoint = descriptors[20]; println("ep = ", endpoint, HEX); if ((endpoint & 0xF0) != 0x80) return false; // must be IN direction endpoint &= 0x0F; if (endpoint == 0) return false; if (descriptors[21] != 3) return false; // must be interrupt type uint32_t size = descriptors[22] | (descriptors[23] << 8); println("packet size = ", size); if (size != 8) { return false; // must be 8 bytes for Keyboard Boot Protocol } #ifdef USBHS_KEYBOARD_INTERVAL uint32_t interval = USBHS_KEYBOARD_INTERVAL; #else uint32_t interval = descriptors[24]; #endif println("polling interval = ", interval); datapipe = new_Pipe(dev, 3, endpoint, 1, 8, interval); datapipe->callback_function = callback; queue_Data_Transfer(datapipe, report, 8, this); mk_setup(setup, 0x21, 10, 0, 0, 0); // 10=SET_IDLE queue_Control_Transfer(dev, &setup, NULL, this); return true; } void KeyboardController::control(const Transfer_t *transfer) { } void KeyboardController::callback(const Transfer_t *transfer) { //println("KeyboardController Callback (static)"); if (transfer->driver) { ((KeyboardController *)(transfer->driver))->new_data(transfer); } } void KeyboardController::disconnect() { // TODO: free resources } // Arduino defined this static weak symbol callback, and their // examples use it as the only way to detect new key presses, // so unfortunate as static weak callbacks are, it probably // needs to be supported for compatibility extern "C" { void __keyboardControllerEmptyCallback() { } } void keyPressed() __attribute__ ((weak, alias("__keyboardControllerEmptyCallback"))); void keyReleased() __attribute__ ((weak, alias("__keyboardControllerEmptyCallback"))); static bool contains(uint8_t b, const uint8_t *data) { if (data[2] == b || data[3] == b || data[4] == b) return true; if (data[5] == b || data[6] == b || data[7] == b) return true; return false; } void KeyboardController::new_data(const Transfer_t *transfer) { processing_new_data_ = true; println("KeyboardController Callback (member)"); print(" KB Data: "); print_hexbytes(transfer->buffer, 8); for (int i=2; i < 8; i++) { uint32_t key = prev_report[i]; if (key >= 4 && !contains(key, report)) { key_release(prev_report[0], key); } } for (int i=2; i < 8; i++) { uint32_t key = report[i]; if (key >= 4 && !contains(key, prev_report)) { key_press(report[0], key); } } memcpy(prev_report, report, 8); queue_Data_Transfer(datapipe, report, 8, this); processing_new_data_ = false; // See if we have any outstanding leds to update if (update_leds_) { updateLEDS(); } } void KeyboardController::numLock(bool f) { if (leds_.numLock != f) { leds_.numLock = f; updateLEDS(); } } void KeyboardController::capsLock(bool f) { if (leds_.capsLock != f) { leds_.capsLock = f; updateLEDS(); } } void KeyboardController::scrollLock(bool f) { if (leds_.scrollLock != f) { leds_.scrollLock = f; updateLEDS(); } } void KeyboardController::key_press(uint32_t mod, uint32_t key) { // TODO: queue events, perform callback from Task println(" press, key=", key); modifiers = mod; keyOEM = key; keyCode = convert_to_unicode(mod, key); println(" unicode = ", keyCode); if (keyPressedFunction) { keyPressedFunction(keyCode); } else { keyPressed(); } } void KeyboardController::key_release(uint32_t mod, uint32_t key) { // TODO: queue events, perform callback from Task println(" release, key=", key); modifiers = mod; keyOEM = key; // Look for modifier keys if (key == M(KEY_NUM_LOCK)) { numLock(!leds_.numLock); // Lets toggle Numlock } else if (key == M(KEY_CAPS_LOCK)) { capsLock(!leds_.capsLock); } else if (key == M(KEY_SCROLL_LOCK)) { scrollLock(!leds_.scrollLock); } else { keyCode = convert_to_unicode(mod, key); if (keyReleasedFunction) { keyReleasedFunction(keyCode); } else { keyReleased(); } } } uint16_t KeyboardController::convert_to_unicode(uint32_t mod, uint32_t key) { // WIP: special keys // TODO: dead key sequences if (key & SHIFT_MASK) { // Many of these keys will look like they are other keys with shift mask... // Check for any of our mapped extra keys for (uint8_t i = 0; i < (sizeof(keycode_numlock)/sizeof(keycode_numlock[0])); i++) { if (keycode_numlock[i].code == key) { // See if the user is using numlock or not... if (leds_.numLock) { return keycode_numlock[i].charNumlockOn; } else { key = keycode_numlock[i].codeNumlockOff; if (!(key & 0x80)) return key; // we have hard coded value key &= 0x7f; // mask off the extra and break out to process as other characters... break; } } } } // Check for any of our mapped extra keys - Done early as some of these keys are // above and some below the SHIFT_MASK value for (uint8_t i = 0; i < (sizeof(keycode_extras)/sizeof(keycode_extras[0])); i++) { if (keycode_extras[i].code == key) { return keycode_extras[i].ascii; } } // If we made it here without doing something then return 0; if (key & SHIFT_MASK) return 0; if ((mod & 0x02) || (mod & 0x20)) key |= SHIFT_MASK; if (leds_.capsLock) key ^= SHIFT_MASK; // Caps lock will switch the Shift; for (int i=0; i < 96; i++) { if (keycodes_ascii[i] == key) { if ((mod & 1) || (mod & 0x10)) return (i+32) & 0x1f; // Control key is down return i + 32; } } #ifdef ISO_8859_1_A0 for (int i=0; i < 96; i++) { if (keycodes_iso_8859_1[i] == key) return i + 160; } #endif return 0; } void KeyboardController::LEDS(uint8_t leds) { println("Keyboard setLEDS ", leds, HEX); leds_.byte = leds; updateLEDS(); } void KeyboardController::updateLEDS() { println("KBD: Update LEDS", leds_.byte, HEX); if (processing_new_data_) { println(" Update defered"); update_leds_ = true; return; // defer until later } // Now lets tell keyboard new state. static uint8_t keyboard_keys_report[1] = {0}; setup_t keys_setup; keyboard_keys_report[0] = leds_.byte; queue_Data_Transfer(datapipe, report, 8, this); mk_setup(keys_setup, 0x21, 9, 0x200, 0, sizeof(keyboard_keys_report)); // hopefully this sets leds queue_Control_Transfer(device, &keys_setup, keyboard_keys_report, this); update_leds_ = false; }