embassy-rs / embassy

Modern embedded framework, using Rust and async.
https://embassy.dev
Apache License 2.0
5.44k stars 752 forks source link

stm32 hid Control Pipe data out is unstable #3459

Open votrungchi opened 13 hours ago

votrungchi commented 13 hours ago

Hello, I am facing a problem with USB HID on STM32F407VG, the data out from Control Pipe is incorrect. The data I only send is [Tx Feature Report [2] --> 0102], but the Control Pipe data out sometime is [00, 00], sometime is [ ], the expected is [01, 02].

The code I use to reproduce the issue.

#![no_std]
#![no_main]

use {defmt_rtt as _, panic_probe as _};

use embassy_stm32::usb::Driver;
use embassy_stm32::{bind_interrupts, usb, Config};
use embassy_stm32::time::Hertz;

use embassy_executor::Spawner;
use embassy_stm32::peripherals::USB_OTG_FS;
use embassy_usb::class::hid::{HidWriter, State};
use embassy_usb::Builder;

use my_crate::handler::MyDeviceHandler;

#[rustfmt::skip]
pub const MY_TEST_DESCRIPTOR: [u8; 37] =
        [
            0x05, 0x01,              // USAGE_PAGE (Generic Desktop)
            0x09, 0x00,              // USAGE (Undefined)
            0xA1, 0x01,              // COLLECTION (Application)

            // Feature Report
            0x85, 0x01,          // REPORT_ID (1)
            0x09, 0x02,          // USAGE (Undefined)
            0x15, 0x00,          // LOGICAL_MINIMUM (0)
            0x26, 0xFF, 0x00,    // LOGICAL_MAXIMUM (255)
            0x75, 0x08,          // REPORT_SIZE (8)
            0x95, 0x01,          // REPORT_COUNT (1)
            0xB1, 0x02,          // FEATURE (Data,Var,Abs)

            // Output Report
            0x85, 0x02,          // REPORT_ID (2)
            0x09, 0x03,          // USAGE (Undefined)
            0x15, 0x00,          // LOGICAL_MINIMUM (0)
            0x26, 0xFF, 0x00,    // LOGICAL_MAXIMUM (255)
            0x75, 0x08,          // REPORT_SIZE (8)
            0x95, 0x01,          // REPORT_COUNT (1)
            0x91, 0x02,          // OUTPUT (Data,Var,Abs)

            0xC0
        ];

bind_interrupts!(struct Irqs {
    OTG_FS => usb::InterruptHandler<USB_OTG_FS>;
});

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    let mut config = Config::default();
    {
        use embassy_stm32::rcc::*;
        config.rcc.hse = Some(Hse {
            freq: Hertz(8_000_000),
            mode: HseMode::Bypass,
        });
        config.rcc.pll_src = PllSource::HSE;
        config.rcc.pll = Some(Pll {
            prediv: PllPreDiv::DIV4,
            mul: PllMul::MUL168,
            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
            divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
            divr: None,
        });
        config.rcc.ahb_pre = AHBPrescaler::DIV1;
        config.rcc.apb1_pre = APBPrescaler::DIV4;
        config.rcc.apb2_pre = APBPrescaler::DIV2;
        config.rcc.sys = Sysclk::PLL1_P;
        config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q;
    }
    let p = embassy_stm32::init(config);

    // Create the driver, from the HAL.
    let mut ep_out_buffer = [0u8; 256];
    let mut stm32_usb_config = embassy_stm32::usb::Config::default();
    stm32_usb_config.vbus_detection = true;
    let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, stm32_usb_config);

    // Create embassy-usb Config
    let mut usb_config = embassy_usb::Config::new(0xc0de, 0xcafe);
    usb_config.manufacturer = Some("Embassy");
    usb_config.product = Some("HID test");
    usb_config.serial_number = Some("12345678");
    usb_config.max_power = 100;
    usb_config.max_packet_size_0 = 64;

    // Required for windows compatibility.
    // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
    usb_config.device_class = 0xEF;
    usb_config.device_sub_class = 0x02;
    usb_config.device_protocol = 0x01;
    usb_config.composite_with_iads = true;

    // Create embassy-usb DeviceBuilder using the driver and config.
    // It needs some buffers for building the descriptors.
    let mut config_descriptor = [0; 256];
    let mut bos_descriptor = [0; 256];
    // You can also add a Microsoft OS descriptor.
    let mut msos_descriptor = [0; 256];
    let mut control_buf = [0; 64];

    let mut my_device_handler = MyDeviceHandler::new();

    let mut state = State::new();

    let mut builder = Builder::new(
        driver,
        usb_config,
        &mut config_descriptor,
        &mut bos_descriptor,
        &mut msos_descriptor,
        &mut control_buf,
    );

    builder.handler(&mut my_device_handler);

    // Create classes on the builder.
    let hid_config = embassy_usb::class::hid::Config {
        report_descriptor: &MY_TEST_DESCRIPTOR,
        request_handler: None,
        poll_ms: 1,
        max_packet_size: 64,
    };

    let _writer = HidWriter::<_, 64>::new(&mut builder, &mut state, hid_config);

    // Build the builder.
    let mut usb = builder.build();

    // Run the USB device.
    usb.run().await;
}

