New USB Host library for Teensy 4.x

jmarsh

Well-known member
I've written a new USB Host support library for Teensy 4.x boards: https://github.com/A-Dunstan/teensy4_usbhost

Key features:
- dynamic driver instantiation, e.g. can support any number of hubs or other devices since driver objects can be created as needed
- transparent support for using any type of memory (DTCM/OCRAM/EXTMEM/SDRAM), all cache operations are taken care of
- supports both synchronous and asynchronous transfers (using std::function callbacks)
- makes an effort to return errors using errno
- fully supports isochronous endpoints / transfers (both full and high speed). These are commonly used by devices that deliver realtime data such as audio input/output DACs, or webcams:
WtDhRYN.jpeg


It makes use of a threading library (which is a modified version of AtomThreads) which is written to be as minimally intrusive as possible; you can use the synchronization objects it provides if you want, but otherwise its existence can be ignored. It's a submodule of the git repository so do a "submodule init" after cloning it.

For the time being the only available documentation is in the examples and the included drivers; there's still a lot of code to be cleaned up before it starts being documented properly.
 
Last edited:
Exellent work:D Tried the USBHost storage "basic.ino" on a FAT32 formatted USB stick through a HUB. Result:
Code:
Found a FAT32 partition.
Checking free space... Done.
Bytes free: 30812176384 of 30927306752 (99%)
--- FILE LISTING BEGIN ---
DIRECTORY: /
Modified: 07:21:22 Mar 27 2021    Size: 10845    Name: dfs_ext.c
Modified: 07:21:22 Mar 27 2021    Size: 1147    Name: dfs_ext.h
Modified: 17:47:14 Oct 15 2023    Size: 2127672    Name: dosemu_1.4.0.7+20130105+b028d3f-2build1_amd64.deb
Modified: 17:05:52 Nov 7 2023    Size: 45123    Name: fail.jpg
Modified: 20:39:04 Oct 29 2023    Size: 2379894    Name: ff15.zip
Modified: 20:39:22 Oct 29 2023    Size: 8151962    Name: ffsample.zip
Modified: 20:23:44 Feb 6 2024    Size: 9430734    Name: fltk-master.zip
Modified: 17:41:14 Oct 28 2023    Size: 16688    Name: fontcnvt
Modified: 17:41:02 Oct 28 2023    Size: 955    Name: fontcnvt.c
Modified: 16:15:22 Dec 10 2023    Size: 92613306    Name: FreeRTOSv202212.01.zip
Modified: 19:15:56 Feb 4 2024    Size: 946    Name: libpng3_1.2.54-1ubuntu1.1+1~ppa0~jammy0_amd64.deb
Modified: 19:17:28 Feb 4 2024    Size: 116466    Name: libpng12-0_1.2.54-1ubuntu1_amd64.deb
Modified: 19:11:32 Feb 4 2024    Size: 19036    Name: libpng_1.2.54-1ubuntu1.1+1~ppa0~jammy0.debian.tar.xz
Modified: 00:21:22 May 16 2022    Size: 4096    Name: sansurf.fnt
Modified: 17:41:22 Oct 28 2023    Size: 22050    Name: sansurf.h
Modified: 07:21:22 Mar 27 2021    Size: 1211    Name: SConscript
--- FILE LISTING COMPLETE ---

USB Drive may now be removed, press return to scan again
Can't wait to test with all of the other features...

EDIT: Did anybody mention CD/DVD usage:
Code:
Found 1 USB Storage devices
Found a USB Mass Storage device 0x20206ce0, has 1 logical units
USB 0x20206ce0:0 Vendor: PBDS, Product: CDRWDVD DH-48C2S
USB 0x20206ce0:0 LBA count: 23340, LBA size: 2048
Found 1 partitions
USB 0x20206ce0:0 Partition 0: Type ISO9660, start 0 - end 23187

Press return to search again
Again, Nice work...
 
Last edited:
Looks like fun.
Who knows, maybe it can replace the USBHost_t36... I always wanted to use something like threads, but...

Note: If I try to clone it using Windows github desktop it gives me a real strange error message.
Maybe the submodule... Will investigate later.

But looks like great stuff!
 
EDIT: Did anybody mention CD/DVD usage:
Code:
Found 1 USB Storage devices
Found a USB Mass Storage device 0x20206ce0, has 1 logical units
USB 0x20206ce0:0 Vendor: PBDS, Product: CDRWDVD DH-48C2S
USB 0x20206ce0:0 LBA count: 23340, LBA size: 2048
Found 1 partitions
USB 0x20206ce0:0 Partition 0: Type ISO9660, start 0 - end 23187

Press return to search again
The CD stuff is a bit of fun... when I get around to it I want to write an ISO9660 filesystem library, so the files can actually be accessed. But in the meantime there's an example that can use CD drives to play audio, and if they have an SPDIF port on the back you can connect it to the Teensy to hear it over USB. The speedtest example can also do digital audio extraction, although it's very basic and doesn't do any verification like a proper extraction app should.
 
Looks like fun.
Who knows, maybe it can replace the USBHost_t36... I always wanted to use something like threads, but...

