Skip to content

Commit ca525c0

Browse files
authored
Merge pull request #2799 from adafruit/usb_bff
usb host bff examples
2 parents 5102e9d + c102b1f commit ca525c0

File tree

4 files changed

+379
-0
lines changed

4 files changed

+379
-0
lines changed
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
// SPDX-FileCopyrightText: 2024 Ha Thach for Adafruit Industries
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
/*********************************************************************
6+
Adafruit invests time and resources providing this open source code,
7+
please support Adafruit and open-source hardware by purchasing
8+
products from Adafruit!
9+
10+
MIT license, check LICENSE for more information
11+
Copyright (c) 2019 Ha Thach for Adafruit Industries
12+
All text above, and the splash screen below must be included in
13+
any redistribution
14+
*********************************************************************/
15+
16+
/* This example demonstrates use of both device and host, where
17+
* - Device run on native usb controller (roothub port0)
18+
* - Host run on MAX3421E controller (roothub port1) tested with:
19+
* - SAMD21, SAMD51, nRF52840, ESP32S2, ESP32S3, ESP32
20+
* - RP2040: "pio_usb.h" must not be included, otherwise pio-usb will be used as host controller
21+
*
22+
* Requirements:
23+
* - SPI instance, CS pin, INT pin are correctly configured
24+
*/
25+
26+
/* Host example will get device descriptors of attached devices and print it out:
27+
* Device 1: ID 046d:c52f
28+
Device Descriptor:
29+
bLength 18
30+
bDescriptorType 1
31+
bcdUSB 0200
32+
bDeviceClass 0
33+
bDeviceSubClass 0
34+
bDeviceProtocol 0
35+
bMaxPacketSize0 8
36+
idVendor 0x046d
37+
idProduct 0xc52f
38+
bcdDevice 2200
39+
iManufacturer 1 Logitech
40+
iProduct 2 USB Receiver
41+
iSerialNumber 0
42+
bNumConfigurations 1
43+
*
44+
*/
45+
#include "Adafruit_TinyUSB.h"
46+
#include "SPI.h"
47+
48+
Adafruit_USBH_Host USBHost(&SPI, A1, A2);
49+
50+
// Language ID: English
51+
#define LANGUAGE_ID 0x0409
52+
53+
typedef struct {
54+
tusb_desc_device_t desc_device;
55+
uint16_t manufacturer[32];
56+
uint16_t product[48];
57+
uint16_t serial[16];
58+
bool mounted;
59+
} dev_info_t;
60+
61+
// CFG_TUH_DEVICE_MAX is defined by tusb_config header
62+
dev_info_t dev_info[CFG_TUH_DEVICE_MAX] = { 0 };
63+
64+
//--------------------------------------------------------------------+
65+
// setup() & loop()
66+
//--------------------------------------------------------------------+
67+
void setup() {
68+
Serial.begin(115200);
69+
while ( !Serial ) delay(10); // wait for native usb
70+
Serial.println("starting usb..");
71+
// init host stack on controller (rhport) 1
72+
USBHost.begin(1);
73+
Serial.println("usb started");
74+
Serial.println("TinyUSB Dual: Device Info Example with MAX3421E");
75+
}
76+
77+
void loop() {
78+
USBHost.task();
79+
Serial.flush();
80+
}
81+
82+
//--------------------------------------------------------------------+
83+
// TinyUSB Host callbacks
84+
//--------------------------------------------------------------------+
85+
void print_device_descriptor(tuh_xfer_t *xfer);
86+
87+
void utf16_to_utf8(uint16_t *temp_buf, size_t buf_len);
88+
89+
void print_lsusb(void) {
90+
bool no_device = true;
91+
for (uint8_t daddr = 1; daddr < CFG_TUH_DEVICE_MAX + 1; daddr++) {
92+
// TODO can use tuh_mounted(daddr), but tinyusb has an bug
93+
// use local connected flag instead
94+
dev_info_t *dev = &dev_info[daddr - 1];
95+
if (dev->mounted) {
96+
Serial.printf("Device %u: ID %04x:%04x %s %s\r\n", daddr,
97+
dev->desc_device.idVendor, dev->desc_device.idProduct,
98+
(char *) dev->manufacturer, (char *) dev->product);
99+
100+
no_device = false;
101+
}
102+
}
103+
104+
if (no_device) {
105+
Serial.println("No device connected (except hub)");
106+
}
107+
}
108+
109+
// Invoked when device is mounted (configured)
110+
void tuh_mount_cb(uint8_t daddr) {
111+
Serial.printf("Device attached, address = %d\r\n", daddr);
112+
113+
dev_info_t *dev = &dev_info[daddr - 1];
114+
dev->mounted = true;
115+
116+
// Get Device Descriptor
117+
tuh_descriptor_get_device(daddr, &dev->desc_device, 18, print_device_descriptor, 0);
118+
}
119+
120+
/// Invoked when device is unmounted (bus reset/unplugged)
121+
void tuh_umount_cb(uint8_t daddr) {
122+
Serial.printf("Device removed, address = %d\r\n", daddr);
123+
dev_info_t *dev = &dev_info[daddr - 1];
124+
dev->mounted = false;
125+
126+
// print device summary
127+
print_lsusb();
128+
}
129+
130+
void print_device_descriptor(tuh_xfer_t *xfer) {
131+
if (XFER_RESULT_SUCCESS != xfer->result) {
132+
Serial.printf("Failed to get device descriptor\r\n");
133+
return;
134+
}
135+
136+
uint8_t const daddr = xfer->daddr;
137+
dev_info_t *dev = &dev_info[daddr - 1];
138+
tusb_desc_device_t *desc = &dev->desc_device;
139+
140+
Serial.printf("Device %u: ID %04x:%04x\r\n", daddr, desc->idVendor, desc->idProduct);
141+
Serial.printf("Device Descriptor:\r\n");
142+
Serial.printf(" bLength %u\r\n" , desc->bLength);
143+
Serial.printf(" bDescriptorType %u\r\n" , desc->bDescriptorType);
144+
Serial.printf(" bcdUSB %04x\r\n" , desc->bcdUSB);
145+
Serial.printf(" bDeviceClass %u\r\n" , desc->bDeviceClass);
146+
Serial.printf(" bDeviceSubClass %u\r\n" , desc->bDeviceSubClass);
147+
Serial.printf(" bDeviceProtocol %u\r\n" , desc->bDeviceProtocol);
148+
Serial.printf(" bMaxPacketSize0 %u\r\n" , desc->bMaxPacketSize0);
149+
Serial.printf(" idVendor 0x%04x\r\n" , desc->idVendor);
150+
Serial.printf(" idProduct 0x%04x\r\n" , desc->idProduct);
151+
Serial.printf(" bcdDevice %04x\r\n" , desc->bcdDevice);
152+
153+
// Get String descriptor using Sync API
154+
Serial.printf(" iManufacturer %u ", desc->iManufacturer);
155+
if (XFER_RESULT_SUCCESS ==
156+
tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, dev->manufacturer, sizeof(dev->manufacturer))) {
157+
utf16_to_utf8(dev->manufacturer, sizeof(dev->manufacturer));
158+
Serial.printf((char *) dev->manufacturer);
159+
}
160+
Serial.printf("\r\n");
161+
162+
Serial.printf(" iProduct %u ", desc->iProduct);
163+
if (XFER_RESULT_SUCCESS ==
164+
tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, dev->product, sizeof(dev->product))) {
165+
utf16_to_utf8(dev->product, sizeof(dev->product));
166+
Serial.printf((char *) dev->product);
167+
}
168+
Serial.printf("\r\n");
169+
170+
Serial.printf(" iSerialNumber %u ", desc->iSerialNumber);
171+
if (XFER_RESULT_SUCCESS ==
172+
tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, dev->serial, sizeof(dev->serial))) {
173+
utf16_to_utf8(dev->serial, sizeof(dev->serial));
174+
Serial.printf((char *) dev->serial);
175+
}
176+
Serial.printf("\r\n");
177+
178+
Serial.printf(" bNumConfigurations %u\r\n", desc->bNumConfigurations);
179+
180+
// print device summary
181+
print_lsusb();
182+
}
183+
184+
//--------------------------------------------------------------------+
185+
// String Descriptor Helper
186+
//--------------------------------------------------------------------+
187+
188+
static void _convert_utf16le_to_utf8(const uint16_t *utf16, size_t utf16_len, uint8_t *utf8, size_t utf8_len) {
189+
// TODO: Check for runover.
190+
(void) utf8_len;
191+
// Get the UTF-16 length out of the data itself.
192+
193+
for (size_t i = 0; i < utf16_len; i++) {
194+
uint16_t chr = utf16[i];
195+
if (chr < 0x80) {
196+
*utf8++ = chr & 0xff;
197+
} else if (chr < 0x800) {
198+
*utf8++ = (uint8_t) (0xC0 | (chr >> 6 & 0x1F));
199+
*utf8++ = (uint8_t) (0x80 | (chr >> 0 & 0x3F));
200+
} else {
201+
// TODO: Verify surrogate.
202+
*utf8++ = (uint8_t) (0xE0 | (chr >> 12 & 0x0F));
203+
*utf8++ = (uint8_t) (0x80 | (chr >> 6 & 0x3F));
204+
*utf8++ = (uint8_t) (0x80 | (chr >> 0 & 0x3F));
205+
}
206+
// TODO: Handle UTF-16 code points that take two entries.
207+
}
208+
}
209+
210+
// Count how many bytes a utf-16-le encoded string will take in utf-8.
211+
static int _count_utf8_bytes(const uint16_t *buf, size_t len) {
212+
size_t total_bytes = 0;
213+
for (size_t i = 0; i < len; i++) {
214+
uint16_t chr = buf[i];
215+
if (chr < 0x80) {
216+
total_bytes += 1;
217+
} else if (chr < 0x800) {
218+
total_bytes += 2;
219+
} else {
220+
total_bytes += 3;
221+
}
222+
// TODO: Handle UTF-16 code points that take two entries.
223+
}
224+
return total_bytes;
225+
}
226+
227+
void utf16_to_utf8(uint16_t *temp_buf, size_t buf_len) {
228+
size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t);
229+
size_t utf8_len = _count_utf8_bytes(temp_buf + 1, utf16_len);
230+
231+
_convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, buf_len);
232+
((uint8_t *) temp_buf)[utf8_len] = '\0';
233+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// SPDX-FileCopyrightText: 2024 Ha Thach for Adafruit Industries
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
/*********************************************************************
6+
Adafruit invests time and resources providing this open source code,
7+
please support Adafruit and open-source hardware by purchasing
8+
products from Adafruit!
9+
10+
MIT license, check LICENSE for more information
11+
Copyright (c) 2019 Ha Thach for Adafruit Industries
12+
All text above, and the splash screen below must be included in
13+
any redistribution
14+
*********************************************************************/
15+
16+
#include "Adafruit_TinyUSB.h"
17+
#include "SPI.h"
18+
19+
Adafruit_USBH_Host USBHost(&SPI, A1, A2);
20+
21+
void setup() {
22+
Serial.begin(115200);
23+
24+
// init host stack on controller (rhport) 1
25+
USBHost.begin(1);
26+
27+
// while ( !Serial ) delay(10); // wait for native usb
28+
Serial.println("TinyUSB Dual: HID Device Report Example");
29+
}
30+
31+
void loop() {
32+
USBHost.task();
33+
Serial.flush();
34+
}
35+
36+
extern "C" {
37+
38+
// Invoked when device with hid interface is mounted
39+
// Report descriptor is also available for use.
40+
// tuh_hid_parse_report_descriptor() can be used to parse common/simple enough
41+
// descriptor. Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE,
42+
// it will be skipped therefore report_desc = NULL, desc_len = 0
43+
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
44+
(void) desc_report;
45+
(void) desc_len;
46+
uint16_t vid, pid;
47+
tuh_vid_pid_get(dev_addr, &vid, &pid);
48+
49+
Serial.printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
50+
Serial.printf("VID = %04x, PID = %04x\r\n", vid, pid);
51+
if (!tuh_hid_receive_report(dev_addr, instance)) {
52+
Serial.printf("Error: cannot request to receive report\r\n");
53+
}
54+
}
55+
56+
// Invoked when device with hid interface is un-mounted
57+
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
58+
Serial.printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
59+
}
60+
61+
// Invoked when received report from device via interrupt endpoint
62+
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
63+
Serial.printf("HIDreport : ");
64+
for (uint16_t i = 0; i < len; i++) {
65+
Serial.printf("0x%02X ", report[i]);
66+
}
67+
Serial.println();
68+
// continue to request to receive report
69+
if (!tuh_hid_receive_report(dev_addr, instance)) {
70+
Serial.printf("Error: cannot request to receive report\r\n");
71+
}
72+
}
73+
74+
} // extern C
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# SPDX-FileCopyrightText: 2024 Scott Shawcroft for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
"""USB Host BFF Device Info CircuitPython Example"""
6+
7+
import time
8+
import board
9+
import max3421e
10+
import usb
11+
12+
spi = board.SPI()
13+
cs = board.A1
14+
irq = board.A2
15+
16+
host_chip = max3421e.Max3421E(spi, chip_select=cs, irq=irq)
17+
18+
while True:
19+
print("Finding devices:")
20+
for device in usb.core.find(find_all=True):
21+
# pylint: disable=line-too-long
22+
print(f"{device.idVendor:04x}:{device.idProduct:04x}: {device.manufacturer} {device.product}")
23+
time.sleep(5)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
"""USB Host BFF CircuitPython Example
6+
Read key report from attached keyboard"""
7+
8+
9+
import time
10+
import array
11+
import board
12+
import max3421e
13+
import usb
14+
15+
spi = board.SPI()
16+
cs = board.A1
17+
irq = board.A2
18+
19+
host_chip = max3421e.Max3421E(spi, chip_select=cs, irq=irq)
20+
21+
device = None
22+
vid = None
23+
pid = None
24+
while device is None:
25+
for d in usb.core.find(find_all=True):
26+
vid = d.idVendor
27+
pid = d.idProduct
28+
device = usb.core.find(idVendor=vid, idProduct=pid)
29+
time.sleep(1)
30+
31+
device.set_configuration()
32+
33+
print(f"{device.idVendor:04x}:{device.idProduct:04x}: {device.manufacturer} {device.product}")
34+
35+
# Test to see if the kernel is using the device and detach it.
36+
if device.is_kernel_driver_active(0):
37+
device.detach_kernel_driver(0)
38+
39+
# Boot keyboards have 8 byte reports
40+
buf = array.array("B", [0] * 8)
41+
while True:
42+
try:
43+
count = device.read(0x81, buf)
44+
# pylint: disable=broad-except
45+
except Exception as e:
46+
continue
47+
for i in range(0, 8):
48+
print(buf[i], end=" ")
49+
print()

0 commit comments

Comments
 (0)