#include "keyboard.h" #include #include #include #include #include #include #include "usbdrv.h" #include "lib/prgKeyboard.h" static uchar reportBuffer[8] = {0,0,0,0,0,0,0,0}; /* buffer for HID reports */ static uchar idleRate; /* in 4 ms units */ static uchar newReport = 1; /* current report */ uint16_t memAddr = 0; uint8_t cMode = 0; // 0->Keyup 1->Keydown uint8_t oldKey=0; uint8_t hasReleased=1; uint8_t flag_keyPress=0; uint8_t keyAction=0; uint8_t keyType[16]; // Will contain whether the key has to be handled as a ... #define KEYTYPE_TEXT 0 // Text-Button (-> only button presses) or whether we need to pay attention to #define KEYTYPE_KEY 1 // push and release events // MEMORY ARCHITECTURE (INTERNAL) #define MEM_SERIAL_OFFSET 0 #define MEM_SERIAL_LENGTH 16 // MEM. ARCH. (EXTERNAL) #define MEM_KEY_LENGTH 512 // 512B * 16 -> 8KByte (64Kbit) // COMMANDS: #define CMD_PING 0 #define CMD_SWVERSION 1 #define CMD_EEWRITE 2 #define CMD_EEREAD 3 #define CMD_EEOPEN 4 // Can be used if errors occour while writing to the device. #define CMD_RESET 5 // WDT RESET #define CMD_SERIAL 6 // DEV-Serial PRGKBD0010911001 --> This data sits in the INETRNAL eeprom // 6B PRGKBD // 3B HW_REV // 4B HW_Date (MMYY) // 3B ID #define CMD_IEEWRITE 7 //internal eeprom #define CMD_IEEREAD 8 // int. eep. #define CMD_EXEC 9 // execute from addr. #define CMD_INITKEYS 10 // Auto-detect keytypes /*Keys[] 0-133 codes.pdf Page 53 (10. Table 12) ModKeys 224-231 codes.pdf Page 59 (10. Table 12) - - - - - - - - - - - - - - - - - - - - - - - - -*/ #define INSTR_KEYDOWN 254 #define INSTR_KEYUP 253 #define INSTR_KEYPRESS 252 #define INSTR_BREAK 255 #define KEYMODE_UP 1 #define KEYMODE_DOWN 0 #define KEYMODE_PRESS 2 //push and release #define KEYACTION_DOWN 0 #define KEYACTION_UP 1 // Very slow programming? // -> Disable verifying the entered data in CMD_EEWRITE /* Reportbuffer format: 0 Modifier byte 1 reserved 2 keycode array (0) 3 keycode array (1) 4 keycode array (2) 5 keycode array (3) 6 keycode array (4) 7 keycode array (5) << This is the standard usb-keyboard reportbuffer. It allows for 6 simultaneous keypresses to be detected (excl. modifier keys). In this application we only use 1, so the last 5 bytes in this buffer will always remain 0. >> << I decided not to optimize this in order to make it easy to add extra keys that can be pressed simultaneously>> Modifier byte: 8 bits, each individual bit represents one of the modifier keys. bit0 LEFT CTRL (1<<0) bit1 LEFT SHIFT (1<<1) bit2 LEFT ALT (1<<2) bit3 LEFT GUI (1<<3) bit4 RIGHT CTRL (1<<4) bit5 RIGHT SHIFT (1<<5) bit6 RIGHT ALT (1<<6) bit7 RIGHT GUI (1<<7) an example of a reportBuffer for a CTRL+ALT+Delete keypress: {((1<<0)+(1<<2)),0,76,0,0,0,0,0} the first byte holds both the LEFT CTRL and LEFT modifier keys the 3rd byte holds the delete key (== decimal 76) */ /* ------------------------------------------------------------------------- */ // MOD-Key defines #define LEFT_CTRL (1<<0) #define LEFT_SHIFT (1<<1) #define LEFT_ALT (1<<2) #define LEFT_GUI (1<<3) #define RIGHT_CTRL (1<<4) #define RIGHT_SHIFT (1<<5) #define RIGHT_ALT (1<<6) #define RIGHT_GUI (1<<7) const PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { // 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x06, // USAGE (Keyboard) 6 0xa1, 0x01, // COLLECTION (Application) 0x05, 0x07, // USAGE_PAGE (Keyboard) 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x75, 0x01, // REPORT_SIZE (1) 0x95, 0x08, // REPORT_COUNT (8) 0x81, 0x02, // INPUT (Data,Var,Abs) ** Modifier Byte ** 0x95, 0x01, // REPORT_COUNT (1) 0x75, 0x08, // REPORT_SIZE (8) 0x81, 0x03, // INPUT (Cnst,Var,Abs) ** Reserved Byte ** 0x95, 0x05, // REPORT_COUNT (5) 0x75, 0x01, // REPORT_SIZE (1) 0x05, 0x08, // USAGE_PAGE (LEDs) 0x19, 0x01, // USAGE_MINIMUM (Num Lock) 0x29, 0x05, // USAGE_MAXIMUM (Kana) 0x91, 0x02, // OUTPUT (Data,Var,Abs) ** LED Report ** 0x95, 0x01, // REPORT_COUNT (1) 0x75, 0x03, // REPORT_SIZE (3) 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) ** LED Report Padding ** 0x95, 0x06, // REPORT_COUNT (6) ** here we define the maximum number of simultaneous keystrokes we can detect ** 0x75, 0x08, // REPORT_SIZE (8) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x84, // LOGICAL_MAXIMUM (101) 0x05, 0x07, // USAGE_PAGE (Keyboard) 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) 0x29, 0x84, // USAGE_MAXIMUM (Keyboard Application) 0x81, 0x00, // INPUT (Data,Ary,Abs) ** Key arrays (6 bytes) ** 0xc0 // END_COLLECTION }; /* -------------------------------------------------------------------------------- */ /* ------------------------ interface to USB driver ------------------------ */ /* -------------------------------------------------------------------------------- */ uchar usbFunctionSetup(uchar data[8]) { usbRequest_t *rq = (void *)data; static uchar reportBuffer[24]; usbMsgPtr = reportBuffer; if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */ if(rq->bRequest == USBRQ_HID_GET_REPORT){ /* wValue: ReportType (highbyte), ReportID (lowbyte) */ /* we only have one report type, so don't look at wValue */ buildReport(); return sizeof(reportBuffer); }else if(rq->bRequest == USBRQ_HID_GET_IDLE){ usbMsgPtr = &idleRate; return 1; }else if(rq->bRequest == USBRQ_HID_SET_IDLE){ idleRate = rq->wValue.bytes[1]; } }else{ // Here we're gonna add the functions to program the keyboard if(rq->bRequest == CMD_PING) { reportBuffer[0] = rq->wValue.bytes[0]; return 1; } else if(rq->bRequest == CMD_SWVERSION) { reportBuffer[0] = '0'; reportBuffer[1] = '3'; reportBuffer[2] = 'B'; return 3; } else if(rq->bRequest == CMD_EEWRITE) { EEWriteByte(rq->wValue.bytes[0] + (rq->wValue.bytes[1] << 8), rq->wIndex.bytes[0]); //reportBuffer[0] = EEReadByte(rq->wValue.bytes[0] + (rq->wValue.bytes[1] << 8)); // WE MIGHT HAVE 2 REMOVE THIS BECAUSE OF PERFORMANCE ISSUES -- Reading back too quickly causes an error (-1) reportBuffer[0] = rq->wIndex.bytes[0]; return 1; } else if(rq->bRequest == CMD_EEREAD) { reportBuffer[0] = EEReadByte(rq->wValue.bytes[0] + (rq->wValue.bytes[1] << 8)); reportBuffer[1] = EEReadByte(rq->wValue.bytes[0] + (rq->wValue.bytes[1] << 8)); // We read twice to ensure the right value was loaded and transmitted. return 2; } else if(rq->bRequest == CMD_EEOPEN) { EEOpen(); reportBuffer[0] = 1; return 1; } else if(rq->bRequest == CMD_RESET) { wdt_enable(WDTO_15MS); // faster reboot while (1); // Wait 'til the watchdog resets our system } else if(rq->bRequest == CMD_SERIAL) { // CURRENTLY NOT WORKING! uint8_t x; for (x=0; x < MEM_SERIAL_LENGTH; x++) { reportBuffer[x] = eeprom_read_byte(x+MEM_SERIAL_OFFSET); } return MEM_SERIAL_LENGTH; } else if(rq->bRequest == CMD_IEEWRITE) { LED_PORT ^= (1 << LED_GREEN); eeprom_write_byte(rq->wValue.bytes[0] + (rq->wValue.bytes[1] << 8), rq->wIndex.bytes[0]); reportBuffer[0] = eeprom_read_byte(rq->wValue.bytes[0] + (rq->wValue.bytes[1] << 8)); // (WE MIGHT HAVE 2 REMOVE THIS BECAUSE OF PERFORMANCE ISSUES) return 1; } else if(rq->bRequest == CMD_IEEREAD) { reportBuffer[0] = eeprom_read_byte(rq->wValue.bytes[0] + (rq->wValue.bytes[1] << 8)); reportBuffer[1] = eeprom_read_byte(rq->wValue.bytes[0] + (rq->wValue.bytes[1] << 8)); // Check return 2; } else if(rq->bRequest == CMD_EXEC) { memAddr = rq->wValue.bytes[0] + (rq->wValue.bytes[1] << 8); // jump to addr. cMode = KEYMODE_PRESS; //default to keymode_press oldKey = 0; newReport = 0; //activate continued exec continueExecution(); // exec NOW } else if (rq->bRequest == CMD_INITKEYS) { detectKeyType(); reportBuffer[0] = 1; return 1; } } return 0; } static void buildReport(void){ if(newReport == 0) continueExecution(); } void continueExecution() { uint8_t newData = 0; while (!newData) { uint8_t instr = EEReadByte(memAddr); if (instr == INSTR_BREAK) { // Release all keys (?) if (keyType[oldKey-1] == KEYTYPE_TEXT) { uint8_t x; for (x=0; x < 8; x++) { reportBuffer[x] = 0; } } newReport = 1; // done newData = 1; //exit loop } else if (instr == INSTR_KEYUP) { if ((keyType[oldKey-1] == KEYTYPE_TEXT) || keyAction == KEYACTION_DOWN) cMode = KEYMODE_UP; else cMode = KEYMODE_DOWN; // When the key on the keyboard was released, we play everything back invertedly. } else if (instr == INSTR_KEYDOWN) { if ((keyType[oldKey-1] == KEYTYPE_TEXT) || keyAction == KEYACTION_DOWN) cMode = KEYMODE_DOWN; else cMode = KEYMODE_UP; // See four lines above. } else if (instr == INSTR_KEYPRESS) { cMode = KEYMODE_PRESS; } else if (instr > 0 && instr <= 133) { //normal keypress uint8_t x=0; for (x=2; x < 8; x++) { //Scan through all possible keys if (cMode == KEYMODE_DOWN) { if (reportBuffer[x] == 0) { //we need 2 find an unused key. reportBuffer[x] = instr; // press key break; //leave loop } } else if (cMode == KEYMODE_UP) { if (reportBuffer[x] == instr) { //we found our key reportBuffer[x] = 0; //release it break; //leave loop } } else { //press if (!flag_keyPress) { if (reportBuffer[x] == 0) { //we found an empty key reportBuffer[x] = instr; //press it flag_keyPress = 1; break; //leave loop } } else { if (reportBuffer[x] == instr) { //we found our key reportBuffer[x] = 0; //release it flag_keyPress = 0; //next cmd break; //leave loop } } } } newData = 1; //exit loop } else if (instr >= 224 && instr <= 231) { //reportBuffer[0] |= (1 << (instr - 224)); // MOD-Keys (en) if ((keyType[oldKey-1] == KEYTYPE_TEXT) || keyAction == KEYACTION_DOWN) reportBuffer[0] |= (1 << (instr - 224)); // MOD-Keys (en) else reportBuffer[0] &= ~(1 << (instr - 224)); // MOD-Keys (dis) } else if (instr >= 232 && instr <= 239) { reportBuffer[0] &= ~(1 << (instr - 232)); // MOD-Keys (dis) if ((keyType[oldKey-1] == KEYTYPE_TEXT) || keyAction == KEYACTION_DOWN) reportBuffer[0] &= ~(1 << (instr - 232)); // MOD-Keys (dis) else reportBuffer[0] |= (1 << (instr - 232)); // MOD-Keys (en) } if (!flag_keyPress) memAddr++; // increase memory address // Currently there's NO protection.The system could jump onto the next key // Can be simply fixed by using a modulo-operation on the current address and the MEM_KEY_LENGTH } } void detectKeyType() { uint8_t keyId; uint16_t x; for (keyId=0;keyId < 16; ++keyId) { LED_PORT ^= (1 << LED_RED); for (x=0; x < MEM_KEY_LENGTH; ++x) { wdt_reset(); uint8_t instr = EEReadByte(keyId*MEM_KEY_LENGTH+x); if (instr == INSTR_KEYDOWN || instr == INSTR_KEYUP) { // If KEYPRESS is used at any time, it is a text-command keyType[keyId] = KEYTYPE_KEY; break; } else if (instr == INSTR_BREAK) { // If only KEYDOWN and KEYUP are used KEYDOWN it is a standard key keyType[keyId] = KEYTYPE_TEXT; break; } } } } void keyboard(void) { LED_PORT |= (1 << LED_RED); _delay_ms(250); // Make sure the cable is securely plugged in. LED_PORT &= ~(1 << LED_RED); _delay_ms(250); wdt_enable(WDTO_2S); EEOpen(); // We open our eeprom uchar i; // First, we scan through the codes so we can determine the key types. detectKeyType(); cli(); usbInit(); usbDeviceDisconnect(); /* enforce re-enumeration, do this while interrupts are disabled! */ i = 0; while(--i){ /* fake USB disconnect for > 250 ms */ wdt_reset(); _delay_ms(1); } usbDeviceConnect(); sei(); // Interrupt enable LED_PORT &= ~(1 << LED_RED); LED_PORT |= (1 << LED_GREEN); for(;;){ /* main event loop */ wdt_reset(); usbPoll(); checkBtn(); if(usbInterruptIsReady()) { LED_PORT |= (1 << LED_RED); // Switch red LED on if (newReport == 0){ /* we can send another report */ buildReport(); usbSetInterrupt(reportBuffer, sizeof(reportBuffer)); } } else LED_PORT &= ~(1 << LED_RED); // Only for nice visual effects again :) } } void checkBtn() { if (newReport == 0) return; // ignore all keypresses if there's a current key in progress if (getKey() != oldKey && getKey() == 0) { keyAction = KEYACTION_UP; if (keyType[oldKey-1] == KEYTYPE_KEY && hasReleased == 0) { newReport = 0; hasReleased = 1; memAddr = MEM_KEY_LENGTH*(oldKey-1); cMode = KEYMODE_PRESS; //default to keymode_press } else { oldKey=0; hasReleased = 1; } _delay_ms(10); // Debounce switches } else if (getKey() != oldKey && hasReleased == 1) { keyAction = KEYACTION_DOWN; hasReleased = 0; oldKey = getKey(); newReport = 0; memAddr = MEM_KEY_LENGTH*(oldKey-1); cMode = KEYMODE_PRESS; //default to keymode_press } }