Переглянути джерело

USBSerial fix add USBSerialEmu

I was workign on some other USBHost extensions and found I needed debug information that was
being generated by a Teensy plugged into the USB host port that was output using Serial Emulation
to be available.  Likewise I wanted the ability to feed data back to that other Teensy.

So I decided to create USBHost_t36 implemention to handle the USB types that include Serial Emulation
USBSerialEmu class.

The USBSerialEmu code was based on the USBSerial code as well as RAWHID.  I found that there was a bug
in the Receiving of data that was sent by the remote SEREMU output, I would miss the first character
Fixed.

USBSerial fixed same bug in reading first character
main
Kurt Eckhardt 3 роки тому
джерело
коміт
2c5cbccebc
7 змінених файлів з 343 додано та 22 видалено
  1. +236
    -0
      SerEMU.cpp
  2. +80
    -6
      USBHost_t36.h
  3. +12
    -0
      hid.cpp
  4. +1
    -0
      keywords.txt
  5. +2
    -2
      library.properties
  6. +6
    -2
      rawhid.cpp
  7. +6
    -12
      serial.cpp

+ 236
- 0
SerEMU.cpp Переглянути файл

@@ -0,0 +1,236 @@
/* USB EHCI Host for Teensy 3.6
* Copyright 2017 Paul Stoffregen (paul@pjrc.com)
*
* 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 <Arduino.h>
#include "USBHost_t36.h" // Read this header first for key info

//#define SEREMU_PRINT_DEBUG


void USBSerialEmu::init()
{
USBHost::contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t));
USBHIDParser::driver_ready_for_hid_collection(this);
}

hidclaim_t USBSerialEmu::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage)
{
// only claim SerEMU devices currently: 16c0:0486
#ifdef SEREMU_PRINT_DEBUG
USBHDBGSerial.printf("SerEMU Claim: %x:%x usage: %x\n", dev->idVendor, dev->idProduct, topusage);
#endif

if (dev->idVendor != 0x16c0) return CLAIM_NO; // NOT PJRC
if (mydevice != NULL && dev != mydevice) return CLAIM_NO;
if (usage_) return CLAIM_NO; // Only claim one

// make sure it is the SEREMU usage
if (topusage != 0xffc90004) return CLAIM_NO; // Not the SEREMU

mydevice = dev;
collections_claimed++;
usage_ = topusage;
driver_ = driver; // remember the driver.
rx_head_ = 0;// receive head
rx_tail_ = 0;// receive tail
tx_head_ = 0;
rx_pipe_size_ = driver->inSize();
tx_pipe_size_ = driver->outSize();
tx_out_data_pending_ = 0;


//if (setup.word1 == 0x03000921 && setup.word2 == ((4<<16)|SEREMU_INTERFACE)) {
tx_buffer_[0] = 0;
tx_buffer_[1] = 0;
tx_buffer_[2] = 0;
tx_buffer_[3] = 0;
driver_->sendControlPacket( 0x21, 0x9, 0x300, driver_->interfaceNumber(), 4, tx_buffer_);

return CLAIM_INTERFACE; // We wa
}

void USBSerialEmu::disconnect_collection(Device_t *dev)
{
if (--collections_claimed == 0) {
mydevice = NULL;
usage_ = 0;
}
}

bool USBSerialEmu::hid_process_in_data(const Transfer_t *transfer)
{
uint16_t len = transfer->length;
const uint8_t *buffer = (const uint8_t *)transfer->buffer;
#ifdef SEREMU_PRINT_DEBUG
USBHDBGSerial.printf("USBSerialEmu::hid_process_in_data: %x %d: %x %x %x\n", usage_, len, buffer[0], buffer[1], buffer[2]);
#endif
const uint8_t *buffer_end = buffer + len -1;
while ((buffer_end > buffer) && (*buffer_end == 0))buffer_end--;
// lets trim off the trailing null characters.

// Now lets move the bytes onto our queue.
uint16_t tail = rx_tail_;
while (buffer <= buffer_end) {
uint16_t new_head = rx_head_ + 1;
if (new_head == RX_BUFFER_SIZE) new_head = 0;
if (new_head == tail) break; // we don't have room so bail out.
rx_buffer_[rx_head_] = *buffer++;
rx_head_ = new_head; // point off to the new next head.
}
return true;
}

bool USBSerialEmu::hid_process_out_data(const Transfer_t *transfer)
{
#ifdef SEREMU_PRINT_DEBUG
USBHDBGSerial.printf("USBSerialEmu::hid_process_out_data: %x\n", usage_);
#endif
if (tx_out_data_pending_) {
tx_out_data_pending_--;

}
return true;
}

bool USBSerialEmu::sendPacket()
{
USBHDBGSerial.printf("SEMU: SendPacket\n");

if (!driver_) return false;
if (!driver_->sendPacket(tx_buffer_)) return false;
tx_out_data_pending_++;
tx_head_ = 0;
return true;
}


int USBSerialEmu::available(void)
{
if (!driver_) return 0;
uint32_t head = rx_head_;
uint32_t tail = rx_tail_;
if (head >= tail) return head - tail;
return RX_BUFFER_SIZE + head - tail;
}

int USBSerialEmu::peek(void)
{
if (!driver_) return -1;
if (rx_head_ == rx_tail_) return -1;
return rx_buffer_[rx_tail_];
}

int USBSerialEmu::read(void)
{
if (!driver_) return -1;
if (rx_head_ == rx_tail_) return -1;
int c = rx_buffer_[rx_tail_];
if (++rx_tail_ >= RX_BUFFER_SIZE) rx_tail_ = 0;

return c;
}

int USBSerialEmu::availableForWrite()
{
if (!driver_) return 0;
return tx_pipe_size_ - tx_head_ + (2-tx_out_data_pending_)*tx_pipe_size_;
}

size_t USBSerialEmu::write(uint8_t c)
{
// Single buffer, as our HID device has double buffers.
if (c >= ' ') USBHDBGSerial.printf("SEMU: %c\n", c);
else USBHDBGSerial.printf("SEMU: 0x%x\n", c);


if (!driver_) return 0;

if (tx_head_ == tx_pipe_size_) {
while (!sendPacket()) yield(); // wait until the device above queues this packet
}
tx_buffer_[tx_head_++] = c;

// if this character filled it. then try to queue it again
if (tx_head_ == tx_pipe_size_) sendPacket();
driver_->stopTimer();
driver_->startTimer(write_timeout_);
return 1;
}

void USBSerialEmu::flush(void)
{
if (!driver_) return;

USBHDBGSerial.printf("SEMU: flush\n");
driver_->stopTimer(); // Stop longer timer.
driver_->startTimer(100); // Start a mimimal timeout
if (tx_head_) sendPacket();

// And wait for HID to say they were all sent.
elapsedMillis em = 0;
while (tx_out_data_pending_ && (em < 10000)) yield(); // wait up to 10 seconds?
}

void USBSerialEmu::hid_timer_event(USBDriverTimer *whichTimer)
{
USBHDBGSerial.printf("SEMU: Timer\n");
if (!driver_) return;
driver_->stopTimer();
if (tx_head_) {
memset(tx_buffer_ + tx_head_, 0, tx_pipe_size_ - tx_head_); // clear the rest of bytes in buffer.
sendPacket();
}
}

void USBSerialEmu::hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax)
{
// These should not be called as we are claiming the whole interface and not
// allowing the parse to happen
#ifdef SEREMU_PRINT_DEBUG
USBHDBGSerial.printf("SerEMU::hid_input_begin %x %x %x %x\n", topusage, type, lgmin, lgmax);
#endif
//hid_input_begin_ = true;
}

void USBSerialEmu::hid_input_data(uint32_t usage, int32_t value)
{
// These should not be called as we are claiming the whole interface and not
// allowing the parse to happen
#ifdef SEREMU_PRINT_DEBUG
USBHDBGSerial.printf("SerEMU: usage=%X, value=%d", usage, value);
if ((value >= ' ') && (value <='~')) USBHDBGSerial.printf("(%c)", value);
USBHDBGSerial.println();
#endif
}

void USBSerialEmu::hid_input_end()
{
// These should not be called as we are claiming the whole interface and not
// allowing the parse to happen
#ifdef SEREMU_PRINT_DEBUG
USBHDBGSerial.println("SerEMU::hid_input_end");
#endif
// if (hid_input_begin_) {
// hid_input_begin_ = false;
// }
}

+ 80
- 6
USBHost_t36.h Переглянути файл

@@ -99,6 +99,7 @@ typedef enum { CLAIM_NO=0, CLAIM_REPORT, CLAIM_INTERFACE} hidclaim_t;
// USBHost.
class USBDriver;
class USBDriverTimer;
class USBHIDInput;

/************************************************/
/* Added Defines */
@@ -488,6 +489,8 @@ class USBDriverTimer {
public:
USBDriverTimer() { }
USBDriverTimer(USBDriver *d) : driver(d) { }
USBDriverTimer(USBHIDInput *hd) : driver(nullptr), hidinput(hd) { }

void init(USBDriver *d) { driver = d; };
void start(uint32_t microseconds);
void stop();
@@ -496,6 +499,7 @@ public:
uint32_t started_micros; // testing only
private:
USBDriver *driver;
USBHIDInput *hidinput;
uint32_t usec;
USBDriverTimer *next;
USBDriverTimer *prev;
@@ -518,6 +522,7 @@ public:
const uint8_t *serialNumber()
{ return ((mydevice == nullptr) || (mydevice->strbuf == nullptr)) ? nullptr : &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; }


private:
virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage);
virtual bool hid_process_in_data(const Transfer_t *transfer) {return false;}
@@ -526,6 +531,7 @@ private:
virtual void hid_input_data(uint32_t usage, int32_t value);
virtual void hid_input_end();
virtual void disconnect_collection(Device_t *dev);
virtual void hid_timer_event(USBDriverTimer *whichTimer) { }
void add_to_list();
USBHIDInput *next = NULL;
friend class USBHIDParser;
@@ -658,13 +664,21 @@ private:

class USBHIDParser : public USBDriver {
public:
USBHIDParser(USBHost &host) { init(); }
USBHIDParser(USBHost &host) : hidTimer(this) { init(); }
static void driver_ready_for_hid_collection(USBHIDInput *driver);
bool sendPacket(const uint8_t *buffer, int cb=-1);
void setTXBuffers(uint8_t *buffer1, uint8_t *buffer2, uint8_t cb);

bool sendControlPacket(uint32_t bmRequestType, uint32_t bRequest,
uint32_t wValue, uint32_t wIndex, uint32_t wLength, void *buf);

// Atempt for RAWhid and SEREMU to take over processing of data
//
uint16_t inSize(void) {return in_size;}
uint16_t outSize(void) {return out_size;}
void startTimer(uint32_t microseconds) {hidTimer.start(microseconds);}
void stopTimer() {hidTimer.stop();}
uint8_t interfaceNumber() { return bInterfaceNumber;}
protected:
enum { TOPUSAGE_LIST_LEN = 4 };
enum { USAGE_LIST_LEN = 24 };
@@ -673,6 +687,7 @@ protected:
virtual void disconnect();
static void in_callback(const Transfer_t *transfer);
static void out_callback(const Transfer_t *transfer);
virtual void timer_event(USBDriverTimer *whichTimer);
void in_data(const Transfer_t *transfer);
void out_data(const Transfer_t *transfer);
bool check_if_using_report_id();
@@ -681,10 +696,6 @@ protected:
void parse(uint16_t type_and_report_id, const uint8_t *data, uint32_t len);
void init();

// Atempt for RAWhid to take over processing of data
//
uint16_t inSize(void) {return in_size;}
uint16_t outSize(void) {return out_size;}

uint8_t activeSendMask(void) {return txstate;}

@@ -708,6 +719,8 @@ private:
uint8_t *tx1 = nullptr;
uint8_t *tx2 = nullptr;
bool hid_driver_claimed_control_ = false;
USBDriverTimer hidTimer;
uint8_t bInterfaceNumber = 0;
};

