HID/avr/libraries/HIDBridge/HIDBridge.cpp
Nico 8392355ebe Updated HID-Bridge
Now works for USB and IO AVR. Still not complete.
2015-02-20 14:43:50 +01:00

388 lines
9.2 KiB
C++

/*
Copyright (c) 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 "HIDBridge.h"
HIDBridge_ HIDBridge;
HIDBridge_::HIDBridge_(void){
// empty
}
void HIDBridge_::begin(void)
{
// start the serial at our own baud rate
HIDBRIDGE_SERIAL.begin(HIDBRIDGE_BAUD);
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_SERIAL.end();
nhp_read.mode = NHP_RESET;
rxReady = false; //TODO
isConnected = false;
//TODO reset more stuff
}
void HIDBridge_::err(uint8_t error)
{
if (!debug)
return;
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
rx_buffer_index_t i;
for (i = 0; i < SERIAL_RX_BUFFER_SIZE; i++){
// read in new Serial byte
int b = HIDBRIDGE_SERIAL.read();
if (b < 0)
break;
// process with NHP protocol
if (readNHP(b, &nhp_read)) {
// 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 hid in/out report data
else if (nhp_read.mode == NHP_ADDRESS)
proceedAddress();
}
// NHP reading error
else if (nhp_read.errorLevel) {
// 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();
}
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
read();
// 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 (!txReady);
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
if (!available()){
err(HIDBRIDGE_ERR_NOT_RDY);
return;
}
// begin transfer with reportID as command
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_SERIAL.write(n.writeBuffer, n.writeLength);
}
// end transfer with zero command
HIDBRIDGE_SERIAL.write(writeNHPCommand(0));
// need a request/acknowledge next time again
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
#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