twitchyliquid64 / usbd-hid

MIT License
87 stars 37 forks source link

OS freezes thread on microcotroller #49

Closed paunstefan closed 1 year ago

paunstefan commented 1 year ago

I'm not sure this issue is related to this crate, it can be deleted if it's considered off-topic.

I made a firmware for a macro keypad using this crate, initially everything worked fine, tested it on PCs with Fedora 36 Gnome, Ubuntu 22.04 and Raspberry Pi OS. The problem appeared on Fedora KDE, where after the device sends a few reports to the PC, the main thread seems to freeze, while the interrupt handler continues trigger. It only happens while logged in to the desktop environment, works fine in the login screen or on a non-graphical TTY.

Possibly relevant code from the project:

#[entry]
fn main() -> ! {
   /*
   ...
   Initialization code
   ...
   */
    loop {
        delay.delay_ms(30);

        let reports = keys::get_keys(keys);

        reports.into_iter().flatten().for_each(|report| {
            push_keyboard_report(report);
        });
    }
}

pub fn get_keys(keys: &[&dyn InputPin<Error = Infallible>]) -> [Option<KeyboardReport>; 9] {
    let mut ret = [None; 9];
    let mut pressed = false;

    for i in 0..keys.len() {
        if keys[i].is_low().unwrap() {
            let mut report = new_report();
            interrupt::free(|cs| {
                let buttons = &KEYS.borrow(cs).borrow().keys;
                report.modifier = buttons[i].modifier;
                report.keycodes = buttons[i].keycodes;
            });
            ret[i] = Some(report);
            pressed = true;
        }
    }

    if !pressed {
        ret[0] = Some(new_report());
    }

    ret
}

fn push_keyboard_report(report: KeyboardReport) -> Result<usize, usb_device::UsbError> {
    cortex_m::interrupt::free(|_| unsafe {
        USB_HID.as_mut().map(|hid| hid.push_input(&report))
    })
    .unwrap()
}

#[interrupt]
unsafe fn USBCTRL_IRQ() {
    // Handle USB request
    let usb_dev = USB_DEVICE.as_mut().unwrap();
    let usb_hid = USB_HID.as_mut().unwrap();
    //let serial = USB_SERIAL.as_mut().unwrap();

    usb_dev.poll(&mut [usb_hid]);
}

Tried to put defmt prints and it doesn't seem to freeze in the same place everytime. GDB doesn't really help because breakpoints also block the interrupt handling.

Any ideas on what could cause it or how could I continue to debug it?

haata commented 1 year ago

Prints and gdb are very tricky with USB as it's a very time sensitive protocol. So you can't take the usual debugging approach.

I would recommend setting up defmt and a very fast debug adapter (something usb 2.0 hispeed, like https://www.nxp.com/design/microcontrollers-developer-resources/mcu-link-debug-probe:MCU-LINK).

From my experience, this is often a bug in the USB hal driver (or a message not being handled correctly).

You may also want to use wireshark to see if there's something wrong going on as well (ideally, you'd have something like this https://www.totalphase.com/products/beagle-usb12/, but they are pricey).

Which MCU are you using?

paunstefan commented 1 year ago

Sorry, I forgot to mention it, it's a Raspberry Pi Pico.

paunstefan commented 1 year ago

I managed to fix after looking through Wireshark captures. The problem was that the PC was sending a report for the Num Lock LED, which I wasn't reading on the device, if I read it and just discard it everything works well.