Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for LED status #109

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
300 changes: 142 additions & 158 deletions digistump-avr/libraries/DigisparkKeyboard/DigiKeyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
*
* TODO: Make a proper file header. :-)
* Modified for Digispark by Digistump
* Added full Keyboard usage values by Danjovic, February 2016
* Report Buffer extended up to 6 keytrokes simultaneous by Danjovic, March 2016
* Added LED control by Danjovic, January 2019
*/
#ifndef __DigiKeyboard_h__
#define __DigiKeyboard_h__
Expand All @@ -14,6 +17,7 @@
#include <string.h>

#include "usbdrv.h"
#include "hidkeys.h"
#include "scancode-ascii-table.h"

// TODO: Work around Arduino 12 issues better.
Expand All @@ -22,111 +26,59 @@

typedef uint8_t byte;


#define BUFFER_SIZE 2 // Minimum of 2: 1 for modifiers + 1 for keystroke


static uchar idleRate; // in 4 ms units


/* We use a simplifed keyboard report descriptor which does not support the
* boot protocol. We don't allow setting status LEDs and but we do allow
* simultaneous key presses.
* The report descriptor has been created with usb.org's "HID Descriptor Tool"
* which can be downloaded from http://www.usb.org/developers/hidpage/.
* Redundant entries (such as LOGICAL_MINIMUM and USAGE_PAGE) have been omitted
* for the second INPUT item.
*/
const PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { /* USB report descriptor */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
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)
0x95, 0x01, // REPORT_COUNT (simultaneous keystrokes)
0x75, 0x08, // REPORT_SIZE (8)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0 // END_COLLECTION
typedef struct {
uint8_t modifier;
uint8_t reserved;
uint8_t keycode[6];
} keyboard_report_t;

// static keyboard_report_t keyboard_report; // sent to PC
static volatile uchar ledState = 0xff; // received from PC
static uchar idleRate; // repeat rate for keyboards in 4 ms units


#define NUM_LOCK (1<<0)
#define CAPS_LOCK (1<<1)
#define SCROLL_LOCK (1<<2)


// From Frank Zhao's USB Business Card project
// http://www.frank-zhao.com/cache/usbbusinesscard_details.php
const PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x05, 0x07, // USAGE_PAGE (Keyboard)(Key Codes)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)(224)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)(231)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
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)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x05, 0x07, // USAGE_PAGE (Keyboard)(Key Codes)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))(0)
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)(101)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0 // END_COLLECTION
};



/* Keyboard usage values, see usb.org's HID-usage-tables document, chapter
* 10 Keyboard/Keypad Page for more codes.
*/
#define MOD_CONTROL_LEFT (1<<0)
#define MOD_SHIFT_LEFT (1<<1)
#define MOD_ALT_LEFT (1<<2)
#define MOD_GUI_LEFT (1<<3)
#define MOD_CONTROL_RIGHT (1<<4)
#define MOD_SHIFT_RIGHT (1<<5)
#define MOD_ALT_RIGHT (1<<6)
#define MOD_GUI_RIGHT (1<<7)

#define KEY_A 4
#define KEY_B 5
#define KEY_C 6
#define KEY_D 7
#define KEY_E 8
#define KEY_F 9
#define KEY_G 10
#define KEY_H 11
#define KEY_I 12
#define KEY_J 13
#define KEY_K 14
#define KEY_L 15
#define KEY_M 16
#define KEY_N 17
#define KEY_O 18
#define KEY_P 19
#define KEY_Q 20
#define KEY_R 21
#define KEY_S 22
#define KEY_T 23
#define KEY_U 24
#define KEY_V 25
#define KEY_W 26
#define KEY_X 27
#define KEY_Y 28
#define KEY_Z 29
#define KEY_1 30
#define KEY_2 31
#define KEY_3 32
#define KEY_4 33
#define KEY_5 34
#define KEY_6 35
#define KEY_7 36
#define KEY_8 37
#define KEY_9 38
#define KEY_0 39

#define KEY_ENTER 40

#define KEY_SPACE 44

#define KEY_F1 58
#define KEY_F2 59
#define KEY_F3 60
#define KEY_F4 61
#define KEY_F5 62
#define KEY_F6 63
#define KEY_F7 64
#define KEY_F8 65
#define KEY_F9 66
#define KEY_F10 67
#define KEY_F11 68
#define KEY_F12 69

#define KEY_ARROW_LEFT 0x50



