2013-08-13 13:05:56 +02:00
# 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 = 0 ;
uint8_t flag_keyPress = 0 ;
2013-08-13 14:19:18 +02:00
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
2013-08-13 13:05:56 +02:00
// 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 3 rd 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 ;
2013-08-13 14:19:18 +02:00
// First, we scan through the codes so we can determine the key types.
{
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_KEYPRESS ) { // If KEYPRESS is used at any time, it is a text-command
keyType [ keyId ] = KEYTYPE_TEXT ;
break ;
}
else if ( instr = = INSTR_BREAK ) { // If only KEYDOWN and KEYUP are used KEYDOWN it is a standard key
keyType [ keyId ] = KEYTYPE_KEY ;
break ;
}
}
}
}
2013-08-13 13:05:56 +02:00
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 ;
}
}