//--------------------------------------------------------------------------
@@ -1758,6 +1771,68 @@ private:

//--------------------------------------------------------------------------

class USBSerialEmu : public USBHIDInput, public Stream {
public:
USBSerialEmu(USBHost &host) { init(); }
uint32_t usage(void) {return usage_;}

// Stream stuff.
uint32_t writeTimeout() {return write_timeout_;}
void writeTimeOut(uint32_t write_timeout) {write_timeout_ = write_timeout;} // Will not impact current ones.
virtual int available(void);
virtual int peek(void);
virtual int read(void);
virtual int availableForWrite();
virtual size_t write(uint8_t c);
virtual void flush(void);

using Print::write;


protected:
virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage);
virtual bool hid_process_in_data(const Transfer_t *transfer);
virtual bool hid_process_out_data(const Transfer_t *transfer);
virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax);
virtual void hid_input_data(uint32_t usage, int32_t value);
virtual void hid_input_end();
virtual void disconnect_collection(Device_t *dev);
virtual void hid_timer_event(USBDriverTimer *whichTimer);

bool sendPacket();

private:
void init();
USBHIDParser *driver_;
enum { MAX_PACKET_SIZE = 64 };
bool (*receiveCB)(uint32_t usage, const uint8_t *data, uint32_t len) = nullptr;
uint8_t collections_claimed = 0;
uint32_t usage_ = 0;

