Windows 上 rusb write_interrupt 中的输入/输出错误

发布于 2025-01-10 14:25:58 字数 2784 浏览 0 评论 0原文

我正在使用 rust、STM32 芯片、usb-device 和 < 构建一个小型“宏”风格键盘a href="https://docs.rs/usbd-hid" rel="nofollow noreferrer">usbd-hid。我的电脑正确检测到该设备,并且制造商字符串等在设备管理器中可见。我可以将该设备用作键盘。

但是,当我尝试使用 rusbhidapi,它因 IO 错误而失败。当我调试 C 代码时,当它尝试 WriteFile 到 USB 设备时,会出现“访问被拒绝”错误。经过进一步研究,这似乎是一个 限制Windows,因为我无法访问 HID 鼠标和键盘,因为它们是系统保留的:

libusb 支持原生 Windows HID 驱动,但有一些限制,例如无法访问 HID 鼠标和键盘,因为它们是系统保留的

但是我找不到任何文档或示例来说明如何解决此问题 - 它似乎我应该能够在我的 USB 描述符中添加不完全由 Windows 持有的另一个配置、接口或端点。有人对从哪里开始有任何提示吗?

我当前的 USB 设备配置(使用 usbd-hid 的宏,如果需要的话我很乐意放弃)如下所示:

#[gen_hid_descriptor(
    (collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = KEYBOARD) = {
        (usage_page = KEYBOARD, usage_min = 0xE0, usage_max = 0xE7) = {
            #[packed_bits 8] #[item_settings data,variable,absolute] modifier=input;
        };
        (usage_min = 0x00, usage_max = 0xFF) = {
            #[item_settings constant,variable,absolute] reserved=input;
        };
        (usage_page = LEDS, usage_min = 0x01, usage_max = 0x05) = {
            #[packed_bits 5] #[item_settings data,variable,absolute] leds=output;
        };
        (usage_page = KEYBOARD, usage_min = 0x00, usage_max = 0xDD) = {
            #[item_settings data,array,absolute] keycodes=input;
        };
        (usage_page = 0xFF17, usage_min = 0x01, usage_max = 0xFF) = {
            #[item_settings data,variable,absolute] command=output;
        };
        (usage_page = 0xFF17, usage_min = 0x01, usage_max = 0xFF) = {
            #[item_settings data,variable,absolute] data=output;
        };
    }
)]
#[allow(dead_code)]
pub struct CustomKeyboardReport {
    pub modifier: u8,
    pub reserved: u8,
    pub leds: u8,
    pub keycodes: [u8; 6],
    pub command: u8,
    pub data: u8,
}

生成的描述符如下所示

0x05 0x01 0x09 0x06 0xA1 0x01 0x05 0x07
0x19 0xE0 0x29 0xE7 0x15 0x00 0x25 0x01
0x75 0x01 0x95 0x08 0x81 0x02 0x19 0x00
0x29 0xFF 0x26 0xFF 0x00 0x75 0x08 0x95
0x01 0x81 0x03 0x05 0x08 0x19 0x01 0x29
0x05 0x25 0x01 0x75 0x01 0x95 0x05 0x91
0x02 0x95 0x03 0x91 0x03 0x05 0x07 0x19
0x00 0x29 0xDD 0x26 0xFF 0x00 0x75 0x08
0x95 0x06 0x81 0x00 0x06 0x17 0xFF 0x19
0x01 0x29 0xFF 0x95 0x01 0x91 0x02 0x06
0x17 0xFF 0x19 0x01 0x29 0xFF 0x91 0x02
0xC0

相关: 1

I'm building a small "macro" style keyboard using rust, STM32 chips, usb-device and usbd-hid. The device is correctly detected by my PC and the manufacturer strings etc are visible in device manager. I can use the device as a keyboard.

However when I try to write from host to device using rusb or hidapi, it fails with an IO error. When I debug the C code it gives an "Access Denied" error when it tries to WriteFile to the USB device. It seems on further research that this is a limitation of Windows, in that I can't access HID mice and keyboards as they're system reserved:

the native Windows HID driver is supported by libusb, but there are some limitations, such as not being able to access HID mice and keyboards since they are system reserved

However I can't find any documentation or examples of how to work around this - it seems I should be able to add another configuration, interface or endpoint in my USB descriptors that isn't exclusively held by Windows. Anybody have any hints on where to start?

My current USB device configuration (using usbd-hid's macro, which I'm happy to abandon if I need to) looks like this:

