#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=0; uint8_t flag_keyPress=0; // 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. /*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 // 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] = '1'; 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 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 } } return 0; } static void buildReport(void){ if(newReport == 0){ if (oldKey == Key_MODE) { //Not yet implemented } else if (oldKey) { // One of the 16 keys was pressed //reportBuffer[2] = 125 + oldKey; // 1+getKey() // Set memAddr to the first byte of the pressed key memAddr = MEM_KEY_LENGTH*(oldKey-1); cMode = KEYMODE_PRESS; //default to keymode_press oldKey = 0; continueExecution(); } else { // Continue code execution. continueExecution(); } } } //WICHTIG: BEIM CANCEL-COMMAND (255) einen release auslösen... oder auch nicht... lassen wirs den user konfigurieren... oder auch nicht.. hmm.... reportBuf komplett auf 0 setzen wäre das dann void continueExecution() { uint8_t newData = 0; while (!newData) { uint8_t instr = EEReadByte(memAddr); if (instr == INSTR_BREAK) { // Release all keys (?) uint8_t x; for (x=0; x < 8; x++) { reportBuffer[x] = 0; } newReport = 1; // done newData = 1; //exit loop } else if (instr == INSTR_KEYUP) { cMode = KEYMODE_UP; } else if (instr == INSTR_KEYDOWN) { cMode = KEYMODE_DOWN; } 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) else if (instr >= 232 && instr <= 239) reportBuffer[0] &= ~(1 << (instr - 232)); // MOD-Keys (dis) // MODS-KEYS are enabled/disabled using different commands. This will make it easier when being in text-mode. /* OLD PART FOR MOD-KEYS: Replaced by new command set New one is independent from mode if (cMode == KEYMODE_DOWN) { // set bit reportBuffer[0] |= (1 << (instr - 224)); } else if (cMode == KEYMODE_UP) { // clear bit reportBuffer[0] &= ~(1 << (instr - 224)); } else { // Err (press) } */ if (!flag_keyPress) memAddr++; // increase memory address // Currently there's NO protection.The system could jump onto the next key } } void keyboard(void) { wdt_enable(WDTO_2S); EEOpen(); // We open our eeprom uchar i; 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_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() == 0) hasReleased = 1; if (getKey() != oldKey && hasReleased == 1) { hasReleased = 0; oldKey = getKey(); newReport = 0; } }