Note: If I try to clone it using Windows github desktop it gives me a real strange error message.
Maybe the submodule... Will investigate later.

But looks like great stuff!
Something you might want to look at is the jpg_server example, for the camera stuff you were working on - it uses QNEthernet to set up a basic webserver to stream continuous jpeg images. You could reuse the EthernetClient related functions to stream images from an OV2640 (or other camera that does mjpg format).
 
I've written a new USB Host support library for Teensy 4.x boards: https://github.com/A-Dunstan/teensy4_usbhost

Key features:
- dynamic driver instantiation, e.g. can support any number of hubs or other devices since driver objects can be created as needed
- transparent support for using any type of memory (DTCM/OCRAM/EXTMEM/SDRAM), all cache operations are taken care of
- supports both synchronous and asynchronous transfers (using std::function callbacks)
- makes an effort to return errors using errno
- fully supports isochronous endpoints / transfers (both full and high speed). These are commonly used by devices that deliver realtime data such as audio input/output DACs, or webcams:
WtDhRYN.jpeg


It makes use of a threading library (which is a modified version of AtomThreads) which is written to be as minimally intrusive as possible; you can use the synchronization objects it provides if you want, but otherwise its existence can be ignored. It's a submodule of the git repository so do a "submodule init" after cloning it.

For the time being the only available documentation is in the examples and the included drivers; there's still a lot of code to be cleaned up before it starts being documented properly.
This library may bridge one of the great chasms I've been facing in developing USB_UVC cameras for the Teensy: The lack of host support for Isochronous reception from UVC cameras. I've managed to develop drivers for some FLIR thermal imaging cameras only because they use bulk endpoints for image data transfer. All the other cameras I've tested want isochronous endpoints. This will be another continuing education project to keep me busy for the next few months! (As if the USB probe/commit protocols weren't enough to keep me busy!)

I applaud your efforts and look forward to exploring the code.
 
Funnily enough, the few cameras I've tested with work fine without even doing any probing/committing... if you just activate the alternate interface without doing any setup they're happy to deliver packets from the streaming endpoint using the default format. That also bypasses activating their "privacy" LED that is meant to indicate when the camera is active...
 
Note: If I try to clone it using Windows github desktop it gives me a real strange error message.
Maybe the submodule... Will investigate later.
I tested cloning with github desktop just now and I didn't get any error message, it just stalled indefinitely at the submodule. So I filed an issue.
You can clone the TeensyAtomThreads library by itself: https://github.com/A-Dunstan/TeensyAtomThreads
Luckily I included a check in the usb library that will look for it as an external library if the submodule in the local tree isn't present:
Code:
// prefer submodule version if it is present
#if defined __has_include && __has_include("TeensyAtomThreads/TeensyAtomThreads.h")
#include "TeensyAtomThreads/TeensyAtomThreads.h"
#else
#include <TeensyAtomThreads.h>
#endif
 
When I added the submodule I used the ssh (git@) URL instead of the web (HTTPS: ) URL. Changing it to https has let github desktop check out the repo without any issues.
 
Last edited:
A while ago I bought a $5 USB wifi/bluetooth adapter from aliexpress. They seem to be very plentiful, advertised under a few different brands but usually including a mention of AX900, Wifi6 and Bluetooth 5.3 or 5.4. I thought it would be a realtek chipset but it turns out it's from AIC, although from what I've seen the hardware's original source may be Ceva/RivieraWaves. There doesn't seem to be any public datasheets available but there are several third-party maintained out-of-tree linux drivers based on an SDK from AIC, including firmware that seems to still be getting regular updates.

Uploading firmware to initialize a USB device isn't out of the ordinary but this one has a few tricks up its sleeve. To begin with here's how the device presents itself when you first plug it in (debug output from my USB Host library):
Code:
!!! Enumeration begin !!!
Port 0:0 bDeviceClass 00 bDeviceSubClass 00 bDeviceProtocol 00 bMaxPacketSize 64
New Endpoint<0x20204680> type 0
New Device<0x20204640>: address 1, port 0, hub 0, speed 2, control_packet_size 64
Port 0:0 successfully set address 1
Device<0x20204640> control callback 0x20204b08 result 4
Device<0x20204640> number of supported languages: 1
Device<0x20204640> using language 0409 for strings
Device<0x20204640> control callback 0x20204c68 result 18
Device<0x20204640> Device Descriptor 0x20204400:
   bcdUSB 0200
   bDeviceClass 00
   bDeviceSubClass 00
   bDeviceProtocol 00
   idVendor A69C
   idProduct 5721
   bcdDevice 0200
   iManufacturer 1
   iProduct 2
   iSerialNumber 3
   bNumConfigurations 1
Device<0x20204640> control callback 0x20204b08 result 16
Device<0x20204640> string index 1: aicsemi
Device<0x20204640> control callback 0x20204ee8 result 16
Device<0x20204640> string index 2: Aic MSC
Device<0x20204640> control callback 0x20205208 result 18
Device<0x20204640> string index 3: 20200203
Device<0x20204640> control callback 0x202054a8 result 9
Device<0x20204640> control callback 0x20204b08 result 32
Device<0x20204640> Configuration 1:
    wTotalLength 32
    bNumInterfaces 1
    iConfiguration 0
    bmAttributes 80
    bMaxPower 50