class DigiKeyboardDevice : public Print {
Expand All @@ -139,19 +91,20 @@ class DigiKeyboardDevice : public Print {


usbInit();

sei();

// TODO: Remove the next two lines once we fix
// missing first keystroke bug properly.
memset(reportBuffer, 0, sizeof(reportBuffer));
usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
memset((void *)&keyboard_report,0,sizeof(keyboard_report));
usbSetInterrupt((unsigned char*)&keyboard_report, sizeof(keyboard_report));
}



void update() {
usbPoll();
}

// delay while updating until we are finished delaying
void delay(long milli) {
unsigned long last = millis();
Expand All @@ -162,86 +115,117 @@ class DigiKeyboardDevice : public Print {
update();
}
}



// get LED State
uchar getLEDs (void) {
return ledState;
}


//sendKeyStroke: sends a key press AND release
void sendKeyStroke(byte keyStroke) {
sendKeyStroke(keyStroke, 0);
}

//sendKeyStroke: sends a key press AND release with modifiers
void sendKeyStroke(byte keyStroke, byte modifiers) {
sendKeyPress(keyStroke, modifiers);
// This stops endlessly repeating keystrokes:
sendKeyPress(0,0);
}

//sendKeyPress: sends a key press only - no release
//to release the key, send again with keyPress=0
void sendKeyPress(byte keyPress) {
sendKeyPress(keyPress, 0);
}

//sendKeyPress: sends a key press only, with modifiers - no release
//to release the key, send again with keyPress=0
void sendKeyPress(byte keyPress, byte modifiers) {
while (!usbInterruptIsReady()) {
// Note: We wait until we can send keystroke
// so we know the previous keystroke was
// Note: We wait until we can send keyPress
// so we know the previous keyPress was
// sent.
usbPoll();
_delay_ms(5);
}

memset(reportBuffer, 0, sizeof(reportBuffer));

reportBuffer[0] = modifiers;
reportBuffer[1] = keyStroke;

usbSetInterrupt(reportBuffer, sizeof(reportBuffer));

while (!usbInterruptIsReady()) {
// Note: We wait until we can send keystroke
// so we know the previous keystroke was
// sent.
usbPoll();
_delay_ms(5);
}

// This stops endlessly repeating keystrokes:
memset(reportBuffer, 0, sizeof(reportBuffer));
usbSetInterrupt(reportBuffer, sizeof(reportBuffer));


memset((void *)&keyboard_report,0,sizeof(keyboard_report)); // memset(reportBuffer, 0, sizeof(reportBuffer));
keyboard_report.modifier = modifiers; // reportBuffer[0] = modifiers;
keyboard_report.keycode[1] = keyPress; // reportBuffer[1] = keyPress;

usbSetInterrupt((unsigned char*)&keyboard_report, sizeof(keyboard_report)); // usbSetInterrupt(reportBuffer, sizeof(reportBuffer));

}

size_t write(uint8_t chr) {
uint8_t data = pgm_read_byte_near(ascii_to_scan_code_table + (chr - 8));
sendKeyStroke(data & 0b01111111, data >> 7 ? MOD_SHIFT_RIGHT : 0);
return 1;
}

//private: TODO: Make friend?
uchar reportBuffer[2]; // buffer for HID reports [ 1 modifier byte + (len-1) key strokes]
// maximum 6 keystrokes, defined in HID report

keyboard_report_t keyboard_report; // buffer for HID reports [ 1 modifier byte + (len-1) key strokes]
using Print::write;
};

DigiKeyboardDevice DigiKeyboard = DigiKeyboardDevice();

#ifdef __cplusplus
extern "C"{
#endif
#endif
// USB_PUBLIC uchar usbFunctionSetup
uchar usbFunctionSetup(uchar data[8]) {
usbRequest_t *rq = (usbRequest_t *)((void *)data);

usbMsgPtr = DigiKeyboard.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 */
// TODO: Ensure it's okay not to return anything here?
return 0;

} else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
//usbMsgPtr = &idleRate;
//return 1;
return 0;

} else if (rq->bRequest == USBRQ_HID_SET_IDLE) {
idleRate = rq->wValue.bytes[1];

}
} else {
/* no vendor specific requests implemented */
// usbMsgPtr = (unsigned char*)&DigiKeyboard.keyboard_report ;//usbMsgPtr = DigiKeyboard.reportBuffer; //


if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) {
switch(rq->bRequest) {
case USBRQ_HID_GET_REPORT: // send "no keys pressed" if asked here
// wValue: ReportType (highbyte), ReportID (lowbyte)
usbMsgPtr = (unsigned char*)&DigiKeyboard.keyboard_report ;//usbMsgPtr = DigiKeyboard.reportBuffer; //
DigiKeyboard.keyboard_report.modifier = 0;
DigiKeyboard.keyboard_report.keycode[0] = 0;
return sizeof(DigiKeyboard.keyboard_report);
case USBRQ_HID_SET_REPORT: // if wLength == 1, should be LED state
return (rq->wLength.word == 1) ? USB_NO_MSG : 0;
case USBRQ_HID_GET_IDLE: // send idle rate to PC as required by spec
usbMsgPtr = &idleRate;
return 1;
case USBRQ_HID_SET_IDLE: // save idle rate as required by spec
idleRate = rq->wValue.bytes[1];
return 0;
}
}
return 0;
}
#ifdef __cplusplus
} // extern "C"
#endif

#ifdef __cplusplus
extern "C"{
#endif
// update LED state
usbMsgLen_t usbFunctionWrite(uint8_t * data, uchar len) {

if (data[0] == ledState)
return 1;
else
ledState = data[0];

return 1; // Data read, not expecting more
}


#ifdef __cplusplus
} // extern "C"
#endif
Expand Down
Loading