#[gen_hid_descriptor(
    (collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = KEYBOARD) = {
        (usage_page = KEYBOARD, usage_min = 0xE0, usage_max = 0xE7) = {
            #[packed_bits 8] #[item_settings data,variable,absolute] modifier=input;
        };
        (usage_min = 0x00, usage_max = 0xFF) = {
            #[item_settings constant,variable,absolute] reserved=input;
        };
        (usage_page = LEDS, usage_min = 0x01, usage_max = 0x05) = {
            #[packed_bits 5] #[item_settings data,variable,absolute] leds=output;
        };
        (usage_page = KEYBOARD, usage_min = 0x00, usage_max = 0xDD) = {
            #[item_settings data,array,absolute] keycodes=input;
        };
        (usage_page = 0xFF17, usage_min = 0x01, usage_max = 0xFF) = {
            #[item_settings data,variable,absolute] command=output;
        };
        (usage_page = 0xFF17, usage_min = 0x01, usage_max = 0xFF) = {
            #[item_settings data,variable,absolute] data=output;
        };
    }
)]
#[allow(dead_code)]
pub struct CustomKeyboardReport {
    pub modifier: u8,
    pub reserved: u8,
    pub leds: u8,
    pub keycodes: [u8; 6],
    pub command: u8,
    pub data: u8,
}

Which produces a descriptor that looks like this

0x05 0x01 0x09 0x06 0xA1 0x01 0x05 0x07
0x19 0xE0 0x29 0xE7 0x15 0x00 0x25 0x01
0x75 0x01 0x95 0x08 0x81 0x02 0x19 0x00
0x29 0xFF 0x26 0xFF 0x00 0x75 0x08 0x95
0x01 0x81 0x03 0x05 0x08 0x19 0x01 0x29
0x05 0x25 0x01 0x75 0x01 0x95 0x05 0x91
0x02 0x95 0x03 0x91 0x03 0x05 0x07 0x19
0x00 0x29 0xDD 0x26 0xFF 0x00 0x75 0x08
0x95 0x06 0x81 0x00 0x06 0x17 0xFF 0x19
0x01 0x29 0xFF 0x95 0x01 0x91 0x02 0x06
0x17 0xFF 0x19 0x01 0x29 0xFF 0x91 0x02
0xC0

Related: 1

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

平安喜乐 2025-01-17 14:25:58

我通过创建一个单独的报告解决了这个问题:


#[gen_hid_descriptor(
    (collection = LOGICAL, usage_page = VENDOR_DEFINED_START, usage = 0x00) = {
        (usage_page = 0xFF17, usage_min = 0x01, usage_max = 0xFF) = {
            #[item_settings data,array,absolute] command=output;
        };
    }
)]
pub struct CommandReport {
    pub command: [u8; 2],
}

然后我创建了一个单独的 HID 接口,只有一个 OUT 端点:

// for the keyboard
let hid = HIDClass::new(&alloc, CustomKeyboardReport::desc(), 10);

// for comms from the host -> device
let command = HIDClass::new_ep_out(&alloc, CommandReport::desc(), 10); 

在我的 poll 函数中,我检查了两个:

bus.poll(&mut [&mut hid, &mut command])

然后我可以读取通过提取原始输出来获取数据:

let mut buffer: [u8; 64] = [0; 64];
match command.pull_raw_output(&mut buffer) {
     Ok(size) => handleCommand(buffer, size),
     Err(UsbError::WouldBlock) => {
         // no pending data
     }
     Err(err) => panic!("Error receiving data {:?}", err),
}

由于只有 1 个报告 ID,因此我不必在使用 libusb 从主机发送的数据中添加 reportId

I resolved this by creating a separate report:


#[gen_hid_descriptor(
    (collection = LOGICAL, usage_page = VENDOR_DEFINED_START, usage = 0x00) = {
        (usage_page = 0xFF17, usage_min = 0x01, usage_max = 0xFF) = {
            #[item_settings data,array,absolute] command=output;
        };
    }
)]
pub struct CommandReport {
    pub command: [u8; 2],
}

Then I created a separate HID interface with only an OUT endoint:

// for the keyboard
let hid = HIDClass::new(&alloc, CustomKeyboardReport::desc(), 10);

// for comms from the host -> device
let command = HIDClass::new_ep_out(&alloc, CommandReport::desc(), 10); 

In my poll function I check both:

bus.poll(&mut [&mut hid, &mut command])

And then I can read the data by pulling raw output:

let mut buffer: [u8; 64] = [0; 64];
match command.pull_raw_output(&mut buffer) {
     Ok(size) => handleCommand(buffer, size),
     Err(UsbError::WouldBlock) => {
         // no pending data
     }
     Err(err) => panic!("Error receiving data {:?}", err),
}

As there is only 1 report ID, I did not have to prepend the reportId in the data I sent from the host using libusb.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文