Interface 0, Length 23:
    bAlternateSetting 0
    bNumEndpoints 2
    bInterfaceClass 08
    bInterfaceSubClass 06
    bInterfaceProtocol 50
    iInterface 4
     Endpoint 02:
        bmAttributes 02
        wMaxPacketSize 0200
        bInterval 0
     Endpoint 82:
        bmAttributes 02
        wMaxPacketSize 0200
        bInterval 0
Interface 0 is a "standard" looking device - the problem is it's a mass storage (disk) device, which is not at all what we want. It seems there's an internal 4MB flash chip that is preloaded with a FAT filesystem containing windows drivers - not particularly useful in this case. How do we get this device to stop pretending it's a disk? Write a custom driver that sends an eject command. The moment the device receives the eject request, it disconnects and reconnects as something new:
Code:
!!! Enumeration begin !!!
Port 0:0 bDeviceClass 00 bDeviceSubClass 00 bDeviceProtocol 00 bMaxPacketSize 64
New Endpoint<0x20204640> type 0
New Device<0x20204600>: address 1, port 0, hub 0, speed 2, control_packet_size 64
Port 0:0 successfully set address 1
Device<0x20204600> control callback 0x20204ac8 result 4
Device<0x20204600> number of supported languages: 1
Device<0x20204600> using language 0409 for strings
Device<0x20204600> control callback 0x20204c28 result 18
Device<0x20204600> Device Descriptor 0x20204560:
   bcdUSB 0200
   bDeviceClass 00
   bDeviceSubClass 00
   bDeviceProtocol 00
   idVendor A69C
   idProduct 8D80
   bcdDevice 0100
   iManufacturer 1
   iProduct 2
   iSerialNumber 3
   bNumConfigurations 1
Device<0x20204600> control callback 0x20204d88 result 16
Device<0x20204600> string index 1: aicsemi
Device<0x20204600> control callback 0x202050e8 result 18
Device<0x20204600> string index 2: AIC Wlan
Device<0x20204600> control callback 0x20205368 result 18
Device<0x20204600> string index 3: 20220103
Device<0x20204600> control callback 0x20205608 result 9
Device<0x20204600> control callback 0x20204ac8 result 32
Device<0x20204600> Configuration 1:
    wTotalLength 32
    bNumInterfaces 1
    iConfiguration 0
    bmAttributes A0
    bMaxPower 250
Interface 0, Length 23:
    bAlternateSetting 0
    bNumEndpoints 2
    bInterfaceClass FF
    bInterfaceSubClass FF
    bInterfaceProtocol FF
    iInterface 0
     Endpoint 01:
        bmAttributes 02
        wMaxPacketSize 0200
        bInterval 0
     Endpoint 82:
        bmAttributes 02
        wMaxPacketSize 0200
        bInterval 0
Now the PID (idProduct) has changed to 8D80 and we've got a vendor specific interface with two bulk endpoints. This is a lot more typical of something that expects a firmware upload. So I dug through the available source code and wrote my own uploader, and now once again the device disconnects and reconnects as something new (when the firmware upload complete). This time it has a completely new VID as well as PID:
Code:
!!! Enumeration begin !!!
Port 0:0 bDeviceClass EF bDeviceSubClass 02 bDeviceProtocol 01 bMaxPacketSize 64
New Endpoint<0x20204640> type 0
New Device<0x20204600>: address 1, port 0, hub 0, speed 2, control_packet_size 64
Port 0:0 successfully set address 1
Device<0x20204600> control callback 0x20204ac8 result 4
Device<0x20204600> number of supported languages: 1
Device<0x20204600> using language 0409 for strings
Device<0x20204600> control callback 0x20204c28 result 18
Device<0x20204600> Device Descriptor 0x20204560:
   bcdUSB 0200
   bDeviceClass EF
   bDeviceSubClass 02
   bDeviceProtocol 01
   idVendor 368B
   idProduct 8D81
   bcdDevice 0100
   iManufacturer 1
   iProduct 2
   iSerialNumber 3
   bNumConfigurations 1
Device<0x20204600> control callback 0x20204d88 result 16
Device<0x20204600> string index 1: AICSemi
Device<0x20204600> control callback 0x202050e8 result 24
Device<0x20204600> string index 2: AIC 8800D80
Device<0x20204600> control callback 0x20205368 result 18
Device<0x20204600> string index 3: 20220103
Device<0x20204600> control callback 0x202054c8 result 9
Device<0x20204600> control callback 0x20204bc8 result 222
Device<0x20204600> Configuration 1:
    wTotalLength 222
    bNumInterfaces 3
    iConfiguration 0
    bmAttributes A0
    bMaxPower 250
Interface 0, Length 30:
    bAlternateSetting 0
    bNumEndpoints 3
    bInterfaceClass E0
    bInterfaceSubClass 01
    bInterfaceProtocol 01
    iInterface 6
     Endpoint 83:
        bmAttributes 03
        wMaxPacketSize 0040
        bInterval 4
     Endpoint 84:
        bmAttributes 02
        wMaxPacketSize 0200
        bInterval 0
     Endpoint 04:
        bmAttributes 02
        wMaxPacketSize 0200
        bInterval 0
