Xbox 360手柄通信协议

/ 默认分类 / 没有评论 / 1545浏览

最近的一个项目一直在尝试修改一些微控制器以充当 XInput 设备,模拟 Xbox 控制器。 此过程的第一步是获取然后分解设备的“USB 描述符”。 这些描述符是标准化报告的层次结构,用于描述设备的特性,包括制造商、支持的 USB 版本、供电方式等。 通过将 Xbox 控制器的描述符复制到我自己的设备上,我可以让计算机相信我的设备也是一个 Xbox 控制器并且会表现得像一个,因此使用 Xbox 控制器的驱动程序可以轻松地与游戏交互。 但是,我不想只是将描述符从一个地方复制粘贴到另一个地方,而是想尝试并准确了解幕后发生的事情。 我想了解这些描述符中的信息如何转化为设备行为的特征。

方法

我使用的是正版 Microsoft Xbox 360 有线控制器,连接到我运行 Windows 10 的桌面。为了嗅探 USB 数据,我使用带有 USBPcap 插件的 Wireshark 2.6.4 版。 插入控制器后,Windows 将请求设备和配置描述符,控制器将做出相应响应。 Wireshark 提供来自 USB 数据包的原始数据和使用其内置分析器的值细分。 我已使用有关 USB 标准的公开信息尽我所能验证了这些故障。

你可以使用许多其他工具从主机 (PC) 或使用内联 USB 分析器读取这些描述符。 这对我来说很容易并且很有效。 最好将在线分析器与实际的 Xbox 360 控制台一起使用,但不幸的是,我手头没有这些东西。 值得庆幸的是,这种方法已经告诉我模拟控制数据端点所需的一切。

如果您不熟悉 USB 通信,其中一些信息可能有点难以理解。 有许多网站帮助我理解了这些描述符,包括 USB Made Simple、USB in a NutShell,当然还有公共标准本身——可在 USB.org 上找到。

还有一些人在网上发布了有关 Xbox 360 控制器 USB 工作原理的信息,例如 Zach Littell,他为 Teensy 创建了一个 XInput 实现。 在 Internet Archive 的(现已不存在的)Free60 wiki 中也可以找到一些信息。 这些都是有用的资源,尽管它们都没有彻底分解描述符的含义。

设备描述符

顶级描述符是设备描述符。 这是大图——它告诉主机在与设备通信时使用什么 USB 标准,制造者的标识和它是什么产品,以及后面的配置描述符的数量。

这是 Wireshark 报告的有线控制器的完整设备描述符:

bLength: 18
bDescriptorType: 0x01 (DEVICE)
bcdUSB: 0x0200
bDeviceClass: 0xFF (Vendor Specific)
bDeviceSubClass: 0xFF
bDeviceProtocol: 0xFF
bMaxPacketSize0: 8
idVendor: 0x045E (Microsoft Corp.)
idProduct: 0x028E (Xbox360 Controller)
bcdDevice: 0x0114
iManufacturer: 1
iProduct: 2
iSerialNumber: 3
bNumConfigurations: 1
Raw Hex: 0x12 0x01 0x00 0x02 0xff 0xff 0xff 0x08 0x5e 0x04 0x8e 0x02 0x14 0x01 0x01 0x02 0x03 0x01

设备描述符(bDescriptorType 为 0x01)的长度为 18 个字节(bLength)。 它告诉我们控制器使用 USB 2.0 (bcdUSB),端点 0(设备控制端点)的最大数据包大小为 8 字节 (bMaxPacketSize0)。

设备的 USB 设备类、子类和设备协议都设置为 0xFF(bDeviceClass、bDeviceSubClass 和 bDeviceProtocol)。 这表明这些是特定于供应商的,不遵循任何现有的 USB 设备标准。

供应商 ID (VID) 将控制器标识为由 Microsoft Corporation (idVendor) 制造,而产品 ID (PID) 将其标识为 Xbox 360 控制器 (idProduct)。 这个特定的控制器是硬件版本 0x0114 (bcdDevice)。 这是供应商定义的版本号,编码为二进制编码的十进制 (bcd)。 使用 8421 编码,这将转换为十进制数字“1”、“1”、“4”,这可能意味着在语义版本控制中此控制器是版本 1.1.4。

描述制造商、产品和序列号的字符串分别位于索引 1、2 和 3(iManufacturer、iProduct 和 iSerialNumber)中。 最后,该设备只有一种配置 (bNumConfigurations)。

