Updated HIDBridge and NHP

This commit is contained in:
Nico 2015-02-17 20:15:23 +01:00
parent 36e125a62d
commit 17045e3a92
4 changed files with 361 additions and 231 deletions

View file

@ -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);
}
}
#endif // #ifdef HIDBRIDGE_TX
//================================================================================
// HIDBridge RX
//================================================================================
#ifdef HIDBRIDGE_RX
#endif // #ifdef HIDBRIDGE_RX

View file

@ -25,18 +25,31 @@ THE SOFTWARE.
#define HIDBRIDGE_H
#include <Arduino.h>
#include <SoftwareSerial.h>
#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

View file

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

View file

@ -24,10 +24,14 @@ THE SOFTWARE.
#ifndef NHP_H
#define NHP_H
#ifdef __cplusplus
extern "C"{
#endif
#include <stdint.h> //uint_t definitions
#include <stdbool.h> //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
#endif // include guard