Interface 1, Length 138:
    bAlternateSetting 0
    bNumEndpoints 2
    bInterfaceClass E0
    bInterfaceSubClass 01
    bInterfaceProtocol 01
    iInterface 6
String 6 was already present ("")
     Endpoint 85:
        bmAttributes 01
        wMaxPacketSize 0000
        bInterval 4
     Endpoint 05:
        bmAttributes 01
        wMaxPacketSize 0000
        bInterval 4
Interface 1, Length 23:
    bAlternateSetting 1
    bNumEndpoints 2
    bInterfaceClass E0
    bInterfaceSubClass 01
    bInterfaceProtocol 01
    iInterface 6
String 6 was already present ("")
     Endpoint 85:
        bmAttributes 01
        wMaxPacketSize 0009
        bInterval 4
     Endpoint 05:
        bmAttributes 01
        wMaxPacketSize 0009
        bInterval 4
Interface 1, Length 23:
    bAlternateSetting 2
    bNumEndpoints 2
    bInterfaceClass E0
    bInterfaceSubClass 01
    bInterfaceProtocol 01
    iInterface 6
String 6 was already present ("")
     Endpoint 85:
        bmAttributes 01
        wMaxPacketSize 0011
        bInterval 4
     Endpoint 05:
        bmAttributes 01
        wMaxPacketSize 0011
        bInterval 4
Interface 1, Length 23:
    bAlternateSetting 3
    bNumEndpoints 2
    bInterfaceClass E0
    bInterfaceSubClass 01
    bInterfaceProtocol 01
    iInterface 6
String 6 was already present ("")
     Endpoint 85:
        bmAttributes 01
        wMaxPacketSize 0019
        bInterval 4
     Endpoint 05:
        bmAttributes 01
        wMaxPacketSize 0019
        bInterval 4
Interface 1, Length 23:
    bAlternateSetting 4
    bNumEndpoints 2
    bInterfaceClass E0
    bInterfaceSubClass 01
    bInterfaceProtocol 01
    iInterface 6
String 6 was already present ("")
     Endpoint 85:
        bmAttributes 01
        wMaxPacketSize 0021
        bInterval 4
     Endpoint 05:
        bmAttributes 01
        wMaxPacketSize 0021
        bInterval 4
Interface 1, Length 23:
    bAlternateSetting 5
    bNumEndpoints 2
    bInterfaceClass E0
    bInterfaceSubClass 01
    bInterfaceProtocol 01
    iInterface 6
String 6 was already present ("")
     Endpoint 85:
        bmAttributes 01
        wMaxPacketSize 0031
        bInterval 4
     Endpoint 05:
        bmAttributes 01
        wMaxPacketSize 0031
        bInterval 4
Interface 2, Length 37:
    bAlternateSetting 0
    bNumEndpoints 4
    bInterfaceClass FF
    bInterfaceSubClass FF
    bInterfaceProtocol FF
    iInterface 7
     Endpoint 01:
        bmAttributes 02
        wMaxPacketSize 0200
        bInterval 0
     Endpoint 81:
        bmAttributes 02
        wMaxPacketSize 0200
        bInterval 0
     Endpoint 02:
        bmAttributes 02
        wMaxPacketSize 0200
        bInterval 0
     Endpoint 82:
        bmAttributes 02
        wMaxPacketSize 0200
        bInterval 0
Device<0x20204600> control callback 0x202050e8 result 20
Device<0x20204600> string index 6: Bluetooth
Device<0x20204600> control callback 0x20205368 result 10
Device<0x20204600> string index 7: Wlan
Now we're in business; interface 0 looks like a standard USB Bluetooth HCI, interface 1 (with all its alternate settings) are the bluetooth isochronous endpoints and interface 2 is the (vendor specific) wifi/wlan interface. For now I'm ignoring the last one (because writing a wifi driver is a lot of work, even with a reference manual), but testing that the bluetooth HCI is working by reading the host's BD address is relatively simple:
Code:
HCI interface startup 83 84 04
HCI event response: 0
send read_bd_addr result: 3
HCI event response: 12
BD ADDR: 90:DE:80:17:EF:FE
Success! We have turned this USB chimera into a usable bluetooth host.
 
