如何使用ioctl + nix宏来获取可变大小的缓冲区

问题描述 投票:2回答:2

这与How to use nix's ioctl?有关但不是同一个问题。

我想检索一个可变大小的缓冲区。还有另一个ioctl告诉我,我需要读取X字节。 C标题也告诉我以下内容:

#define HID_MAX_DESCRIPTOR_SIZE     4096
#define HIDIOCGRDESC        _IOR('H', 0x02, struct hidraw_report_descriptor)

struct hidraw_report_descriptor {
    __u32 size;
    __u8 value[HID_MAX_DESCRIPTOR_SIZE];
};

我通过以下方式定义宏:

ioctl_read_buf!(hid_read_descr, b'H', 0x02, u8);

后来打电话:

let mut desc_raw = [0u8; 4 + 4096];
let err = unsafe { hid_read_descr(file.as_raw_fd(), &mut desc_raw); };

这样做时,desc_raw充满了零。我原本期望前4个字节包含基于结构定义的size

替代方案似乎也不起作用

ioctl_read!(hid_read_descr2, b'H', 0x02, [u8; 4+4096]);
// ...
let mut desc_raw = [0xFFu8; 4 + 4096];
let err = unsafe { hid_read_descr2(file.as_raw_fd(), &mut desc_raw); };

在这两种情况下,我都尝试用0xFF初始化desc_raw,并且在调用之后,它似乎没有被触及。

我是否错误地使用了ioctl_read_buf宏?

rust usb ioctl usb-hid
2个回答
3
投票

现在,Digikata has thoughtfully provided enough code to drive the program ......

我是否错误地使用了ioctl_read_buf宏?

我会说在这里使用它是不正确的。您不想读取数据数组,而是希望读取特定类型的单个实例。这就是ioctl_read!的用途。

我们定义了一个模仿C定义的repr(C)结构。这确保了对齐,填充,字段排序等重要细节都与我们调用的代码一对一匹配。

然后,我们可以构造此结构的未初始化实例,并将其传递给新定义的函数。

#[macro_use]
extern crate nix;
extern crate libc;

use std::{
    fs::OpenOptions,
    mem,
    os::unix::{fs::OpenOptionsExt, io::AsRawFd},
};

const HID_MAX_DESCRIPTOR_SIZE: usize = 4096;

#[repr(C)]
pub struct hidraw_report_descriptor {
    size: u32,
    value: [u8; HID_MAX_DESCRIPTOR_SIZE],
}

ioctl_read!(hid_read_sz, b'H', 0x01, libc::c_int);
ioctl_read!(hid_read_descr, b'H', 0x02, hidraw_report_descriptor);

fn main() -> Result<(), Box<std::error::Error>> {
    let file = OpenOptions::new()
        .read(true)
        .write(true)
        .custom_flags(libc::O_NONBLOCK)
        .open("/dev/hidraw0")?;

    unsafe {
        let fd = file.as_raw_fd();

        let mut size = 0;
        hid_read_sz(fd, &mut size)?;
        println!("{}", size);

        let mut desc_raw: hidraw_report_descriptor = mem::uninitialized();
        desc_raw.size = size as u32;
        hid_read_descr(file.as_raw_fd(), &mut desc_raw)?;
        let data = &desc_raw.value[..desc_raw.size as usize];
        println!("{:02x?}", data);
    }

    Ok(())
}

2
投票

我想你在这里有几个问题。一些在Rust方面,有些使用HIDIOCGRDESC ioctl错误。如果你在hidraw.txt和hid-example.c代码中查看Linux内核发行版,结构的使用如下:

struct hidraw_report_descriptor rpt_desc;

memset(&rpt_desc, 0x0, sizeof(rpt_desc));

/* Get Report Descriptor */
rpt_desc.size = desc_size;
res = ioctl(fd, HIDIOCGRDESC, &rpt_desc);

desc_size来自之前的HIDIOCGRDESCSIZE ioctl电话。除非我填写正确的大小参数,否则ioctl会返回错误(ENOTTYEINVAL)。

在不使用O_NONBLOCK的情况下传递libc::open标志以打开HID设备也存在问题。我最终得到了这个:

#[macro_use]
extern crate nix;

extern crate libc;

ioctl_read!(hid_read_sz, b'H', 0x01, i32);
ioctl_read_buf!(hid_read_descr, b'H', 0x02, u8);

fn main() {
    // see /usr/include/linux/hidraw.h
    // and hid-example.c
    extern crate ffi;
    use std::ffi::CString;
    let fname = CString::new("/dev/hidraw0").unwrap();
    let fd = unsafe { libc::open(fname.as_ptr(), libc::O_NONBLOCK | libc::O_RDWR) };

    let mut sz = 0i32;
    let err = unsafe { hid_read_sz(fd, &mut sz) };
    println!("{:?} size is {:?}", err, sz);

    let mut desc_raw = [0x0u8; 4 + 4096];

    // sz on my system ended up as 52 - this handjams in the value
    // w/ a little endian swizzle into the C struct .size field, but
    // really we should properly define the struct
    desc_raw[0] = sz as u8;

    let err = unsafe { hid_read_descr(fd, &mut desc_raw) };
    println!("{:?}", err);

    for (i, &b) in desc_raw.iter().enumerate() {
        if b != 0 {
            println!("{:4} {:?}", i, b);
        }
    }
}

最后,您不应该将结构大小调整为可变大小,ioctl标头表示预期有一个固定的最大值。可变性全部在系统ioctl上处理,它只需要来自另一个ioctl调用的预期大小提示。

© www.soinside.com 2019 - 2024. All rights reserved.