// We have max of 512 byte packets coming in. How about enough room for 3+3
enum { RX_BUFFER_SIZE=1024, TX_BUFFER_SIZE = 512 };
enum { DEFAULT_WRITE_TIMEOUT = 3500};

uint8_t rx_buffer_[RX_BUFFER_SIZE];
uint8_t tx_buffer_[TX_BUFFER_SIZE];

volatile uint8_t tx_out_data_pending_ = 0;

volatile uint16_t rx_head_;// receive head
volatile uint16_t rx_tail_;// receive tail
volatile uint16_t tx_head_;
uint16_t rx_pipe_size_;// size of receive circular buffer
uint16_t tx_pipe_size_;// size of transmit circular buffer
uint32_t write_timeout_ = DEFAULT_WRITE_TIMEOUT;


// See if we can contribute transfers
Transfer_t mytransfers[2] __attribute__ ((aligned(32)));

};

//--------------------------------------------------------------------------

class BluetoothController: public USBDriver {
public:
static const uint8_t MAX_CONNECTIONS = 4;
@@ -2037,5 +2112,4 @@ private:
bool deviceAvailable = false;
};


#endif

+ 12
- 0
hid.cpp Переглянути файл

@@ -55,6 +55,7 @@ bool USBHIDParser::claim(Device_t *dev, int type, const uint8_t *descriptors, ui
uint32_t numendpoint = descriptors[4];
if (numendpoint < 1 || numendpoint > 2) return false;
if (descriptors[5] != 3) return false; // bInterfaceClass, 3 = HID
println(" bInterfaceNumber = ", descriptors[2]);
println(" bInterfaceClass = ", descriptors[5]);
println(" bInterfaceSubClass = ", descriptors[6]);
println(" bInterfaceProtocol = ", descriptors[7]);
@@ -148,6 +149,7 @@ bool USBHIDParser::claim(Device_t *dev, int type, const uint8_t *descriptors, ui
topusage_drivers[i] = NULL;
}
// request the HID report descriptor
bInterfaceNumber = descriptors[2]; // save away the interface number;
mk_setup(setup, 0x81, 6, 0x2200, descriptors[2], descsize); // get report desc
queue_Control_Transfer(dev, &setup, descriptor, this);
return true;
@@ -213,10 +215,12 @@ void USBHIDParser::in_data(const Transfer_t *transfer)
}
USBHDBGSerial.printf("\n"); */

