如何使用 PyUSB 写入控制端点

Posted

技术标签:

【中文标题】如何使用 PyUSB 写入控制端点【英文标题】:How to write to Control Endpoint with PyUSB 【发布时间】:2022-01-19 02:41:54 【问题描述】:

我有一个使用以下代码的 USB 设备

import usb.core
import usb.util

device = usb.core.find(idVendor=0xC251, idProduct=0x2201)
print(device)

生产

DEVICE ID c251:2201 on Bus 002 Address 020 =================
 bLength                :   0x12 (18 bytes)
 bDescriptorType        :    0x1 Device
 bcdUSB                 :  0x200 USB 2.0
 bDeviceClass           :    0x0 Specified at interface
 bDeviceSubClass        :    0x0
 bDeviceProtocol        :    0x0
 bMaxPacketSize0        :   0x40 (64 bytes)
 idVendor               : 0xc251
 idProduct              : 0x2201
 bcdDevice              :  0x100 Device 1.0
 iManufacturer          :    0x1 LASER Driver 
 iProduct               :    0x2 LASER Driver IJS
 iSerialNumber          :    0x3 0001A0000000
 bNumConfigurations     :    0x1
  CONFIGURATION 1: 100 mA ==================================
   bLength              :    0x9 (9 bytes)
   bDescriptorType      :    0x2 Configuration
   wTotalLength         :   0x22 (34 bytes)
   bNumInterfaces       :    0x1
   bConfigurationValue  :    0x1
   iConfiguration       :    0x0 
   bmAttributes         :   0xc0 Self 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         :    0x4 HID
      ENDPOINT 0x81: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x1

在 Python 3 下的 Ubuntu 20.04 中。可以看出没有 OUT 端点。我不是 USB 方面的专家,但据我所知,我们需要一个输出端点来向设备发送数据,所以这个设备看起来像一个只读设备。

但是,我知道有一些方法可以向设备发送/写入数据,因为它是一个激光控制器,我可以在 Windows 中打开/关闭激光、更改强度等。我有一部分 C++此控制器的源代码用于 Windows,它使用 hidapi。根据the documentation of hid_write,当没有其他输出端点时,它会写入“控制端点”,这里似乎就是这种情况。所以现在我想使用 PyUSB 从 Python 复制它。

到目前为止我有这个

import usb.core
import usb.util
import array

device = usb.core.find(idVendor=0xC251, idProduct=0x2201)

if device is None:
    raise RuntimeError('Device not found')
interface = device[0].interfaces()[0]
endpoint = device[0].interfaces()[0].endpoints()[0] # This is the in endpoint, I can read the status of the laser from here and it works fine.

if device.is_kernel_driver_active(interface.bInterfaceNumber):
    device.detach_kernel_driver(interface.bInterfaceNumber)

cmd = chr(90) # This is the command to turn off the laser.
packet = chr(0) + cmd + chr(0)*(64-len(cmd)-1) # The first byte has to be always 0, see https://codedocs.xyz/GerryFerdinandus/hidapi/group__API.html#gad14ea48e440cf5066df87cc6488493af
packet = array.array('B', [ord(c) for c in packet])
bytes_sent = endpoint.write(packet)
print(bytes_sent) # This prints out 64 so it is fine.

似乎正在写入,但激光什么也没做(它应该关闭)。我怀疑它以某种方式写入“IN端点”而不是“控制端点”。我想将此packet 发送到控制端点。如何做到这一点?

PD:我也试过了

device.write(0x0, packet)

但这会产生ValueError: Invalid endpoint address 0x0

【问题讨论】:

【参考方案1】:

要写入端点 0,您需要 device.ctrl_transfer(bmRequestType, bmRequest, wValue, wIndex, packet) 而不是 endpoint.write(packet)

bmRequestTypebmRequestwValuewIndex 对应于 USB 控制请求中的相同元素。 Windows 软件使用hidapi 的事实表明控制传输是根据 USB HID 规范完成的。

This answer here on Stack Overflow 描述了如何在普通 PyUSB 上进行 USB HID 设置/获取操作。

但是由于您要移植的源代码使用hidapi,因此使用Python hidapi interface 可能会使过程更简单。 This question 有一个在 Python 中使用 hidapi 的例子,答案也谈到了替代方案。

【讨论】:

以上是关于如何使用 PyUSB 写入控制端点的主要内容,如果未能解决你的问题,请参考以下文章

pyUSB read() 总是返回相同的值

PyUSB:从 USB 设备读取

Windows 8.1 上的 Pyusb - 没有可用的后端 - 如何安装 libusb?

如何使用STM32的USB非控制端点发送多个数据包

PyUSB 设备已声明,detach_kernel_driver 返回未找到实体

PyUSB怎么理解? [关闭]