diff --git a/avr/libraries/HIDBridge/HIDBridge.cpp b/avr/libraries/HIDBridge/HIDBridge.cpp index 438b649..99c32aa 100644 --- a/avr/libraries/HIDBridge/HIDBridge.cpp +++ b/avr/libraries/HIDBridge/HIDBridge.cpp @@ -23,12 +23,6 @@ THE SOFTWARE. #include "HIDBridge.h" -//================================================================================ -// HIDBridge TX -//================================================================================ - -#ifdef HIDBRIDGE_TX - HIDBridge_ HIDBridge; HIDBridge_::HIDBridge_(void){ @@ -38,19 +32,23 @@ HIDBridge_::HIDBridge_(void){ void HIDBridge_::begin(void) { // start the serial at our own baud rate - HIDBRIDGE_TX_SERIAL.begin(HIDBRIDGE_BAUD); + HIDBRIDGE_SERIAL.begin(HIDBRIDGE_BAUD); - // wait for the first request to see if usb device is connected + rxReady = true; + txReady = false; //move to end? + + // wait for/send the first request to see if usb device is connected available(); } void HIDBridge_::end(void) { // end the serial transmission and reset our helper values - HIDBRIDGE_TX_SERIAL.end(); + HIDBRIDGE_SERIAL.end(); nhp_read.mode = NHP_RESET; - isReady = false; + rxReady = false; //TODO isConnected = false; + //TODO reset more stuff } @@ -58,73 +56,97 @@ void HIDBridge_::err(uint8_t error) { if (!debug) return; - debug->print("Bridge Err TX: "); + debug->print("err"); debug->println(error); } + +void HIDBridge_::writeState(void) { + // send the current state to the other mcu + NHP_Write_Data_t n; + writeNHPAddress(HIDBRIDGE_CONTROL_ADDRESS, rxReady, &n); + HIDBRIDGE_SERIAL.write(n.writeBuffer, n.writeLength); + + // save new timeout + writeTimeout = millis(); +} + +void HIDBridge_::task(void){ + //TODO implement Serialevent strong for this task? +#ifdef HIDBRIDGE_RX + // if hid request timed out, send a new one + // this is important if the main mcu gets a reset or misses an acknowledge + uint32_t currentMillis = millis(); + if ((currentMillis - writeTimeout) > HIDBRIDGE_TX_TIMEOUT) { + writeState(); + // do not write timeout value, will be written in the function above +#ifdef USB_DEBUG + debug->println("ack 1s"); +#endif + } +#endif + + // read in new controls or data + read(); +} + void HIDBridge_::read(void) { + // check for read timeout only if really needed + if (!nhp_read.mode){ + if (millis() - readTimeout > HIDBRIDGE_RX_TIMEOUT) { + // reset reportID and NHP if we have a timeout + reportID = 0; + nhp_read.mode = NHP_RESET; + err(HIDBRIDGE_ERR_READ_TO); + } + } + // read as long as the Serial is available // but do not block forever - for (rx_buffer_index_t i = 0; i < SERIAL_RX_BUFFER_SIZE; i++){ + rx_buffer_index_t i; + for (i = 0; i < SERIAL_RX_BUFFER_SIZE; i++){ // read in new Serial byte - int b = HIDBRIDGE_TX_SERIAL.read(); + int b = HIDBRIDGE_SERIAL.read(); if (b < 0) break; // process with NHP protocol - bool newInput = readNHP(b, &nhp_read); + if (readNHP(b, &nhp_read)) { - // proceed new valid NHP input - if (newInput) { + // command indicates a new hidReport (command==reportID) or the end (command==0) + if (nhp_read.mode == NHP_COMMAND) + proceedCommand(); - // NHP address contains control data or out report data - if (nhp_read.mode == NHP_ADDRESS) { - - // received a control address command - if (nhp_read.address == HIDBRIDGE_ADDRESS_CONTROL) { - // acknowledge/request - if (nhp_read.data == HIDBRIDGE_CONTROL_ISREADY){ - isReady = true; - isConnected = true; - } - - // pause - else if (nhp_read.data == HIDBRIDGE_CONTROL_NOTREADY){ - isReady = false; - isConnected = true; - } - - // usb device detached - else if (nhp_read.data == HIDBRIDGE_CONTROL_NOTCONNECTED){ - isReady = false; - isConnected = false; - } - - // not defined control command - else - err(HIDBRIDGE_ERR_CONTROL); - } - - // received HID out report TODO - else - err(HIDBRIDGE_ERR_ADDRESS); - } - - // received HID out report TODO - else if (nhp_read.mode == NHP_COMMAND) { - err(HIDBRIDGE_ERR_COMMAND); - } + // NHP address contains control data or hid in/out report data + else if (nhp_read.mode == NHP_ADDRESS) + proceedAddress(); } // NHP reading error else if (nhp_read.errorLevel) { - err(HIDBRIDGE_ERR_NHP_ERR); - // do not change isReady state because of a possible full buffer + // ASCII + if (b < 128) { + // possible main mcu reset + if (!b) + err(HIDBRIDGE_ERR_MCU_RST); + else { //TODO different errors? + err(HIDBRIDGE_ERR_SERIALB); + } + } + else + err(HIDBRIDGE_ERR_SERIALB); + + // do not change rxReady state because of a possible full buffer // which causes NHP corruption + err(HIDBRIDGE_ERR_NHP_ERR); } - } + } // end of for reading loop + + // save new time + if (i) + readTimeout = millis(); } @@ -142,11 +164,172 @@ bool HIDBridge_::available(void) err(HIDBRIDGE_ERR_TIMEOUT); break; } - } while (!isReady); + } while (!txReady); - return isReady; + return txReady; } + +void HIDBridge_::proceedCommand(void){ +#ifdef DEBUG + debug->print("c"); + debug->println(nhp_read.command); +#endif + +#ifdef HIDBRIDGE_RX + // proceed a possible end flag + if (nhp_read.command == HIDBRIDGE_COMMAND_END) { + + // we've got a correct USB protocol received. Write it to the host now. + if (reportID && (recvLength == reportLength)) { + rxReady = false; //TODO not needed? +#ifdef USB_DEBUG + // debug print HID reports + debug->print("USBk "); + debug->print(reportID, DEC); + for (uint8_t i = 0; i < reportLength; i++) { + debug->print(", 0x"); + debug->print(hidReport[i], HEX); + } + debug->println(); +#endif + HID_SendReport(reportID, hidReport, reportLength); + + // acknowledge signal + rxReady = true; + writeState(); + } + // log an error + else + err(HIDBRIDGE_ERR_CMD_END); + + // reset reportID in any case + reportID = 0; + } + + // proceed a possible new reportID lead + else { + // flag an error if we have a pending report + if (reportID) + err(HIDBRIDGE_ERR_CMD_RID); + + // determine the new report length + switch (nhp_read.command) { //TODO progmem lookup table +#ifdef HID_MOUSE_ENABLE + case HID_REPORTID_MOUSE: + reportLength = sizeof(HID_MouseReport_Data_t); + break; +#endif + +#ifdef HID_MOUSE_ABSOLUTE_ENABLE + case HID_REPORTID_MOUSE_ABSOLUTE: + reportLength = sizeof(HID_MouseAbsoluteReport_Data_t); + break; +#endif + +#ifdef HID_KEYBOARD_ENABLE + case HID_REPORTID_KEYBOARD: + reportLength = sizeof(HID_KeyboardReport_Data_t); + break; +#endif + +#ifdef HID_CONSUMERCONTROL_ENABLE + case HID_REPORTID_CONSUMERCONTROL: + reportLength = sizeof(HID_ConsumerControlReport_Data_t); + break; +#endif + +#ifdef HID_SYSTEMCONTROL_ENABLE + case HID_REPORTID_SYSTEMCONTROL: + reportLength = sizeof(HID_SystemControlReport_Data_t); + break; +#endif + +#ifdef HID_GAMEPAD_ENABLE + case HID_REPORTID_GAMEPAD: + reportLength = sizeof(HID_GamepadReport_Data_t); + break; +#endif + + default: + // error + reportLength = 0; + break; + } + + if (reportLength) { + // save new report properties + reportID = nhp_read.command; + recvLength = 0; + } + else { + // new reportID is not supported + //TODO recv length =0? + reportID = 0; + err(HIDBRIDGE_ERR_COMMAND); + } + } +#else // ifdef HIDBRIDGE_RX + err(HIDBRIDGE_ERR_COMMAND); +#endif +} + + +void HIDBridge_::proceedAddress(void){ +#ifdef DEBUG + debug->print("a"); + debug->print(nhp_read.address); + debug->print(", "); + debug->println(nhp_read.data, HEX); +#endif + + // received a control address + if (nhp_read.address == HIDBRIDGE_CONTROL_ADDRESS) { + reportID = 0; + + // acknowledge/request + if (nhp_read.data == HIDBRIDGE_CONTROL_READY){ + txReady = true; + isConnected = true; + } + + // pause + else if (nhp_read.data == HIDBRIDGE_CONTROL_NOTREADY){ + txReady = false; + isConnected = true; + } + + // usb device detached + else if (nhp_read.data == HIDBRIDGE_CONTROL_NOTCONNECTED){ + txReady = false; + isConnected = false; + } + + // not defined control command + else + err(HIDBRIDGE_ERR_CONTROL); + } +#ifdef HIDBRIDGE_RX + // received correct reportID (in/out) + else if (reportID && (reportID == nhp_read.address) && (recvLength < reportLength)) { + for (uint8_t i = 0; i < 4; i++) { + // save hidReport + hidReport[recvLength++] = nhp_read.data8[i]; + + // abort if report finished + if (recvLength == reportLength) + break; + } + } +#endif + // received wrong data: lead command missing/wrong reportID/report too long + else { + reportID = 0; + err(HIDBRIDGE_ERR_ADDRESS); + } +} + + void HIDBridge_::SendReport(uint8_t reportID, const void* data, int len) { // check the latest request/acknowledge, pause or error @@ -156,37 +339,50 @@ void HIDBridge_::SendReport(uint8_t reportID, const void* data, int len) } // begin transfer with reportID as command - HIDBRIDGE_TX_SERIAL.write(writeNHPCommand(reportID)); + HIDBRIDGE_SERIAL.write(writeNHPCommand(reportID)); // send data in 4 byte packets with the address of the reportID // the rest (if any, e.g. with 2 byte) is filled with random bytes NHP_Write_Data_t n; for (int i = 0; i < len; i += 4) { writeNHPAddress(reportID, UINT32_AT_OFFSET(data, i), &n); - HIDBRIDGE_TX_SERIAL.write(n.writeBuffer, n.writeLength); + HIDBRIDGE_SERIAL.write(n.writeBuffer, n.writeLength); } // end transfer with zero command - HIDBRIDGE_TX_SERIAL.write(writeNHPCommand(0)); + HIDBRIDGE_SERIAL.write(writeNHPCommand(0)); // need a request/acknowledge next time again - isReady = false; + txReady = false; err(42); } +//================================================================================ +// Strong function implementations +//================================================================================ + +// IO MCU only +#ifdef HIDBRIDGE_IO +#ifdef HIDBRIDGE_TX // overwrites the HID_SendReport function which is empty/not used on a 328/2560 void HID_SendReport(uint8_t reportID, const void* data, int len) { HIDBridge.SendReport(reportID, data, len); } +#endif +#endif // #ifdef HIDBRIDGE_IO -#endif // #ifdef HIDBRIDGE_TX -//================================================================================ -// HIDBridge RX -//================================================================================ - -#ifdef HIDBRIDGE_RX - -#endif // #ifdef HIDBRIDGE_RX \ No newline at end of file +#ifdef HIDBRIDGE_USE_SERIAL_EVENT +/* +SerialEvent occurs whenever a new data comes in the +hardware serial RX. This routine is run between each +time loop() runs, so using delay inside loop can delay +response. Multiple bytes of data may be available. +*/ +void HIDBRIDGE_SERIAL_EVENT(void) { + HIDBridge.task(); + Serial.println("kk"); +} +#endif diff --git a/avr/libraries/HIDBridge/HIDBridge.h b/avr/libraries/HIDBridge/HIDBridge.h index b3aaf7d..089d680 100644 --- a/avr/libraries/HIDBridge/HIDBridge.h +++ b/avr/libraries/HIDBridge/HIDBridge.h @@ -31,11 +31,32 @@ THE SOFTWARE. // Settings //================================================================================ -#define HIDBRIDGE_TX_SERIAL Serial +//TODO replace with USB and IO +#define HIDBRIDGE_SERIAL SERIAL_PORT_HARDWARE -#ifdef HOODLOADER2 -#define HIDBRIDGE_RX +#define HIDBRIDGE_BAUD 2000000 // transfer at highest possible baud + +// use Arduino builtin serialEvent() function to call the HIDBridge task? +#define HIDBRIDGE_USE_SERIAL_EVENT + +//#define DEBUG +#define USB_DEBUG + +// which MCU should act as USB or IO part? +#ifdef USBCON +#define HIDBRIDGE_USB #else +#define HIDBRIDGE_IO +#endif + +// define if the MCU can do RX, TX or both +// minimal setup: USB RX, IO TX +#ifdef HIDBRIDGE_USB +#define HIDBRIDGE_RX +//#define HIDBRIDGE_TX +#endif +#ifdef HIDBRIDGE_IO +//#define HIDBRIDGE_RX #define HIDBRIDGE_TX #endif @@ -43,22 +64,28 @@ THE SOFTWARE. // Definitions //================================================================================ -#if defined(HIDBRIDGE_RX) && defined(HIDBRIDGE_TX) +#if defined(HIDBRIDGE_USB) && defined(HIDBRIDGE_IO) #error Cannot send and receive at the same time // also because it will create the instance of the class // even if its not used, so we dont separate the TX and RX class names // function names are kept similar #endif -#define HIDBRIDGE_TX_TIMEOUT 1000 +#define HIDBRIDGE_SERIAL_EVENT_WRAPPER_Serial serialEvent +#define HIDBRIDGE_SERIAL_EVENT_WRAPPER_Serial1 serialEvent1 +#define HIDBRIDGE_SERIAL_EVENT HIDBRIDGE_SERIAL_EVENT_WRAPPER_ ## Serial1 -#define HIDBRIDGE_BAUD 2000000 -#define HIDBRIDGE_ADDRESS_CONTROL 0 +#define HIDBRIDGE_TX_TIMEOUT 800 // should be smaller than RX +#define HIDBRIDGE_RX_TIMEOUT 1000 + +#define HIDBRIDGE_CONTROL_ADDRESS 0x00 +#define HIDBRIDGE_CONTROL_NOTREADY 0 +#define HIDBRIDGE_CONTROL_READY 1 +#define HIDBRIDGE_CONTROL_NOTCONNECTED 2 + +#define HIDBRIDGE_COMMAND_END 0 -#define HIDBRIDGE_CONTROL_ISREADY 0 -#define HIDBRIDGE_CONTROL_NOTREADY 1 -#define HIDBRIDGE_CONTROL_NOTCONNECTED 1 #define HIDBRIDGE_ERR_TIMEOUT 0 #define HIDBRIDGE_ERR_NHP_ERR 1 @@ -66,13 +93,18 @@ THE SOFTWARE. #define HIDBRIDGE_ERR_ADDRESS 3 #define HIDBRIDGE_ERR_CONTROL 4 #define HIDBRIDGE_ERR_NOT_RDY 5 +#define HIDBRIDGE_ERR_READ_TO 6 +#define HIDBRIDGE_ERR_CMD_END 7 +#define HIDBRIDGE_ERR_CMD_RID 8 +#define HIDBRIDGE_ERR_MCU_RST 9 +#define HIDBRIDGE_ERR_SERIALB 10 //================================================================================ -// HIDBridge TX +// HIDBridge //================================================================================ -#ifdef HIDBRIDGE_TX + class HIDBridge_{ public: HIDBridge_(void); @@ -81,6 +113,8 @@ public: void begin(void); void end(void); + void task(void); + // advanced user functions void read(void); bool available(void); @@ -90,58 +124,40 @@ public: // debug void err(uint8_t error); + void writeState(void); + inline void debugStream(Stream* s){ debug = s; } Stream* debug; + + + bool txReady; + bool rxReady; + + private: - bool isReady; + + // inlined functions to clear the code + // used in read(), not public accessible + inline void proceedAddress(void); //TODO change to always inline? + inline void proceedCommand(void); + bool isConnected; + uint32_t readTimeout; + uint32_t writeTimeout; + uint8_t recvLength; + uint8_t reportID; + uint8_t reportLength; + uint8_t hidReport[USB_EP_SIZE - 1]; // temporary NHP protocol read data NHP_Read_Data_t nhp_read; }; extern HIDBridge_ HIDBridge; -#endif -//================================================================================ -// HIDBridge RX -//================================================================================ - -#ifdef HIDBRIDGE_RX -class HIDBridge_{ -public: - HIDBridge_(void); - - // user functions - void begin(void); - void end(void); - - // advanced user functions - void read(void); - bool available(void); - - // public to access via HID_SendReport - void SendReport(uint8_t reportID, const void* data, int len); - - // debug - void err(uint8_t error); - inline void debugStream(Stream* s){ - debug = s; - } - Stream* debug; - -private: - bool isReady; - - // temporary NHP protocol read data - NHP_Read_Data_t nhp_read; -}; - -extern HIDBridge_ HIDBridge; -#endif //================================================================================ // Function prototypes