#配置描述符

接下来是配置描述符。 对于每种配置,这都描述了设备的供电方式以及它有多少个接口。 这可以解释为以下接口和端点描述符的各种“标头”。

根据上面的设备描述符,有线 Xbox 360 控制器只有一种配置:

bLength: 9
bDescriptorType: 0x02 (CONFIGURATION)
wTotalLength: 153
bNumInterfaces: 4
bConfigurationValue: 1
iConfiguration: 0
bmAttributes: 0b10100000 (NOT SELF-POWERED, REMOTE-WAKEUP)
bMaxPower: 250 (500 mA)

Raw Hex: 0x09 0x02 0x99 0x00 0x04 0x01 0x00 0xa0 0xfa

配置描述符(bDescriptorType 为 0x02)的长度为 9 个字节(bLength)。 所有后续接口和端点描述符(包括配置描述符本身的 9 个字节)的总长度为 153 个字节 (wTotalLength)。 该设备总共包含 4 个接口 (bNumInterfaces),用于选择此特定配置的值为 1 (bConfigurationValue)。 从可用的字符串描述符中,没有字符串描述此配置(iConfiguration 为 0)。

设备属性 (bmAttributes) 是描述设备功能的布尔标志位图。 位 0-4 保留,在描述符中为 0。 第 5 位为“1”,表示控制器支持远程唤醒(它可以将主机从待机状态唤醒)。 第 6 位为“0”表示设备不是自供电,第 7 位为“1”表示设备由总线供电。

由于设备属性表示控制器由总线供电,bMaxPower 变量告诉主机控制器可以消耗多少功率(以 2 mA 为单位)。 在这种情况下,500 毫安。

接口描述

紧跟在配置描述符之后的是接口描述符。 接口描述符将较低级别的端点分组为逻辑组,这些逻辑组执行设备的单一功能。 根据配置描述符,有线 Xbox 360 控制器有 4 个接口。

接口0:控制数据

bLength: 9
bDescriptorType: 0x04 (INTERFACE)
bInterfaceNumber: 0
bAlternateSetting: 0
bNumEndpoints: 2
bInterfaceClass: 0xFF (Vendor Specific)
bInterfaceSubClass: 0x5D
bInterfaceProtocol: 0x01
iInterface: 0
Raw Hex: 0x09 0x04 0x00 0x00 0x02 0xff 0x5d 0x01 0x00

第一个接口描述符(0x04 的 bDescriptorType)的长度为 9 个字节(bLength)。 这是第一个接口(bInterfaceNumber,索引为 0)。 它包含两个端点 (bNumEndpoints),并且没有描述它的字符串描述符(iInterface 索引为 0)。

所有四个接口都使用特定于供应商的接口类 (bInterfaceClass)。 这使得无法推断出有关接口子类或协议的任何特定信息,因为它们是专有的。 USB 规范还允许设置 (bAlternateSetting) 将接口描述符标记为备用。 四个控制器的接口均未使用此功能。

从测试来看,该接口似乎用于发送/接收控制数据。

未知描述符 (If0)

紧跟在接口描述符之后的是一个额外的 0x21 类型的未知描述符。 这个 bDescriptorType ID 没有在我能找到的任何公共 USB 标准中列出,而且我使用的 USB 分析程序似乎也不知道。 我最好的猜测是,这是某种特定于供应商的描述符,其中包含有关界面布局的更多具体细节,很像 HID 使用页面。

所有四个接口都有这些未知描述符之一,使用类型 0x21 或类型 0x41(接口 3)。

bLength: 17
bDescriptorType: 0x21 (UNKNOWN)
???: 0x00, 0x01, 0x01
???: 0x25
bEndpointAddress: 0x81 IN Endpoint: 1
bMaxDataSize: 20
???: 0x00, 0x00, 0x00, 0x00, 0x13
bEndpointAddress: 0x01 OUT Endpoint: 1
bMaxDataSize: 8
???: 0x00, 0x00
Raw Hex: 0x11 0x21 0x00 0x01 0x01 0x25 0x81 0x14 0x00 0x00 0x00 0x00 0x13 0x01 0x08 0x00 0x00

前两个字节由USB标准定义,必须指描述符的长度(bLength)和描述符类型(bDescriptorType)。 过去,描述符其余部分的属性是任何人的猜测。

