Added KeyboardLeds/HID Out Report
This also enables the option for further raw HID development.
This commit is contained in:
parent
02684edc0a
commit
f34df6edef
9 changed files with 250 additions and 69 deletions
43
examples/KeyboardLed/KeyboardLed.ino
Normal file
43
examples/KeyboardLed/KeyboardLed.ino
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright (c) 2014-2015 NicoHood
|
||||
See the readme for credit to other people.
|
||||
|
||||
KeyboardLed example
|
||||
|
||||
Press a button to toogle caps lock.
|
||||
Caps lock state is represented by the onboard led.
|
||||
|
||||
See HID Project documentation for more information.
|
||||
https://github.com/NicoHood/HID/wiki/Keyboard-API
|
||||
*/
|
||||
|
||||
#include "HID-Project.h"
|
||||
|
||||
const int pinLed = LED_BUILTIN;
|
||||
const int pinButton = 2;
|
||||
|
||||
void setup() {
|
||||
pinMode(pinLed, OUTPUT);
|
||||
pinMode(pinButton, INPUT_PULLUP);
|
||||
|
||||
// Sends a clean report to the host. This is important on any Arduino type.
|
||||
Keyboard.begin();
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
// Update Led equal to the caps lock state.
|
||||
// Keep in mind that on a 16u2 and Arduino Micro HIGH and LOW for TX/RX Leds are inverted.
|
||||
if (Keyboard.getLeds()&LED_CAPS_LOCK)
|
||||
digitalWrite(pinLed, HIGH);
|
||||
else
|
||||
digitalWrite(pinLed, LOW);
|
||||
|
||||
// Trigger caps lock manually via button
|
||||
if (!digitalRead(pinButton)) {
|
||||
Keyboard.write(KEY_CAPS_LOCK);
|
||||
|
||||
// Simple debounce
|
||||
delay(300);
|
||||
}
|
||||
}
|
||||
|
|
@ -109,10 +109,10 @@ extern HID_ HID;
|
|||
#include "HID-Tables.h"
|
||||
|
||||
// Include all HID libraries (.a linkage required to work) properly
|
||||
#include "AbsoluteMouse.h"
|
||||
#include "Consumer.h"
|
||||
#include "Gamepad.h"
|
||||
#include "System.h"
|
||||
//#include "AbsoluteMouse.h"
|
||||
//#include "Consumer.h"
|
||||
//#include "Gamepad.h"
|
||||
//#include "System.h"
|
||||
|
||||
// Include Teensy HID afterwards to overwrite key definitions if used
|
||||
#ifdef USE_TEENSY_KEYBOARD
|
||||
|
|
|
|||
|
|
@ -30,46 +30,58 @@ THE SOFTWARE.
|
|||
// Keyboard
|
||||
|
||||
static const u8 _hidReportDescriptor[] PROGMEM = {
|
||||
// Keyboard
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) 47 */
|
||||
0x09, 0x06, /* USAGE (Keyboard) */
|
||||
0xa1, 0x01, /* COLLECTION (Application) */
|
||||
0x85, HID_REPORTID_KEYBOARD, /* REPORT_ID */
|
||||
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
|
||||
|
||||
// Keyboard
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop) // 47
|
||||
0x09, 0x06, // USAGE (Keyboard)
|
||||
0xa1, 0x01, // COLLECTION (Application)
|
||||
0x85, HID_REPORTID_KEYBOARD, // REPORT_ID
|
||||
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 (1)
|
||||
0x75, 0x08, // REPORT_SIZE (8)
|
||||
0x81, 0x03, // INPUT (Cnst,Var,Abs)
|
||||
|
||||
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)
|
||||
|
||||
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
|
||||
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
|
||||
0x81, 0x00, // INPUT (Data,Ary,Abs)
|
||||
0xc0, // END_COLLECTION
|
||||
/* Keyboard Modifiers (shift, alt, ...) */
|
||||
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) */
|
||||
|
||||
/* Reserved byte */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x08, /* REPORT_SIZE (8) */
|
||||
0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
|
||||
|
||||
/* 5 LEDs for num lock etc */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */
|
||||
0x29, 0x05, /* USAGE_MAXIMUM (Kana) */
|
||||
0x95, 0x05, /* REPORT_COUNT (5) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
/* Reserved 3 bits */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x03, /* REPORT_SIZE (3) */
|
||||
0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */
|
||||
|
||||
/* 6 Keyboard keys */
|
||||
0x95, 0x06, /* REPORT_COUNT (6) */
|
||||
0x75, 0x08, /* REPORT_SIZE (8) */
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x26, 0xE7, 0x00, /* LOGICAL_MAXIMUM (231) */
|
||||
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
|
||||
0x19, 0x00, /* USAGE_MINIMUM (Reserved (no event indicated)) */
|
||||
0x29, 0xE7, /* USAGE_MAXIMUM (Keyboard Right GUI) */
|
||||
0x81, 0x00, /* INPUT (Data,Ary,Abs) */
|
||||
|
||||
/* End */
|
||||
0xc0 /* END_COLLECTION */
|
||||
};
|
||||
|
||||
Keyboard_::Keyboard_(void)
|
||||
Keyboard_::Keyboard_(void) :
|
||||
HIDDevice((uint8_t*)_hidReportDescriptor, sizeof(_hidReportDescriptor), HID_REPORTID_KEYBOARD),
|
||||
leds(0)
|
||||
{
|
||||
static HID_Descriptor cb = {
|
||||
.length = sizeof(_hidReportDescriptor),
|
||||
.descriptor = _hidReportDescriptor,
|
||||
};
|
||||
static HIDDescriptorListNode node(&cb);
|
||||
HID.AppendDescriptor(&node);
|
||||
// HID Descriptor is appended via the inherited HIDDevice class
|
||||
}
|
||||
|
||||
void Keyboard_::begin(void)
|
||||
|
|
@ -82,9 +94,22 @@ void Keyboard_::end(void)
|
|||
|
||||
void Keyboard_::sendReport(HID_KeyboardReport_Data_t* keys)
|
||||
{
|
||||
HID.SendReport(HID_REPORTID_KEYBOARD,keys,sizeof(HID_KeyboardReport_Data_t));
|
||||
// Call the inherited function.
|
||||
// This wrapper still saves us some bytes
|
||||
SendReport(keys,sizeof(HID_KeyboardReport_Data_t));
|
||||
}
|
||||
|
||||
void Keyboard_::setReportData(const void* data, int len){
|
||||
// Save led state
|
||||
if(len == 1)
|
||||
leds = *(uint8_t*)data;
|
||||
}
|
||||
|
||||
uint8_t Keyboard_::getLeds(void){
|
||||
return leds;
|
||||
}
|
||||
|
||||
|
||||
// press() adds the specified key (printing, non-printing, or modifier)
|
||||
// to the persistent key report and sends the report. Because of the way
|
||||
// USB HID works, the host acts like the key remains pressed until we
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ THE SOFTWARE.
|
|||
// Keyboard
|
||||
|
||||
#include "HID-Project.h"
|
||||
#include "PluggableHID/HIDDevice.h"
|
||||
#include "ImprovedKeylayouts.h"
|
||||
|
||||
// Low level key report: up to 6 keys and shift, ctrl etc at once
|
||||
|
|
@ -51,11 +52,13 @@ typedef union{
|
|||
};
|
||||
} HID_KeyboardReport_Data_t;
|
||||
|
||||
class Keyboard_ : public Print
|
||||
class Keyboard_ : public Print, public HIDDevice
|
||||
{
|
||||
private:
|
||||
HID_KeyboardReport_Data_t _keyReport;
|
||||
void sendReport(HID_KeyboardReport_Data_t* keys);
|
||||
virtual void setReportData(const void* data, int len);
|
||||
uint8_t leds;
|
||||
public:
|
||||
Keyboard_(void);
|
||||
void begin(void);
|
||||
|
|
@ -70,6 +73,8 @@ public:
|
|||
size_t releaseKeycode(uint8_t k);
|
||||
size_t addKeycodeToReport(uint8_t k);
|
||||
size_t removeKeycodeFromReport(uint8_t k);
|
||||
|
||||
uint8_t getLeds(void);
|
||||
};
|
||||
extern Keyboard_ Keyboard;
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ THE SOFTWARE.
|
|||
#define KEY_RIGHT_WINDOWS KEY_RIGHT_GUI
|
||||
#define KEY_PRINTSCREEN KEY_PRINT
|
||||
|
||||
// TODO implement Leds
|
||||
// Keyboard Leds
|
||||
#define LED_NUM_LOCK 0x01
|
||||
#define LED_CAPS_LOCK 0x02
|
||||
#define LED_SCROLL_LOCK 0x04
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "PluggableUSB.h"
|
||||
#include "HID.h"
|
||||
#include "HIDDevice.h"
|
||||
|
||||
#if defined(USBCON)
|
||||
|
||||
|
|
@ -42,7 +43,7 @@ static u8 HID_INTERFACE;
|
|||
|
||||
HIDDescriptor _hidInterface;
|
||||
|
||||
static HIDDescriptorListNode* rootNode = NULL;
|
||||
static HIDDevice* rootDevice = NULL;
|
||||
static uint16_t sizeof_hidReportDescriptor = 0;
|
||||
static uint8_t modules_count = 0;
|
||||
//================================================================================
|
||||
|
|
@ -67,10 +68,10 @@ int HID_GetInterface(u8* interfaceNum)
|
|||
int HID_GetDescriptor(int8_t t)
|
||||
{
|
||||
if (HID_REPORT_DESCRIPTOR_TYPE == t) {
|
||||
HIDDescriptorListNode* current = rootNode;
|
||||
HIDDevice* current = rootDevice;
|
||||
int total = 0;
|
||||
while(current != NULL) {
|
||||
total += USB_SendControl(TRANSFER_PGM,current->cb->descriptor,current->cb->length);
|
||||
total += USB_SendControl(TRANSFER_PGM,current->descriptorData,current->descriptorLength);
|
||||
current = current->next;
|
||||
}
|
||||
return total;
|
||||
|
|
@ -79,19 +80,19 @@ int HID_GetDescriptor(int8_t t)
|
|||
}
|
||||
}
|
||||
|
||||
void HID_::AppendDescriptor(HIDDescriptorListNode *node)
|
||||
void HID_::AppendDescriptor(HIDDevice *device)
|
||||
{
|
||||
if (modules_count == 0) {
|
||||
rootNode = node;
|
||||
rootDevice = device;
|
||||
} else {
|
||||
HIDDescriptorListNode *current = rootNode;
|
||||
HIDDevice *current = rootDevice;
|
||||
while(current->next != NULL) {
|
||||
current = current->next;
|
||||
}
|
||||
current->next = node;
|
||||
current->next = device;
|
||||
}
|
||||
modules_count++;
|
||||
sizeof_hidReportDescriptor += (uint16_t)node->cb->length;
|
||||
sizeof_hidReportDescriptor += (uint16_t)device->descriptorLength;
|
||||
}
|
||||
|
||||
void HID_::SendReport(u8 id, const void* data, int len)
|
||||
|
|
@ -100,7 +101,7 @@ void HID_::SendReport(u8 id, const void* data, int len)
|
|||
USB_Send(HID_TX | TRANSFER_RELEASE,data,len);
|
||||
}
|
||||
|
||||
bool HID_Setup(USBSetup& setup, u8 i)
|
||||
bool HID_::HID_Setup(USBSetup& setup, u8 i)
|
||||
{
|
||||
if (HID_INTERFACE != i) {
|
||||
return false;
|
||||
|
|
@ -134,6 +135,35 @@ bool HID_Setup(USBSetup& setup, u8 i)
|
|||
_hid_idle = setup.wValueL;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HID_SET_REPORT == r)
|
||||
{
|
||||
// Get reportID and search for the suited HIDDevice
|
||||
uint8_t ID = setup.wIndex;
|
||||
HIDDevice *current = rootDevice;
|
||||
while(current != NULL)
|
||||
{
|
||||
// TODO implementation for non reportID HID devices
|
||||
// This would could make rawHID work. reportID 0 could be used as indicator
|
||||
if(current->reportID == ID)
|
||||
{
|
||||
// Get the data length information and the corresponding bytes
|
||||
uint8_t length = setup.wLength;
|
||||
uint8_t data[length];
|
||||
USB_RecvControl(data, length);
|
||||
|
||||
// Skip report ID data
|
||||
current->setReportData(data+1, length-1);
|
||||
|
||||
// Dont search any further
|
||||
break;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
//TODO return true??
|
||||
// https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/USBCore.cpp#L613-L618
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -146,7 +176,7 @@ HID_::HID_(void)
|
|||
endpointType[0] = EP_TYPE_INTERRUPT_IN;
|
||||
|
||||
static PUSBCallbacks cb = {
|
||||
.setup = &HID_Setup,
|
||||
.setup = HID_Setup,
|
||||
.getInterface = &HID_GetInterface,
|
||||
.getDescriptor = &HID_GetDescriptor,
|
||||
.numEndpoints = 1,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@
|
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef HID_h
|
||||
#pragma once
|
||||
|
||||
#define HID_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
|
@ -44,17 +45,7 @@
|
|||
#define HID_REPORT_DESCRIPTOR_TYPE 0x22
|
||||
#define HID_PHYSICAL_DESCRIPTOR_TYPE 0x23
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint16_t length;
|
||||
const void* descriptor;
|
||||
} HID_Descriptor;
|
||||
|
||||
class HIDDescriptorListNode {
|
||||
public:
|
||||
HIDDescriptorListNode *next = NULL;
|
||||
const HID_Descriptor * cb;
|
||||
HIDDescriptorListNode(const HID_Descriptor *ncb) {cb = ncb;}
|
||||
};
|
||||
class HIDDevice;
|
||||
|
||||
class HID_
|
||||
{
|
||||
|
|
@ -62,7 +53,9 @@ public:
|
|||
HID_(void);
|
||||
int begin(void);
|
||||
void SendReport(uint8_t id, const void* data, int len);
|
||||
void AppendDescriptor(HIDDescriptorListNode* node);
|
||||
void AppendDescriptor(HIDDevice* device);
|
||||
private:
|
||||
static bool HID_Setup(USBSetup& setup, u8 i);
|
||||
};
|
||||
|
||||
typedef struct
|
||||
|
|
@ -93,6 +86,3 @@ typedef struct
|
|||
#define WEAK __attribute__ ((weak))
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
29
src/PluggableHID/HIDDevice.cpp
Normal file
29
src/PluggableHID/HIDDevice.cpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
#include "PluggableUSB.h"
|
||||
#include "HIDDevice.h"
|
||||
#include "HID.h"
|
||||
|
||||
#ifdef kkk
|
||||
#error
|
||||
#endif
|
||||
|
||||
#if defined(USBCON)
|
||||
|
||||
HIDDevice::HIDDevice(uint8_t* data, uint16_t length, uint8_t ID) :
|
||||
descriptorData(data), descriptorLength(length), reportID(ID)
|
||||
{
|
||||
// TODO call Append
|
||||
// TODO init const data
|
||||
// TODO template?
|
||||
HID.AppendDescriptor(this);
|
||||
}
|
||||
|
||||
void HIDDevice::SendReport(const void* data, int len){
|
||||
HID.SendReport(reportID, data, len);
|
||||
}
|
||||
|
||||
void HIDDevice::setReportData(const void* data, int len){
|
||||
// Discard this information if its not implemented by the HIDDevice
|
||||
}
|
||||
|
||||
#endif
|
||||
59
src/PluggableHID/HIDDevice.h
Normal file
59
src/PluggableHID/HIDDevice.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
Copyright (c) 2014-2015 NicoHood
|
||||
See the readme for credit to other people.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Include guard
|
||||
#pragma once
|
||||
|
||||
#define HID_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
//http://stackoverflow.com/questions/1837165/can-two-classes-see-each-other-using-c
|
||||
|
||||
class HID_;
|
||||
extern HID_ HID;
|
||||
|
||||
class HIDDevice
|
||||
{
|
||||
public:
|
||||
HIDDevice(uint8_t* data, uint16_t length, uint8_t ID);
|
||||
|
||||
// Needs to be public for static HID_ function access
|
||||
//private:
|
||||
HIDDevice* next = NULL;
|
||||
|
||||
// HID Descriptor
|
||||
const uint8_t* descriptorData;
|
||||
const uint16_t descriptorLength;
|
||||
const uint8_t reportID;
|
||||
|
||||
virtual void setReportData(const void* data, int len);
|
||||
|
||||
protected:
|
||||
// TODO make this public for custom, professional usage?
|
||||
void SendReport(const void* data, int len);
|
||||
};
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in a new issue