Wifi work is progressing... I can scan 2.4GHz and 5GHz channels and probe access points:
Code:
Device<0x20207300> activate configuration 1(-1)
Device<0x20207300> activate interface 00 altSetting 00
New Endpoint<0x20207900> type 3
New Endpoint<0x20207c00> type 2
New Endpoint<0x20207180> type 2
Device<0x20207300> activate interface 01 altSetting 00
activate_endpoint: ISO endpoint has wMaxPacketSize==0, skipping
activate_endpoint: ISO endpoint has wMaxPacketSize==0, skipping
Device<0x20207300> activate interface 02 altSetting 00
New Endpoint<0x20208000> type 2
New Endpoint<0x20205280> type 2
New Endpoint<0x20205400> type 2
New Endpoint<0x20205580> type 2
AIC8800D81 attach 81 01 82 02
Endpoint<0x20207900> interval 8 offset 7 stime 5 ctime 0 s_mask 80 c_mask 00
Device<0x20207300> control callback 0x20204cc8 result 0
Device<0x20207300> new configuration set: 01
Device<0x20207300> activate configuration 1(1)
Device<0x20207300> control callback 0x20204e28 result 0
New interface set: interface 0, altSetting 0
Device<0x20207300> activate interface 00 altSetting 00
RX_THREAD begin 0x202090e0
Device<0x20207300> control callback 0x20207ee8 result 0
New interface set: interface 1, altSetting 0
Device<0x20207300> activate interface 01 altSetting 00
Device<0x20207300> control callback 0x202050e8 result 0
New interface set: interface 2, altSetting 0
Device<0x20207300> activate interface 02 altSetting 00
chip_id=7, chip_mcu_id = 1
is 5g support: 1
Firmware Version: la Dec 05 2025 19:45:42 - g586bc1e8
RF_CALIB: rx_gain_2g_addr 00175A64, rx_gain_5g_addr 00175CF8, txgain_24g_addr 0017683C, txgain_5g_addr 001769C4
MAC Address: 90:DE:80:17:EF:FD
MM VERSION:
    LMAC: 06090101
    MACHW: 0002FDFB 00014047
    PHY: 5EE24111 01020000
    FEAT: 01E877D7
    MAX_STA: 32
    MAX_VIF: 4
Interface added, index #0
scan start response: 1
init done
Scan Result: D4:31:27:C0:79:C4    SSID: "                      Johns Wifi"    freq: 2412    RSSI: -78
Scan Result: 6A:20:51:2D:35:11    SSID: "                                "    freq: 2412    RSSI: -79
Scan Result: 6A:20:51:2D:35:12    SSID: "                                "    freq: 2412    RSSI: -80
Scan Result: 6A:20:51:2D:35:15    SSID: "                     WiFi-2D3514"    freq: 2412    RSSI: -76
Channel Survey: freq 2412MHz    noise -77dBm    busy time 10 / 50ms
Channel Survey: freq 2417MHz    noise -86dBm    busy time 0 / 30ms
Scan Result: C8:D7:19:01:76:33    SSID: "                           DLINK"    freq: 2422    RSSI: -59
Channel Survey: freq 2422MHz    noise -84dBm    busy time 3 / 30ms
Channel Survey: freq 2427MHz    noise -94dBm    busy time 0 / 30ms
Channel Survey: freq 2432MHz    noise -94dBm    busy time 0 / 30ms
Scan Result: D0:DB:B7:B8:EA:08    SSID: "                    NetComm 3922"    freq: 2437    RSSI: -78
Channel Survey: freq 2437MHz    noise -95dBm    busy time 3 / 50ms
Channel Survey: freq 2442MHz    noise -94dBm    busy time 0 / 30ms
Channel Survey: freq 2447MHz    noise -94dBm    busy time 0 / 30ms
Channel Survey: freq 2452MHz    noise -93dBm    busy time 0 / 30ms
Channel Survey: freq 2457MHz    noise -86dBm    busy time 1 / 30ms
Channel Survey: freq 2462MHz    noise -43dBm    busy time 0 / 50ms
Scan Result: 10:2C:B1:87:F5:99    SSID: "                                "    freq: 2467    RSSI: -77
Channel Survey: freq 2467MHz    noise -87dBm    busy time 2 / 30ms
Channel Survey: freq 2472MHz    noise -93dBm    busy time 0 / 50ms
Channel Survey: freq 2484MHz    noise -93dBm    busy time 0 / 30ms
Channel Survey: freq 5180MHz    noise -94dBm    busy time 2 / 50ms
Channel Survey: freq 5200MHz    noise -89dBm    busy time 0 / 30ms
Scan Result: C8:D7:19:01:76:35    SSID: "               Linksys06953_5GHz"    freq: 5220    RSSI: -56
Channel Survey: freq 5220MHz    noise -89dBm    busy time 0 / 50ms
Channel Survey: freq 5240MHz    noise -93dBm    busy time 0 / 30ms
Channel Survey: freq 5260MHz    noise -94dBm    busy time 0 / 30ms
Channel Survey: freq 5280MHz    noise -81dBm    busy time 0 / 30ms
Channel Survey: freq 5300MHz    noise -92dBm    busy time 0 / 30ms
Channel Survey: freq 5320MHz    noise -91dBm    busy time 0 / 30ms
Channel Survey: freq 5500MHz    noise -89dBm    busy time 0 / 30ms
Channel Survey: freq 5520MHz    noise -95dBm    busy time 0 / 30ms
Channel Survey: freq 5540MHz    noise -92dBm    busy time 0 / 30ms
Channel Survey: freq 5560MHz    noise -97dBm    busy time 0 / 30ms
Scan Result: 90:F8:91:DF:DC:CB    SSID: "                          DLINK5"    freq: 5500    RSSI: -89
Channel Survey: freq 5580MHz    noise -89dBm    busy time 0 / 30ms
Channel Survey: freq 5600MHz    noise -96dBm    busy time 0 / 30ms
Channel Survey: freq 5620MHz    noise -95dBm    busy time 0 / 30ms
Channel Survey: freq 5640MHz    noise -95dBm    busy time 0 / 30ms
Channel Survey: freq 5660MHz    noise -95dBm    busy time 0 / 30ms
Channel Survey: freq 5680MHz    noise -100dBm    busy time 0 / 30ms
Channel Survey: freq 5700MHz    noise -100dBm    busy time 0 / 30ms
Channel Survey: freq 5720MHz    noise -96dBm    busy time 0 / 30ms
Channel Survey: freq 5745MHz    noise -96dBm    busy time 0 / 30ms
Channel Survey: freq 5765MHz    noise -94dBm    busy time 1 / 50ms
Scan Result: D0:DB:B7:B8:EA:09    SSID: "                    NetComm 3922"    freq: 5785    RSSI: -91
Channel Survey: freq 5785MHz    noise -93dBm    busy time 1 / 50ms
Channel Survey: freq 5805MHz    noise -95dBm    busy time 2 / 50ms
Channel Survey: freq 5825MHz    noise -95dBm    busy time 0 / 30ms
Scan on virtual interface #0 complete    status: 0    found: 10
 