The log:

TRACE irq
└─ embassy_stm32::usb::_version::{impl#0}::on_interrupt @ embassy\embassy-stm32\src\usb\otg.rs:27
TRACE control request: Request { direction: Out, request_type: Class, recipient: Interface, request: 9, value: 769, index: 0, length: 2 }
└─ embassy_usb::{impl#1}::handle_control::{async_fn#0} @ embassy\embassy-usb\src\lib.rs:347
TRACE irq
└─ embassy_stm32::usb::_version::{impl#0}::on_interrupt @ embassy\embassy-stm32\src\usb\otg.rs:27
TRACE   control out data: [00, 00]
└─ embassy_usb::{impl#1}::handle_control_out::{async_fn#0} @ embassy\embassy-usb\src\lib.rs:425
TRACE HID control_out Request { direction: Out, request_type: Class, recipient: Interface, request: 9, value: 769, index: 0, length: 2 } [0, 0]
└─ embassy_usb::class::hid::{impl#8}::control_out @ embassy\embassy-usb\src\class\hid.rs:468
TRACE irq
└─ embassy_stm32::usb::_version::{impl#0}::on_interrupt @ embassy\embassy-stm32\src\usb\otg.rs:27
TRACE control request: Request { direction: Out, request_type: Class, recipient: Interface, request: 9, value: 769, index: 0, length: 2 }
└─ embassy_usb::{impl#1}::handle_control::{async_fn#0} @ embassy\embassy-usb\src\lib.rs:347
TRACE irq
└─ embassy_stm32::usb::_version::{impl#0}::on_interrupt @ embassy\embassy-stm32\src\usb\otg.rs:27
TRACE   control out data: []
└─ embassy_usb::{impl#1}::handle_control_out::{async_fn#0} @ embassy\embassy-usb\src\lib.rs:425
TRACE HID control_out Request { direction: Out, request_type: Class, recipient: Interface, request: 9, value: 769, index: 0, length: 2 } []
└─ embassy_usb::class::hid::{impl#8}::control_out @ embassy\embassy-usb\src\class\hid.rs:468
TRACE control request: Request { direction: Out, request_type: Standard, recipient: Device, request: 0, value: 488, index: 32, length: 485 }
└─ embassy_usb::{impl#1}::handle_control::{async_fn#0} @ embassy\embassy-usb\src\lib.rs:347
WARN  got CONTROL OUT with length 485 higher than the control_buf len 64, rejecting.
└─ embassy_usb::{impl#1}::handle_control_out::{async_fn#0} @ embassy\embassy-usb\src\lib.rs:399
TRACE irq
└─ embassy_stm32::usb::_version::{impl#0}::on_interrupt @ embassy\embassy-stm32\src\usb\otg.rs:27
TRACE control request: Request { direction: Out, request_type: Class, recipient: Interface, request: 9, value: 769, index: 0, length: 2 }
└─ embassy_usb::{impl#1}::handle_control::{async_fn#0} @ embassy\embassy-usb\src\lib.rs:347
TRACE irq
└─ embassy_stm32::usb::_version::{impl#0}::on_interrupt @ embassy\embassy-stm32\src\usb\otg.rs:27
TRACE   control out data: [00, 00]
└─ embassy_usb::{impl#1}::handle_control_out::{async_fn#0} @ embassy\embassy-usb\src\lib.rs:425
TRACE HID control_out Request { direction: Out, request_type: Class, recipient: Interface, request: 9, value: 769, index: 0, length: 2 } [0, 0]
└─ embassy_usb::class::hid::{impl#8}::control_out @ embassy\embassy-usb\src\class\hid.rs:468
TRACE irq
└─ embassy_stm32::usb::_version::{impl#0}::on_interrupt @ embassy\embassy-stm32\src\usb\otg.rs:27
TRACE irq
└─ embassy_stm32::usb::_version::{impl#0}::on_interrupt @ embassy\embassy-stm32\src\usb\otg.rs:27
TRACE control request: Request { direction: Out, request_type: Class, recipient: Interface, request: 9, value: 769, index: 0, length: 2 }
└─ embassy_usb::{impl#1}::handle_control::{async_fn#0} @ embassy\embassy-usb\src\lib.rs:347
TRACE irq
└─ embassy_stm32::usb::_version::{impl#0}::on_interrupt @ embassy\embassy-stm32\src\usb\otg.rs:27
TRACE   control out data: [01, 02]
└─ embassy_usb::{impl#1}::handle_control_out::{async_fn#0} @ embassy\embassy-usb\src\lib.rs:425
TRACE HID control_out Request { direction: Out, request_type: Class, recipient: Interface, request: 9, value: 769, index: 0, length: 2 } [1, 2]
└─ embassy_usb::class::hid::{impl#8}::control_out @ embassy\embassy-usb\src\class\hid.rs:468
TRACE irq
└─ embassy_stm32::usb::_version::{impl#0}::on_interrupt @ embassy\embassy-stm32\src\usb\otg.rs:27
TRACE irq
└─ embassy_stm32::usb::_version::{impl#0}::on_interrupt @ embassy\embassy-stm32\src\usb\otg.rs:27
TRACE control request: Request { direction: Out, request_type: Class, recipient: Interface, request: 9, value: 769, index: 0, length: 2 }
└─ embassy_usb::{impl#1}::handle_control::{async_fn#0} @ embassy\embassy-usb\src\lib.rs:347
TRACE irq
└─ embassy_stm32::usb::_version::{impl#0}::on_interrupt @ embassy\embassy-stm32\src\usb\otg.rs:27
TRACE   control out data: [01, 02]
└─ embassy_usb::{impl#1}::handle_control_out::{async_fn#0} @ embassy\embassy-usb\src\lib.rs:425
TRACE HID control_out Request { direction: Out, request_type: Class, recipient: Interface, request: 9, value: 769, index: 0, length: 2 } [1, 2]
└─ embassy_usb::class::hid::{impl#8}::control_out @ embassy\embassy-usb\src\class\hid.rs:468
Dirbaio commented 8 hours ago

Can you try enabling trace logs in embassy-usb-synopsys-otg? Add it here https://github.com/embassy-rs/embassy/blob/main/embassy-stm32/Cargo.toml#L113 so the defmt feature of embassy-stm32 enables it, then make sure you have DEFMT_LOG=trace

votrungchi commented 3 hours ago

Hello, here is the trace: hid.log Note: I have to comment out the assert because of compile error:

Compiling embassy-usb-synopsys-otg v0.1.0 (C:\Users\Chi\workspace\github.com\votrungchi\formula-rs\embassy\embassy-usb-synopsys-otg)
error[E0015]: cannot call non-const fn `defmt::export::acquire` in constant functions
   --> embassy\embassy-usb-synopsys-otg\src\fmt.rs:16:13
    |
16  |             ::defmt::assert!($($x)*);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^
    #[doc = "Device IN endpoint transmit FIFO size register"]
    #[inline(always)]
    pub const fn dieptxf(self, n: usize) -> Reg<regs::Fsiz, RW> {
        // assert!(n < 7usize);
        unsafe { Reg::from_ptr(self.ptr.add(0x0104usize + n * 4usize) as _) }
    }