add hil test, update readme

This commit is contained in:
hathach
2026-03-06 17:56:05 +07:00
parent 73cd531295
commit aeb121f94c
3 changed files with 190 additions and 0 deletions

View File

@ -87,6 +87,7 @@ Supports multiple device configurations by dynamically changing USB descriptors,
- Communication Device Class (CDC)
- Device Firmware Update (DFU): DFU mode (WIP) and Runtime
- Human Interface Device (HID): Generic (In & Out), Keyboard, Mouse, Gamepad etc ...
- Printer class
- Mass Storage Class (MSC): with multiple LUNs
- Musical Instrument Digital Interface (MIDI)
- Media Transfer Protocol (MTP/PTP)

View File

@ -0,0 +1,80 @@
#### Printer to CDC
This example demonstrates a USB composite device with a Printer class interface and a CDC serial interface. Data flows bidirectionally between the two:
- Data sent to the Printer (from host) is forwarded to the CDC serial port
- Data sent to the CDC serial port (from host) is forwarded to the Printer IN endpoint
This is useful for debugging printer class communication or as a reference for implementing printer class devices.
#### USB Interfaces
| Interface | Class | Description |
|-----------|-------|-------------|
| 0 | CDC ACM | Virtual serial port |
| 2 | Printer | USB Printer (bidirectional, protocol 2) |
#### How to Test
The device exposes two endpoints on the host:
- `/dev/ttyACM0` (CDC serial port)
- `/dev/usb/lp0` (USB printer)
Note: the actual device numbers may vary depending on your system.
**Prerequisites (Linux):**
```bash
# Load the USB printer kernel module if not already loaded
sudo modprobe usblp
# Check devices exist
ls /dev/ttyACM* /dev/usb/lp*
```
**Test Printer to CDC (host writes to printer, reads from CDC):**
```bash
# Terminal 1: read from CDC
cat /dev/ttyACM0
# Terminal 2: write to printer
echo "hello from printer" > /dev/usb/lp0
# "hello from printer" appears in Terminal 1
```
**Test CDC to Printer (host writes to CDC, reads from printer):**
```bash
# Terminal 1: read from printer IN endpoint
cat /dev/usb/lp0
# Terminal 2: write to CDC
echo "hello from cdc" > /dev/ttyACM0
# "hello from cdc" appears in Terminal 1
```
**Interactive bidirectional test:**
```bash
# Terminal 1: open CDC serial port
minicom -D /dev/ttyACM0
# Terminal 2: send to printer
echo "tinyusb print example" > /dev/usb/lp0
# Text appears in minicom. Type in minicom to send data back through printer TX.
```
#### IEEE 1284 Device ID
The device responds to GET_DEVICE_ID requests with:
```
MFG:TinyUSB;MDL:Printer to CDC;CMD:PS;CLS:PRINTER;
```
Verify with:
```bash
cat /sys/class/usbmisc/lp0/device/ieee1284_id
```

View File

@ -166,6 +166,32 @@ def open_mtp_dev(uid):
return None
def get_printer_dev(id, vendor_str, product_str, ifnum):
"""Find /dev/usb/lpX by matching USB serial, vendor, product, and interface number via sysfs"""
vendor_str = vendor_str.replace(' ', '_') if vendor_str else ''
product_str = product_str.replace(' ', '_') if product_str else ''
for lp in glob.glob('/sys/class/usbmisc/lp*'):
try:
sn = open(f'{lp}/device/../serial').read().strip()
if sn == id:
return f'/dev/usb/{os.path.basename(lp)}'
except (FileNotFoundError, PermissionError, ValueError):
pass
return None
def open_printer_dev(id, vendor_str, product_str, ifnum):
"""Wait for printer device to enumerate and return its path"""
timeout = ENUM_TIMEOUT
while timeout > 0:
lp_dev = get_printer_dev(id, vendor_str, product_str, ifnum)
if lp_dev and os.path.exists(lp_dev):
return lp_dev
time.sleep(1)
timeout -= 1
assert False, f'Printer device not found for {id} if{ifnum:02d}'
# -------------------------------------------------------------
# Flashing firmware
# -------------------------------------------------------------
@ -552,6 +578,88 @@ def test_device_hid_composite_freertos(id):
pass
def test_device_printer_to_cdc(board):
import threading
uid = board['uid']
# Wait for CDC port and printer device
cdc_port = get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 0)
ser = open_serial_dev(cdc_port)
lp_dev = open_printer_dev(uid, 'TinyUSB', 'TinyUSB_Device', 2)
# Test 0: Verify IEEE 1284 Device ID from sysfs
expected_id = 'MFG:TinyUSB;MDL:Printer to CDC;CMD:PS;CLS:PRINTER;'
lp_name = os.path.basename(lp_dev)
sysfs_id_path = f'/sys/class/usbmisc/{lp_name}/device/ieee1284_id'
if os.path.exists(sysfs_id_path):
with open(sysfs_id_path) as f:
ieee1284_id = f.read().strip()
if ieee1284_id:
assert ieee1284_id == expected_id, (f'IEEE 1284 ID mismatch:\n'
f' expected: {expected_id}\n got: {ieee1284_id}')
def rand_ascii(length):
return "".join(random.choices(string.ascii_letters + string.digits, k=length)).encode("ascii")
sizes = [32, 64, 128, 256, 512, random.randint(2000, 5000)]
# flush any stale data
ser.reset_input_buffer()
# Test 1: Printer -> CDC with multiple sizes
for size in sizes:
test_data = rand_ascii(size)
with open(lp_dev, 'wb') as lp:
lp.write(test_data)
lp.flush()
rd = b''
while len(rd) < size:
chunk = ser.read(size - len(rd))
assert chunk, f'Printer->CDC timeout at {len(rd)}/{size} bytes'
rd += chunk
assert rd == test_data, (f'Printer->CDC wrong data ({size} bytes):\n'
f' expected: {test_data[:64]}\n received: {rd[:64]}')
# Test 2: CDC -> Printer with multiple sizes
# Use a thread to read from printer since /dev/usb/lp read blocks
for size in sizes:
test_data = rand_ascii(size)
rd_result = [b'', None] # [data, error]
def lp_reader():
try:
rd = b''
with open(lp_dev, 'rb') as lp:
while len(rd) < size:
chunk = lp.read(size - len(rd))
if not chunk:
break
rd += chunk
rd_result[0] = rd
except Exception as e:
rd_result[1] = e
reader = threading.Thread(target=lp_reader, daemon=True)
reader.start()
# Write to CDC in chunks
offset = 0
while offset < size:
chunk_size = min(random.randint(1, 64), size - offset)
ser.write(test_data[offset:offset + chunk_size])
ser.flush()
offset += chunk_size
reader.join(timeout=10)
assert not reader.is_alive(), f'CDC->Printer timeout ({size} bytes)'
assert rd_result[1] is None, f'CDC->Printer read error: {rd_result[1]}'
assert rd_result[0] == test_data, (f'CDC->Printer wrong data ({size} bytes):\n'
f' expected: {test_data[:64]}\n received: {rd_result[0][:64]}')
ser.close()
def test_device_mtp(board):
uid = board['uid']
@ -623,6 +731,7 @@ device_tests = [
'device/dfu_runtime',
'device/cdc_msc_freertos',
'device/hid_boot_interface',
'device/printer_to_cdc',
# 'device/mtp'
]