Yes, UGREEN and Tenda are listed in the driver source code. I'd say that device does support bluetooth but it just isn't enabled in the windows drivers (it has a very minor quirk that makes it non-standard).
 
This library was exactly what I was looking for - the ability to a host audio device with a Teensy. ChatGPT wrote an audio driver for me and as far as I can tell its working! The device I want to use is composite with audio, MIDI and CDC interfaces. Its a Teensy 4.1 USB device but I don't have the source code for it. I need the CDC interface working to test the audio stream so in the meantime I'm using a Raspberry Pi Pico 2 with a simple USB serial sketch that sends a test message every few seconds.

I derived a CDC driver from the example CH341 driver and I asked ChatGPT to write a CDC driver as well - neither works. It seems to enumerate correctly but I never get data. I can't really follow what's going on in the driver - I think it needs to call activate_endpoint to set it up for bulk transfers but I don't see this happening. I can't figure out what has to happen in the driver for that code in device.cpp to be called.

Output of the enumeration process - I added some debug messages:


Starting USB PLL... done.
starting USB serial driver
begin ehci reset (1 root ports). done
PERIODIC_LIST_SIZE: 32 (128 bytes @ 0x20206000)
Enumeration QH: 20201300
USBCMD (FS): 00008004
USB: beginning main loop
Root Port 0 status: 10001803
Port 0:0 starting debounce
Root Port 0 status: 1C001002
Root Port 0 status: 10001003
Port 0:0 starting debounce
Adjusted timer start time
Port 0:0 finished debounce, starting reset
Port 0:0 Reset on
Root Port 0 status: 10001805
Port 0:0 starting recovery
Port 0:0 finished recovery
!!! Enumeration begin !!!
Port 0:0 bDeviceClass EF bDeviceSubClass 02 bDeviceProtocol 01 bMaxPacketSize 64
New Endpoint<0x202062c0> type 0
New Device<0x20206280>: address 1, port 0, hub 0, speed 0, control_packet_size 64
Port 0:0 successfully set address 1
Device<0x20206280> control callback 0x20206748 result 4
Device<0x20206280> number of supported languages: 1
Device<0x20206280> using language 0409 for strings
Device<0x20206280> control callback 0x202068a8 result 18
Device<0x20206280> Device Descriptor 0x202055a0:
bcdUSB 0200
bDeviceClass EF
bDeviceSubClass 02
bDeviceProtocol 01
idVendor 2E8A
idProduct 000F
bcdDevice 0100
iManufacturer 2
iProduct 3
iSerialNumber 4
bNumConfigurations 1
Device<0x20206280> control callback 0x20206108 result 26
Device<0x20206280> string index 2: Raspberry Pi
Device<0x20206280> control callback 0x20206b28 result 14
Device<0x20206280> string index 3: Pico 2
Device<0x20206280> control callback 0x20206da8 result 34
Device<0x20206280> string index 4: 964F86C7634C7A98
Device<0x20206280> control callback 0x20206f08 result 9
Device<0x20206280> control callback 0x20206748 result 75
Device<0x20206280> Configuration 1:
wTotalLength 75
bNumInterfaces 2
iConfiguration 0
bmAttributes A0
bMaxPower 125
Interface 0, Length 35:
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 02
bInterfaceSubClass 02
bInterfaceProtocol 00
iInterface 1
Endpoint 81:
bmAttributes 03
wMaxPacketSize 0008
bInterval 16
Interface 1, Length 23:
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 0A
bInterfaceSubClass 00
bInterfaceProtocol 00
iInterface 0
Endpoint 01:
bmAttributes 02
wMaxPacketSize 0040
bInterval 0
Endpoint 82:
bmAttributes 02
wMaxPacketSize 0040
bInterval 0
Device<0x20206280> control callback 0x20206a08 result 24
Device<0x20206280> string index 1: Pico Serial
Device<0x20206280> activate configuration 1(-1)
Device<0x20206280> activate interface 00 altSetting 00
Device<0x20206280> activate interface 01 altSetting 00
CDC serial attached: ctrl=0 data=1 IN=82 OUT=01
Device<0x20206280> control callback 0x20206b68 result 0
Device<0x20206280> new configuration set: 01
Device<0x20206280> activate configuration 1(1)


