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 charactermain
| /* 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; | |||||
| // } | |||||
| } |
| // USBHost. | // USBHost. | ||||
| class USBDriver; | class USBDriver; | ||||
| class USBDriverTimer; | class USBDriverTimer; | ||||
| class USBHIDInput; | |||||
| /************************************************/ | /************************************************/ | ||||
| /* Added Defines */ | /* Added Defines */ | ||||
| public: | public: | ||||
| USBDriverTimer() { } | USBDriverTimer() { } | ||||
| USBDriverTimer(USBDriver *d) : driver(d) { } | USBDriverTimer(USBDriver *d) : driver(d) { } | ||||
| USBDriverTimer(USBHIDInput *hd) : driver(nullptr), hidinput(hd) { } | |||||
| void init(USBDriver *d) { driver = d; }; | void init(USBDriver *d) { driver = d; }; | ||||
| void start(uint32_t microseconds); | void start(uint32_t microseconds); | ||||
| void stop(); | void stop(); | ||||
| uint32_t started_micros; // testing only | uint32_t started_micros; // testing only | ||||
| private: | private: | ||||
| USBDriver *driver; | USBDriver *driver; | ||||
| USBHIDInput *hidinput; | |||||
| uint32_t usec; | uint32_t usec; | ||||
| USBDriverTimer *next; | USBDriverTimer *next; | ||||
| USBDriverTimer *prev; | USBDriverTimer *prev; | ||||
| const uint8_t *serialNumber() | const uint8_t *serialNumber() | ||||
| { return ((mydevice == nullptr) || (mydevice->strbuf == nullptr)) ? nullptr : &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; } | { return ((mydevice == nullptr) || (mydevice->strbuf == nullptr)) ? nullptr : &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; } | ||||
| private: | private: | ||||
| virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); | 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;} | virtual bool hid_process_in_data(const Transfer_t *transfer) {return false;} | ||||
| virtual void hid_input_data(uint32_t usage, int32_t value); | virtual void hid_input_data(uint32_t usage, int32_t value); | ||||
| virtual void hid_input_end(); | virtual void hid_input_end(); | ||||
| virtual void disconnect_collection(Device_t *dev); | virtual void disconnect_collection(Device_t *dev); | ||||
| virtual void hid_timer_event(USBDriverTimer *whichTimer) { } | |||||
| void add_to_list(); | void add_to_list(); | ||||
| USBHIDInput *next = NULL; | USBHIDInput *next = NULL; | ||||
| friend class USBHIDParser; | friend class USBHIDParser; | ||||
| class USBHIDParser : public USBDriver { | class USBHIDParser : public USBDriver { | ||||
| public: | public: | ||||
| USBHIDParser(USBHost &host) { init(); } | |||||
| USBHIDParser(USBHost &host) : hidTimer(this) { init(); } | |||||
| static void driver_ready_for_hid_collection(USBHIDInput *driver); | static void driver_ready_for_hid_collection(USBHIDInput *driver); | ||||
| bool sendPacket(const uint8_t *buffer, int cb=-1); | bool sendPacket(const uint8_t *buffer, int cb=-1); | ||||
| void setTXBuffers(uint8_t *buffer1, uint8_t *buffer2, uint8_t cb); | void setTXBuffers(uint8_t *buffer1, uint8_t *buffer2, uint8_t cb); | ||||
| bool sendControlPacket(uint32_t bmRequestType, uint32_t bRequest, | bool sendControlPacket(uint32_t bmRequestType, uint32_t bRequest, | ||||
| uint32_t wValue, uint32_t wIndex, uint32_t wLength, void *buf); | 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: | protected: | ||||
| enum { TOPUSAGE_LIST_LEN = 4 }; | enum { TOPUSAGE_LIST_LEN = 4 }; | ||||
| enum { USAGE_LIST_LEN = 24 }; | enum { USAGE_LIST_LEN = 24 }; | ||||
| virtual void disconnect(); | virtual void disconnect(); | ||||
| static void in_callback(const Transfer_t *transfer); | static void in_callback(const Transfer_t *transfer); | ||||
| static void out_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 in_data(const Transfer_t *transfer); | ||||
| void out_data(const Transfer_t *transfer); | void out_data(const Transfer_t *transfer); | ||||
| bool check_if_using_report_id(); | bool check_if_using_report_id(); | ||||
| void parse(uint16_t type_and_report_id, const uint8_t *data, uint32_t len); | void parse(uint16_t type_and_report_id, const uint8_t *data, uint32_t len); | ||||
| void init(); | 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;} | uint8_t activeSendMask(void) {return txstate;} | ||||
| uint8_t *tx1 = nullptr; | uint8_t *tx1 = nullptr; | ||||
| uint8_t *tx2 = nullptr; | uint8_t *tx2 = nullptr; | ||||
| bool hid_driver_claimed_control_ = false; | bool hid_driver_claimed_control_ = false; | ||||
| USBDriverTimer hidTimer; | |||||
| uint8_t bInterfaceNumber = 0; | |||||
| }; | }; | ||||
| //-------------------------------------------------------------------------- | //-------------------------------------------------------------------------- | ||||
| //-------------------------------------------------------------------------- | //-------------------------------------------------------------------------- | ||||
| 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 { | class BluetoothController: public USBDriver { | ||||
| public: | public: | ||||
| static const uint8_t MAX_CONNECTIONS = 4; | static const uint8_t MAX_CONNECTIONS = 4; | ||||
| bool deviceAvailable = false; | bool deviceAvailable = false; | ||||
| }; | }; | ||||
| #endif | #endif |
| uint32_t numendpoint = descriptors[4]; | uint32_t numendpoint = descriptors[4]; | ||||
| if (numendpoint < 1 || numendpoint > 2) return false; | if (numendpoint < 1 || numendpoint > 2) return false; | ||||
| if (descriptors[5] != 3) return false; // bInterfaceClass, 3 = HID | if (descriptors[5] != 3) return false; // bInterfaceClass, 3 = HID | ||||
| println(" bInterfaceNumber = ", descriptors[2]); | |||||
| println(" bInterfaceClass = ", descriptors[5]); | println(" bInterfaceClass = ", descriptors[5]); | ||||
| println(" bInterfaceSubClass = ", descriptors[6]); | println(" bInterfaceSubClass = ", descriptors[6]); | ||||
| println(" bInterfaceProtocol = ", descriptors[7]); | println(" bInterfaceProtocol = ", descriptors[7]); | ||||
| topusage_drivers[i] = NULL; | topusage_drivers[i] = NULL; | ||||
| } | } | ||||
| // request the HID report descriptor | // 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 | mk_setup(setup, 0x81, 6, 0x2200, descriptors[2], descsize); // get report desc | ||||
| queue_Control_Transfer(dev, &setup, descriptor, this); | queue_Control_Transfer(dev, &setup, descriptor, this); | ||||
| return true; | return true; | ||||
| } | } | ||||
| USBHDBGSerial.printf("\n"); */ | USBHDBGSerial.printf("\n"); */ | ||||
| /* | |||||
| print("HID: "); | print("HID: "); | ||||
| print(use_report_id); | print(use_report_id); | ||||
| print(" - "); | print(" - "); | ||||
| print_hexbytes(transfer->buffer, transfer->length); | print_hexbytes(transfer->buffer, transfer->length); | ||||
| */ | |||||
| const uint8_t *buf = (const uint8_t *)transfer->buffer; | const uint8_t *buf = (const uint8_t *)transfer->buffer; | ||||
| uint32_t len = transfer->length; | uint32_t len = transfer->length; | ||||
| } | } | ||||
| } | } | ||||
| 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) { | bool USBHIDParser::sendPacket(const uint8_t *buffer, int cb) { | ||||
| if (!out_size || !out_pipe) return false; | if (!out_size || !out_pipe) return false; | ||||
| if (!tx1) { | if (!tx1) { |
| MIDIDevice_BigBuffer KEYWORD1 | MIDIDevice_BigBuffer KEYWORD1 | ||||
| USBSerial KEYWORD1 | USBSerial KEYWORD1 | ||||
| USBSerial_BigBuffer KEYWORD1 | USBSerial_BigBuffer KEYWORD1 | ||||
| USBSerialEmu KEYWORD1 | |||||
| USBSerialBase KEYWORD1 | USBSerialBase KEYWORD1 | ||||
| AntPlus KEYWORD1 | AntPlus KEYWORD1 | ||||
| JoystickController KEYWORD1 | JoystickController KEYWORD1 |
| version=0.1 | version=0.1 | ||||
| author=Paul Stoffregen | author=Paul Stoffregen | ||||
| maintainer=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= | paragraph= | ||||
| category=Communication | category=Communication | ||||
| url=https://github.com/PaulStoffregen/USBHost_t36 | url=https://github.com/PaulStoffregen/USBHost_t36 | ||||
| architectures=* | architectures=* | ||||
| includes=USBHost_t36 |
| USBHDBGSerial.printf("Rawhid Claim: %x:%x usage: %x\n", dev->idVendor, dev->idProduct, topusage); | USBHDBGSerial.printf("Rawhid Claim: %x:%x usage: %x\n", dev->idVendor, dev->idProduct, topusage); | ||||
| #endif | #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 (mydevice != NULL && dev != mydevice) return CLAIM_NO; | ||||
| if (usage_) return CLAIM_NO; // Only claim one | 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; | mydevice = dev; | ||||
| collections_claimed++; | collections_claimed++; | ||||
| usage_ = topusage; | usage_ = topusage; |
| int USBSerialBase::peek(void) | int USBSerialBase::peek(void) | ||||
| { | { | ||||
| if (!device) return -1; | 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) | int USBSerialBase::read(void) | ||||
| { | { | ||||
| if (!device) return -1; | 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) { | if ((rxstate & 0x03) != 0x03) { | ||||
| NVIC_DISABLE_IRQ(IRQ_USBHS); | NVIC_DISABLE_IRQ(IRQ_USBHS); | ||||
| rx_queue_packets(head, tail); | |||||
| rx_queue_packets(rxhead, rxtail); | |||||
| NVIC_ENABLE_IRQ(IRQ_USBHS); | NVIC_ENABLE_IRQ(IRQ_USBHS); | ||||
| } | } | ||||
| return c; | return c; |