dcuddeback / libusb-rs

A safe Rust wrapper for libusb.
MIT License
199 stars 64 forks source link

InvalidParam #7

Closed manvir-singh closed 8 years ago

manvir-singh commented 8 years ago

I keep getting "InvalidParam" I have no idea what I am doing wrong here. I know a param is incorrect but witch one is it?

Part of my code:

let mut data: Vec<u8> = Vec::new();
handle.read_control(
                request_type(Direction::Out, RequestType::Class, Recipient::Interface),
                0x09,
                0x0200,
                0x0000,
                data.as_mut_slice(),
                Duration::from_secs(1)
).expect("Cant get data");

Info about the usb device I'm trying to talk with:

Frame 1: 36 bytes on wire (288 bits), 36 bytes captured (288 bits) on interface 0
USB URB
    [Source: host]
    [Destination: 6.1.0]
    USBPcap pseudoheader length: 28
    IRP ID: 0xffffe00029ceb500
    IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
    URB Function: URB_FUNCTION_CLASS_INTERFACE (0x001b)
    IRP information: 0x00, Direction: FDO -> PDO
    URB bus id: 6
    Device address: 1
    Endpoint: 0x00, Direction: OUT
        0... .... = Direction: OUT (0)
        .000 0000 = Endpoint value: 0
    URB transfer type: URB_CONTROL (0x02)
    Packet Data Length: 8
    [Response in: 2]
    Control transfer stage: Setup (0)
    [bInterfaceClass: Unknown (0xffff)]
URB setup
    bmRequestType: 0x21
        0... .... = Direction: Host-to-device
        .01. .... = Type: Class (0x01)
        ...0 0001 = Recipient: Interface (0x01)
    bRequest: 9
    wValue: 0x0200
    wIndex: 0 (0x0000)
    wLength: 64
goertzenator commented 8 years ago

Should you be providing a slice with 64 bytes of data instead of an empty slice?

manvir-singh commented 8 years ago

Isn't read_control suppose to write the response into data ? From the docs

If the return value is Ok(n), then buf is populated with n bytes of data.

dcuddeback commented 8 years ago

I think @goertzenator is right. It can't allocate more space for a slice, and an empty slice doesn't have anywhere to store the read data. It will pass the slice's pointer and a length of zero to libusb_control_transfer, which probably returns LIBUSB_ERROR_INVALID_PARAM. This crate's read_control method merely translates that error to an enum.

To read into a Vec, you need to give it a capacity and then obtain a mutable slice that matches the vec's capacity. as_mut_slice() returns a slice for only the part of the Vec that is filled.

Try something like this:

let mut vec = Vec::<u8>::with_capacity(64);

let buf = unsafe {
  slice::from_raw_parts_mut(vec.as_mut_ptr(), vec.capacity())
};

Then don't forget to call vec.set_len() to update the vec's length after reading.

goertzenator commented 8 years ago

You would have to use a non-empty slice in that case. Try pushing some data into that Vec first.

But you've just made me see the actual issue here: you are calling control_read but specifying an OUT (write) command.

edit: grammar

manvir-singh commented 8 years ago

I tried this. I still get the same error. Using Direction::In or Direction::Out does not seem to change anything, both give me the same error.

let mut vec: Vec<u8> = Vec::<u8>::with_capacity(64);
            let buf = unsafe {
                slice::from_raw_parts_mut(vec.as_mut_ptr(), vec.capacity())
            };
            handle.read_control(
                request_type(Direction::In, RequestType::Class, Recipient::Interface),
                0x09,
                0x0200,
                0x0000,
                buf,
                Duration::from_secs(1)
            ).expect("Cant get data");

            println!("Done");
goertzenator commented 8 years ago

That trace frame you listed shows an OUT command, so you should be using write_control() and Direction::OUT (and forming appropriate data to send to the device, if any).

goertzenator commented 8 years ago

