diff --git a/examples/KeyboardLed/KeyboardLed.ino b/examples/KeyboardLed/KeyboardLed.ino new file mode 100644 index 0000000..3d7e584 --- /dev/null +++ b/examples/KeyboardLed/KeyboardLed.ino @@ -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); + } +} diff --git a/src/HID-Project.h b/src/HID-Project.h index 0260c32..009023b 100644 --- a/src/HID-Project.h +++ b/src/HID-Project.h @@ -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 diff --git a/src/ImprovedKeyboard.cpp b/src/ImprovedKeyboard.cpp index 06a0ed5..680648a 100644 --- a/src/ImprovedKeyboard.cpp +++ b/src/ImprovedKeyboard.cpp @@ -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 diff --git a/src/ImprovedKeyboard.h b/src/ImprovedKeyboard.h index c0cd373..49d6cc5 100644 --- a/src/ImprovedKeyboard.h +++ b/src/ImprovedKeyboard.h @@ -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; diff --git a/src/ImprovedKeylayouts.h b/src/ImprovedKeylayouts.h index 5a5a5f4..2ba3d45 100644 --- a/src/ImprovedKeylayouts.h +++ b/src/ImprovedKeylayouts.h @@ -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 diff --git a/src/PluggableHID/HID.cpp b/src/PluggableHID/HID.cpp index 881296f..de7b8ed 100644 --- a/src/PluggableHID/HID.cpp +++ b/src/PluggableHID/HID.cpp @@ -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, diff --git a/src/PluggableHID/HID.h b/src/PluggableHID/HID.h index e2c9469..8b1c150 100644 --- a/src/PluggableHID/HID.h +++ b/src/PluggableHID/HID.h @@ -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 @@ -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 - diff --git a/src/PluggableHID/HIDDevice.cpp b/src/PluggableHID/HIDDevice.cpp new file mode 100644 index 0000000..a0c311e --- /dev/null +++ b/src/PluggableHID/HIDDevice.cpp @@ -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 diff --git a/src/PluggableHID/HIDDevice.h b/src/PluggableHID/HIDDevice.h new file mode 100644 index 0000000..54d6804 --- /dev/null +++ b/src/PluggableHID/HIDDevice.h @@ -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 +#include + +//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); +}; + + +