a1ien / rusb

A safe Rust wrapper for libusb.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Device busy

CandySunPlus opened this issue · comments

When I try to get the USB information, like read_serial_number_string_ascii in the Hotplug event, it prompts Device busy.

use std::thread;

use rusb::{self, Hotplug, UsbContext};

struct HotplugHandler;

impl<T: UsbContext> Hotplug<T> for HotplugHandler {
    fn device_left(&mut self, device: rusb::Device<T>) {
        println!("device left: {:?}", device);
    }
    fn device_arrived(&mut self, device: rusb::Device<T>) {
        let desc = device.device_descriptor().unwrap();
        let handle = device.open().unwrap();

        let serial_num = handle.read_serial_number_string_ascii(&desc).unwrap();
        println!("device arrived: {:?}, serial_num: {}", device, serial_num);
    }
}

fn main() {
    if rusb::has_hotplug() {
        let ctx = rusb::GlobalContext::default();

        let mut reg = Some(
            ctx.register_callback(None, None, None, Box::new(HotplugHandler {}))
                .unwrap(),
        );

        let handle = thread::spawn(move || loop {
            ctx.handle_events(None).unwrap();
        });

        handle.join().unwrap();

        if let Some(reg) = reg.take() {
            ctx.unregister_callback(reg);
        }
    } else {
        eprintln!("libusb hotplug api unsupported");
    }
}

error log

device left: Bus 001 Device 070: ID 2717:ff48
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Busy', src/main.rs:15:72
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I found that we cannot call device synchronous API in hot-plug events, so how can I do in rusb

It's libusb limitation.

When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED event it is considered safe to call any libusb function that takes a libusb_device. It also safe to open a device and submit asynchronous transfers. However, most other functions that take a libusb_device_handle are not safe to call. Examples of such functions are any of the synchronous API functions or the blocking functions that retrieve various USB descriptors. These functions must be used outside of the context of the hotplug callback.

I think we must add this to documentation.

You can add std::sync::mpsc::Sender in to HotplugHandler and send arrived device

I tried many times, but failed.
Please add a example to help me.
Thanks 🥺

add std::sync::mpsc::Sender in to HotplugHandler and send arrived device

use rusb::{Context, Device, HotplugBuilder, UsbContext};
use std::sync::mpsc;

struct HotPlugHandler<T: UsbContext> {
    sender: mpsc::Sender<Device<T>>,
}

impl<T: UsbContext> rusb::Hotplug<T> for HotPlugHandler<T> {
    fn device_arrived(&mut self, device: Device<T>) {
        println!("device arrived {:?}", device);
        self.sender.send(device);
    }

    fn device_left(&mut self, device: Device<T>) {
        println!("device left {:?}", device);
    }
}

impl<T: UsbContext> Drop for HotPlugHandler<T> {
    fn drop(&mut self) {
        println!("HotPlugHandler dropped");
    }
}

fn main() -> rusb::Result<()> {
    if rusb::has_hotplug() {
        let context = Context::new()?;
        let (tx, rx) = mpsc::channel::<Device<Context>>();
        let mut reg = Some(
            HotplugBuilder::new()
                .enumerate(true)
                .register(&context, Box::new(HotPlugHandler { sender: tx }))?,
        );

        std::thread::spawn(move || loop {
            let dev = rx.recv().unwrap();
            let desc = dev.device_descriptor().unwrap();
            println!("{:?}", desc);
        });

        loop {
            context.handle_events(None).unwrap();
            if let Some(reg) = reg.take() {
                context.unregister_callback(reg);
                break;
            }
        }
        Ok(())
    } else {
        eprint!("libusb hotplug api unsupported");
        Ok(())
    }
}