我确定接口的端点编号及其预期的数据包大小都包含在这里。 0x81 是从控制器发送到主机的控制数据的端点,通常为 20 字节长(0x14,端点后的下一个数字)。 0x01 是从主机接收到的控制数据的端点,在接收隆隆声数据时最长可达 8 个字节(0x08,也是其端点后的下一个数字)。 端点编号至少可以通过修改端点描述符本身中的 bEndpointAddresss 值来确认——如果这些未知的描述符值也没有更改,Windows 驱动程序将无法正常通信。

除此之外,bDescriptorType 之后的前三个字节似乎在未知描述符之间进行了标准化:0x00、0x01、0x01。 我不知道这些值指的是什么,或者其他值是什么??? 值参考。 希望有更多 USB 知识的人可以帮我填写。

端点 1 IN:控制数据发送

下一个描述符是第一个端点描述符。 端点描述符描述了总线的最低层部分,并帮助主机确定带宽要求。

bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x81 IN Endpoint: 1
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 4
Raw Hex: 0x07 0x05 0x81 0x03 0x20 0x00 0x04

第一个端点描述符(bDescriptorType 为 0x05)的长度为 7 个字节(bLength)。 bEndpointAddress 字段指定端点编号和端点方向。 位 0-3 描述端点编号 (0x#1 = 1),位 7 描述方向(0x8# = IN,从主机的角度来看)。 它能够在单个更新中发送的最大字节数是 32 (wMaxPacketSize)。

该端点使用中断传输类型(bmAttributes 位 0 和 1 设置为高)。 对于中断传输类型,bInterval 设置指定 USB“帧”中的轮询间隔。 对于低速和全速设备,每个“帧”为 1 毫秒,这意味着该端点每 4 毫秒或 250 赫兹轮询一次新数据。

请注意,虽然这是一个“输入”端点,但它被命名为控制数据发送,因为“输入”和“输出”术语是指从主机角度来看的连接。 当您查看端点方向时,请记住这一点。

这是用于将控制表面信息从控制器发送到计算机的端点。

端点 1 OUT:控制数据接收

bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x01 OUT Endpoint: 1
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 8
Raw Hex: 0x07 0x05 0x01 0x03 0x20 0x00 0x08

第二个端点描述符与第一个几乎相同,除了它是一个 OUT 端点(从主机接收数据),并以一半的速度更新(bInterval 为 8 而不是 4,或 125 Hz 与 250 Hz)。

这是用于接收从计算机发送到控制器的控制数据信息的端点,包括 LED 模式和隆隆声电机数据。

接口 1:耳机(和扩展端口?)

bLength: 9
bDescriptorType: 0x04 (INTERFACE)
bInterfaceNumber: 1
bAlternateSetting: 0
bNumEndpoints: 4
bInterfaceClass: 0xFF (Vendor Specific)
bInterfaceSubClass: 0x5D
bInterfaceProtocol: 0x03
iInterface: 0
Raw Hex: 0x09 0x04 0x01 0x00 0x04 0xff 0x5d 0x03 0x00

接口 1 与接口 0 非常相似,只是它有 4 个端点而不是 2 个 (bNumEndpoints) 并且使用 0x03 而不是 0x01 的 bInterfaceProtocol(特定于供应商,未知)。

从测试来看,这个接口似乎可以处理控制器的耳机。 由于它们是物理连接的,我还假设它处理控制器底部的扩展端口。

未知描述符 (If1)

bLength: 27
bDescriptorType: 0x21 (UNKNOWN)
???: 0x00, 0x01, 0x01
???: 0x01
bEndpointAddress: 0x82 IN Endpoint: 2
bMaxDataSize: 64
???: 0x01
bEndpointAddress: 0x02 OUT Endpoint: 2
bMaxdataSize: 32
???: 0x16
bEndpointAddress: 0x83 IN Endpoint: 3
bMaxDataSize: 0
???: 0x00, 0x00, 0x00, 0x00, 0x00, 0x16
bEndpointAddress: 0x03 OUT Endpoint: 3
bMaxDataSize: 0
???: 0x00, 0x00, 0x00, 0x00, 0x00
Raw Hex: 0x1b 0x21 0x00 0x01 0x01 0x01 0x82 0x40 0x01 0x02 0x20 0x16 0x83 0x00 0x00 0x00 0x00 0x00 0x00 0x16 0x03 0x00 0x00 0x00 0x00 0x00 0x00

另一个未知的描述符,这个明显更长并且包含更多有趣的未知细节。 如果我对端点地址和数据包长度的假设成立,那么这似乎表明这对 #3 端点并不需要任何数据。 多么奇怪……

端点 2 IN:麦克风数据发送

bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x82 IN Endpoint: 2
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 2
Raw Hex: 0x07 0x05 0x82 0x03 0x20 0x00 0x02

与接口 0 的端点类似,接口 1 的第一个端点使用中断传输类型,每 2 毫秒最多接收 32 个字节。 它是一个地址为 2 的 IN 端点。

这是用于将麦克风数据从控制器的耳机端口发送到主机的端点。

端点 2 OUT:耳机音频接收

bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x02 OUT Endpoint: 2
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 4
Raw Hex: 0x07 0x05 0x02 0x03 0x20 0x00 0x04

这个 OUT 端点几乎与其姊妹“IN”端点相同,只是它以一半的速率运行(bInterval 为 4 而不是 2)。

这是用于将音频数据从主机推送到控制器的单声道耳机输出的端点。

端点 3 IN:未知,发送

bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x83 IN Endpoint: 3
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 64
Raw Hex: 0x07 0x05 0x83 0x03 0x20 0x00 0x40

此 IN 端点与其他两个音频端点类似,但它定义了明显更长的 64 帧 bInterval。

由于它与耳机音频端点捆绑在一起,因此该端点可能用于控制器底部的 4 针扩展端口。 我尝试将 Xbox 360 聊天小键盘连接到控制器的扩展端口,但没有看到通过 USB 报告的任何数据。 也许聊天小键盘需要以某种方式进行初始化,尽管我没有实际的 Xbox 360 可以用来测试。

端点 3 OUT:未知,接收

bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x03 OUT Endpoint: 3
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 16
Raw Hex: 0x07 0x05 0x03 0x03 0x20 0x00 0x10

此 OUT 端点与上面的其他三个端点类似,但定义了更短的 16 帧 bInterval。

我再次假设此端点用于耳机插孔旁边的 4 针扩展端口,尽管除了初始化外我没有注意到我的 PC 端点上的任何数据。

接口 2:未知

bLength: 9
bDescriptorType: 0x04 (INTERFACE)
bInterfaceNumber: 2
bAlternateSetting: 0
bNumEndpoints: 1
bInterfaceClass: 0xFF (Vendor Specific)
bInterfaceSubClass: 0x5D
bInterfaceProtocol: 0x02
iInterface: 0
Raw Hex: 0x09 0x04 0x02 0x00 0x01 0xff 0x5d 0x02 0x00

接口 2 与接口 1 和 0 非常相似,只是它只有一个端点 (bNumEndpoints) 并使用 0x02 的 bInterfaceProtocol(特定于供应商,未知)而不是 0x01 (If0) 或 0x02 (If1)。

未知描述符 (If2)

bLength: 9
bDescriptorType: 0x21 (UNKNOWN)
???: 0x00, 0x01, 0x01
???: 0x22
bEndpointAddress: 0x84 IN Endpoint: 4
bMaxDataSize: 7
???: 0x00
Raw Hex: 0x09 0x21 0x00 0x01 0x01 0x22 0x84 0x07 0x00

与未知接口一起使用的另一个未知描述符。 与来自接口 1 的更复杂的未知描述符相比,解包的次数要少得多,尽管同样神秘

端点 4 IN:未知,发送

bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x84 IN Endpoint: 4
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 16
Raw Hex: 0x07 0x05 0x84 0x03 0x20 0x00 0x10

这是配置描述符定义的最后一个端点。 它是另一个中断型数据输出 (IN) 端点,使用 16 毫秒的间隔 (bInterval)。 在我的测试中,Windows 驱动程序从不轮询此端点以获取数据,因此我不知道它的用途。

接口 3:安全方法

bLength: 9
bDescriptorType: 0x04 (INTERFACE)
bInterfaceNumber: 3
bAlternateSetting: 0
bNumEndpoints: 0
bInterfaceClass: 0xFF (Vendor Specific)
bInterfaceSubClass: 0xFD
bInterfaceProtocol: 0x13
iInterface: 4
Raw Hex: 0x09 0x04 0x03 0x00 0x00 0xff 0xfd 0x13 0x04

最后一个接口没有与之关联的端点,但有一个与之关联的字符串描述符 (iInterface)。 索引 4 处的字符串描述符如下:

Xbox 安全方法 3,版本 1.00,© 2005 Microsoft Corporation。 版权所有。

这表明虽然它没有关联的端点,但此接口用于控制器安全方法,确保只有正版或许可的控制器才能连接到 Xbox 360 控制台。 从我的测试来看,这种安全方法似乎没有在 Windows 10 驱动程序上实现。

未知描述符 (If3)

bLength: 6
bDescriptorType: 0x41 (UNKNOWN)
???: 0x00, 0x01, 0x01
???: 0x03
Raw Hex: 0x06 0x41 0x00 0x01 0x01 0x03

最后是最后一个未知的描述符。 这是与其他未知数(0x41 而不是 0x21)不同的 bDescriptorType。 由于此接口没有端点,因此此未知描述符没有 bEndpointAddress 字段。 那里有典型的 3 字节开始,还有一个额外的“0x03”字节。 我仍然不知道这些字节的意义是什么。

这个未知的描述符标志着配置描述符的结束,总共有 153 个字节。

字符串描述符

最后但并非最不重要的是字符串描述符。 这些是可选的、人类可读的信息,可以通过索引链接到设备信息、配置描述符或接口描述符。 我使用这个工具获得了字符串描述符,它也被用来仔细检查其他描述符。

IndexLANGIDString
0x010x0000"©Microsoft Corporation"
0x020x0000"Controller"
0x030x0000"08FEC93"
0x040x0000"Xbox Security Method 3, Version 1.00, © 2005 Microsoft Corporation. All rights reserved."

根据 Xbox 360 有线控制器的设备描述符,制造商由字符串 0x01 (iManufacturer) 提供,产品由字符串 0x02 (iProduct) 提供,序列号由字符串 0x03 (iSerialNumber) 提供。 第四个描述符用于描述接口 3(iInterface)。

我假设每个控制器的序列号字符串都不同,尽管我手头只有一个控制器,所以无法比较它们。

完整描述符

希望这能很好地分解控制器的 USB 描述符及其含义。 由于我的最终目标是重新使用这些描述符以将 XInput 支持添加到我自己的嵌入式项目中,因此我需要 C 数组中的这些描述符以用于微控制器的 USB 堆栈。 以下是控制器设备和配置描述符的完整数组,以及注释:

// Xbox 360 Wired Controller USB Descriptors
// Assembled by David Madison
// www.partsnotincluded.com

const byte DeviceDescriptor[] = {
	0x12,        // bLength
	0x01,        // bDescriptorType
	0x00, 0x02,  // bcdUSB (2.0)
	0xFF,        // bDeviceClass
	0xFF,        // bDeviceSubClass
	0xFF,        // bDeviceProtocol
	0x08,        // bMaxPacketSize0
	0x5E, 0x04,  // idEVendor (Microsoft Corp.)
	0x8E, 0x02,  // idProduct (Xbox360 Controller)
	0x14, 0x01,  // bcdDevice
	0x01,        // iManufacturer
	0x02,        // iProduct
	0x03,        // iSerialNumber
	0x01,        // bNumConfigurations
};

const byte ConfigurationDescriptor[] = {
	// Configuration Descriptor
	0x09,        // bLength
	0x02,        // bDescriptorType (CONFIGURATION)
	0x99, 0x00,  // wTotalLength (153)
	0x04,        // bNumInterfaces
	0x01,        // bConfigurationValue
	0x00,        // iConfiguration
	0xA0,        // bmAttributes
	0xFA,        // bMaxPower

	/* ---------------------------------------------------- */
	// Interface 0: Control Data
	0x09,        // bLength
	0x04,        // bDescriptorType (INTERFACE)
	0x00,        // bInterfaceNumber
	0x00,        // bAlternateSetting
	0x02,        // bNumEndpoints
	0xFF,        // bInterfaceClass
	0x5D,        // bInterfaceSubClass
	0x01,        // bInterfaceProtocol
	0x00,        // iInterface

	// Unknown Descriptor (If0)
	0x11,        // bLength
	0x21,        // bDescriptorType
	0x00, 0x01, 0x01, 0x25,  // ???
	0x81,        // bEndpointAddress (IN, 1)
	0x14,        // bMaxDataSize
	0x00, 0x00, 0x00, 0x00, 0x13,  // ???
	0x01,        // bEndpointAddress (OUT, 1)
	0x08,        // bMaxDataSize
	0x00, 0x00,  // ???

	// Endpoint 1: Control Surface Send
	0x07,        // bLength
	0x05,        // bDescriptorType (ENDPOINT)
	0x81,        // bEndpointAddress (IN, 1)
	0x03,        // bmAttributes
	0x20, 0x00,  // wMaxPacketSize
	0x04,        // bInterval

	// Endpoint 1: Control Surface Receive
	0x07,        // bLength
	0x05,        // bDescriptorType (ENDPOINT)
	0x01,        // bEndpointAddress (OUT, 1)
	0x03,        // bmAttributes
	0x20, 0x00,  // wMaxPacketSize
	0x08,        // bInterval

	/* ---------------------------------------------------- */
	// Interface 1: Headset (and Expansion Port?)
	0x09,        // bLength
	0x04,        // bDescriptorType (INTERFACE)
	0x01,        // bInterfaceNumber
	0x00,        // bAlternateSetting
	0x04,        // bNumEndpoints
	0xFF,        // bInterfaceClass
	0x5D,        // bInterfaceSubClass
	0x03,        // bInterfaceProtocol
	0x00,        // iInterface

	// Unknown Descriptor (If1)
	0x1B,        // bLength
	0x21,        // bDescriptorType
	0x00, 0x01, 0x01, 0x01,  // ???
	0x82,        // bEndpointAddress (IN, 2)
	0x40,        // bMaxDataSize
	0x01,        // ???
	0x02,        // bEndpointAddress (OUT, 2)
	0x20,        // bMaxDataSize
	0x16,        // ???
	0x83,        // bEndpointAddress (IN, 3)
	0x00,        // bMaxDataSize
	0x00, 0x00, 0x00, 0x00, 0x00, 0x16,  // ???
	0x03,        // bEndpointAddress (OUT, 3)
	0x00,        // bMaxDataSize
	0x00, 0x00, 0x00, 0x00, 0x00,  // ???

	// Endpoint 2: Microphone Data Send
	0x07,        // bLength
	0x05,        // bDescriptorType (ENDPOINT)
	0x82,        // bEndpointAddress (IN, 2)
	0x03,        // bmAttributes
	0x20, 0x00,  // wMaxPacketSize
	0x02,        // bInterval

	// Endpoint 2: Headset Audio Receive
	0x07,        // bLength
	0x05,        // bDescriptorType (ENDPOINT)
	0x02,        // bEndpointAddress (OUT, 2)
	0x03,        // bmAttributes
	0x20, 0x00,  // wMaxPacketSize
	0x04,        // bInterval

	// Endpoint 3: Unknown, Send
	0x07,        // bLength
	0x05,        // bDescriptorType (ENDPOINT)
	0x83,        // bEndpointAddress (IN, 3)
	0x03,        // bmAttributes
	0x20, 0x00,  // wMaxPacketSize
	0x40,        // bInterval

	// Endpoint 3: Unknown, Receive
	0x07,        // bLength
	0x05,        // bDescriptorType (ENDPOINT)
	0x03,        // bEndpointAddress (OUT, 3)
	0x03,        // bmAttributes
	0x20, 0x00,  // wMaxPacketSize
	0x10,        // bInterval

	/* ---------------------------------------------------- */
	// Interface 2: Unknown
	0x09,        // bLength
	0x04,        // bDescriptorType (INTERFACE)
	0x02,        // bInterfaceNumber
	0x00,        // bAlternateSetting
	0x01,        // bNumEndpoints
	0xFF,        // bInterfaceClass
	0x5D,        // bInterfaceSubClass
	0x02,        // bInterfaceProtocol
	0x00,        // iInterface

	// Unknown Descriptor (If2)
	0x09,        // bLength
	0x21,        // bDescriptorType
	0x00, 0x01, 0x01, 0x22,  // ???
	0x84,        // bEndpointAddress (IN, 4)
	0x07,        // bMaxDataSize
	0x00,        // ???

	// Endpoint 4: Unknown, Send
	0x07,        // bLength
	0x05,        // bDescriptorType (ENDPOINT)
	0x84,        // bEndpointAddress (IN, 4)
	0x03,        // bmAttributes
	0x20, 0x00,  // wMaxPacketSize
	0x10,        // bInterval

	/* ---------------------------------------------------- */
	// Interface 3: Security Method
	0x09,        // bLength
	0x04,        // bDescriptorType (INTERFACE)
	0x03,        // bInterfaceNumber
	0x00,        // bAlternateSetting
	0x00,        // bNumEndpoints
	0xFF,        // bInterfaceClass
	0xFD,        // bInterfaceSubClass
	0x13,        // bInterfaceProtocol
	0x04,        // iInterface

	// Unknown Descriptor (If3)
	0x06,        // bLength
	0x41,        // bDescriptorType
	0x00, 0x01, 0x01, 0x03,  // ???
};

控制数据格式

完成设备、配置和字符串描述符后,我可以称此分解完成之前的最后一步是弄清楚控制界面信息的格式是如何用于 USB 传输的。 通过操纵设备的控件并将它们与来自 Wireshark 的 USB 数据流进行比较,这很容易弄清楚。 这是细分: alt

来自控制器的每个更新都是从接口 0 上的端点 1 IN (0x81) 发送的 20 字节数据包。字节 0 是消息类型 (0x00),字节 1 是字节长度 (0x14 = 20)。 打包的控制表面数据存储在消息中心的 12 个字节中 (2 – 13)。 最后 6 个字节 14 – 19 未使用 (0x00)。

按键

字节 2 和 3 包含位数组中控制器数字按钮的打包状态,其中“1”被按下,“0”被释放。 这包括所有 8 个表面按钮、2 个操纵杆按钮、中间的“Xbox”按钮和方向键。

从低位到高位,字节 2 映射到方向键(上、下、左、右)、开始、后退、L3 和 R3 按钮。 字节 3 的前三位映射到左保险杠 (LB)、右保险杠 (RB) 和“Xbox”按钮。 位 4-7 分别映射到 A、B、X 和 Y 按钮。 位 3 未使用 (0)。

触发器

字节 4 和 5 包含 8 位无符号整数,分别表示左右模拟触发器的状态,其中“0”值被释放,“255”值被完全压下。

操纵杆

字节 6 – 13 包含双模拟操纵杆的位置,作为 16 位有符号整数。 这些存储在小端(低字节在前),X 轴在 Y 之前,东北为正。

左摇杆位置存储在字节 6、7、8 和 9 中,右摇杆位置存储在字节 10、11、12 和 13 中。

隆隆声和 LED 数据(输出)

控制器用户反馈功能的更新在端点 1 OUT (0x01) 和接口 0 上发送。这些更新遵循与发送数据相同的格式,其中字节 0 是“类型”标识符,字节 1 是数据包长度( 以字节为单位)。

隆隆声 (0x00)

“0x00”类型字节表示一个隆隆声数据包。 控制器包含两个隆隆声马达:左手柄中的大重量和右手柄中的小重量。 这两个电机的值在单个数据包中更新。

隆隆声值是表示电机速度的 8 位无符号整数,其中“0”为关闭,“255”为最大速度。 左电机的隆隆声值存储在索引 3 中,而右电机的隆隆声值存储在索引 4 中。

此数据包通常为 8 个字节长,其中字节 2、5、6 和 7 未使用 (0x00)。

LEDs (0x01)

“0x01”类型字节表示 LED 数据包。 控制器在其中心环周围有四个绿色 LED,显示分配的玩家编号或其他状态信息。 该数据包选择控制器显示哪个 LED 动画。 有关 LED 动画及其时间的更多信息,请参阅这篇文章。

LED 动画的标识符存储在数据包索引 2 中。该数据包通常有 3 个字节长,所有字节都已使用。

结论

这是一篇冗长、详细、枯燥的技术文章……希望我已经把事情解释得足够好。 我知道我肯定对 Xbox 360 控制器的 USB 功能如何工作有了更好的了解,我希望你也这样做。

然而,这并不是一个完整的细分。 这个控制器还有很多谜团需要解决,其中最重要的是用于通过控制台(接口 3)进行身份验证的安全方法。 它的逆向工程取得了一些成功,甚至中间人攻击也取得了成功(他甚至确定了安全命令!)——尽管到目前为止我不相信有人能够(公开地)破解它。

下一个挑战是将这些知识应用到使用支持 USB 的微控制器来模拟 Xbox 控制器。 敬请关注!

原文