@dcuddeback , this problem reveals a usability issue in read_control() and write_control(). The underlying libusb has just single control function, but you had to split it up on the Rust wrapper for const/mut data purposes. In the Rust functions it is easy to specify incorrect direction, but this can be implied by the function call. Perhaps the request_type parameter should be unrolled such that a call looks like this...

            handle.read_control(
                RequestType::Class,
                Recipient::Interface,
                0x09,
                0x0200,
                0x0000,
                buf,
                Duration::from_secs(1)
            ).expect("Cant get data");
manvir-singh commented 8 years ago

@dcuddeback Success using write_control with Direction::OUT worked. Only problem I have is I dont know what data is being sent to the device. Do you happen to know how I can obtain this information?

USB packet that was intercepted: This is the packet I'm trying to recreate in libusb-rs

Frame 7: 36 bytes on wire (288 bits), 36 bytes captured (288 bits) on interface 0
    Interface id: 0 (-)
    Encapsulation type: USB packets with USBPcap header (153)
    Arrival Time: Apr  8, 2016 13:31:00.973567000 Pacific Daylight Time
    [Time shift for this packet: 0.000000000 seconds]
    Epoch Time: 1460147460.973567000 seconds
    [Time delta from previous captured frame: 0.000000000 seconds]
    [Time delta from previous displayed frame: 0.000000000 seconds]
    [Time since reference or first frame: 0.002000000 seconds]
    Frame Number: 7
    Frame Length: 36 bytes (288 bits)
    Capture Length: 36 bytes (288 bits)
    [Frame is marked: False]
    [Frame is ignored: False]
    [Protocols in frame: usb]
USB URB
    [Source: host]
    [Destination: 6.1.0]
    USBPcap pseudoheader length: 28
    IRP ID: 0xffffe001f87be680
    IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
    URB Function: URB_FUNCTION_CLASS_INTERFACE (0x001b)
    IRP information: 0x00, Direction: FDO -> PDO
        0000 000. = Reserved: 0x00
        .... ...0 = Direction: FDO -> PDO (0x00)
    URB bus id: 6
    Device address: 1
    Endpoint: 0x00, Direction: OUT
        0... .... = Direction: OUT (0)
        .000 0000 = Endpoint value: 0
    URB transfer type: URB_CONTROL (0x02)
    Packet Data Length: 8
    [Response in: 8]
    Control transfer stage: Setup (0)
    [bInterfaceClass: Unknown (0xffff)]
URB setup
    bmRequestType: 0x21
        0... .... = Direction: Host-to-device
        .01. .... = Type: Class (0x01)
        ...0 0001 = Recipient: Interface (0x01)
    bRequest: 9
    wValue: 0x0200
    wIndex: 0 (0x0000)
    wLength: 64
goertzenator commented 8 years ago

What device are you talking to? It is a class request so maybe that class is documented somewhere.

manvir-singh commented 8 years ago

I was unable to find any docs. Corsair h80i: http://www.corsair.com/en-ca/hydro-series-h80i-high-performance-liquid-cpu-cooler

manvir-singh commented 8 years ago

@dcuddeback I think I found the bytes that are sent.

03 2E 09 01

Then the rest 60 bytes contain 00 So is this how i would send this data?

            let mut vec: Vec<u8> = Vec::new();
            vec.resize(64, 0x00);
            vec.push(0x03);
            vec.push(0x2E);
            vec.push(0x09);
            vec.push(0x01);
            handle.write_control(
                request_type(Direction::Out, RequestType::Class, Recipient::Interface),
                9,
                0x0200,
                0x0000,
                vec.as_slice(),
                Duration::from_secs(1)
            ).expect("Cant get data");
dcuddeback commented 8 years ago

@Programming4life Maybe try to do the resize() after pushing the other four values so that they're at the beginning of your buffer:

let mut vec: Vec<u8> = Vec::new();
vec.push(0x03);
vec.push(0x2E);
vec.push(0x09);
vec.push(0x01);
vec.resize(64, 0x00);

I think the way you have it will send a buffer of 68 bytes, where the first 64 bytes are 0.

manvir-singh commented 8 years ago

It works! @goertzenator @dcuddeback Thank you both for the help I really appreciate it.