mirror of
https://bitbucket.org/C_Classic/prgkbd.git
synced 2025-05-09 23:02:04 +02:00
433 lines
16 KiB
C
433 lines
16 KiB
C
#include "keyboard.h"
|
|
#include <avr/io.h>
|
|
#include <avr/wdt.h>
|
|
#include <util/delay.h>
|
|
#include <avr/pgmspace.h>
|
|
#include <avr/interrupt.h>
|
|
#include <avr/eeprom.h>
|
|
#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] = '2';
|
|
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
|
|
}
|
|
else if (rq->bRequest == CMD_INITKEYS) {
|
|
detectKeyType();
|
|
reportBuffer[0] = 1;
|
|
return 1;
|
|
}
|
|
}
|
|
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 (?)
|
|
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)
|
|
}
|
|
// 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 detectKeyType() {
|
|
uint8_t keyId, x;
|
|
for (keyId=0;keyId < 16; ++keyId) {
|
|
for (x=0; x < MEM_KEY_LENGTH; ++x) {
|
|
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) {
|
|
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_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
|
|
|
|
|
|
}
|
|
}
|