diff --git a/avr/libraries/HIDBridge/HIDBridge.cpp b/avr/libraries/HIDBridge/HIDBridge.cpp index 9f59045..438b649 100644 --- a/avr/libraries/HIDBridge/HIDBridge.cpp +++ b/avr/libraries/HIDBridge/HIDBridge.cpp @@ -24,23 +24,33 @@ THE SOFTWARE. #include "HIDBridge.h" //================================================================================ -// HIDBridge +// HIDBridge TX //================================================================================ +#ifdef HIDBRIDGE_TX + HIDBridge_ HIDBridge; HIDBridge_::HIDBridge_(void){ // empty } -bool HIDBridge_::begin(Stream &s) +void HIDBridge_::begin(void) { - begin((Stream*)&s); + // start the serial at our own baud rate + HIDBRIDGE_TX_SERIAL.begin(HIDBRIDGE_BAUD); + + // wait for the first request to see if usb device is connected + available(); } -bool HIDBridge_::begin(Stream* s) +void HIDBridge_::end(void) { - HIDStream = s; + // end the serial transmission and reset our helper values + HIDBRIDGE_TX_SERIAL.end(); + nhp_read.mode = NHP_RESET; + isReady = false; + isConnected = false; } @@ -48,48 +58,60 @@ void HIDBridge_::err(uint8_t error) { if (!debug) return; - debug->println("Softserial"); + debug->print("Bridge Err TX: "); debug->println(error); } -void HIDBridge_::readSerial(void) +void HIDBridge_::read(void) { // 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++){ + // read in new Serial byte - int b = Serial.read(); + int b = HIDBRIDGE_TX_SERIAL.read(); if (b < 0) break; // process with NHP protocol - bool newInput = NHPread(b, &nhp_read); + bool newInput = readNHP(b, &nhp_read); // proceed new valid NHP input if (newInput) { + + // NHP address contains control data or out report data if (nhp_read.mode == NHP_ADDRESS) { - switch (nhp_read.address) { - // received a control address command - case HIDBRIDGE_ADDRESS_CONTROL: + + // received a control address command + if (nhp_read.address == HIDBRIDGE_ADDRESS_CONTROL) { // acknowledge/request - if (nhp_read.data == HIDBRIDGE_CONTROL_ISREADY) + if (nhp_read.data == HIDBRIDGE_CONTROL_ISREADY){ isReady = true; + isConnected = true; + } // pause - else if (nhp_read.data == HIDBRIDGE_CONTROL_NOTREADY) + else if (nhp_read.data == HIDBRIDGE_CONTROL_NOTREADY){ isReady = false; + isConnected = true; + } - // not + // usb device detached + else if (nhp_read.data == HIDBRIDGE_CONTROL_NOTCONNECTED){ + isReady = false; + isConnected = false; + } + + // not defined control command else err(HIDBRIDGE_ERR_CONTROL); - - break; - // received HID out report TODO - default: - err(HIDBRIDGE_ERR_ADDRESS); - break; } + + // 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); @@ -106,52 +128,46 @@ void HIDBridge_::readSerial(void) } -bool HIDBridge_::waitForReady(void) +bool HIDBridge_::available(void) { // try to wait for a new request/acknowledge uint32_t currentMillis = millis(); do{ // check for new state information // maybe the host sended a pause signal - readSerial(); + read(); - // check for timeout - if ((millis() - currentMillis) > HIDBRIDGE_TX_TIMEOUT) { + // check for timeout, do not wait longer if usb device is not connected + if (!isConnected || (millis() - currentMillis) > HIDBRIDGE_TX_TIMEOUT) { err(HIDBRIDGE_ERR_TIMEOUT); break; } - } while (!isReady); //TODO andn no error in readSerial? + } while (!isReady); return isReady; } void HIDBridge_::SendReport(uint8_t reportID, const void* data, int len) { - // check if stream pointer is set - if (!HIDStream){ - err(HIDBRIDGE_ERR_NO_SPTR); - return; - } - - // check the latest request/acknowledge,a pause, error - if (!waitForReady()){ + // check the latest request/acknowledge, pause or error + if (!available()){ err(HIDBRIDGE_ERR_NOT_RDY); return; } // begin transfer with reportID as command - HIDStream->write(NHPwriteCommand(reportID)); + HIDBRIDGE_TX_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) { - NHPwriteAddress(reportID, UINT32_AT_OFFSET(data, i), &n); - HIDStream->write(n.writeBuffer, n.writeLength); + writeNHPAddress(reportID, UINT32_AT_OFFSET(data, i), &n); + HIDBRIDGE_TX_SERIAL.write(n.writeBuffer, n.writeLength); } // end transfer with zero command - HIDStream->write(NHPwriteCommand(0)); + HIDBRIDGE_TX_SERIAL.write(writeNHPCommand(0)); // need a request/acknowledge next time again isReady = false; @@ -163,4 +179,14 @@ void HIDBridge_::SendReport(uint8_t reportID, const void* data, int len) void HID_SendReport(uint8_t reportID, const void* data, int len) { HIDBridge.SendReport(reportID, data, len); -} \ No newline at end of file +} + +#endif // #ifdef HIDBRIDGE_TX + +//================================================================================ +// HIDBridge RX +//================================================================================ + +#ifdef HIDBRIDGE_RX + +#endif // #ifdef HIDBRIDGE_RX \ No newline at end of file diff --git a/avr/libraries/HIDBridge/HIDBridge.h b/avr/libraries/HIDBridge/HIDBridge.h index 07e4952..b3aaf7d 100644 --- a/avr/libraries/HIDBridge/HIDBridge.h +++ b/avr/libraries/HIDBridge/HIDBridge.h @@ -25,18 +25,31 @@ THE SOFTWARE. #define HIDBRIDGE_H #include -#include #include "NHP.h" //================================================================================ // Settings //================================================================================ +#define HIDBRIDGE_TX_SERIAL Serial + +#ifdef HOODLOADER2 +#define HIDBRIDGE_RX +#else +#define HIDBRIDGE_TX +#endif //================================================================================ // Definitions //================================================================================ +#if defined(HIDBRIDGE_RX) && defined(HIDBRIDGE_TX) +#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_BAUD 2000000 @@ -45,6 +58,7 @@ THE SOFTWARE. #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 @@ -52,41 +66,82 @@ THE SOFTWARE. #define HIDBRIDGE_ERR_ADDRESS 3 #define HIDBRIDGE_ERR_CONTROL 4 #define HIDBRIDGE_ERR_NOT_RDY 5 -#define HIDBRIDGE_ERR_NO_SPTR 6 //================================================================================ -// HIDBridge +// HIDBridge TX //================================================================================ - +#ifdef HIDBRIDGE_TX class HIDBridge_{ public: HIDBridge_(void); - inline void debugStream(Stream* s){ - debug = s; - } - bool begin(Stream &s); - bool begin(Stream* s); - void readSerial(void); - bool waitForReady(void); - bool isReady; - void task(void); - void err(uint8_t error); + + // 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); -private: + // debug + void err(uint8_t error); + inline void debugStream(Stream* s){ + debug = s; + } Stream* debug; - Stream* HIDStream; //TODO template? +private: + bool isReady; + bool isConnected; + + // 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 diff --git a/avr/libraries/HIDBridge/NHP.c b/avr/libraries/HIDBridge/NHP.c new file mode 100644 index 0000000..d2560fc --- /dev/null +++ b/avr/libraries/HIDBridge/NHP.c @@ -0,0 +1,195 @@ +/* +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 "NHP.h" + +//================================================================================ +// Read NHP +//================================================================================ + +bool readNHP(uint8_t input, NHP_Read_Data_t* protocol) { + // get old protocol states and save into temporary variables (better compiler optimization) + uint8_t readLength = protocol->readLength; + uint8_t mode; + uint8_t blocks = protocol->blocks; + uint8_t errorLevel = protocol->errorLevel; + uint32_t data = protocol->data; + uint8_t address; + bool newInput = false; + + // completely reset the protocol after sucessfull reading/error last time + if (protocol->mode) { + blocks = 0; + readLength = 0; + } + // check if previous reading had a lead error, copy that lead byte to the beginning + else if (errorLevel == NHP_ERR_LEAD) { + protocol->readBuffer[0] = protocol->readBuffer[readLength]; + readLength = 1; + } + + // write new byte input to the buffer + protocol->readBuffer[readLength++] = input; + + // reset mode and errorLevel to the default (no error, no input) + errorLevel = NHP_ERR_NONE; + mode = NHP_IN_PROGRESS; + + // check the header(lead/data/end) indicator + uint8_t header = input & NHP_HEADER_MASK; + + if (header == NHP_HEADER_LEAD) + { + if (blocks) { + // we were still reading! Log an error but continue reading with this new lead + // set indicator to move this lead byte to the beginning next reading + errorLevel = NHP_ERR_LEAD; + // write the buffer without the new lead, move it next reading + readLength--; + } + + // read command indicator or block length + blocks = (input >> NHP_LENGTH_OFFSET) & (NHP_LENGTH_MASK >> NHP_LENGTH_OFFSET); + + if (blocks == NHP_LENGTH_COMMAND_0 || blocks == NHP_LENGTH_COMMAND_1) { + // save command in data variable + data = input & NHP_COMMAND_MASK; + // return command indicator, reset next reading + mode = NHP_COMMAND; + newInput = true; + } + // address data + else if (blocks == NHP_LENGTH_HIGH_MSB31) { + // save block length + first 4 data bits (special 32 bit case) + data = input & NHP_DATA_4BIT_MASK; + blocks -= 2; + } + else { + // save block length + first 3 data bits + data = input & NHP_DATA_3BIT_MASK; + blocks--; + } + } + + else if (header == NHP_HEADER_END) + { + // reset next reading on both: valid input or error + if (blocks == 1){ + // valid input, save the address + address = input & NHP_ADDRESS_MASK; + mode = NHP_ADDRESS; + newInput = true; + } + else{ + // too early for an end, reset next time + errorLevel = NHP_ERR_END; + mode = NHP_RESET; + } + } + + else if (header == NHP_HEADER_DATA_A || header == NHP_HEADER_DATA_B) + { + if (blocks > 1) { + // get next 7 bits of data + blocks--; + data <<= NHP_DATA_7BIT; + // normally dont need & NHP_DATA_7BIT_MASK because the MSB bit is zero + data |= (input & NHP_DATA_7BIT_MASK); + } + else { + // log an error, expecting a lead or end byte + errorLevel = NHP_ERR_DATA; + mode = NHP_RESET; + } + } + + // save temporary values to the data struct + protocol->blocks = blocks; + protocol->mode = mode; + protocol->readLength = readLength; + protocol->address = address; + protocol->errorLevel = errorLevel; + protocol->data = data; + + // return if we have a new address or command + return newInput; +} + +//================================================================================ +// Write NHP +//================================================================================ + +uint8_t writeNHPCommand(uint8_t command) { + // write lead mask 11 + length 00|0 or 00|1 including the last bit for the 4 bit command + // return the command with protocol around + return NHP_HEADER_LEAD | (command & NHP_COMMAND_MASK); +} + +void writeNHPAddress(uint8_t address, uint32_t data, NHP_Write_Data_t* protocol) { + // start with the maximum size of blocks (6+1 for special MSB case) + uint8_t blocks = 7; + + // check for the first 7 bit block that doesnt fit into the first 3 bits + while (blocks > 2) { + // get the next 7 bit data block, starting from MSB to LSB + uint8_t nextvalue = (data >> (7 * (blocks - 3))); + if (nextvalue > NHP_DATA_3BIT_MASK) { + // data won't fit into the first 3 bits, wee need an extra block for them + // don't write them to the lead block, keep the data for the data blocks + if (blocks == 7) { + // special case for the MSB where we still want to write + // the 'too big' value into the lead block + protocol->writeBuffer[0] = nextvalue; + blocks = 6; + } + break; + } + else { + // write the possible first 3 bits and check again if the value is zero + // this also ensures that the first byte is always initialized + protocol->writeBuffer[0] = nextvalue; + blocks--; + + // we have our first bits, stop + if (nextvalue) + break; + } + } + + // write the rest of the data blocks + uint8_t datablocks = blocks - 2; + while (datablocks > 0) { + protocol->writeBuffer[datablocks] = data & NHP_DATA_7BIT_MASK; + data >>= 7; + datablocks--; + } + + // add lead 11 + length to the first 3 (or 4 for special MSB case) data bits + protocol->writeBuffer[0] |= NHP_HEADER_LEAD | (blocks << NHP_LENGTH_OFFSET); + + // write end 10 + address + protocol->writeBuffer[blocks - 1] = NHP_HEADER_END | (address & NHP_ADDRESS_MASK); + + // save the number of blocks + protocol->writeLength = blocks; +} \ No newline at end of file diff --git a/avr/libraries/HIDBridge/NHP.h b/avr/libraries/HIDBridge/NHP.h index 99e8eee..71e29f9 100644 --- a/avr/libraries/HIDBridge/NHP.h +++ b/avr/libraries/HIDBridge/NHP.h @@ -24,10 +24,14 @@ THE SOFTWARE. #ifndef NHP_H #define NHP_H + +#ifdef __cplusplus +extern "C"{ +#endif + #include //uint_t definitions #include //bool type - //================================================================================ //Settings //================================================================================ @@ -48,9 +52,13 @@ THE SOFTWARE. // Lead #define NHP_LENGTH_MASK 0x38 // B00|111|000 #define NHP_LENGTH_OFFSET 3 +#define NHP_LENGTH_COMMAND_0 0 // length 0 indicates a command +#define NHP_LENGTH_COMMAND_1 1 // length 1 indicates a command +#define NHP_LENGTH_HIGH_MSB31 7 // length 7 indicates MSB 31 is high (1) #define NHP_COMMAND_MASK 0x0F // B0000|1111 // 4bit command(0-15) in lead block // Data +#define NHP_DATA_7BIT 7 // data blocks contain 7 bit of information #define NHP_DATA_7BIT_MASK 0x7F // B0|1111111 // data in data block #define NHP_DATA_4BIT_MASK 0x0F // B0000|1111 // data in lead (32 bit special MSB case) #define NHP_DATA_3BIT_MASK 0x07 // B00000|111 // data in lead @@ -79,9 +87,13 @@ THE SOFTWARE. #define UINT16_AT_OFFSET(p_to_8, offset) ((uint16_t)*((const uint16_t *)((p_to_8)+(offset)))) #define UINT32_AT_OFFSET(p_to_8, offset) ((uint32_t)*((const uint32_t *)((p_to_8)+(offset)))) -// protocol data for temporary variables +//================================================================================ +// Typedefs +//================================================================================ + +// protocol read data for temporary variables typedef union { - struct { //TODO change order to save flash? + struct { // buffer for read operations uint8_t readBuffer[NHP_READ_BUFFER_SIZE]; @@ -96,15 +108,15 @@ typedef union { // temporary + final data uint32_t data; uint32_t data32; - uint16_t data16[sizeof(data) / sizeof(uint16_t)]; - uint8_t data8[sizeof(data)]; + uint16_t data16[sizeof(uint32_t) / sizeof(uint16_t)]; + uint8_t data8[sizeof(uint32_t) / sizeof(uint8_t)]; uint8_t command : 4; }; }; - uint8_t raw[sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t) + sizeof(readBuffer)]; + uint8_t raw[NHP_READ_BUFFER_SIZE + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t)]; } NHP_Read_Data_t; -// protocol data for temporary variables +// protocol write data for temporary variables typedef union { struct{ // buffer for write operations @@ -112,177 +124,19 @@ typedef union { uint8_t writeLength; }; - uint8_t raw[sizeof(writeBuffer) + sizeof(uint8_t)]; + uint8_t raw[NHP_WRITE_BUFFER_SIZE + sizeof(uint8_t)]; } NHP_Write_Data_t; //================================================================================ -// Protocol Function Prototypes +// Function Prototypes //================================================================================ -static bool NHPread(uint8_t input, NHP_Read_Data_t* protocol) { - // get old protocol states and save into temporary variables (better compiler optimization) - uint8_t readLength = protocol->readLength; - uint8_t mode; - uint8_t blocks = protocol->blocks; - uint8_t errorLevel = protocol->errorLevel; - uint32_t data = protocol->data; - uint8_t address; - bool newInput = false; +bool readNHP(uint8_t input, NHP_Read_Data_t* protocol); +uint8_t writeNHPCommand(uint8_t command); +void writeNHPAddress(uint8_t address, uint32_t data, NHP_Write_Data_t* protocol); - // completely reset the protocol after sucessfull reading/error last time - if (protocol->mode) { - blocks = 0; - readLength = 0; - } - // check if previous reading had a lead error, copy that lead byte to the beginning - else if (errorLevel == NHP_ERR_LEAD) { - protocol->readBuffer[0] = protocol->readBuffer[readLength]; - readLength = 1; - } +#ifdef __cplusplus +} // extern "C" +#endif - // write new byte input to the buffer - protocol->readBuffer[readLength++] = input; - - // reset mode and errorLevel to the default (no error, no input) - errorLevel = NHP_ERR_NONE; - mode = NHP_IN_PROGRESS; - - // check the header(lead/data/end) indicator - uint8_t header = input & NHP_HEADER_MASK; - - if (header == NHP_HEADER_LEAD) - { - if (blocks) { - // we were still reading! Log an error but continue reading with this new lead - // set indicator to move this lead byte to the beginning next reading - errorLevel = NHP_ERR_LEAD; - // write the buffer without the new lead, move it next reading - readLength--; - } - - // read command indicator or block length - blocks = (input >> NHP_LENGTH_OFFSET) & (NHP_LENGTH_MASK >> NHP_LENGTH_OFFSET); - - if (blocks == 0 || blocks == 1) { - // save command in data variable - data = input & NHP_COMMAND_MASK; - // return command indicator, reset next reading - mode = NHP_COMMAND; - newInput = true; - } - // address data - else if (blocks == 7) { - // save block length + first 4 data bits (special 32 bit case) - data = input & NHP_DATA_4BIT_MASK; - blocks -= 2; - } - else { - // save block length + first 3 data bits - data = input & NHP_DATA_3BIT_MASK; - blocks--; - } - } - - else if (header == NHP_HEADER_END) - { - // reset next reading on both: valid input or error - if (blocks == 1){ - // valid input, save the address - address = input & NHP_ADDRESS_MASK; - mode = NHP_ADDRESS; - newInput = true; - } - else{ - // too early for an end, reset next time - errorLevel = NHP_ERR_END; - mode = NHP_RESET; - } - } - - else if (header == NHP_HEADER_DATA_A || header == NHP_HEADER_DATA_B) - { - if (blocks >= 2) { - // get next 7 bits of data - blocks--; - data <<= 7; - // normally dont need & NHP_DATA_7BIT_MASK because the MSB bit is zero - data |= (input & NHP_DATA_7BIT_MASK); - } - else { - // log an error, expecting a lead or end byte - errorLevel = NHP_ERR_DATA; - mode = NHP_RESET; - } - } - - // save temporary values to the data struct - protocol->blocks = blocks; - protocol->mode = mode; - protocol->readLength = readLength; - protocol->address = address; - protocol->errorLevel = errorLevel; - protocol->data = data; - - // return if we have a new address or command - return newInput; -} - -//================================================================================ -// Write NHP -//================================================================================ - -static uint8_t NHPwriteCommand(uint8_t command) { - // write lead mask 11 + length 00|0 or 00|1 including the last bit for the 4 bit command - // return the command with protocol around - return NHP_HEADER_LEAD | (command & NHP_COMMAND_MASK); -} - -static void NHPwriteAddress(uint8_t address, uint32_t data, NHP_Write_Data_t* protocol) { - // start with the maximum size of blocks (6+1 for special MSB case) - uint8_t blocks = 7; - - // check for the first 7 bit block that doesnt fit into the first 3 bits - while (blocks > 2) { - uint8_t nextvalue = (data >> (7 * (blocks - 3))); - if (nextvalue > NHP_DATA_3BIT_MASK) { - // data won't fit into the first 3 bits, wee need an extra block for them - // don't write them to the lead block, keep the data for the data blocks - if (blocks == 7) { - // special case for the MSB where we still want to write - // the 'too big' value into the lead block - protocol->writeBuffer[0] = nextvalue; - blocks = 6; - } - break; - } - else { - // write the possible first 3 bits and check again if the value is zero - // this also ensures that the first byte is always initialized - protocol->writeBuffer[0] = nextvalue; - blocks--; - - // we have our first bits, stop - if (nextvalue) - break; - } - } - - // write the rest of the data blocks - uint8_t datablocks = blocks - 2; - while (datablocks > 0) { - protocol->writeBuffer[datablocks] = data & NHP_DATA_7BIT_MASK; - data >>= 7; - datablocks--; - } - - // add lead 11 + length to the first 3 (or 4 for special MSB case) data bits - protocol->writeBuffer[0] |= NHP_HEADER_LEAD | (blocks << NHP_LENGTH_OFFSET); - - // write end 10 + address - protocol->writeBuffer[blocks - 1] = NHP_HEADER_END | (address & NHP_ADDRESS_MASK); - - // save the number of blocks - protocol->writeLength = blocks; -} - -#endif \ No newline at end of file +#endif // include guard \ No newline at end of file