kopia lustrzana https://github.com/mobilinkd/tnc3-firmware
Handle single-byte transfers over USB more efficiently. Linux seems to do only single byte transfers for ACM ports.
rodzic
05bbab157b
commit
b19e1a5a5d
195
TNC/UsbPort.cpp
195
TNC/UsbPort.cpp
|
@ -1,10 +1,11 @@
|
||||||
// Copyright 2016 Rob Riggs <rob@mobilinkd.com>
|
// Copyright 2016 Rob Riggs <rob@mobilinkd.com>
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
#include <Log.h>
|
|
||||||
#include "UsbPort.hpp"
|
#include "UsbPort.hpp"
|
||||||
#include "HdlcFrame.hpp"
|
#include "HdlcFrame.hpp"
|
||||||
#include "Kiss.hpp"
|
#include "Kiss.hpp"
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
#include "usbd_cdc_if.h"
|
#include "usbd_cdc_if.h"
|
||||||
#include "usb_device.h"
|
#include "usb_device.h"
|
||||||
#include "cmsis_os.h"
|
#include "cmsis_os.h"
|
||||||
|
@ -13,115 +14,138 @@ extern "C" void TNC_Error_Handler(int dev, int err);
|
||||||
|
|
||||||
extern osMessageQId ioEventQueueHandle;
|
extern osMessageQId ioEventQueueHandle;
|
||||||
|
|
||||||
extern "C" void cdc_receive(const uint8_t* buf, uint32_t len) {
|
extern "C" void cdc_receive(const uint8_t* buf, uint32_t len)
|
||||||
using namespace mobilinkd::tnc::hdlc;
|
{
|
||||||
if (mobilinkd::tnc::getUsbPort()->queue() != 0) {
|
using namespace mobilinkd::tnc;
|
||||||
auto frame = mobilinkd::tnc::hdlc::acquire();
|
if (mobilinkd::tnc::getUsbPort()->queue() != 0)
|
||||||
if (frame) {
|
{
|
||||||
for (uint32_t i = 0; i != len; ++i) {
|
if (len == 1)
|
||||||
frame->push_back(*buf++);
|
{
|
||||||
}
|
// Send single byte via queue directly. Linux seems to do
|
||||||
frame->source(IoFrame::SERIAL_DATA);
|
// this for ttyACM ports.
|
||||||
if (osMessagePut(mobilinkd::tnc::getUsbPort()->queue(), (uint32_t) frame,
|
osMessagePut(getUsbPort()->queue(), *buf, osWaitForever);
|
||||||
osWaitForever) != osOK) {
|
return;
|
||||||
mobilinkd::tnc::hdlc::release(frame);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
auto frame = hdlc::acquire();
|
||||||
|
if (frame)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i != len; ++i)
|
||||||
|
{
|
||||||
|
frame->push_back(*buf++);
|
||||||
|
}
|
||||||
|
frame->source(hdlc::IoFrame::SERIAL_DATA);
|
||||||
|
if (osMessagePut(mobilinkd::tnc::getUsbPort()->queue(),
|
||||||
|
(uint32_t) frame,
|
||||||
|
osWaitForever) != osOK)
|
||||||
|
{
|
||||||
|
mobilinkd::tnc::hdlc::release(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" void startCDCTask(void const* arg)
|
extern "C" void startCDCTask(void const* arg)
|
||||||
{
|
{
|
||||||
using namespace mobilinkd::tnc;
|
using namespace mobilinkd::tnc;
|
||||||
|
|
||||||
auto usbPort = static_cast<const UsbPort*>(arg);
|
auto usbPort = const_cast<UsbPort*>(static_cast<UsbPort const*>(arg));
|
||||||
|
|
||||||
const uint8_t FEND = 0xC0;
|
usbPort->run();
|
||||||
const uint8_t FESC = 0xDB;
|
}
|
||||||
const uint8_t TFEND = 0xDC;
|
|
||||||
const uint8_t TFESC = 0xDD;
|
|
||||||
|
|
||||||
uint8_t frame_type = kiss::FRAME_DATA;
|
namespace mobilinkd { namespace tnc {
|
||||||
|
|
||||||
enum State {WAIT_FBEGIN, WAIT_FRAME_TYPE, WAIT_FEND, WAIT_ESCAPED};
|
|
||||||
|
|
||||||
State state = WAIT_FBEGIN;
|
void UsbPort::add_char(uint8_t c)
|
||||||
|
{
|
||||||
|
switch (state_) {
|
||||||
|
case WAIT_FBEGIN:
|
||||||
|
if (c == FEND) state_ = WAIT_FRAME_TYPE;
|
||||||
|
break;
|
||||||
|
case WAIT_FRAME_TYPE:
|
||||||
|
if (c == FEND) break; // Still waiting for FRAME_TYPE.
|
||||||
|
frame_->type(c);
|
||||||
|
state_ = WAIT_FEND;
|
||||||
|
break;
|
||||||
|
case WAIT_FEND:
|
||||||
|
switch (c) {
|
||||||
|
case FESC:
|
||||||
|
state_ = WAIT_ESCAPED;
|
||||||
|
break;
|
||||||
|
case FEND:
|
||||||
|
frame_->source(hdlc::IoFrame::SERIAL_DATA);
|
||||||
|
osMessagePut(ioEventQueueHandle, reinterpret_cast<uint32_t>(frame_),
|
||||||
|
osWaitForever);
|
||||||
|
frame_ = hdlc::acquire();
|
||||||
|
state_ = WAIT_FBEGIN;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (not frame_->push_back(c)) {
|
||||||
|
hdlc::release(frame_);
|
||||||
|
state_ = WAIT_FBEGIN; // Drop frame;
|
||||||
|
frame_ = hdlc::acquire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WAIT_ESCAPED:
|
||||||
|
state_ = WAIT_FEND;
|
||||||
|
switch (c) {
|
||||||
|
case TFESC:
|
||||||
|
if (not frame_->push_back(FESC)) {
|
||||||
|
hdlc::release(frame_);
|
||||||
|
state_ = WAIT_FBEGIN; // Drop frame;
|
||||||
|
frame_ = hdlc::acquire();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TFEND:
|
||||||
|
if (not frame_->push_back(FEND)) {
|
||||||
|
hdlc::release(frame_);
|
||||||
|
state_ = WAIT_FBEGIN; // Drop frame;
|
||||||
|
frame_ = hdlc::acquire();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
hdlc::release(frame_);
|
||||||
|
state_ = WAIT_FBEGIN; // Drop frame;
|
||||||
|
frame_ = hdlc::acquire();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hdlc::IoFrame* frame = hdlc::acquire();
|
void UsbPort::run()
|
||||||
|
{
|
||||||
|
frame_ = hdlc::acquire();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
osEvent evt = osMessageGet(usbPort->queue(), osWaitForever);
|
osEvent evt = osMessageGet(queue(), osWaitForever);
|
||||||
if (evt.status != osEventMessage) {
|
if (evt.status != osEventMessage) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t c = evt.value.v;
|
||||||
|
|
||||||
|
if (c < 0x100) // Assume single byte transfer.
|
||||||
|
{
|
||||||
|
add_char(c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto input = static_cast<hdlc::IoFrame*>(evt.value.p);
|
auto input = static_cast<hdlc::IoFrame*>(evt.value.p);
|
||||||
|
|
||||||
if (!usbPort->isOpen()) {
|
if (!isOpen()) {
|
||||||
hdlc::release(input);
|
hdlc::release(input);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& c : *input) {
|
for (uint8_t c : *input) {
|
||||||
switch (state) {
|
add_char(c);
|
||||||
case WAIT_FBEGIN:
|
|
||||||
if (c == FEND) state = WAIT_FRAME_TYPE;
|
|
||||||
break;
|
|
||||||
case WAIT_FRAME_TYPE:
|
|
||||||
if (c == FEND) break; // Still waiting for FRAME_TYPE.
|
|
||||||
frame->type(c);
|
|
||||||
state = WAIT_FEND;
|
|
||||||
break;
|
|
||||||
case WAIT_FEND:
|
|
||||||
switch (c) {
|
|
||||||
case FESC:
|
|
||||||
state = WAIT_ESCAPED;
|
|
||||||
break;
|
|
||||||
case FEND:
|
|
||||||
frame->source(hdlc::IoFrame::SERIAL_DATA);
|
|
||||||
osMessagePut(ioEventQueueHandle, reinterpret_cast<uint32_t>(frame), osWaitForever);
|
|
||||||
frame = hdlc::acquire();
|
|
||||||
state = WAIT_FBEGIN;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (not frame->push_back(c)) {
|
|
||||||
hdlc::release(frame);
|
|
||||||
state = WAIT_FBEGIN; // Drop frame;
|
|
||||||
frame = hdlc::acquire();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WAIT_ESCAPED:
|
|
||||||
state = WAIT_FEND;
|
|
||||||
switch (c) {
|
|
||||||
case TFESC:
|
|
||||||
if (not frame->push_back(FESC)) {
|
|
||||||
hdlc::release(frame);
|
|
||||||
state = WAIT_FBEGIN; // Drop frame;
|
|
||||||
frame = hdlc::acquire();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TFEND:
|
|
||||||
if (not frame->push_back(FEND)) {
|
|
||||||
hdlc::release(frame);
|
|
||||||
state = WAIT_FBEGIN; // Drop frame;
|
|
||||||
frame = hdlc::acquire();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
hdlc::release(frame);
|
|
||||||
state = WAIT_FBEGIN; // Drop frame;
|
|
||||||
frame = hdlc::acquire();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
hdlc::release(input);
|
hdlc::release(input);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
namespace mobilinkd { namespace tnc {
|
}
|
||||||
|
|
||||||
void UsbPort::init()
|
void UsbPort::init()
|
||||||
{
|
{
|
||||||
|
@ -310,6 +334,11 @@ bool UsbPort::write(hdlc::IoFrame* frame, uint32_t timeout)
|
||||||
result = transmit_buffer(pos, start, timeout);
|
result = transmit_buffer(pos, start, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result and pos == TX_BUFFER_SIZE) {
|
||||||
|
// Must send an empty packet to flush the endpoint.
|
||||||
|
result = transmit_buffer(0, start, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
hdlc::release(frame);
|
hdlc::release(frame);
|
||||||
osMutexRelease(mutex_);
|
osMutexRelease(mutex_);
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -29,13 +29,26 @@ struct UsbPort : PortInterface
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool transmit_buffer(size_t pos, uint32_t start, uint32_t timeout);
|
bool transmit_buffer(size_t pos, uint32_t start, uint32_t timeout);
|
||||||
|
|
||||||
|
static constexpr const uint8_t FEND = 0xC0;
|
||||||
|
static constexpr const uint8_t FESC = 0xDB;
|
||||||
|
static constexpr const uint8_t TFEND = 0xDC;
|
||||||
|
static constexpr const uint8_t TFESC = 0xDD;
|
||||||
|
|
||||||
|
enum State {WAIT_FBEGIN, WAIT_FRAME_TYPE, WAIT_FEND, WAIT_ESCAPED};
|
||||||
|
|
||||||
|
void add_char(uint8_t c);
|
||||||
|
|
||||||
bool open_{false}; // opened/closed
|
bool open_{false}; // opened/closed
|
||||||
osMutexId mutex_{0}; // TX Mutex
|
osMutexId mutex_{0}; // TX Mutex
|
||||||
osMessageQId queue_{0}; // ISR read queue
|
osMessageQId queue_{0}; // ISR read queue
|
||||||
osThreadId cdcTaskHandle_{0}; // CDC read handler
|
osThreadId cdcTaskHandle_{0}; // CDC read handler
|
||||||
|
State state_{WAIT_FBEGIN};
|
||||||
|
hdlc::IoFrame* frame_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
UsbPort* getUsbPort();
|
UsbPort* getUsbPort();
|
||||||
|
|
Ładowanie…
Reference in New Issue