|
| 1 | +/* |
| 2 | + * Based on Obdev's AVRUSB code and under the same license. |
| 3 | + * |
| 4 | + * TODO: Make a proper file header. :-) |
| 5 | + * Modified for Digispark by Digistump |
| 6 | + */ |
| 7 | +#ifndef __DigiKeyboard_h__ |
| 8 | +#define __DigiKeyboard_h__ |
| 9 | + |
| 10 | +#include <Arduino.h> |
| 11 | +#include <avr/pgmspace.h> |
| 12 | +#include <avr/interrupt.h> |
| 13 | +#include <avr/delay.h> |
| 14 | +#include <string.h> |
| 15 | + |
| 16 | +#include "usbdrv.h" |
| 17 | +#include "scancode-ascii-table-be.h" |
| 18 | + |
| 19 | +// TODO: Work around Arduino 12 issues better. |
| 20 | +//#include <WConstants.h> |
| 21 | +//#undef int() |
| 22 | + |
| 23 | +typedef uint8_t byte; |
| 24 | + |
| 25 | + |
| 26 | +#define BUFFER_SIZE 2 // Minimum of 2: 1 for modifiers + 1 for keystroke |
| 27 | + |
| 28 | + |
| 29 | +static uchar idleRate; // in 4 ms units |
| 30 | + |
| 31 | + |
| 32 | +/* We use a simplifed keyboard report descriptor which does not support the |
| 33 | + * boot protocol. We don't allow setting status LEDs and but we do allow |
| 34 | + * simultaneous key presses. |
| 35 | + * The report descriptor has been created with usb.org's "HID Descriptor Tool" |
| 36 | + * which can be downloaded from http://www.usb.org/developers/hidpage/. |
| 37 | + * Redundant entries (such as LOGICAL_MINIMUM and USAGE_PAGE) have been omitted |
| 38 | + * for the second INPUT item. |
| 39 | + */ |
| 40 | +const PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { /* USB report descriptor */ |
| 41 | + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) |
| 42 | + 0x09, 0x06, // USAGE (Keyboard) |
| 43 | + 0xa1, 0x01, // COLLECTION (Application) |
| 44 | + 0x05, 0x07, // USAGE_PAGE (Keyboard) |
| 45 | + 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) |
| 46 | + 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) |
| 47 | + 0x15, 0x00, // LOGICAL_MINIMUM (0) |
| 48 | + 0x25, 0x01, // LOGICAL_MAXIMUM (1) |
| 49 | + 0x75, 0x01, // REPORT_SIZE (1) |
| 50 | + 0x95, 0x08, // REPORT_COUNT (8) |
| 51 | + 0x81, 0x02, // INPUT (Data,Var,Abs) |
| 52 | + 0x95, 0x01, // REPORT_COUNT (simultaneous keystrokes) |
| 53 | + 0x75, 0x08, // REPORT_SIZE (8) |
| 54 | + 0x25, 0x65, // LOGICAL_MAXIMUM (101) |
| 55 | + 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) |
| 56 | + 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) |
| 57 | + 0x81, 0x00, // INPUT (Data,Ary,Abs) |
| 58 | + 0xc0 // END_COLLECTION |
| 59 | +}; |
| 60 | + |
| 61 | + |
| 62 | + |
| 63 | +/* Keyboard usage values, see usb.org's HID-usage-tables document, chapter |
| 64 | + * 10 Keyboard/Keypad Page for more codes. |
| 65 | + */ |
| 66 | +#define MOD_CONTROL_LEFT (1<<0) |
| 67 | +#define MOD_SHIFT_LEFT (1<<1) |
| 68 | +#define MOD_ALT_LEFT (1<<2) |
| 69 | +#define MOD_GUI_LEFT (1<<3) |
| 70 | +#define MOD_CONTROL_RIGHT (1<<4) |
| 71 | +#define MOD_SHIFT_RIGHT (1<<5) |
| 72 | +#define MOD_ALT_RIGHT (1<<6) |
| 73 | +#define MOD_GUI_RIGHT (1<<7) |
| 74 | + |
| 75 | +#define KEY_A 20 |
| 76 | +#define KEY_B 5 |
| 77 | +#define KEY_C 6 |
| 78 | +#define KEY_D 7 |
| 79 | +#define KEY_E 8 |
| 80 | +#define KEY_F 9 |
| 81 | +#define KEY_G 10 |
| 82 | +#define KEY_H 11 |
| 83 | +#define KEY_I 12 |
| 84 | +#define KEY_J 13 |
| 85 | +#define KEY_K 14 |
| 86 | +#define KEY_L 15 |
| 87 | +#define KEY_M 51 |
| 88 | +#define KEY_N 17 |
| 89 | +#define KEY_O 18 |
| 90 | +#define KEY_P 19 |
| 91 | +#define KEY_Q 4 |
| 92 | +#define KEY_R 21 |
| 93 | +#define KEY_S 22 |
| 94 | +#define KEY_T 23 |
| 95 | +#define KEY_U 24 |
| 96 | +#define KEY_V 25 |
| 97 | +#define KEY_W 29 |
| 98 | +#define KEY_X 27 |
| 99 | +#define KEY_Y 28 |
| 100 | +#define KEY_Z 26 |
| 101 | +#define KEY_1 30 |
| 102 | +#define KEY_2 31 |
| 103 | +#define KEY_3 32 |
| 104 | +#define KEY_4 33 |
| 105 | +#define KEY_5 34 |
| 106 | +#define KEY_6 35 |
| 107 | +#define KEY_7 36 |
| 108 | +#define KEY_8 37 |
| 109 | +#define KEY_9 38 |
| 110 | +#define KEY_0 39 |
| 111 | + |
| 112 | +#define KEY_ENTER 40 |
| 113 | + |
| 114 | +#define KEY_SPACE 44 |
| 115 | + |
| 116 | +#define KEY_F1 58 |
| 117 | +#define KEY_F2 59 |
| 118 | +#define KEY_F3 60 |
| 119 | +#define KEY_F4 61 |
| 120 | +#define KEY_F5 62 |
| 121 | +#define KEY_F6 63 |
| 122 | +#define KEY_F7 64 |
| 123 | +#define KEY_F8 65 |
| 124 | +#define KEY_F9 66 |
| 125 | +#define KEY_F10 67 |
| 126 | +#define KEY_F11 68 |
| 127 | +#define KEY_F12 69 |
| 128 | + |
| 129 | +#define KEY_ARROW_LEFT 0x50 |
| 130 | + |
| 131 | + |
| 132 | +class DigiKeyboardDevice : public Print { |
| 133 | + public: |
| 134 | + DigiKeyboardDevice () { |
| 135 | + cli(); |
| 136 | + usbDeviceDisconnect(); |
| 137 | + _delay_ms(250); |
| 138 | + usbDeviceConnect(); |
| 139 | + |
| 140 | + |
| 141 | + usbInit(); |
| 142 | + |
| 143 | + sei(); |
| 144 | + |
| 145 | + // TODO: Remove the next two lines once we fix |
| 146 | + // missing first keystroke bug properly. |
| 147 | + memset(reportBuffer, 0, sizeof(reportBuffer)); |
| 148 | + usbSetInterrupt(reportBuffer, sizeof(reportBuffer)); |
| 149 | + } |
| 150 | + |
| 151 | + void update() { |
| 152 | + usbPoll(); |
| 153 | + } |
| 154 | + |
| 155 | + // delay while updating until we are finished delaying |
| 156 | + void delay(long milli) { |
| 157 | + unsigned long last = millis(); |
| 158 | + while (milli > 0) { |
| 159 | + unsigned long now = millis(); |
| 160 | + milli -= now - last; |
| 161 | + last = now; |
| 162 | + update(); |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | + //sendKeyStroke: sends a key press AND release |
| 167 | + void sendKeyStroke(byte keyStroke) { |
| 168 | + sendKeyStroke(keyStroke, 0); |
| 169 | + } |
| 170 | + |
| 171 | + //sendKeyStroke: sends a key press AND release with modifiers |
| 172 | + void sendKeyStroke(byte keyStroke, byte modifiers) { |
| 173 | + sendKeyPress(keyStroke, modifiers); |
| 174 | + // This stops endlessly repeating keystrokes: |
| 175 | + sendKeyPress(0,0); |
| 176 | + } |
| 177 | + |
| 178 | + //sendKeyPress: sends a key press only - no release |
| 179 | + //to release the key, send again with keyPress=0 |
| 180 | + void sendKeyPress(byte keyPress) { |
| 181 | + sendKeyPress(keyPress, 0); |
| 182 | + } |
| 183 | + |
| 184 | + //sendKeyPress: sends a key press only, with modifiers - no release |
| 185 | + //to release the key, send again with keyPress=0 |
| 186 | + void sendKeyPress(byte keyPress, byte modifiers) { |
| 187 | + while (!usbInterruptIsReady()) { |
| 188 | + // Note: We wait until we can send keyPress |
| 189 | + // so we know the previous keyPress was |
| 190 | + // sent. |
| 191 | + usbPoll(); |
| 192 | + _delay_ms(5); |
| 193 | + } |
| 194 | + |
| 195 | + memset(reportBuffer, 0, sizeof(reportBuffer)); |
| 196 | + |
| 197 | + reportBuffer[0] = modifiers; |
| 198 | + reportBuffer[1] = keyPress; |
| 199 | + |
| 200 | + usbSetInterrupt(reportBuffer, sizeof(reportBuffer)); |
| 201 | + } |
| 202 | + |
| 203 | + size_t write(uint8_t chr) { |
| 204 | + if(chr == '<') |
| 205 | + sendKeyStroke(100); |
| 206 | + else if(chr == '>') |
| 207 | + sendKeyStroke(100, MOD_SHIFT_RIGHT); |
| 208 | + else if(chr == '\\') |
| 209 | + sendKeyStroke(100, MOD_ALT_RIGHT); |
| 210 | + |
| 211 | + else |
| 212 | + { |
| 213 | + uint8_t data = pgm_read_byte_near(ascii_to_scan_code_table + (chr - 8)); |
| 214 | + sendKeyStroke(data & 0b00111111, data >> 7 ? MOD_SHIFT_RIGHT : 0 | (data << 1) >> 7 ? MOD_ALT_RIGHT : 0); |
| 215 | + } |
| 216 | +//#define CONVERTDEADKEYS |
| 217 | +#ifdef CONVERTDEADKEYS |
| 218 | + /* To convert some dead keys to symbols */ |
| 219 | + if(chr == '~' || chr == '`') |
| 220 | + { |
| 221 | + sendKeyStroke(0x2C); //Space |
| 222 | + } |
| 223 | +#endif |
| 224 | + |
| 225 | + return 1; |
| 226 | + } |
| 227 | + |
| 228 | + //private: TODO: Make friend? |
| 229 | + uchar reportBuffer[2]; // buffer for HID reports [ 1 modifier byte + (len-1) key strokes] |
| 230 | + using Print::write; |
| 231 | +}; |
| 232 | + |
| 233 | +DigiKeyboardDevice DigiKeyboard = DigiKeyboardDevice(); |
| 234 | + |
| 235 | +#ifdef __cplusplus |
| 236 | +extern "C"{ |
| 237 | +#endif |
| 238 | + // USB_PUBLIC uchar usbFunctionSetup |
| 239 | + uchar usbFunctionSetup(uchar data[8]) { |
| 240 | + usbRequest_t *rq = (usbRequest_t *)((void *)data); |
| 241 | + |
| 242 | + usbMsgPtr = DigiKeyboard.reportBuffer; // |
| 243 | + if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) { |
| 244 | + /* class request type */ |
| 245 | + |
| 246 | + if (rq->bRequest == USBRQ_HID_GET_REPORT) { |
| 247 | + /* wValue: ReportType (highbyte), ReportID (lowbyte) */ |
| 248 | + |
| 249 | + /* we only have one report type, so don't look at wValue */ |
| 250 | + // TODO: Ensure it's okay not to return anything here? |
| 251 | + return 0; |
| 252 | + |
| 253 | + } else if (rq->bRequest == USBRQ_HID_GET_IDLE) { |
| 254 | + //usbMsgPtr = &idleRate; |
| 255 | + //return 1; |
| 256 | + return 0; |
| 257 | + |
| 258 | + } else if (rq->bRequest == USBRQ_HID_SET_IDLE) { |
| 259 | + idleRate = rq->wValue.bytes[1]; |
| 260 | + |
| 261 | + } |
| 262 | + } else { |
| 263 | + /* no vendor specific requests implemented */ |
| 264 | + } |
| 265 | + |
| 266 | + return 0; |
| 267 | + } |
| 268 | +#ifdef __cplusplus |
| 269 | +} // extern "C" |
| 270 | +#endif |
| 271 | + |
| 272 | + |
| 273 | +#endif // __DigiKeyboard_h__ |
0 commit comments