使用 PyUSB 发送 HID 报告

Posted

技术标签:

【中文标题】使用 PyUSB 发送 HID 报告【英文标题】:Send HID report with PyUSB 【发布时间】:2016-10-22 23:11:55 【问题描述】:

更新


我设法正确发送数据。对于遇到相同问题的任何人,我使用了以下代码:

data=[0x00, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0x00, 0x00]
result=dev.ctrl_transfer(0x21, 0x9, wValue=0x200, wIndex=0x00, data_or_wLength=data)

(这是基于此处发布的答案:link)

但是我没有详细理解,为什么要使用

bmRequestType=0x21
bRequest=0x9
wValue=0x200

解释是什么?


初始请求:


我正在拼命尝试使用 PyUSB 向 HID 设备发送一个简单的报告。

使用“SimpleHIDwrite”我确认该设备按预期工作。我想发送这些数据:

报告 ID:00

数据:[00, 04, 04, FF, FF, FF, 00, 00]

Sending data using SimpleHIDwrite

我对 Python 和 USB 还很陌生,我不知道如何使用 dev.ctrl_transfer 或 dev.write 来做到这一点。

另外,还有一些关于向 HID 设备发送数据的帖子,但我不知道如何解决我的问题。我该如何解决?

这里有更多细节:

 # Based on https://github.com/walac/pyusb/blob/master/docs/tutorial.rst

import usb.core
import usb.util

# Find our device
# dev = usb.core.find(idVendor=0xfffe, idProduct=0x0001)
dev = usb.core.find(idVendor=0x1781, idProduct=0x8c0)


# Was it found?
if dev is None:
    raise ValueError('Device not found')

dev.set_configuration()

cfg = dev[0]
intf = cfg[(0,0)]
ep = intf[0]

# dev.write(ep.bEndpointAddress, [0x00, 0x00,0x04,0x04,0xFF,0xFF,0xFF,0x00, 0x00], 1000)
# dev.ctrl_transfer(bmRequestType, bRequest, wValue=0, wIndex=0, data_or_wLength=None, timeout=None)

print("print ep")
print(ep)
print("print cfg")
print(cfg)
print("print intf")
print(intf)

上面脚本的结果是这样的:

print ep
      ENDPOINT 0x81: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :    0x8 (8 bytes)
       bInterval        :    0xa
print cfg
  CONFIGURATION 1: 100 mA ==================================
   bLength              :    0x9 (9 bytes)
   bDescriptorType      :    0x2 Configuration
   wTotalLength         :   0x22 (34 bytes)
   bNumInterfaces       :    0x1
   bConfigurationValue  :    0x1
   iConfiguration       :    0x0
   bmAttributes         :   0x80 Bus Powered
   bMaxPower            :   0x32 (100 mA)
    INTERFACE 0: Human Interface Device ====================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x1
     bInterfaceClass    :    0x3 Human Interface Device
     bInterfaceSubClass :    0x0
     bInterfaceProtocol :    0x0
     iInterface         :    0x0
      ENDPOINT 0x81: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :    0x8 (8 bytes)
       bInterval        :    0xa
print intf
    INTERFACE 0: Human Interface Device ====================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x1
     bInterfaceClass    :    0x3 Human Interface Device
     bInterfaceSubClass :    0x0
     bInterfaceProtocol :    0x0
     iInterface         :    0x0
      ENDPOINT 0x81: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :    0x8 (8 bytes)
       bInterval        :    0xa

Process finished with exit code 0

【问题讨论】:

【参考方案1】:

这就是你需要做的所有HID 只是 PyUSB:

  def hid_set_report(dev, report):
      """ Implements HID SetReport via USB control transfer """
      dev.ctrl_transfer(
          0x21,  # REQUEST_TYPE_CLASS | RECIPIENT_INTERFACE | ENDPOINT_OUT
          9,     # SET_REPORT
          0x200, # "Vendor" Descriptor Type + 0 Descriptor Index
          0,     # USB interface № 0
          report # the HID payload as a byte array -- e.g. from struct.pack()
      )

  def hid_get_report(dev):
      """ Implements HID GetReport via USB control transfer """
      return dev.ctrl_transfer(
          0xA1,  # REQUEST_TYPE_CLASS | RECIPIENT_INTERFACE | ENDPOINT_IN
          1,     # GET_REPORT
          0x200, # "Vendor" Descriptor Type + 0 Descriptor Index
          0,     # USB interface № 0
          64     # max reply size
      )

没有任何必要加入 library-wrappers-around-libraries 的潮流。你是工程师还是什么?只需read the documentation。协议不会很快改变。

最后,是的。我见过的所有四个 libusbhid 都是用极其糟糕的 C 语言编写的,并且依赖于更多的库。基本上只有 10 行代码。自己做决定。

【讨论】:

作为python初学者,有没有比较完整的例子?这是否允许通过鼠标移动编写报告? @Notflip 是的,你也可以移动鼠标。 report 只是一个字节数组;它的格式在 HID 描述符中描述。参见例如here — 您基本上将 struct.pack 部分(坐标、按钮、report_id)放入 report 字节数组,并通过 hid_set_report() 发送。解决这个问题的好方法:使用 USB 嗅探设置 Wireshark,查看真实的 HID 报告并尝试找出每个字节的含义。然后模仿它。 -1 因为这不能回答问题。 OP 显然已经在使用这种方法,但正在询问它为什么有效。这就是这个答案失败的地方。它没有解释参数,尤其是 wValue=0x200,顺便说一下,我(我不是这方面的专家)在这个答案中似乎是错误的。 “为什么有效”——做你自己的研究。 Open the spec 并看到 Ctrl-F wValue 的魔力:前三个点击解释了 wValue=0x200 的编码。我无法在这里提供完整的 USB 讲座。我也不打算理清 OP 在基础知识方面的困惑——请注意,他们很久以前就离开了 ***,在人们仍然可以在网络搜索中找到的好标题下留下了一个令人讨厌的问题。在提出“问题”两年后回答时,我试图帮助后者,而不是 OP。 谢谢Ulidtko,你能添加一个读写的例子吗?【参考方案2】:

不要使用 PyUSB(除非您还需要其他协议)。管理 HID 并不难,但有一个更简单的解决方案。

HIDAPI is a C-library 管理协议,也有a Python wrapper 可用。

此外,它隐藏了从操作系统收回控制权的必要性,操作系统识别连接时的 HID 协议,并安装自己的驱动程序。

【讨论】:

谢谢!你是对的:我只需要 HID,所以不需要实际使用 PyUSB。所以,我会试试 hidapi。但是,我设法使用 ctrl_transfer 正确发送数据。 您可以使用 PyUSB,但需要付出很多额外的努力。我们在我任教的大学使用过 HIDAPI,它让 HID 只是一种形式。 python 包装器用于读取,返回整数列表而不是字节数组。我发现这很不方便,不是一个好的设计选择,因为它不忠实于发送的内容,在许多情况下强制另一个副本,可能导致不必要的缓存未命中,并且很容易成为大型快速传输的问题。换句话说,我认为它是垃圾,我真的想取代它。

以上是关于使用 PyUSB 发送 HID 报告的主要内容,如果未能解决你的问题,请参考以下文章

HID 报告请求

使用 PyUSB(不是 HIDAPI)读写 HID 设备

如何定义STM32 USB HID设备缓冲区地址?请求帮助

HID 游戏手柄报告描述符问题

浅析USB HID ReportDesc (HID报告描述符)

USB鼠标实现——HID 报告的返回