/*
print("HID: ");
print(use_report_id);
print(" - ");
print_hexbytes(transfer->buffer, transfer->length);
*/
const uint8_t *buf = (const uint8_t *)transfer->buffer;
uint32_t len = transfer->length;

@@ -249,6 +253,14 @@ void USBHIDParser::out_data(const Transfer_t *transfer)
}
}

void USBHIDParser::timer_event(USBDriverTimer *whichTimer)
{
if (topusage_drivers[0]) {
topusage_drivers[0]->hid_timer_event(whichTimer);
}
}


bool USBHIDParser::sendPacket(const uint8_t *buffer, int cb) {
if (!out_size || !out_pipe) return false;
if (!tx1) {

+ 1
- 0
keywords.txt Переглянути файл

@@ -9,6 +9,7 @@ MIDIDevice KEYWORD1
MIDIDevice_BigBuffer KEYWORD1
USBSerial KEYWORD1
USBSerial_BigBuffer KEYWORD1
USBSerialEmu KEYWORD1
USBSerialBase KEYWORD1
AntPlus KEYWORD1
JoystickController KEYWORD1

+ 2
- 2
library.properties Переглянути файл

@@ -2,9 +2,9 @@ name=USBHost_t36
version=0.1
author=Paul Stoffregen
maintainer=Paul Stoffregen
sentence=Connect USB devices to the USB Host of Teensy 3.6.
sentence=Connect USB devices to the USB Host of Teensy 3.6 and Teensy 4.x
paragraph=
category=Communication
url=https://github.com/PaulStoffregen/USBHost_t36
architectures=*
includes=USBHost_t36

+ 6
- 2
rawhid.cpp Переглянути файл

@@ -37,10 +37,14 @@ hidclaim_t RawHIDController::claim_collection(USBHIDParser *driver, Device_t *de
USBHDBGSerial.printf("Rawhid Claim: %x:%x usage: %x\n", dev->idVendor, dev->idProduct, topusage);
#endif

if ((dev->idVendor != 0x16c0 || (dev->idProduct) != 0x486)) return CLAIM_NO;
if (dev->idVendor != 0x16c0) return CLAIM_NO; // NOT PJRC
if (mydevice != NULL && dev != mydevice) return CLAIM_NO;
if (usage_) return CLAIM_NO; // Only claim one
if (fixed_usage_ && (fixed_usage_ != topusage)) return CLAIM_NO; // See if we want specific one and if so is it this one

if (fixed_usage_ ) {
if (fixed_usage_ != topusage) return CLAIM_NO; // See if we want specific one and if so is it this one
} else if (dev->idProduct != 0x486) return CLAIM_NO; // otherwise mainly used for RAWHID Serial type.

mydevice = dev;
collections_claimed++;
usage_ = topusage;

+ 6
- 12
serial.cpp Переглянути файл

@@ -1239,25 +1239,19 @@ int USBSerialBase::available(void)
int USBSerialBase::peek(void)
{
if (!device) return -1;
uint32_t head = rxhead;
uint32_t tail = rxtail;
if (head == tail) return -1;
if (++tail >= rxsize) tail = 0;
return rxbuf[tail];
if (rxhead == rxtail) return -1;
return rxbuf[rxtail];
}

int USBSerialBase::read(void)
{
if (!device) return -1;
uint32_t head = rxhead;
uint32_t tail = rxtail;
if (head == tail) return -1;
if (++tail >= rxsize) tail = 0;
int c = rxbuf[tail];
rxtail = tail;
if (rxhead == rxtail) return -1;
int c = rxbuf[rxtail];
if (++rxtail >= rxsize) rxtail = 0;
if ((rxstate & 0x03) != 0x03) {
NVIC_DISABLE_IRQ(IRQ_USBHS);
rx_queue_packets(head, tail);
rx_queue_packets(rxhead, rxtail);
NVIC_ENABLE_IRQ(IRQ_USBHS);
}
return c;

Завантаження…
Відмінити
Зберегти