save work

This commit is contained in:
nanosonde 2019-11-19 11:49:40 +01:00
parent de210d3e31
commit ac2952b35e

View File

@ -15,93 +15,189 @@
extern bool sendKnxHidReport(uint8_t* data, uint16_t length); extern bool sendKnxHidReport(uint8_t* data, uint16_t length);
extern bool isKnxHidSendReportPossible(); extern bool isKnxHidSendReportPossible();
static const uint8_t descHidReport[] = // class UsbTunnelInterface
{
//TUD_HID_REPORT_DESC_KNXHID_INOUT(64)
0x06, 0xA0, 0xFF, // Usage Page (Vendor Defined 0xFFA0)
0x09, 0x01, // Usage (0x01)
0xA1, 0x01, // Collection (Application)
0x09, 0x01, // Usage (0x01)
0xA1, 0x00, // Collection (Physical)
0x06, 0xA1, 0xFF, // Usage Page (Vendor Defined 0xFFA1)
0x09, 0x03, // Usage (0x03)
0x09, 0x04, // Usage (0x04)
0x15, 0x80, // Logical Minimum (-128)
0x25, 0x7F, // Logical Maximum (127)
0x35, 0x00, // Physical Minimum (0)
0x45, 0xFF, // Physical Maximum (-1)
0x75, 0x08, // Report Size (8)
0x85, 0x01, // Report ID (1)
0x95, 0x3F, // Report Count (63)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x05, // Usage (0x05)
0x09, 0x06, // Usage (0x06)
0x15, 0x80, // Logical Minimum (-128)
0x25, 0x7F, // Logical Maximum (127)
0x35, 0x00, // Physical Minimum (0)
0x45, 0xFF, // Physical Maximum (-1)
0x75, 0x08, // Report Size (8)
0x85, 0x01, // Report ID (1)
0x95, 0x3F, // Report Count (63)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xC0 // End Collection
};
static uint16_t manufacturerId; UsbTunnelInterface::UsbTunnelInterface(CemiServer& cemiServer,
static uint16_t maskVersion; uint16_t mId,
uint16_t mV)
struct _rx_queue_frame_t : _cemiServer(cemiServer),
_manufacturerId(mId),
_maskVersion(mV)
{ {
uint8_t* data;
uint16_t length;
_rx_queue_frame_t* next;
};
static struct _rx_queue_t
{
_rx_queue_frame_t* front = nullptr;
_rx_queue_frame_t* back = nullptr;
} _rx_queue;
const uint8_t* getKnxHidReportDescriptor()
{
return &descHidReport[0];
} }
uint16_t getHidReportDescriptorLength() void UsbTunnelInterface::loop()
{ {
return sizeof(descHidReport); // Make sure that the USB HW is also ready to send another report
if (!isTxQueueEmpty() && isKnxHidSendReportPossible())
{
uint8_t* buffer;
uint16_t length;
loadNextTxFrame(&buffer, &length);
/*
print("cEMI USB TX len: ");
print(length);
print(" data: ");
printHex(" data: ", buffer, length);
*/
sendKnxTunnelHidReport(buffer, length);
}
if (!isRxQueueEmpty())
{
uint8_t* buffer;
uint16_t length;
loadNextRxBuffer(&buffer, &length);
handleKnxHidReport(buffer, length);
delete buffer;
}
} }
static void addFrameRxQueue(CemiFrame& frame) /* USB TX */
bool UsbTunnelInterface::sendCemiFrame(CemiFrame& frame)
{ {
_rx_queue_frame_t* rx_frame = new _rx_queue_frame_t; addFrameTxQueue(frame);
rx_frame->length = frame.dataLength(); return true;
rx_frame->data = new uint8_t[rx_frame->length]; }
rx_frame->next = nullptr;
memcpy(rx_frame->data, frame.data(), rx_frame->length); void UsbTunnelInterface::addFrameTxQueue(CemiFrame& frame)
{
_queue_buffer_t* tx_frame = new _queue_buffer_t;
tx_frame->length = frame.dataLength();
tx_frame->data = new uint8_t[tx_frame->length];
tx_frame->next = nullptr;
memcpy(tx_frame->data, frame.data(), tx_frame->length);
/* /*
print("cEMI USB RX len: "); print("cEMI USB TX len: ");
print(rx_frame->length); print(tx_frame->length);
printHex(" data:", rx_frame->data, rx_frame->length); printHex(" data:", tx_frame->data, tx_frame->length);
*/ */
if (_rx_queue.back == nullptr) if (_tx_queue.back == nullptr)
{ {
_rx_queue.front = _rx_queue.back = rx_frame; _tx_queue.front = _tx_queue.back = tx_frame;
} }
else else
{ {
_rx_queue.back->next = rx_frame; _tx_queue.back->next = tx_frame;
_rx_queue.back = rx_frame; _tx_queue.back = tx_frame;
} }
} }
static bool isRxQueueEmpty() bool UsbTunnelInterface::isTxQueueEmpty()
{
if (_tx_queue.front == nullptr)
{
return true;
}
return false;
}
void UsbTunnelInterface::loadNextTxFrame(uint8_t** sendBuffer, uint16_t* sendBufferLength)
{
if (_tx_queue.front == nullptr)
{
return;
}
_queue_buffer_t* tx_frame = _tx_queue.front;
*sendBuffer = tx_frame->data;
*sendBufferLength = tx_frame->length;
_tx_queue.front = tx_frame->next;
if (_tx_queue.front == nullptr)
{
_tx_queue.back = nullptr;
}
delete tx_frame;
}
void UsbTunnelInterface::sendKnxTunnelHidReport(uint8_t* data, uint16_t length)
{
uint8_t buffer[length + 11];
buffer[0] = 0x01; // ReportID (fixed 0x01)
buffer[1] = 0x13; // PacketInfo must be 0x13 (SeqNo: 1, Type: 3)
buffer[3] = 0x00; // Protocol version (fixed 0x00)
buffer[4] = 0x08; // USB KNX Transfer Protocol Header Length (fixed 0x08)
buffer[7] = 0x01; // KNX Tunneling (0x01)
buffer[8] = 0x03; // EMI ID: 3 -> cEMI
buffer[9] = 0x00; // Manufacturer
buffer[10] = 0x00; // Manufacturer
// Copy cEMI frame to buffer
memcpy(&buffer[11], data, length + HID_HEADER_SIZE + 8);
buffer[2] = 8 + length; // KNX USB Transfer Protocol Header length (8, only first packet!) + cEMI length
pushWord(length, &buffer[5]); // KNX USB Transfer Protocol Body length (cEMI length)
#ifdef DEBUG_TX_HID_REPORT
Serial1.print("TX HID report: len: ");
Serial1.println(buffer[2] + HID_HEADER_SIZE, DEC);
for (int i = 0; i < (buffer[2] + HID_HEADER_SIZE); i++)
{
if (buffer[i] < 16)
Serial1.print("0");
Serial1.print(buffer[i], HEX);
Serial1.print(" ");
}
Serial1.println("");
#endif
sendKnxHidReport(buffer, MAX_EP_SIZE);
}
/* USB RX */
// Invoked when received SET_REPORT control request or via interrupt out pipe
void UsbTunnelInterface::receiveKnxHidReport(uint8_t const* data, uint16_t bufSize)
{
// Check KNX ReportID (fixed 0x01)
if (data[0] == 0x01)
{
// We just store only the used space of the HID report buffer
// which is normally padded with 0 to fill the complete USB EP size (e.g. 64 bytes)
uint8_t packetLength = data[2] + HID_HEADER_SIZE;
UsbTunnelInterface::addBufferRxQueue(data, packetLength);
}
}
UsbTunnelInterface::_queue_t UsbTunnelInterface::_rx_queue;
void UsbTunnelInterface::addBufferRxQueue(const uint8_t* data, uint16_t length)
{
_queue_buffer_t* rx_buffer = new _queue_buffer_t;
rx_buffer->length = length;
rx_buffer->data = new uint8_t[rx_buffer->length];
rx_buffer->next = nullptr;
memcpy(rx_buffer->data, data, rx_buffer->length);
/*
print("cEMI USB RX len: ");
print(rx_buffer->length);
printHex(" data:", rx_buffer->data, rx_buffer->length);
*/
if (_rx_queue.back == nullptr)
{
_rx_queue.front =_rx_queue.back = rx_buffer;
}
else
{
_rx_queue.back->next = rx_buffer;
_rx_queue.back = rx_buffer;
}
}
bool UsbTunnelInterface::isRxQueueEmpty()
{ {
if (_rx_queue.front == nullptr) if (_rx_queue.front == nullptr)
{ {
@ -110,29 +206,79 @@ static bool isRxQueueEmpty()
return false; return false;
} }
static void loadNextRxFrame(uint8_t** receiveBuffer, uint16_t* receiveBufferLength) void UsbTunnelInterface::loadNextRxBuffer(uint8_t** receiveBuffer, uint16_t* receiveBufferLength)
{ {
if (_rx_queue.front == nullptr) if (_rx_queue.front == nullptr)
{ {
return; return;
} }
_rx_queue_frame_t* rx_frame = _rx_queue.front; _queue_buffer_t* rx_buffer = _rx_queue.front;
*receiveBuffer = rx_frame->data; *receiveBuffer = rx_buffer->data;
*receiveBufferLength = rx_frame->length; *receiveBufferLength = rx_buffer->length;
_rx_queue.front = rx_frame->next; _rx_queue.front = rx_buffer->next;
if (_rx_queue.front == nullptr) if (_rx_queue.front == nullptr)
{ {
_rx_queue.back = nullptr; _rx_queue.back = nullptr;
} }
delete rx_frame; delete rx_buffer;
} }
static void handleBusAccessServerProtocol(const uint8_t* requestData, uint16_t packetLength) void UsbTunnelInterface::handleKnxHidReport(uint8_t const* data, uint16_t bufSize)
{
if (data[1] == 0x13) // PacketInfo must be 0x13 (SeqNo: 1, Type: 3)
{
uint8_t packetLength = data[2];
#ifdef DEBUG_RX_HID_REPORT
Serial1.print("RX HID report: len: ");
Serial1.println(packetLength, DEC);
for (int i = 0; i < (packetLength + HID_HEADER_SIZE); i++)
{
if (data[i] < 16)
Serial1.print("0");
Serial1.print(data[i], HEX);
Serial1.print(" ");
}
Serial1.println("");
#endif
if (data[3] == 0x00 && // Protocol version (fixed 0x00)
data[4] == 0x08) // USB KNX Transfer Protocol Header Length (fixed 0x08)
{
if (data[7] == 0x0F) // Bus Access Server Feature (0x0F)
{
handleBusAccessServerProtocol(&data[0], packetLength + HID_HEADER_SIZE);
}
else if (data[7] == 0x01 && // KNX Tunneling (0x01)
data[8] == 0x03) // EMI type: only cEMI supported
{
uint16_t bodyLength;
popWord(bodyLength, (uint8_t*)&data[5]); // KNX USB Transfer Protocol Body length
// Prepare the cEMI frame
CemiFrame frame((uint8_t*)&data[11], bodyLength);
/*
print("cEMI USB RX len: ");
print(length);
print(" data: ");
printHex(" data: ", buffer, length);
*/
_cemiServer.frameReceived(frame);
}
}
}
}
void UsbTunnelInterface::handleBusAccessServerProtocol(const uint8_t* requestData, uint16_t packetLength)
{ {
if (packetLength > MAX_EP_SIZE) if (packetLength > MAX_EP_SIZE)
return; return;
// Create a copy of the request data so that we can
// simply append the response data later
uint8_t data[MAX_EP_SIZE]; uint8_t data[MAX_EP_SIZE];
memset(data, 0, sizeof(data)); memset(data, 0, sizeof(data));
memcpy(data, requestData, packetLength); memcpy(data, requestData, packetLength);
@ -158,7 +304,7 @@ static void handleBusAccessServerProtocol(const uint8_t* requestData, uint16_t p
case 0x02: // Host Device Descriptor Type 0 case 0x02: // Host Device Descriptor Type 0
Serial1.println("Device Feature Get: Host Device Descriptor Type 0"); Serial1.println("Device Feature Get: Host Device Descriptor Type 0");
respDataSize = 2; respDataSize = 2;
pushWord(maskVersion, &data[12]); // USB KNX Transfer Protocol Body: Feature Data -> Mask version pushWord(_maskVersion, &data[12]); // USB KNX Transfer Protocol Body: Feature Data -> Mask version
break; break;
case 0x03: // Bus connection status case 0x03: // Bus connection status
Serial1.println("Device Feature Get: Bus connection status"); Serial1.println("Device Feature Get: Bus connection status");
@ -168,7 +314,7 @@ static void handleBusAccessServerProtocol(const uint8_t* requestData, uint16_t p
case 0x04: // KNX manufacturer code case 0x04: // KNX manufacturer code
Serial1.println("Device Feature Get: KNX manufacturer code"); Serial1.println("Device Feature Get: KNX manufacturer code");
respDataSize = 2; respDataSize = 2;
pushWord(manufacturerId, &data[12]); // USB KNX Transfer Protocol Body: Feature Data -> Manufacturer Code pushWord(_manufacturerId, &data[12]); // USB KNX Transfer Protocol Body: Feature Data -> Manufacturer Code
break; break;
case 0x05: // Active EMI type case 0x05: // Active EMI type
Serial1.println("Device Feature Get: Active EMI type"); Serial1.println("Device Feature Get: Active EMI type");
@ -235,191 +381,47 @@ static void handleBusAccessServerProtocol(const uint8_t* requestData, uint16_t p
} }
} }
static void sendKnxTunnelHidReport(uint8_t* data, uint16_t length) /* USB HID report descriptor for KNX HID */
const uint8_t UsbTunnelInterface::descHidReport[] =
{ {
uint8_t buffer[length + 11]; //TUD_HID_REPORT_DESC_KNXHID_INOUT(64)
0x06, 0xA0, 0xFF, // Usage Page (Vendor Defined 0xFFA0)
0x09, 0x01, // Usage (0x01)
0xA1, 0x01, // Collection (Application)
0x09, 0x01, // Usage (0x01)
0xA1, 0x00, // Collection (Physical)
0x06, 0xA1, 0xFF, // Usage Page (Vendor Defined 0xFFA1)
0x09, 0x03, // Usage (0x03)
0x09, 0x04, // Usage (0x04)
0x15, 0x80, // Logical Minimum (-128)
0x25, 0x7F, // Logical Maximum (127)
0x35, 0x00, // Physical Minimum (0)
0x45, 0xFF, // Physical Maximum (-1)
0x75, 0x08, // Report Size (8)
0x85, 0x01, // Report ID (1)
0x95, 0x3F, // Report Count (63)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x05, // Usage (0x05)
0x09, 0x06, // Usage (0x06)
0x15, 0x80, // Logical Minimum (-128)
0x25, 0x7F, // Logical Maximum (127)
0x35, 0x00, // Physical Minimum (0)
0x45, 0xFF, // Physical Maximum (-1)
0x75, 0x08, // Report Size (8)
0x85, 0x01, // Report ID (1)
0x95, 0x3F, // Report Count (63)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xC0 // End Collection
};
buffer[0] = 0x01; // ReportID (fixed 0x01) const uint8_t* UsbTunnelInterface::getKnxHidReportDescriptor()
buffer[1] = 0x13; // PacketInfo must be 0x13 (SeqNo: 1, Type: 3) {
buffer[3] = 0x00; // Protocol version (fixed 0x00) return &descHidReport[0];
buffer[4] = 0x08; // USB KNX Transfer Protocol Header Length (fixed 0x08)
buffer[7] = 0x01; // KNX Tunneling (0x01)
buffer[8] = 0x03; // EMI ID: 3 -> cEMI
buffer[9] = 0x00; // Manufacturer
buffer[10] = 0x00; // Manufacturer
// Copy cEMI frame to buffer
memcpy(&buffer[11], data, length + HID_HEADER_SIZE + 8);
buffer[2] = 8 + length; // KNX USB Transfer Protocol Header length (8, only first packet!) + cEMI length
pushWord(length, &buffer[5]); // KNX USB Transfer Protocol Body length (cEMI length)
#ifdef DEBUG_TX_HID_REPORT
Serial1.print("TX HID report: len: ");
Serial1.println(buffer[2] + HID_HEADER_SIZE, DEC);
for (int i = 0; i < (buffer[2] + HID_HEADER_SIZE); i++)
{
if (buffer[i] < 16)
Serial1.print("0");
Serial1.print(buffer[i], HEX);
Serial1.print(" ");
}
Serial1.println("");
#endif
sendKnxHidReport(buffer, MAX_EP_SIZE);
} }
// Invoked when received SET_REPORT control request or via interrupt out pipe uint16_t UsbTunnelInterface::getHidReportDescriptorLength()
void handleKnxHidReport(uint8_t const* data, uint16_t bufSize)
{ {
if (bufSize!=MAX_EP_SIZE) return sizeof(descHidReport);
return;
if (data[0] == 0x01 && // ReportID (fixed 0x01)
data[1] == 0x13) // PacketInfo must be 0x13 (SeqNo: 1, Type: 3)
{
uint8_t packetLength = data[2];
#ifdef DEBUG_RX_HID_REPORT
Serial1.print("RX HID report: len: ");
Serial1.println(packetLength, DEC);
for (int i = 0; i < (packetLength + HID_HEADER_SIZE); i++)
{
if (data[i] < 16)
Serial1.print("0");
Serial1.print(data[i], HEX);
Serial1.print(" ");
}
Serial1.println("");
#endif
if (data[3] == 0x00 && // Protocol version (fixed 0x00)
data[4] == 0x08) // USB KNX Transfer Protocol Header Length (fixed 0x08)
{
if (data[7] == 0x0F) // Bus Access Server Feature (0x0F)
{
handleBusAccessServerProtocol(&data[0], packetLength + HID_HEADER_SIZE);
}
else if (data[7] == 0x01 && // KNX Tunneling (0x01)
data[8] == 0x03) // EMI type: only cEMI supported
{
uint16_t bodyLength;
popWord(bodyLength, (uint8_t*)&data[5]); // KNX USB Transfer Protocol Body length
CemiFrame frame((uint8_t*)&data[11], bodyLength);
addFrameRxQueue(frame);
}
}
}
}
// class UsbTunnelInterface
UsbTunnelInterface::UsbTunnelInterface(CemiServer& cemiServer,
uint16_t mId,
uint16_t mV)
: _cemiServer(cemiServer)
{
manufacturerId = mId;
maskVersion = mV;
}
void UsbTunnelInterface::loop()
{
// Make sure that the USB HW is also ready to send another report
if (!isTxQueueEmpty() && isKnxHidSendReportPossible())
{
uint8_t* buffer;
uint16_t length;
loadNextTxFrame(&buffer, &length);
/*
print("cEMI USB TX len: ");
print(length);
print(" data: ");
printHex(" data: ", buffer, length);
*/
sendKnxTunnelHidReport(buffer, length);
}
if (!isRxQueueEmpty())
{
uint8_t* buffer;
uint16_t length;
loadNextRxFrame(&buffer, &length);
// Prepare the cEMI frame
CemiFrame frame(buffer, length);
/*
print("cEMI USB RX len: ");
print(length);
print(" data: ");
printHex(" data: ", buffer, length);
*/
_cemiServer.frameReceived(frame);
}
}
bool UsbTunnelInterface::sendCemiFrame(CemiFrame& frame)
{
addFrameTxQueue(frame);
return true;
}
void UsbTunnelInterface::addFrameTxQueue(CemiFrame& frame)
{
_tx_queue_frame_t* tx_frame = new _tx_queue_frame_t;
tx_frame->length = frame.dataLength();
tx_frame->data = new uint8_t[tx_frame->length];
tx_frame->next = nullptr;
memcpy(tx_frame->data, frame.data(), tx_frame->length);
/*
print("cEMI USB TX len: ");
print(tx_frame->length);
printHex(" data:", tx_frame->data, tx_frame->length);
*/
if (_tx_queue.back == nullptr)
{
_tx_queue.front = _tx_queue.back = tx_frame;
}
else
{
_tx_queue.back->next = tx_frame;
_tx_queue.back = tx_frame;
}
}
bool UsbTunnelInterface::isTxQueueEmpty()
{
if (_tx_queue.front == nullptr)
{
return true;
}
return false;
}
void UsbTunnelInterface::loadNextTxFrame(uint8_t** sendBuffer, uint16_t* sendBufferLength)
{
if (_tx_queue.front == nullptr)
{
return;
}
_tx_queue_frame_t* tx_frame = _tx_queue.front;
*sendBuffer = tx_frame->data;
*sendBufferLength = tx_frame->length;
_tx_queue.front = tx_frame->next;
if (_tx_queue.front == nullptr)
{
_tx_queue.back = nullptr;
}
delete tx_frame;
} }