I also tested the library CH341 driver with an old ESP8266 board that has a CH340 on it. It does the same thing - enumerates but no data is transferred.

Suggestions would be much appreciated!
 
I've tested the CH341 example with a CH340 and it definitely works. It is configured to use hardware flow control (RTS/CTS) by default, maybe your ESP8266 doesn't have those pins connected.

For your CDC driver I really can't offer help without seeing any code. When a driver accepts a device's configuration, that configuration is activated along with all default interfaces and endpoints within them so the driver doesn't need to do any of that manually. What it does need to do is use the BulkMessage/InterruptMessage functions to send or receive data through the endpoints.
 
Thanks for the reply. I tried setting DTR and RTS active by default in my driver thinking that the device might be expecting that but it didn't help. I didn't try fiddling with that with the CH341 driver.

I'll take another look at the CH341 driver to see if I can figure out how bulk messages get transferred. I could put the driver on github if you'd be willing to take a look at it. A working CDC driver would be nice to have for your library - I'm sure others could use it. Ditto the MIDI driver which I also need. When I've verified the audio driver I can contribute that too.
 
For the CH341 driver, serial::queue_read is the function that queues transfers from the IN endpoint while serial::flush is the function that pushes filled buffers to the OUT endpoint. Both functions are used asynchronously with USBCallbacks that get executed when the transfers finish.
 
I cloned the Teensy4_USBhost library and added my CDC and audio drivers plus a test sketch.

The CDC driver is M8_serial which I created by hacking up the CH341 driver. The enumeration code was generated by ChatGPT and I pasted in a few other things from a ChatGPT generated driver that didn't work either but seemed more correct for a CDC driver. The driver seems to enumerate correctly but I don't see any data transfers.

The audio driver is USB_Audio_Host which was entirely written by ChatGPT - impressive! From what I can tell it works but I can't test it with the device I'm targeting until I have a working CDC serial interface. Its a composite device that needs CDC serial, USB audio and MIDI drivers.

I've included a test sketch as well. It forwards the device audio to the Teensy Audio I2S output. The CDC driver is currently commented out but there is some simple test code in there as well. Audio seems to transfer correctly but the device only sends silence until its initiated using the CDC interface. I plugged in a cheap USB dongle and was sending audio but got a lot of ISO transfer errors which I assume are due to mismatched sample rates.

If you can take a look at it and provide suggestions that would be much appreciated.

 
I've been working on a CDC-ACM driver myself, it's nearly done but still has issues working properly with USB2 (high speed) devices like Teensy4. It works fine if I force the port to USB1 - in that case T4 maxes out at 64000 bytes/sec (one 64-byte transfer per USB frame) but performance of a raspberry pi pico in loopback mode is awful; it only manages around 3200 bytes/sec but I'm pretty sure it's just a bad implementation in the pi pico arduino core.

For more than one driver to be able to talk to the same device, they have to use the interface offer() and attach() methods rather than the device/configuration overloads. This can be tricky if the driver needs to claim more than one interface because they're only presented one at a time, with CDC-ACM I check for the union descriptor to know ahead of time what bInterfaceNumber to expect for the data interface and make sure the USB_Device pointer matches the one that was already attached.
 
I've written a new USB Host support library for Teensy 4.x boards: https://github.com/A-Dunstan/teensy4_usbhost

Key features:
- dynamic driver instantiation, e.g. can support any number of hubs or other devices since driver objects can be created as needed
- transparent support for using any type of memory (DTCM/OCRAM/EXTMEM/SDRAM), all cache operations are taken care of
- supports both synchronous and asynchronous transfers (using std::function callbacks)
- makes an effort to return errors using errno
- fully supports isochronous endpoints / transfers (both full and high speed). These are commonly used by devices that deliver realtime data such as audio input/output DACs, or webcams:
WtDhRYN.jpeg


It makes use of a threading library (which is a modified version of AtomThreads) which is written to be as minimally intrusive as possible; you can use the synchronization objects it provides if you want, but otherwise its existence can be ignored. It's a submodule of the git repository so do a "submodule init" after cloning it.

For the time being the only available documentation is in the examples and the included drivers; there's still a lot of code to be cleaned up before it starts being documented properly.
This looks really interesting. When I get back home next week, I want to see what it would take to adapt my USBTMC host library to use this new USBHost system. It looks like it should be free of all the mods to the TEENSY USB Host source code that was needed to add a new device type to the existing devices in the USB Host library. I had gotten the USBTMC project to the point where I could do Teensy to Teensy high-speed bulk transfers fast enough to be useful for ADCs and oscillopses, etc. I even got my PC to recognize the device--but I didn't go much further than that. Teensy-to-Teensy transfers were a bit like filling your water bottle from a fire hose! Data arrived as fast I could write it to SD (about 20MB/sec, IIRC).

