Updated HID-Bridge

Now works for USB and IO AVR. Still not complete.
This commit is contained in:
Nico 2015-02-20 14:43:50 +01:00
parent 389a45db9f
commit 8392355ebe
2 changed files with 332 additions and 120 deletions

View file

@ -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

View file

@ -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