From 6c9e2fd99ce4dc7430ff420d622b5bf9a947230c Mon Sep 17 00:00:00 2001 From: NicoHood Date: Tue, 22 Sep 2015 22:08:13 +0200 Subject: [PATCH] Added RawHID Works under linux with a patched Teensy RawHID testprogram Only works without any other HID device (like keyboard) used together --- examples/RawHID/RawHID.ino | 133 ++++++++++++++++++++++++ src/HID-Project.h | 7 ++ src/PluggableHID/HID.cpp | 11 +- src/RawHID.cpp | 30 ++++++ src/RawHID.h | 204 +++++++++++++++++++++++++++++++++++++ 5 files changed, 375 insertions(+), 10 deletions(-) create mode 100644 examples/RawHID/RawHID.ino create mode 100644 src/RawHID.cpp create mode 100644 src/RawHID.h diff --git a/examples/RawHID/RawHID.ino b/examples/RawHID/RawHID.ino new file mode 100644 index 0000000..ae6c3a7 --- /dev/null +++ b/examples/RawHID/RawHID.ino @@ -0,0 +1,133 @@ +/* + Copyright (c) 2014-2015 NicoHood + See the readme for credit to other people. + + Advanced RawHID example + + Shows how to send bytes via RawHID. + Press a button to send some example values. + Keep in mind that you can only send full data packets, + the rest is filled with zero! + + Definitions from HID_Reports.h: + RAWHID_USAGE_PAGE 0xFFC0 // recommended: 0xFF00 to 0xFFFF + RAWHID_USAGE 0x0C00 // recommended: 0x0100 to 0xFFFF + RAWHID_TX_SIZE 15 // 1 byte for report ID + RAWHID_RX_SIZE 15 // 1 byte for report ID + + See HID Project documentation for more information. + https://github.com/NicoHood/HID/wiki/RawHID-API +*/ + +#include "HID-Project.h" + +const int pinLed = LED_BUILTIN; +const int pinButton = 2; + +void setup() { + pinMode(pinLed, OUTPUT); + pinMode(pinButton, INPUT_PULLUP); + Serial.begin(0);//TODO + //Keyboard.begin(); + // No begin function needed for RawHID +} + +void loop() { + if (!digitalRead(pinButton)) { + digitalWrite(pinLed, HIGH); + + // Direct without library. Always send RAWHID_RX_SIZE bytes! + uint8_t buff[RAWHID_TX_SIZE] = {0}; + + // With library + memset(&buff, 42, sizeof(buff)); + RawHID.write(buff, sizeof(buff)); + + // Write a single byte, will fill the rest with zeros + RawHID.write(0xCD); + + // Huge buffer with library, will fill the rest with zeros + uint8_t megabuff[100]; + for (int i = 0; i < sizeof(megabuff); i++) + megabuff[i] = i; + RawHID.write(megabuff, sizeof(megabuff)); + + // You can use print too, but better do not use a linefeed. + // A linefeed will send the \r and \n in a separate report. + RawHID.println("Hello World"); + + // Compare print to write: + RawHID.write("Hello World\r\n"); + + // Simple debounce + delay(300); + digitalWrite(pinLed, LOW); + } + + uint8_t len = RawHID.available(); + if (len) { + digitalWrite(pinLed, HIGH); + + // Mirror the incoming data from the host back + uint8_t buff[len + 1]; + buff[0] = len; + for (int i = 1; i < sizeof(buff); i++) { + buff[i] = RawHID.read(); + } + RawHID.write(buff, len); + + // Simple debounce + delay(300); + digitalWrite(pinLed, LOW); + } +} + +/* + Expected output: + + // manual with unintialized buff + recv 15 bytes: + 01 55 C1 FF 01 01 01 00 00 01 00 00 01 00 20 + + // filled buff + recv 15 bytes: + 2A 2A 2A 2A 2A 2A 2A 2A 2A 2A 2A 2A 2A 2A 2A + + // single byte filled with zero + recv 15 bytes: + CD 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + // huge buffer filled with zero at the end + recv 15 bytes: + 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E + + recv 15 bytes: + 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D + + recv 15 bytes: + 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C + + recv 15 bytes: + 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B + + recv 15 bytes: + 3C 3D 3E 3F 00 00 00 00 00 00 00 00 00 00 00 + + // print + recv 15 bytes: + 48 65 6C 6C 6F 20 57 6F 72 6C 64 00 00 00 00 + + //\r + recv 15 bytes: + 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + //\n + recv 15 bytes: + 0A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + //write + recv 15 bytes: + 48 65 6C 6C 6F 20 57 6F 72 6C 64 0D 0A 00 00 + +*/ + diff --git a/src/HID-Project.h b/src/HID-Project.h index 222e6b5..86800b2 100644 --- a/src/HID-Project.h +++ b/src/HID-Project.h @@ -92,6 +92,12 @@ THE SOFTWARE. #define HID_REPORTID_KEYBOARD 2 #endif +#ifndef HID_REPORTID_RAWHID +// On Windows you might want to use 0 here and no other HID device combined. +// Make sure to also disable the boot protocol for keyboard or mouse. +#define HID_REPORTID_RAWHID 3 +#endif + #ifndef HID_REPORTID_CONSUMERCONTROL #define HID_REPORTID_CONSUMERCONTROL 4 #endif @@ -131,6 +137,7 @@ THE SOFTWARE. #include "Consumer.h" #include "Gamepad.h" #include "System.h" +#include "RawHID.h" // Include Teensy HID afterwards to overwrite key definitions if used #ifdef USE_TEENSY_KEYBOARD diff --git a/src/PluggableHID/HID.cpp b/src/PluggableHID/HID.cpp index e609add..ec8dcd2 100644 --- a/src/PluggableHID/HID.cpp +++ b/src/PluggableHID/HID.cpp @@ -27,16 +27,7 @@ HID_ HID; //================================================================================ //================================================================================ - -// HID report descriptor - -#define LSB(_x) ((_x) & 0xFF) -#define MSB(_x) ((_x) >> 8) - -#define RAWHID_USAGE_PAGE 0xFFC0 -#define RAWHID_USAGE 0x0C00 -#define RAWHID_TX_SIZE 64 -#define RAWHID_RX_SIZE 64 +// HID Interface // Static variables uint8_t HID_::HID_ENDPOINT_INT; diff --git a/src/RawHID.cpp b/src/RawHID.cpp new file mode 100644 index 0000000..3358c2f --- /dev/null +++ b/src/RawHID.cpp @@ -0,0 +1,30 @@ +/* +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 "RawHID.h" + +//================================================================================ +// RawHID +//================================================================================ + +RawHID_ RawHID; diff --git a/src/RawHID.h b/src/RawHID.h new file mode 100644 index 0000000..d4b8230 --- /dev/null +++ b/src/RawHID.h @@ -0,0 +1,204 @@ +/* +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. +*/ + +#pragma once + +#include "HID.h" + +#if !defined(_USING_HID) + +#warning "Using legacy HID core (non pluggable)" + +#else + +#include "HID-Project.h" + +//================================================================================ +// RawHID +//================================================================================ + +// RawHID might never work with multireports, because of OS problems +// therefore we have to make it a single report with no idea. No other HID device will be supported then. +#undef RAWHID_USAGE_PAGE +#define RAWHID_USAGE_PAGE 0xFFC0 // recommended: 0xFF00 to 0xFFFF + +#undef RAWHID_USAGE +#define RAWHID_USAGE 0x0C00 // recommended: 0x0100 to 0xFFFF + +// Keep one byte offset for the reportID if used +#if (HID_REPORTID_RAWHID) +#define RAWHID_SIZE (USB_EP_SIZE-1) +#else +#define RAWHID_SIZE (USB_EP_SIZE) +#endif + +#undef RAWHID_TX_SIZE +#define RAWHID_TX_SIZE RAWHID_SIZE + +#undef RAWHID_RX_SIZE +#define RAWHID_RX_SIZE RAWHID_SIZE + +#define LSB(_x) ((_x) & 0xFF) +#define MSB(_x) ((_x) >> 8) + +static const uint8_t _rawhidReportDescriptor[] PROGMEM = { + /* RAW HID */ + 0x06, LSB(RAWHID_USAGE_PAGE), MSB(RAWHID_USAGE_PAGE), /* 30 */ + 0x0A, LSB(RAWHID_USAGE), MSB(RAWHID_USAGE), + + 0xA1, 0x01, /* Collection 0x01 */ +#if (HID_REPORTID_RAWHID) + 0x85, HID_REPORTID_RAWHID, /* REPORT_ID */ +#endif + 0x75, 0x08, /* report size = 8 bits */ + 0x15, 0x00, /* logical minimum = 0 */ + 0x26, 0xFF, 0x00, /* logical maximum = 255 */ + + 0x95, RAWHID_TX_SIZE, /* report count TX */ + 0x09, 0x01, /* usage */ + 0x81, 0x02, /* Input (array) */ + + 0x95, RAWHID_RX_SIZE, /* report count RX */ + 0x09, 0x02, /* usage */ + 0x91, 0x02, /* Output (array) */ + 0xC0 /* end collection */ +}; + +typedef union{ + // a RAWHID_TX_SIZE byte buffer for tx + uint8_t whole8[]; + uint16_t whole16[]; + uint32_t whole32[]; + uint8_t buff[RAWHID_TX_SIZE]; +} HID_RawKeyboardTXReport_Data_t; + +typedef union{ + // a RAWHID_TX_SIZE byte buffer for rx + uint8_t whole8[]; + uint16_t whole16[]; + uint32_t whole32[]; + uint8_t buff[RAWHID_RX_SIZE]; +} HID_RawKeyboardRXReport_Data_t; + +class RawHID_ : public Stream, private HIDDevice +{ +public: + RawHID_(void) : + HIDDevice((uint8_t*)_rawhidReportDescriptor, sizeof(_rawhidReportDescriptor), HID_REPORTID_RAWHID), + dataLength(0) + { + // HID Descriptor is appended via the inherited HIDDevice class + } + + void begin(void){ + // empty + } + + void end(void){ + // empty + } + + virtual int available(void){ + return dataLength; + } + + virtual int read(){ + if(dataLength){ + // Get next data byte + uint8_t data = *(dataTail - dataLength); + dataLength--; + + // Release buffer if its read fully + if(!dataLength){ + free(dataHead); + } + + return data; + } + return -1; + } + + virtual int peek(){ + if(dataLength){ + return *(dataTail - dataLength); + } + return -1; + } + + virtual void flush(void){ + // Delete all incoming bytes + if(dataLength){ + free(dataHead); + dataLength = 0; + } + } + + using Print::write; + virtual size_t write(uint8_t b){ + write(&b, 1); + } + + virtual size_t write(const uint8_t *buffer, size_t size){ + // TODO this only sends the report ID in the first packat + // TODO this will split the data into USB_EP_SIZE packets + SendReport(buffer, size); + return size; + + size_t bytesleft = size; + // First work through the buffer thats already there + while (bytesleft >= RAWHID_TX_SIZE){ + SendReport(&buffer[size - bytesleft], RAWHID_TX_SIZE); + bytesleft -= RAWHID_TX_SIZE; + } + + // Write down the leftover bytes and fill with zeros + if (bytesleft){ + SendReport(&buffer[size - bytesleft], bytesleft); + } + + return size; + } + +private: + virtual void setReportData(void* &data, int len){ + // Only overwrite the buffer if its empty. + // This avoids corrupted data while reading. + if(!dataLength){ + // Save new data + dataLength = len; + dataHead = (uint8_t*) data; + dataTail = (uint8_t*)(data + len); + + // Clear the passed in pointer to not free the data + data = NULL; + } + } + + // Buffer pointers to hold the received data + int dataLength; + uint8_t* dataHead; + uint8_t* dataTail; +}; +extern RawHID_ RawHID; + +#endif