使用PyUSB发送HID报告

5

更新


我成功地发送了数据。对于遇到同样问题的人,我使用了以下代码:

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

这是基于此处发布的答案(链接)。

但我不详细理解,为什么我必须使用它。

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

什么是解释?

初始请求:


我正在尝试使用PyUSB向HID设备发送简单报告。

使用“SimpleHIDwrite”,我确认设备正常工作。我想发送以下数据:

报告ID:00

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

使用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
2个回答

8

以下是使用PyUSB实现HID的全部步骤:

  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
      )

没有必要跳上库包装器的浪潮。你是工程师还是什么?只需要阅读文档。协议不会很快改变。

最后,是的。我见过的所有四个libusbhid都是用灾难性可怕的C语言编写的,并依赖于更多的库。而这本质上只是10行代码。做出自己的决定。


1
@Notflip,是的,你也可以移动鼠标。report只是一个字节数组;它的格式在HID描述符中有描述。例如,在这里中,你基本上需要将各个部分(坐标、按钮、报告ID)打包到report字节数组中,并通过hid_set_report()发送。一个好的方法是:使用USB嗅探设置Wireshark,查看真实的HID报告,并尝试弄清楚每个字节的含义。然后模拟它。 - ulidtko
1
为什么它能工作——请自行研究。打开规范并使用Ctrl-F“wValue”:前三个结果解释了“wValue=0x200”的编码方式。我无法在此提供完整的USB讲座。我也不打算解决原帖作者对基础知识的困惑——请注意,他们早已离开StackOverflow,在一个好标题下留下了一个混乱的问题,但人们仍然可以在网络搜索中找到它。当在“问题”发布两年后回答时,我试图帮助后来的人,而不是原帖作者。 - ulidtko
@DrM 读写 - 是针对什么样的设备?您传递到 hid_set_report() 和从 hid_get_report() 获取的内容完全取决于设备。上面评论中有一个鼠标示例。如需进一步帮助,请私信联系我(我的用户名 @ gmail)。 - ulidtko
@ulidtko 我正在寻找一个简单的读写操作。我不确定报告(report)是什么意思。但在hidapi中,报告(report)是一种不同于读写操作的API。 - DrM
@DrM "HID报告"是HID协议中的有效载荷数据。当您移动鼠标或点击其按钮时,坐标增量和按下的按钮位图被封装成一个HID报告由鼠标内部的微控制器,并通过HID [通过USB]发送到主机计算机。HID支持许多不同类型的设备:游戏手柄,条码扫描仪,键盘,赛车方向盘等等。事实上,HID本身对实际设备的具体细节是不可知的。这一层细节被封装到HID报告中,并由HID描述符描述。 - ulidtko
显示剩余8条评论

6
不要使用PyUSB(除非您还需要其他协议)。管理HID并不困难,但有一个更简单的解决方案。 HIDAPI是一个C库,它管理协议,并且也有一个可用的Python封装
此外,它隐藏了从操作系统重新获得控制的必要性,操作系统在连接时识别HID协议并安装自己的驱动程序。

你说得对:我只需要HID,所以实际上没有必要使用PyUSB。因此,我会尝试使用hidapi。然而,我成功地使用ctrl_transfer发送了数据。 - xquitz
你可以使用PyUSB,但这需要额外的努力。我们在我教学的大学中使用HIDAPI,它使得HID只是一种形式上的事情。 - jcoppens
Python封装器用于读取时返回整数列表而不是字节数组。我发现这非常不方便,不是一个好的设计选择,因为它不忠实于发送的内容,在许多情况下需要另一个副本,可能会导致不必要的缓存未命中,并且对于大型快速传输可能很容易成为问题。换句话说,我认为它是垃圾,我真的想替换它。 - DrM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接