使用Pi Zero W模拟HID键盘和鼠标

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

我的目的是使用 PiZeroW 作为计算机的 HID 键盘和鼠标 USB 输入设备。

我咨询过以下内容

我想我可以将键盘和鼠标描述符合并到一个文件中,并为每个文件添加 REPORT_ID

0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x06,        // Usage (Keyboard)
0xA1, 0x01,        // Collection (Application)
0x85, 0x01,        //   Report ID (1)
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
0x19, 0xE0,        //   Usage Minimum (0xE0)
0x29, 0xE7,        //   Usage Maximum (0xE7)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x01,        //   Logical Maximum (1)
0x75, 0x01,        //   Report Size (1)
0x95, 0x08,        //   Report Count (8)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //   Report Count (1)
0x75, 0x08,        //   Report Size (8)
0x81, 0x03,        //   Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x05,        //   Report Count (5)
0x75, 0x01,        //   Report Size (1)
0x05, 0x08,        //   Usage Page (LEDs)
0x19, 0x01,        //   Usage Minimum (Num Lock)
0x29, 0x05,        //   Usage Maximum (Kana)
0x91, 0x02,        //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x01,        //   Report Count (1)
0x75, 0x03,        //   Report Size (3)
0x91, 0x03,        //   Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x06,        //   Report Count (6)
0x75, 0x08,        //   Report Size (8)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x65,        //   Logical Maximum (101)
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
0x19, 0x00,        //   Usage Minimum (0x00)
0x29, 0x65,        //   Usage Maximum (0x65)
0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              // End Collection
0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x02,        // Usage (Mouse)
0xA1, 0x01,        // Collection (Application)
0x09, 0x01,        //   Usage (Pointer)
0xA1, 0x00,        //   Collection (Physical)
0x85, 0x02,        //     Report ID (2)
0x05, 0x09,        //     Usage Page (Button)
0x19, 0x01,        //     Usage Minimum (0x01)
0x29, 0x03,        //     Usage Maximum (0x03)
0x15, 0x00,        //     Logical Minimum (0)
0x25, 0x01,        //     Logical Maximum (1)
0x95, 0x03,        //     Report Count (3)
0x75, 0x01,        //     Report Size (1)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //     Report Count (1)
0x75, 0x05,        //     Report Size (5)
0x81, 0x03,        //     Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
0x09, 0x30,        //     Usage (X)
0x09, 0x31,        //     Usage (Y)
0x15, 0x81,        //     Logical Minimum (-127)
0x25, 0x7F,        //     Logical Maximum (127)
0x75, 0x08,        //     Report Size (8)
0x95, 0x02,        //     Report Count (2)
0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              //   End Collection
0xC0,              // End Collection

// 117 bytes

然后,就是设置 rc.local 自动执行 /usr/bin/isticktoit_usb,即

#!/bin/bash

cd /sys/kernel/config/usb_gadget/
mkdir -p isticktoit
cd isticktoit
echo 0x0100 > bcdDevice # v1.0.0
echo 0x0200 > bcdUSB # USB2
#echo 0x1d6b > idVendor # Linux Foundation
#echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x2341 > idVendor # pretend leonardo
echo 0x8036 > idProduct # pretend leonardo
mkdir -p strings/0x409
echo "fedcba9876543210" > strings/0x409/serialnumber
echo "pi zero w HID" > strings/0x409/manufacturer
echo "iSticktoit.net USB Device" > strings/0x409/product
mkdir -p configs/c.1/strings/0x409
echo "Config 1: ECM network" > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower
# Add functions here
# see gadget configurations below
# End functions

# Add functions here
mkdir -p functions/hid.usb0
echo 1 > functions/hid.usb0/protocol
echo 1 > functions/hid.usb0/subclass
echo 8 > functions/hid.usb0/report_length

echo -ne \\x05\\x01\\x09\\x06\\xA1\\x01\\x85\\x01\\x05\\x07\\x19\\xE0\\x29\\xE7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xC0\\x05\\x01\\x09\\x02\\xA1\\x01\\x09\\x01\\xA1\\x00\\x85\\x02\\x05\\x09\\x19\\x01\\x29\\x03\\x15\\x00\\x25\\x01\\x95\\x03\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x05\\x81\\x03\\x05\\x01\\x09\\x30\\x09\\x31\\x15\\x81\\x25\\x7F\\x75\\x08\\x95\\x02\\x81\\x06\\xC0\\xC0 > functions/hid.usb0/report_desc


ln -s functions/hid.usb0 configs/c.1/
# End functions

echo 0x80 > configs/c.1/bmAttributes
echo 200 > configs/c.1/MaxPower # 200 mA