I suppose the ultimate test is to see how it handles web cams that support only Isochronous transfers. I never did find a cheap web cam that supported bulk transfers, so my camera driver project stopped after I got the FLIR Boson camera driver working.
 
The pic in the opening post is from my desktop usb webcam which delivers jpg frames over isochronous transfers, so it definitely works. There's an example that pipes the received frames into a basic web server so a browser can view them.
 
CDC-ACM driver (interface level) and an example sketch have been added.

There's a bit of CDC-ACM functionality not implemented (the status notifications) because I don't have any way to test them - it's not implemented by the Teensy serial device.
 
Last edited:
Have to say a massive thank you for the library.

I’ve managed to use it to send my synthengines output to my Numa compact 2x via the usb host. I can now control the synths midi via the controller and use its built in speakers for output. I did have some issues with memory but after some brilliant guidance I worked around that and have the standard library in use.

This was something I’d wanted for some time and will now let the synth output audio via host usb devices going forward

many thanks again for creating
 
Added a new driver for ASIX88772 10/100mbps USB to Ethernet adapter. These were very common back in the day as it was the same chipset as used in the official Nintendo wii usb ethernet adapter, and the consoles would recognize any clones as long as they had the expected VID/PID.

Benchmark speed is roughly 60mbit/s using QNEthernet SimpleIPerfServer example. Using the adapter with QNEthernet is as easy as cloning this repository into your Arduino libraries and using #include <asix88772_QNEthernet.h> instead of #include <QNEthernet.h>.
 
An interesting find running iperf on the ASIX88772... These are the results of the "default" code:
Code:
$ iperf -c 192.168.0.110 -i 1
------------------------------------------------------------
Client connecting to 192.168.0.110, TCP port 5001
TCP window size:  512 KByte (default)
------------------------------------------------------------
[  1] local 192.168.0.30 port 62189 connected with 192.168.0.110 port 5001
[ ID] Interval       Transfer     Bandwidth
[  1] 0.0000-1.0000 sec  7.50 MBytes  62.9 Mbits/sec
[  1] 1.0000-2.0000 sec  7.25 MBytes  60.8 Mbits/sec
[  1] 2.0000-3.0000 sec  7.25 MBytes  60.8 Mbits/sec
[  1] 3.0000-4.0000 sec  7.25 MBytes  60.8 Mbits/sec
[  1] 4.0000-5.0000 sec  7.25 MBytes  60.8 Mbits/sec
[  1] 5.0000-6.0000 sec  7.25 MBytes  60.8 Mbits/sec
[  1] 6.0000-7.0000 sec  7.25 MBytes  60.8 Mbits/sec
[  1] 7.0000-8.0000 sec  7.25 MBytes  60.8 Mbits/sec
[  1] 8.0000-9.0000 sec  7.25 MBytes  60.8 Mbits/sec
[  1] 9.0000-10.0000 sec  6.62 MBytes  55.6 Mbits/sec
[  1] 10.0000-10.5640 sec   128 KBytes  1.86 Mbits/sec
[  1] 0.0000-10.5640 sec  72.3 MBytes  57.4 Mbits/sec
But if I make one minor change to the EHCI setup and lower the interrupt interval (ITC in the USB_CMD register) from 1 micro-frame to immediate, I get these results:
Code:
$ iperf -c 192.168.0.110 -i 1
------------------------------------------------------------
Client connecting to 192.168.0.110, TCP port 5001
TCP window size:  512 KByte (default)
------------------------------------------------------------
[  1] local 192.168.0.30 port 62199 connected with 192.168.0.110 port 5001
[ ID] Interval       Transfer     Bandwidth
[  1] 0.0000-1.0000 sec  11.1 MBytes  93.3 Mbits/sec
[  1] 1.0000-2.0000 sec  11.2 MBytes  94.4 Mbits/sec
[  1] 2.0000-3.0000 sec  11.2 MBytes  94.4 Mbits/sec
[  1] 3.0000-4.0000 sec  11.2 MBytes  94.4 Mbits/sec
[  1] 4.0000-5.0000 sec  11.2 MBytes  94.4 Mbits/sec
[  1] 5.0000-6.0000 sec  11.1 MBytes  93.3 Mbits/sec
[  1] 6.0000-7.0000 sec  11.0 MBytes  92.3 Mbits/sec
[  1] 7.0000-8.0000 sec  11.2 MBytes  94.4 Mbits/sec
[  1] 8.0000-9.0000 sec  10.8 MBytes  90.2 Mbits/sec
[  1] 9.0000-10.0000 sec  10.9 MBytes  91.2 Mbits/sec
[  1] 0.0000-10.0260 sec   111 MBytes  93.1 Mbits/sec

Obviously this is pretty much the limit; it's only a 100TX adapter, not 1000TX.
I'm not very keen on changing the ITC value permanently since the driver should already be queueing more than enough buffers to cover a full micro-frame and immediate interrupts cause a lot more processing overhead...
 
Back
Top