rust-embedded-community / usbd-serial

Work-in progress minimal CDC-ACM (USB serial port) class for usb-device
MIT License
118 stars 35 forks source link

What is needed to make a composite device with two Serials? #34

Open csarn opened 1 year ago

csarn commented 1 year ago

I'm trying what others seem to have done before, but I can't find any examples online. I want to have two serial CDC-ACM interfaces, so that one can stream data continuously while the other can be used for interaction with the device. Calling the second SerialPort::new() panics at https://github.com/rust-embedded-community/usbd-serial/blob/master/src/cdc_acm.rs#L60 with alloc_ep failed: EndpointOverflow. My microcontroller is the stm32f411. This is supported by the blackmagic probe firmware, which also offers two Serial interfaces, so I assume that the microcontroller should be able to handle it: https://github.com/blackmagic-debug/blackmagic/blob/df0c092165a5e6784ba94a0eaa0ec08a71c3a0c7/src/platforms/common/usb_serial.c#L30-L39 Is it maybe relevant that the blackmagic uses an IN endpoint for one CDC CTRL endpoint and an OUT endpoint for the other one? Is this somehow possible with usbd-serial?

ololoshka2871 commented 11 months ago

I found the solution: The problem is that the 128 byte buffer (default) is not enough for the descriptors of 2 CDC-acm devices, you need to enable the 256 byte buffer:

  1. Cargo.toml

    usb-device = { version = "...", features = ["control-buffer-256"] }
  2. Initialise two separate ports

    
    static mut USB_BUS: Option<usb_device::bus::UsbBusAllocator<UsbBusType>> = None;

let usb = Peripheral { usb: ctx.device.USB, pin_dm: gpioa.pa11, pin_dp: gpioa.pa12, };

unsafe { USB_BUS.replace(UsbBus::new(usb)); }

let serial1 = SerialPort::new(unsafe { USB_BUS.as_ref().unwrap_unchecked() }); let serial2 = SerialPort::new(unsafe { USB_BUS.as_ref().unwrap_unchecked() });

let usb_dev = UsbDeviceBuilder::new( unsafe { USB_BUS.as_ref().unwrap_unchecked() }, usb_device::prelude::UsbVidPid(0x0001, 0x1111), ) .manufacturer("myvendor") .product("maydevice") .serial_number("1") .composite_with_iads() # <- enable composite device .build();

NickCao commented 5 months ago

Is it maybe relevant that the blackmagic uses an IN endpoint for one CDC CTRL endpoint and an OUT endpoint for the other one? Is this somehow possible with usbd-serial?

It is. stm32f411 only has 4 bi-directional endpoints. 1 is used for EP0, only 3 left for serial ports. Every serial port uses 1.5 endpoints (1 for data, 0.5 for control). However the control endpoints of the two serial ports have to be created in different directions just like what blackmagic is doing.

I've implemented the idea in https://github.com/NickCao/usbd-serial/commit/41faf423713e62b1831fbc24721331e122a98f80

Edit: According to https://stackoverflow.com/questions/61386938/use-cdc-acm-without-interrupt-endpoint, this can cause some issues due to lost interrupt data, but we don't seem to be handling interrupt data anyway.