ls /sys/class/udc > UDC

chown pi /dev/hidg0
sleep 3

chown行只是让用户pi访问hidg0端口,用测试程序如

#!/usr/bin/python3

import time

time.sleep(3)

NULL_CHAR = chr(0)

def write_report(report):
    with open('/dev/hidg0', 'rb+') as fd:
        fd.write(report.encode())

# Press a
write_report(chr(2) + NULL_CHAR*2+chr(4)+NULL_CHAR*5)

# Release keys
write_report(chr(2)+ NULL_CHAR*8)

# Press B
write_report(chr(2) + chr(32) + NULL_CHAR + chr(5)+NULL_CHAR*5)

# Release keys
write_report(chr(2) + NULL_CHAR*8)

第一个字符表示报告适用于 REPORT_ID 2,即键盘。

我能够让 PZW 模拟键盘。但添加 REPORT_ID 和鼠标描述符似乎会使仿真无法识别为 USB 设备。

测试程序返回

Traceback (most recent call last):
  File "./composite.py", line 14, in <module>
    write_report(chr(2) + NULL_CHAR*2+chr(4)+NULL_CHAR*5)
  File "./composite.py", line 11, in write_report
    fd.write(report.encode())
BrokenPipeError: [Errno 108] Cannot send after transport endpoint shutdown

我哪里做错了?

usb hid
2个回答
1
投票

这仍然是一个很好的教程https://www.rmedgar.com/blog/using-rpi-zero-as-keyboard-setup-and-device-definition/

这是我用过的文件

(1) /usr/bin/isticktoit_usb

#!/bin/bash

cd /sys/kernel/config/usb_gadget/
mkdir -p isticktoit
cd isticktoit
echo 0x0100 > bcdDevice # v1.0.0
echo 0x0200 > bcdUSB # USB2
#echo 0x1d6b > idVendor # Linux Foundation
#echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x2341 > idVendor # pretend leonardo
echo 0x8036 > idProduct # pretend leonardo
mkdir -p strings/0x409
echo "fedcba9876543210" > strings/0x409/serialnumber
echo "pi zero w HID" > strings/0x409/manufacturer
echo "iSticktoit.net USB Device" > strings/0x409/product
mkdir -p configs/c.1/strings/0x409
echo "Config 1: ECM network" > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower
# Add functions here
# see gadget configurations below
# End functions

# Add functions here
mkdir -p functions/hid.usb0
echo 1 > functions/hid.usb0/protocol
echo 1 > functions/hid.usb0/subclass
echo 8 > functions/hid.usb0/report_length

echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x85\\x01\\x05\\x09\\x19\\x01\\x29\\x03\\x15\\x00\\x25\\x01\\x95\\x03\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x05\\x81\\x03\\x05\\x01\\x09\\x30\\x09\\x31\\x09\\x38\\x15\\x81\\x25\\x7f\\x75\\x08\\x95\\x03\\x81\\x06\\xc0\\xc0\\x05\\x01\\x09\\x06\\xa1\\x01\\x85\\x02\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.usb0/report_desc


ln -s functions/hid.usb0 configs/c.1/
# End functions

echo 0x80 > configs/c.1/bmAttributes
echo 200 > configs/c.1/MaxPower # 200 mA

sleep 10

ls /sys/class/udc > UDC

sleep 2

chown pi /dev/hidg0

sleep 2

(2) 测试.py

#!/usr/bin/python3

import time

time.sleep(3)

NULL_CHAR = chr(0)

def write_report(report):
    with open('/dev/hidg0', 'rb+') as fd:
        fd.write(report.encode())

# Press a
write_report(chr(2) + NULL_CHAR*2+chr(4)+NULL_CHAR*5)

# Release keys
write_report(chr(2)+ NULL_CHAR*8)

# Press B
write_report(chr(2) + chr(32) + NULL_CHAR + chr(5)+NULL_CHAR*5)

# Release keys
write_report(chr(2) + NULL_CHAR*8)

# testing mouse
write_report(chr(1)+NULL_CHAR+chr(100)+chr(0)+NULL_CHAR)
time.sleep(2)
write_report(chr(1)+NULL_CHAR+chr(100)+chr(100)+NULL_CHAR)
time.sleep(2)
write_report(chr(1)+NULL_CHAR+chr(228)+chr(228)+NULL_CHAR)

0
投票

我实际上有一个类似的问题导致了 BrokenPipeError,解决方案非常简单。基本上,您需要将 USB 电缆插入 RPI 上标有 USB 的端口,并确保该电缆允许数据传输。这对我有用。

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