mirror of
https://github.com/thelsing/knx.git
synced 2025-08-13 13:46:20 +02:00
save work
This commit is contained in:
parent
de210d3e31
commit
ac2952b35e
@ -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;
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user