From 73cd53129529f2dce70072953bfd50ec9dc6b8ea Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 6 Mar 2026 17:22:38 +0700 Subject: replace printer_to_hid example with printer_to_cdc example fix printer GET_DEVICE_ID request weird wIndex (interface high, alt low) --- examples/device/printer_to_cdc/src/main.c | 115 ++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 examples/device/printer_to_cdc/src/main.c (limited to 'examples/device/printer_to_cdc/src/main.c') diff --git a/examples/device/printer_to_cdc/src/main.c b/examples/device/printer_to_cdc/src/main.c new file mode 100644 index 000000000..aba79025c --- /dev/null +++ b/examples/device/printer_to_cdc/src/main.c @@ -0,0 +1,115 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Ha Thach (tinyusb.org) + * + * 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. + * + */ + +/* This example demonstrates a USB Printer + CDC composite device. + * Data received on the Printer interface is forwarded to the CDC serial port, + * and data received on the CDC serial port is forwarded back to the Printer interface. + * + * To test: + * 1. Flash the device + * 2. Open a serial terminal on the CDC port (e.g. /dev/ttyACM0) + * 3. Send data to the printer: echo "hello" > /dev/usb/lp0 + * 4. The data appears on the CDC serial terminal + * 5. Type in the serial terminal to send data back through the printer TX + */ + +#include +#include +#include + +#include "bsp/board_api.h" +#include "tusb.h" + +#include "usb_descriptors.h" + +// -------------------------------------------------------------------+ +// Tasks +// -------------------------------------------------------------------+ + +// Forward data from Printer RX to CDC TX +static void printer_to_cdc_task(void) { + if (tud_printer_read_available() == 0 || !tud_cdc_write_available()) { + return; + } + + uint8_t buf[64]; + uint32_t count = tud_printer_read(buf, sizeof(buf)); + if (count > 0) { + tud_cdc_write(buf, count); + tud_cdc_write_flush(); + } +} + +// Forward data from CDC RX to Printer TX +static void cdc_to_printer_task(void) { + if (tud_cdc_available() == 0 || !tud_printer_write_available()) { + return; + } + + uint8_t buf[64]; + uint32_t count = tud_cdc_read(buf, sizeof(buf)); + if (count > 0) { + tud_printer_write(buf, count); + tud_printer_write_flush(); + } +} + +int main(void) { + board_init(); + // init device stack on configured roothub port + tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO}; + tusb_init(BOARD_TUD_RHPORT, &dev_init); + board_init_after_tusb(); + + while (1) { + tud_task(); // tinyusb device task + printer_to_cdc_task(); // forward printer data to CDC + cdc_to_printer_task(); // forward CDC data to printer + } +} + +//--------------------------------------------------------------------+ +// Printer callbacks +//--------------------------------------------------------------------+ + +void tud_printer_rx_cb(uint8_t itf) { + (void)itf; +} + +// IEEE 1284 Device ID: first 2 bytes are big-endian total length (including the 2 length bytes). +// The rest is the Device ID string using standard abbreviated keys. +static const char printer_device_id[] = + "\x00\x34" // total length = 52 = 0x0034 (big-endian) + "MFG:TinyUSB;" + "MDL:Printer to CDC;" + "CMD:PS;" + "CLS:PRINTER;"; + +TU_VERIFY_STATIC(sizeof(printer_device_id) - 1 == 52, "device ID length mismatch"); + +uint8_t const *tud_printer_get_device_id_cb(uint8_t itf) { + (void)itf; + return (uint8_t const *)printer_device_id; +} -- cgit v1.3.1 From 8878e02c3040ee0b30f9b26c20f45a36260d854a Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 6 Mar 2026 19:24:27 +0700 Subject: fix hil test --- examples/device/printer_to_cdc/src/main.c | 10 ++++--- test/hil/hil_test.py | 43 +++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 15 deletions(-) (limited to 'examples/device/printer_to_cdc/src/main.c') diff --git a/examples/device/printer_to_cdc/src/main.c b/examples/device/printer_to_cdc/src/main.c index aba79025c..ffaf709b4 100644 --- a/examples/device/printer_to_cdc/src/main.c +++ b/examples/device/printer_to_cdc/src/main.c @@ -50,12 +50,13 @@ // Forward data from Printer RX to CDC TX static void printer_to_cdc_task(void) { - if (tud_printer_read_available() == 0 || !tud_cdc_write_available()) { + uint32_t avail = tud_printer_read_available(); + if (avail == 0 || !tud_cdc_write_available()) { return; } uint8_t buf[64]; - uint32_t count = tud_printer_read(buf, sizeof(buf)); + uint32_t count = tud_printer_read(buf, TU_MIN(sizeof(buf), tud_cdc_write_available())); if (count > 0) { tud_cdc_write(buf, count); tud_cdc_write_flush(); @@ -64,12 +65,13 @@ static void printer_to_cdc_task(void) { // Forward data from CDC RX to Printer TX static void cdc_to_printer_task(void) { - if (tud_cdc_available() == 0 || !tud_printer_write_available()) { + uint32_t avail = tud_printer_write_available(); + if (tud_cdc_available() == 0 || avail == 0) { return; } uint8_t buf[64]; - uint32_t count = tud_cdc_read(buf, sizeof(buf)); + uint32_t count = tud_cdc_read(buf, TU_MIN(sizeof(buf), avail)); if (count > 0) { tud_printer_write(buf, count); tud_printer_write_flush(); diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index 46cb79e01..f3cead7a3 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -607,48 +607,68 @@ def test_device_printer_to_cdc(board): # flush any stale data ser.reset_input_buffer() - # Test 1: Printer -> CDC with multiple sizes + # Test 1: Printer -> CDC with multiple sizes, write in random 1-64 byte chunks for size in sizes: test_data = rand_ascii(size) - with open(lp_dev, 'wb') as lp: - lp.write(test_data) - lp.flush() + ser.reset_input_buffer() rd = b'' + offset = 0 + with open(lp_dev, 'wb') as lp: + while offset < size: + chunk_size = min(random.randint(1, 64), size - offset) + lp.write(test_data[offset:offset + chunk_size]) + lp.flush() + rd += ser.read(chunk_size) + offset += chunk_size + # read any remaining bytes (fullspeed devices may need extra time) while len(rd) < size: - chunk = ser.read(size - len(rd)) - assert chunk, f'Printer->CDC timeout at {len(rd)}/{size} bytes' - rd += chunk + remaining = ser.read(size - len(rd)) + if not remaining: + break + rd += remaining 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 + # Test 2: CDC -> Printer with multiple sizes, write in random 1-64 byte chunks # Use a thread to read from printer since /dev/usb/lp read blocks + ser.reset_input_buffer() + time.sleep(0.5) for size in sizes: test_data = rand_ascii(size) rd_result = [b'', None] # [data, error] + reader_ready = threading.Event() def lp_reader(): try: rd = b'' - with open(lp_dev, 'rb') as lp: + fd = os.open(lp_dev, os.O_RDONLY) + reader_ready.set() + try: while len(rd) < size: - chunk = lp.read(size - len(rd)) + chunk = os.read(fd, min(64, size - len(rd))) if not chunk: break rd += chunk + finally: + os.close(fd) rd_result[0] = rd except Exception as e: rd_result[1] = e + reader_ready.set() reader = threading.Thread(target=lp_reader, daemon=True) reader.start() + # wait for reader to open lp device before writing + reader_ready.wait(timeout=5) + time.sleep(0.1) - # Write to CDC in chunks + # Write to CDC in small chunks with flush to avoid overflowing device FIFO offset = 0 while offset < size: chunk_size = min(random.randint(1, 64), size - offset) ser.write(test_data[offset:offset + chunk_size]) ser.flush() + time.sleep(0.01) offset += chunk_size reader.join(timeout=10) @@ -656,6 +676,7 @@ def test_device_printer_to_cdc(board): 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]}') + time.sleep(0.2) ser.close() -- cgit v1.3.1