Updated HID-Bridge
Now works for USB and IO AVR. Still not complete.
This commit is contained in:
parent
389a45db9f
commit
8392355ebe
2 changed files with 332 additions and 120 deletions
|
|
@ -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
|
||||
#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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue