├── .gitignore ├── Makefile ├── README.md ├── LICENSE ├── keymap.h └── usb-next.ino /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ./build: 2 | mkdir -p ./build 3 | 4 | ./build/usb-next.ino.hex: usb-next.ino keymap.h ./build 5 | arduino-cli compile --fqbn arduino:avr:micro --output-dir ./build 6 | 7 | .PHONY: upload 8 | upload: ./build/usb-next.ino.hex 9 | arduino-cli upload -p /dev/$(shell readlink /dev/arduino) --fqbn arduino:avr:micro --verbose --input-dir ./build 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # usb-next # 2 | 3 | This is an implementation of an adapter to let you run a NeXT keyboard over USB. 4 | 5 | A great deal of credit goes to Limor Fried, who [wrote a 6 | tutorial](https://learn.adafruit.com/usb-next-keyboard-with-arduino-micro/code) 7 | that I used heavily in my early versions of this code. Parts of that code are 8 | reused here under the BSD license. 9 | 10 | I used this as a way to learn Arduino, and embedded software work, and hardware 11 | reverse engineering. I don't think this code is especially well-structured or 12 | thought out! 13 | 14 | ## Compiling and flashing 15 | 16 | Requires `arduino-cli`. Use `make upload` to upload to an Arduino Nano. 17 | 18 | Looks for the arduino at `/dev/arduino`, which assumes that udev has a rule 19 | like this: 20 | 21 | ``` 22 | SUBSYSTEM=="tty", ACTION=="add", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="8037", SYMLINK+="arduino" 23 | ``` 24 | 25 | (that's in my /etc/udev/rules.d/10-local.rules file). 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Spencer Nelson 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /keymap.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef unsigned short keycode; 4 | 5 | /* 6 | Note to self (2021-11-19) 7 | 8 | These are USB HID scancodes. But the Arduino Keyboard library expects literal 9 | ascii characters to be sent. So everything here is wrong. 10 | 11 | The mapping table is correct, at least in the sense that I have entered all 12 | the values from the NeXT keyboard. But the defined constants that are in the 13 | big table are incorrect, at least for arduino's Keyboard library. 14 | 15 | One option is to try to get a lower-level library that allows sending the USB 16 | scancodes. Another option is to change the table to map NeXT scancodes to 17 | ASCII. The latter is easier, but it would unfortunately mean we need to drop 18 | support for the volume keys, and would make numpad no different than the top 19 | row, and so on. 20 | 21 | */ 22 | 23 | // Modifiers 24 | #define KEY_LEFTCTRL 1<<0 // Keyboard Left Control 25 | #define KEY_LEFTSHIFT 1<<1 // Keyboard Left Shift 26 | #define KEY_LEFTALT 1<<2 // Keyboard Left Alt 27 | #define KEY_LEFTMETA 1<<3 // Keyboard Left GUI 28 | #define KEY_RIGHTCTRL 1<<4 // Keyboard Right Control 29 | #define KEY_RIGHTSHIFT 1<<5 // Keyboard Right Shift 30 | #define KEY_RIGHTALT 1<<6 // Keyboard Right Alt 31 | #define KEY_RIGHTMETA 1<<7 // Keyboard Right GUI 32 | 33 | #define KEY_NONE 0x00 // No key pressed 34 | #define KEY_ERR_OVF 0x01 // Keyboard Error Roll Over - used for all slots if too many keys are pressed ("Phantom key") 35 | // 0x02 // Keyboard POST Fail 36 | // 0x03 // Keyboard Error Undefined 37 | #define KEY_A 0x04 // Keyboard a and A 38 | #define KEY_B 0x05 // Keyboard b and B 39 | #define KEY_C 0x06 // Keyboard c and C 40 | #define KEY_D 0x07 // Keyboard d and D 41 | #define KEY_E 0x08 // Keyboard e and E 42 | #define KEY_F 0x09 // Keyboard f and F 43 | #define KEY_G 0x0a // Keyboard g and G 44 | #define KEY_H 0x0b // Keyboard h and H 45 | #define KEY_I 0x0c // Keyboard i and I 46 | #define KEY_J 0x0d // Keyboard j and J 47 | #define KEY_K 0x0e // Keyboard k and K 48 | #define KEY_L 0x0f // Keyboard l and L 49 | #define KEY_M 0x10 // Keyboard m and M 50 | #define KEY_N 0x11 // Keyboard n and N 51 | #define KEY_O 0x12 // Keyboard o and O 52 | #define KEY_P 0x13 // Keyboard p and P 53 | #define KEY_Q 0x14 // Keyboard q and Q 54 | #define KEY_R 0x15 // Keyboard r and R 55 | #define KEY_S 0x16 // Keyboard s and S 56 | #define KEY_T 0x17 // Keyboard t and T 57 | #define KEY_U 0x18 // Keyboard u and U 58 | #define KEY_V 0x19 // Keyboard v and V 59 | #define KEY_W 0x1a // Keyboard w and W 60 | #define KEY_X 0x1b // Keyboard x and X 61 | #define KEY_Y 0x1c // Keyboard y and Y 62 | #define KEY_Z 0x1d // Keyboard z and Z 63 | 64 | #define KEY_1 0x1e // Keyboard 1 and ! 65 | #define KEY_2 0x1f // Keyboard 2 and @ 66 | #define KEY_3 0x20 // Keyboard 3 and # 67 | #define KEY_4 0x21 // Keyboard 4 and $ 68 | #define KEY_5 0x22 // Keyboard 5 and % 69 | #define KEY_6 0x23 // Keyboard 6 and ^ 70 | #define KEY_7 0x24 // Keyboard 7 and & 71 | #define KEY_8 0x25 // Keyboard 8 and * 72 | #define KEY_9 0x26 // Keyboard 9 and ( 73 | #define KEY_0 0x27 // Keyboard 0 and ) 74 | 75 | #define KEY_ENTER 0x28 // Keyboard Return (ENTER) 76 | #define KEY_ESC 0x29 // Keyboard ESCAPE 77 | #define KEY_BACKSPACE 0x2a // Keyboard DELETE (Backspace) 78 | #define KEY_TAB 0x2b // Keyboard Tab 79 | #define KEY_SPACE 0x2c // Keyboard Spacebar 80 | #define KEY_MINUS 0x2d // Keyboard - and _ 81 | #define KEY_EQUAL 0x2e // Keyboard = and + 82 | #define KEY_LEFTBRACE 0x2f // Keyboard [ and { 83 | #define KEY_RIGHTBRACE 0x30 // Keyboard ] and } 84 | #define KEY_BACKSLASH 0x31 // Keyboard \ and | 85 | #define KEY_HASHTILDE 0x32 // Keyboard Non-US # and ~ 86 | #define KEY_SEMICOLON 0x33 // Keyboard ; and : 87 | #define KEY_APOSTROPHE 0x34 // Keyboard ' and " 88 | #define KEY_GRAVE 0x35 // Keyboard ` and ~ 89 | #define KEY_COMMA 0x36 // Keyboard , and < 90 | #define KEY_DOT 0x37 // Keyboard . and > 91 | #define KEY_SLASH 0x38 // Keyboard / and ? 92 | #define KEY_CAPSLOCK 0x39 // Keyboard Caps Lock 93 | 94 | #define KEY_F1 0x3a // Keyboard F1 95 | #define KEY_F2 0x3b // Keyboard F2 96 | #define KEY_F3 0x3c // Keyboard F3 97 | #define KEY_F4 0x3d // Keyboard F4 98 | #define KEY_F5 0x3e // Keyboard F5 99 | #define KEY_F6 0x3f // Keyboard F6 100 | #define KEY_F7 0x40 // Keyboard F7 101 | #define KEY_F8 0x41 // Keyboard F8 102 | #define KEY_F9 0x42 // Keyboard F9 103 | #define KEY_F10 0x43 // Keyboard F10 104 | #define KEY_F11 0x44 // Keyboard F11 105 | #define KEY_F12 0x45 // Keyboard F12 106 | 107 | #define KEY_SYSRQ 0x46 // Keyboard Print Screen 108 | #define KEY_SCROLLLOCK 0x47 // Keyboard Scroll Lock 109 | #define KEY_PAUSE 0x48 // Keyboard Pause 110 | #define KEY_INSERT 0x49 // Keyboard Insert 111 | #define KEY_HOME 0x4a // Keyboard Home 112 | #define KEY_PAGEUP 0x4b // Keyboard Page Up 113 | #define KEY_DELETE 0x4c // Keyboard Delete Forward 114 | #define KEY_END 0x4d // Keyboard End 115 | #define KEY_PAGEDOWN 0x4e // Keyboard Page Down 116 | #define KEY_RIGHT 0x4f // Keyboard Right Arrow 117 | #define KEY_LEFT 0x50 // Keyboard Left Arrow 118 | #define KEY_DOWN 0x51 // Keyboard Down Arrow 119 | #define KEY_UP 0x52 // Keyboard Up Arrow 120 | 121 | #define KEY_NUMLOCK 0x53 // Keyboard Num Lock and Clear 122 | #define KEY_KPSLASH 0x54 // Keypad / 123 | #define KEY_KPASTERISK 0x55 // Keypad * 124 | #define KEY_KPMINUS 0x56 // Keypad - 125 | #define KEY_KPPLUS 0x57 // Keypad + 126 | #define KEY_KPENTER 0x58 // Keypad ENTER 127 | #define KEY_KP1 0x59 // Keypad 1 and End 128 | #define KEY_KP2 0x5a // Keypad 2 and Down Arrow 129 | #define KEY_KP3 0x5b // Keypad 3 and PageDn 130 | #define KEY_KP4 0x5c // Keypad 4 and Left Arrow 131 | #define KEY_KP5 0x5d // Keypad 5 132 | #define KEY_KP6 0x5e // Keypad 6 and Right Arrow 133 | #define KEY_KP7 0x5f // Keypad 7 and Home 134 | #define KEY_KP8 0x60 // Keypad 8 and Up Arrow 135 | #define KEY_KP9 0x61 // Keypad 9 and Page Up 136 | #define KEY_KP0 0x62 // Keypad 0 and Insert 137 | #define KEY_KPDOT 0x63 // Keypad . and Delete 138 | 139 | #define KEY_102ND 0x64 // Keyboard Non-US \ and | 140 | #define KEY_COMPOSE 0x65 // Keyboard Application 141 | #define KEY_POWER 0x66 // Keyboard Power 142 | #define KEY_KPEQUAL 0x67 // Keypad = 143 | 144 | #define KEY_F13 0x68 // Keyboard F13 145 | #define KEY_F14 0x69 // Keyboard F14 146 | #define KEY_F15 0x6a // Keyboard F15 147 | #define KEY_F16 0x6b // Keyboard F16 148 | #define KEY_F17 0x6c // Keyboard F17 149 | #define KEY_F18 0x6d // Keyboard F18 150 | #define KEY_F19 0x6e // Keyboard F19 151 | #define KEY_F20 0x6f // Keyboard F20 152 | #define KEY_F21 0x70 // Keyboard F21 153 | #define KEY_F22 0x71 // Keyboard F22 154 | #define KEY_F23 0x72 // Keyboard F23 155 | #define KEY_F24 0x73 // Keyboard F24 156 | 157 | #define KEY_OPEN 0x74 // Keyboard Execute 158 | #define KEY_HELP 0x75 // Keyboard Help 159 | #define KEY_PROPS 0x76 // Keyboard Menu 160 | #define KEY_FRONT 0x77 // Keyboard Select 161 | #define KEY_STOP 0x78 // Keyboard Stop 162 | #define KEY_AGAIN 0x79 // Keyboard Again 163 | #define KEY_UNDO 0x7a // Keyboard Undo 164 | #define KEY_CUT 0x7b // Keyboard Cut 165 | #define KEY_COPY 0x7c // Keyboard Copy 166 | #define KEY_PASTE 0x7d // Keyboard Paste 167 | #define KEY_FIND 0x7e // Keyboard Find 168 | #define KEY_MUTE 0x7f // Keyboard Mute 169 | #define KEY_VOLUMEUP 0x80 // Keyboard Volume Up 170 | #define KEY_VOLUMEDOWN 0x81 // Keyboard Volume Down 171 | 172 | // Indexed by the NeXT scancode, values are USB HID scancodes. 173 | static const uint8_t keymap[0x80] = 174 | { 175 | KEY_NONE, // 00 176 | KEY_F5, // 01 177 | KEY_NONE, // 02 178 | KEY_BACKSLASH, // 03 179 | KEY_RIGHTBRACE, // 04 180 | KEY_LEFTBRACE, // 05 181 | KEY_I, // 06 182 | KEY_O, // 07 183 | KEY_P, // 08 184 | KEY_LEFT, // 09 185 | KEY_NONE, // 0A 186 | KEY_KP0, // 0B 187 | KEY_KPDOT, // 0C 188 | KEY_KPENTER, // 0D 189 | KEY_NONE, // 0E 190 | KEY_DOWN, // 0F 191 | KEY_RIGHT, // 10 192 | KEY_KP1, // 11 193 | KEY_KP4, // 12 194 | KEY_KP6, // 13 195 | KEY_KP3, // 14 196 | KEY_KPPLUS, // 15 197 | KEY_UP, // 16 198 | KEY_KP2, // 17 199 | KEY_KP5, // 18 200 | KEY_F6, // 19 201 | KEY_VOLUMEUP, // 1A 202 | KEY_BACKSPACE, // 1B 203 | KEY_EQUAL, // 1C 204 | KEY_MINUS, // 1D 205 | KEY_8, // 1E 206 | KEY_9, // 1F 207 | KEY_0, // 20 208 | KEY_KP7, // 21 209 | KEY_KP8, // 22 210 | KEY_KP9, // 23 211 | KEY_KPMINUS, // 24 212 | KEY_KPASTERISK, // 25 213 | KEY_GRAVE, // 26 214 | KEY_KPEQUAL, // 27 215 | KEY_KPSLASH, // 28 216 | KEY_NONE, // 29 217 | KEY_ENTER, // 2A 218 | KEY_APOSTROPHE, // 2B 219 | KEY_SEMICOLON, // 2C 220 | KEY_L, // 2D 221 | KEY_COMMA, // 2E 222 | KEY_DOT, // 2F 223 | KEY_SLASH, // 30 224 | KEY_Z, // 31 225 | KEY_X, // 32 226 | KEY_C, // 33 227 | KEY_V, // 34 228 | KEY_B, // 35 229 | KEY_M, // 36 230 | KEY_N, // 37 231 | KEY_SPACE, // 38 232 | KEY_A, // 39 233 | KEY_S, // 3A 234 | KEY_D, // 3B 235 | KEY_F, // 3C 236 | KEY_G, // 3D 237 | KEY_K, // 3E 238 | KEY_J, // 3F 239 | KEY_H, // 40 240 | KEY_TAB, // 41 241 | KEY_Q, // 42 242 | KEY_W, // 43 243 | KEY_E, // 44 244 | KEY_R, // 45 245 | KEY_U, // 46 246 | KEY_Y, // 47 247 | KEY_T, // 48 248 | KEY_ESC, // 49 249 | KEY_1, // 4A 250 | KEY_2, // 4B 251 | KEY_3, // 4C 252 | KEY_4, // 4D 253 | KEY_7, // 4E 254 | KEY_6, // 4F 255 | KEY_5, // 50 256 | KEY_NONE, // 51 257 | KEY_NONE, // 52 258 | KEY_NONE, // 53 259 | KEY_NONE, // 54 260 | KEY_NONE, // 55 261 | KEY_NONE, // 56 262 | KEY_NONE, // 57 263 | KEY_NONE, // 58 264 | KEY_NONE, // 59 265 | KEY_NONE, // 5A 266 | KEY_NONE, // 5B 267 | KEY_NONE, // 5C 268 | KEY_NONE, // 5D 269 | KEY_NONE, // 5E 270 | KEY_NONE, // 5F 271 | KEY_NONE, // 60 272 | KEY_NONE, // 61 273 | KEY_NONE, // 62 274 | KEY_NONE, // 63 275 | KEY_NONE, // 64 276 | KEY_NONE, // 65 277 | KEY_NONE, // 66 278 | KEY_NONE, // 67 279 | KEY_NONE, // 68 280 | KEY_NONE, // 69 281 | KEY_NONE, // 6A 282 | KEY_NONE, // 6B 283 | KEY_NONE, // 6C 284 | KEY_NONE, // 6D 285 | KEY_NONE, // 6E 286 | KEY_NONE, // 6F 287 | KEY_NONE, // 70 288 | KEY_NONE, // 71 289 | KEY_NONE, // 72 290 | KEY_NONE, // 73 291 | KEY_NONE, // 74 292 | KEY_NONE, // 75 293 | KEY_NONE, // 76 294 | KEY_NONE, // 77 295 | KEY_NONE, // 78 296 | KEY_NONE, // 79 297 | KEY_NONE, // 7A 298 | KEY_NONE, // 7B 299 | KEY_NONE, // 7C 300 | KEY_NONE, // 7D 301 | KEY_NONE, // 7E 302 | KEY_NONE, // 7F 303 | }; 304 | -------------------------------------------------------------------------------- /usb-next.ino: -------------------------------------------------------------------------------- 1 | #define PAUSE_DURATION 100 // Time, in microseconds, between KB queries. 2 | #define USB_SEND_INTERVAL 1000 // microseconds 3 | //#define DEBUG 4 | #undef DEBUG 5 | #define KB_ENABLE 6 | 7 | #include 8 | #include "keymap.h" 9 | 10 | #ifdef DEBUG 11 | #define PAUSE_DURATION 100 12 | #define debug(MSG) Serial.print(MSG) 13 | #define debugf(MSG, MODE) Serial.print(MSG, MODE) 14 | #define debugln(MSG) Serial.println(MSG) 15 | #else 16 | #define debug(MSG) 17 | #define debugf(MSG, MODE) 18 | #define debugln(MSG) 19 | #endif 20 | 21 | #define OUT_PIN 3 22 | #define IN_PIN 2 23 | #define SAMPLE_INDICATOR_PIN 5 24 | #define LED_PIN 13 25 | 26 | #define SET_REGISTER_1(REGISTER, POSITION) REGISTER |= (1 << POSITION) 27 | #define SET_REGISTER_0(REGISTER, POSITION) REGISTER &= ~(1 << POSITION) 28 | 29 | // Macros and caches for reading data from the input pin 30 | volatile uint8_t *input_pin_reg; 31 | uint8_t input_pin_mask; 32 | #define readbit() (PIND & 0b00000010) 33 | 34 | volatile uint8_t *sample_pin_reg; 35 | uint8_t sample_pin_mask; 36 | #define enable_sample_pin() PORTC |= 0b01000000 37 | #define disable_sample_pin() PORTC &= 0b10111111 38 | 39 | // Sampling math 40 | #define CLOCK_FREQ 16000000 41 | // discovered with a logical analyzer: a 455 kHz clock powers signals that last 42 | // for 24 clock cycles. 43 | #define KB_DATA_FREQ (455000 / 24) // 18958 44 | #define PULSE_WIDTH (1000000 / KB_DATA_FREQ) // 52 45 | #define PULSE_WIDTH_NANOS (1000000000 / KB_DATA_FREQ) // 52748 46 | 47 | #define KB_SAMPLE_COMP (CLOCK_FREQ / KB_DATA_FREQ) // 843 48 | #define SAMPLE_INTERVAL_NANOS (1000000000 / CLOCK_FREQ) * KB_SAMPLE_COMP // 52687 49 | #define ERROR_PER_SAMPLE (PULSE_WIDTH_NANOS - SAMPLE_INTERVAL_NANOS) 50 | #define PULSE_SETTLE_TIME 5 51 | 52 | 53 | #define RESPONSE_SIZE 20 54 | 55 | const uint16_t t1_load = 0; 56 | const uint16_t t1_comp = KB_SAMPLE_COMP; 57 | 58 | enum state{ 59 | ready, 60 | query_inflight, 61 | query_sent, 62 | query_response_timeout, 63 | query_response_ready, 64 | query_response_complete, 65 | pause, 66 | }; 67 | 68 | // Global state 69 | volatile enum state currentState = ready; 70 | volatile uint8_t currentKeycode = 0; 71 | volatile uint8_t currentModifier = 0; 72 | volatile uint32_t currentData = 0; 73 | 74 | volatile uint8_t currentResponseIndex = 0; 75 | 76 | /////// 77 | // Protocol 78 | /////// 79 | 80 | #define IDLE_SIGNAL 0x180600 81 | 82 | struct KeyCommand { 83 | uint8_t keycode; 84 | uint8_t modifiers; 85 | bool pressed; // vs released 86 | }; 87 | 88 | struct KeyCommand parseCurrentData() { 89 | struct KeyCommand cmd; 90 | cmd.keycode = (currentData >> 1) & 0xFF; 91 | cmd.modifiers = (currentData >> 11) & 0xFF; 92 | cmd.pressed = !(cmd.keycode & 0x80); 93 | cmd.keycode = cmd.keycode & 0x7F; 94 | return cmd; 95 | } 96 | 97 | void sendKBQuery() { 98 | digitalWrite(OUT_PIN, LOW); 99 | delayMicroseconds(PULSE_WIDTH *5); 100 | digitalWrite(OUT_PIN, HIGH); 101 | delayMicroseconds(PULSE_WIDTH ); 102 | digitalWrite(OUT_PIN, LOW); 103 | delayMicroseconds(PULSE_WIDTH *3); 104 | digitalWrite(OUT_PIN, HIGH); 105 | } 106 | 107 | void sendKBReset() { 108 | // reset the keyboard 109 | digitalWrite(OUT_PIN, LOW); 110 | delayMicroseconds(PULSE_WIDTH); 111 | digitalWrite(OUT_PIN, HIGH); 112 | delayMicroseconds(PULSE_WIDTH*4); 113 | digitalWrite(OUT_PIN, LOW); 114 | delayMicroseconds(PULSE_WIDTH); 115 | digitalWrite(OUT_PIN, HIGH); 116 | delayMicroseconds(PULSE_WIDTH*6); 117 | digitalWrite(OUT_PIN, LOW); 118 | delayMicroseconds(PULSE_WIDTH*10); 119 | digitalWrite(OUT_PIN, HIGH); 120 | } 121 | // 122 | 123 | ///////// 124 | /// State transition handlers 125 | //////// 126 | void handleReady() { 127 | // Disable interrupts while we configure things 128 | cli(); 129 | 130 | // Set up interrupt for when we get a response back. 131 | enableResponseInterrupt(); 132 | // Set up interrupt for when we timeout. 133 | enableTimeoutInterrupt(); 134 | 135 | // Proceed to next state. 136 | currentState = query_sent; 137 | // Turn interrupts back on 138 | sei(); 139 | // Send the query. 140 | sendKBQuery(); 141 | } 142 | 143 | ///// 144 | // Start-of-response interrupt 145 | ///// 146 | ISR(INT1_vect) { 147 | // Triggered when we get the falling edge of Pin INT1. 148 | // Turn off the response interrupt. 149 | disableResponseInterrupt(); 150 | 151 | // We can turn off the timeout, since we have data. 152 | disableTimeoutInterrupt(); 153 | 154 | delayMicroseconds(PULSE_SETTLE_TIME); 155 | // Turn on the ticker for reading data. 156 | enableReaderInterrupt(); 157 | 158 | // Mark us as reading data. 159 | currentState = query_response_ready; 160 | } 161 | 162 | void enableResponseInterrupt() { 163 | // Enable interrupts for INT1. 164 | SET_REGISTER_1(EIMSK, INT1); 165 | } 166 | 167 | void configureResponseInterrupt() { 168 | // Enable interrupts on a low level of external interrupt pin 1 169 | SET_REGISTER_0(EICRA, ISC10); 170 | SET_REGISTER_0(EICRA, ISC11); 171 | // Clear any existing interrupts for pin 1 172 | SET_REGISTER_1(EIFR, INTF1); 173 | 174 | debug("EICRA: 0x"); 175 | debugf(EICRA, HEX); 176 | debug(" EIFR: 0x"); 177 | debugf(EIFR, HEX); 178 | debugln(); 179 | } 180 | 181 | void disableResponseInterrupt() { 182 | // Enable the interrupt 183 | SET_REGISTER_0(EIMSK, INT1); 184 | // Clear any interrupts that have happened 185 | SET_REGISTER_1(EIFR, INTF1); 186 | } 187 | 188 | ///// 189 | // Timeout interrupt (TODO) 190 | ///// 191 | void enableTimeoutInterrupt() {} 192 | void configureTimeoutInterrupt() {} 193 | void disableTimeoutInterrupt() {} 194 | 195 | ///// 196 | // Reader interrupt 197 | ///// 198 | ISR(TIMER1_COMPA_vect) { 199 | TCNT1 = 0; 200 | 201 | enable_sample_pin(); 202 | if (readbit()) { 203 | currentData |= ((uint32_t)1 << currentResponseIndex); 204 | } 205 | disable_sample_pin(); 206 | 207 | currentResponseIndex++; 208 | if (currentResponseIndex > RESPONSE_SIZE) { 209 | // Done reading. 210 | currentResponseIndex = 0; 211 | disableReaderInterrupt(); 212 | currentState = query_response_complete; 213 | } 214 | } 215 | 216 | void enableReaderInterrupt() { 217 | // Reset to zero 218 | TCNT1 = 0; 219 | // Enable register A comparison interrupts 220 | SET_REGISTER_1(TIMSK1, OCIE1A); 221 | // Clear any existing interrupts for pin 1 222 | SET_REGISTER_1(EIFR, INTF1); 223 | } 224 | 225 | void configureReaderInterrupt() { 226 | // Set timer to defaults in case arduino has done anything funky 227 | TCCR1A = 0; 228 | 229 | // No prescaling 230 | TCCR1B &= ~(1 << CS12); 231 | TCCR1B &= ~(1 << CS11); 232 | TCCR1B |= (1 << CS10); 233 | 234 | // Set the trigger threshold 235 | OCR1A = t1_comp; 236 | } 237 | 238 | void disableReaderInterrupt() { 239 | // Disable interrupts 240 | TIMSK1 &= ~(1 << OCIE1A); 241 | // Clear any active interrupts 242 | } 243 | 244 | 245 | void handleQueryResponseTimeout() { 246 | // Send a reset 247 | sendKBReset(); 248 | currentState = pause; 249 | } 250 | 251 | void handlePause() { 252 | delayMicroseconds(PAUSE_DURATION); 253 | currentState = ready; 254 | } 255 | 256 | void handleQueryResponse() { 257 | // Send current key over USB. 258 | debug("current data: "); 259 | if (currentData == IDLE_SIGNAL) { 260 | debugln("idle"); 261 | } else { 262 | struct KeyCommand cmd = parseCurrentData(); 263 | debug("mod: "); debugf(cmd.modifiers, HEX); debugln(); 264 | debug("key: "); debugf(cmd.keycode, HEX); debugln(); 265 | debug("prs: "); debug(cmd.pressed); debugln(); 266 | 267 | if (cmd.pressed) { 268 | pressKey(cmd.keycode, cmd.modifiers); 269 | } else { 270 | releaseKey(cmd.keycode, cmd.modifiers); 271 | } 272 | } 273 | 274 | currentData = 0; 275 | currentState = pause; 276 | } 277 | 278 | volatile KeyReport report = {0}; 279 | volatile uint32_t last_send_time = micros(); 280 | 281 | void pressKey(uint8_t keycode, uint8_t modifier) { 282 | #ifdef KB_ENABLE 283 | report.modifiers = mapModifiers(modifier); 284 | uint8_t mapped_code = keymap[keycode]; 285 | if (mapped_code != 0) { 286 | // Only set if it's not present 287 | if (report.keys[0] != mapped_code && report.keys[1] != mapped_code && 288 | report.keys[2] != mapped_code && report.keys[3] != mapped_code && 289 | report.keys[4] != mapped_code && report.keys[5] != mapped_code) { 290 | for (int i = 0; i < 6; i ++) { 291 | if (report.keys[i] == 0) { 292 | report.keys[i] = mapped_code; 293 | break; 294 | } 295 | } 296 | } 297 | } 298 | sendUSBReport(); 299 | #endif 300 | 301 | debug("press key code="); 302 | debugf(keycode, HEX); 303 | debug(" mod="); 304 | debugf(modifier, HEX); 305 | debug(" mapped-code="); 306 | debugf(mapped_code, HEX); 307 | debugln(); 308 | } 309 | 310 | void releaseKey(uint8_t keycode, uint8_t modifier) { 311 | #ifdef KB_ENABLE 312 | uint8_t mapped_code = keymap[keycode]; 313 | if (mapped_code != 0) { 314 | for (int i = 0; i < 6; i ++) { 315 | if (report.keys[i] == mapped_code) { 316 | report.keys[i] = 0; 317 | break; 318 | } 319 | } 320 | } 321 | if (modifier != 0) { 322 | report.modifiers = mapModifiers(modifier); 323 | } 324 | sendUSBReport(); 325 | #endif 326 | 327 | debug("release key code="); 328 | debugf(keycode, HEX); 329 | debug(" mod="); 330 | debugf(modifier, HEX); 331 | debug(" mapped-code="); 332 | debugf(mapped_code, HEX); 333 | debugln(); 334 | } 335 | 336 | void sendUSBReport() { 337 | uint32_t now = micros(); 338 | if ((now - last_send_time) < USB_SEND_INTERVAL) { 339 | return; 340 | } 341 | HID().SendReport(2, &report, sizeof(KeyReport)); 342 | debug("elapsed: "); debugf(now - last_send_time, DEC); 343 | last_send_time = now; 344 | 345 | debug(" report[0]="); debugf(report.keys[0], HEX); 346 | debug(" report[1]="); debugf(report.keys[1], HEX); 347 | debug(" report[2]="); debugf(report.keys[2], HEX); 348 | debug(" report[3]="); debugf(report.keys[3], HEX); 349 | debug(" report[4]="); debugf(report.keys[4], HEX); 350 | debug(" report[5]="); debugf(report.keys[5], HEX); 351 | debugln(); 352 | } 353 | 354 | uint8_t mapModifiers(uint8_t nextMod) { 355 | uint8_t result = 0; 356 | if (nextMod & 0x2) { 357 | result |= KEY_LEFTCTRL; 358 | } 359 | if (nextMod & 0x4) { 360 | result |= KEY_LEFTSHIFT; 361 | } 362 | if (nextMod & 0x8) { 363 | result |= KEY_RIGHTSHIFT; 364 | } 365 | if (nextMod & 0x10) { 366 | result |= KEY_LEFTMETA; 367 | } 368 | if (nextMod & 0x20) { 369 | result |= KEY_RIGHTMETA; 370 | } 371 | if (nextMod & 0x40) { 372 | result |= KEY_LEFTALT; 373 | } 374 | return result; 375 | } 376 | 377 | void loop() { 378 | switch (currentState) { 379 | case ready: 380 | handleReady(); 381 | break; 382 | case query_sent: 383 | // No-op. Waiting for a timeout or ready state. 384 | break; 385 | case query_response_timeout: 386 | debugln("timeout"); 387 | handleQueryResponseTimeout(); 388 | break; 389 | case query_response_ready: 390 | // No-op. Wait for response complete. 391 | break; 392 | case query_response_complete: 393 | handleQueryResponse(); 394 | break; 395 | case pause: 396 | handlePause(); 397 | break; 398 | } 399 | } 400 | void setup() { 401 | pinMode(OUT_PIN, OUTPUT); 402 | pinMode(SAMPLE_INDICATOR_PIN, OUTPUT); 403 | pinMode(LED_PIN, OUTPUT); 404 | pinMode(IN_PIN, INPUT); 405 | 406 | digitalWrite(LED_PIN, HIGH); 407 | input_pin_mask = digitalPinToBitMask(IN_PIN); 408 | 409 | sample_pin_reg = portInputRegister(digitalPinToPort(SAMPLE_INDICATOR_PIN)); 410 | sample_pin_mask = digitalPinToBitMask(SAMPLE_INDICATOR_PIN); 411 | 412 | int start = millis(); 413 | while ((!Serial) & (millis() - start < 2000)) { 414 | Serial.begin(57600); 415 | } 416 | digitalWrite(LED_PIN, LOW); 417 | 418 | configureResponseInterrupt(); 419 | configureReaderInterrupt(); 420 | 421 | debugln("--== NeXT Keyboard Initialization ==--"); 422 | debug("Pulse width: "); debugln(PULSE_WIDTH); 423 | debug("Sample Frequency: "); debugln(KB_SAMPLE_COMP); 424 | 425 | delay(200); 426 | sendKBQuery(); 427 | delay(5); 428 | sendKBReset(); 429 | delay(8); 430 | sendKBQuery(); 431 | delay(5); 432 | sendKBReset(); 433 | delay(8); 434 | Keyboard.begin(); 435 | debugln("keyboard on!"); 436 | digitalWrite(LED_PIN, LOW); 437 | } 438 | --------------------------------------------------------------------------------