开源xow 实测及代码分析(xbox one S 非官方linux版驱动)

Xow是一个非官方的Linux版本Xbox one 手柄无线适配器(后续简称“适配器”)Driver,其底层基于libusb进行工作,通过Wifi与游戏手柄进行连接,其使用MT76xx的Wifi chip;

本文将以“问题点--答疑”形式展开:

问题点1:Ubuntu 编译前的相关确认

- Linux (kernel 4.5 or newer)  ---通过指令“uname -r”进行确认

这里强调内核版本的原因是:uinput在低于4.5内核版本时,其使用的uinput.h找不到相关定义;
- curl (for proprietary driver download) ---用于下载,安装指令“apt-get update -y && apt-get install curl -y”
- cabextract (for firmware extraction) ---可通过指令“apt-get install ubuntu-restricted-extras” 安装,这个是附带安装cabextract,不然会提示“cabextract: Command not found”,
- libusb (libusb-1.0-0-dev for Debian) ---通过“apt-get install libusb-1.0-0-dev”进行安装
- systemd (version 232 or newer) ---“systemctl --version”知晓当前Ubuntu中的具体版本;

编译需要使用Git command ---通过“apt install git” 安装;

实测发现,安装Ubuntu20.04,可以使得systemctl Kernel都能直接满足要求;

Attention:

编译时需Ubuntu 联网,因编译时会自动下载,其下载动作在makefile中有体现

 

问题点2:编译指令;

make BUILD=RELEASE” 或者
**NOTE:** Please use `BUILD=DEBUG` when asked for your debug logs.

问题点3:编译过程;

->下载微软.cab文件; 
->通过指令cabextract 解压为FW_ACC_00U.bin,进行校验;
->FW_ACC_00U.bin更名为firmware.bin;
//实际上当第一次下载后,可以修改makefile, 使其不再每次下载、校验;
->firmware.bin 转换为firmware.o;
->firmware.o 和其他.cpp对应.o一同被编译到执行档中;
OBJECTS := $(SOURCES:.cpp=.o) firmware.o;

 

 

问题点4:xow的架构实现;

xow的代码量并不大, 其集中在以下截图的红圈部分;

Xow中,类的包含关系如下:

GipDevice最为父类存在;

       ---其核心功能是定义了微软GIP 数据格式结构体,并提供了虚函数(需子类实现);

Controller类GipDevice的子类;

   ---用于每一只连接手柄的管理,包括接收数据的最终解析(最后一级,体现在API handlePacket),一级震动反馈也在此管理;

Mt76类做为父类存在;

   ----管理适配器的初始化、设置Wifi Beacon、设置Led等状态、提供封装的data 发送接口(sendClientPacket、sendCommand、controlWrite) 以及主动读取data接口(controlRead)…

Dongle 类Mt76的子类;

  ---用于多只手柄的管理,包括其连接和断开。提供Controller 类的controllers变量数组,进行多手柄的管理;提供独立线程监听USB批量传输内容(实现的API readBulkPackets);所以这里是USB 批量接收数据(一般是连接后的传送数据)xow的入口

UsbDevice类独立存在,没有继承关系;

       --- Base class for interfacing with USB devices, provides control/bulk transfer

capabilities; 其工作原理基于libusb

InputDevice类独立存在,没有继承关系;

       ---这个类的实现基于linux/uinput.h,但在实测中没有发现实际效果(也没有出现UI等);

Log类独立存在,没有继承关系;

       ---用于xow的log打印管理;

Bytes类独立存在,没有继承关系;

       ---作为一个数据组织类,数据被最终组织为uint8_t的 data buffer;

Buffer类独立存在,没有继承关系;

   ---作为一个模板类存在,提供具体数据存储和读取的接口;一般用于数据结构体;

使用例子如:” Buffer<RumbleData> rumbleBuffer;”

InterruptibleReader独立存在,没有继承关系;

  ---Interruptible reader for file descriptors

问题点5:xow中的Dongle部分;

因适配器使用USB作为与PC 、平板等连接载体,所以当其插入PC时,其通过libusb完成USB的枚举流程;通过libusb 接口与适配器中的MT76xx wifi芯片进行通讯;

 Dongle部分---MT76xx的初始化;

Source Code中定义MT76xx相关初始化以及操作接口的是mt76.cpp其在自定义类“Mt76的构造函数中完成 MT76xx chip的初始化动作;

 

 其调用的读取与写入动作基于自定义类“UsbDevice中自定义函数controlTransfer其参数设置基于USB 控制传输的定义,具体请参考《USB开发打全(4)》的第5章“控制传输:用于关键数据的结构化请求”;Page78

 

在整个MT76xx的操作过程中,其涉及到了USB 的两种传输方式“控制传输”和“批量传输”;

分别对应自定义API 是

“控制传输”:

“controlRead”、“controlWrite”

“批量传输”

sendCommand

具体分析:

Dongle的初始化开始于“Waiting for device...”,其阻塞在libusb_handle_events_complete(导致API getDevice也处于阻塞状态),等待注册的特定Dongle 被插入;

 

-->Dongle被插入时,将退出当前while循环;API getDevice也将完成并退出,其实执行getDevicemain函数将继续往下执行;

 

这里需注意的是,Dongle继承于MT76,所以当Dongle的构造函数被执行前,先执行其父类MT76的构造函数,当MT76的构造函数执行完成时,就算是完成Dongle的初始化动作;

 

 

-->所以当前Dongle的初始化实现全在Mt76的构造函数中;

 

Dongle部分---关于自定义类UsbDevice

其作为xow的操作基类,基于libusb, 提供了USB控制传输批量传输两种接口实现;

其自定义结构体“ControlPacket”的参数基于USB 控制传输定义,具体请参考《USB开发打全(4)》的第5章“控制传输:用于关键数据的结构化请求”;Page78

libusb的初始化函数是libusb_init

通过API libusb_hotplug_register_callback 进行热插拔检测,并注册了当前需检测USB dongle的vendor ID 和Product ID

 

Dongle部分---关于自定义类Dongle

其作为自定义类MT76的子类存在, 使用了容器类Vector;

 

 

 函数readBulkPackets的作用是:通过自定义类UsbDevice,提供的批量读函数bulkRead进行周期读取,当读取到data后,调用函数handleBulkData进行数据解析;

问题点6:xow中的Controller部分;

 这部分是基于Dongle部分,所以可以理解为xow的应用实现;

Controller部分---关于自定义类GipDevice(gip.h/gip.cpp)

其基于Game Input Protocol(GIP)

以下截图的

from controller”指代当前的xow, 再具体实现就是自定义类Controller;

from dongle”指待适配器;

Controller部分---关于自定义类Controller 

 其继承于自定义类GIPDevice;用于把接收到的手柄Event 发送到xow整合的虚拟input中;同时也负责把反馈发送给手柄;

 

问题点7:xow中如何处理来自手柄的按键操作,以及手柄按键的数据格式;

当前讨论的数据流向是:xbox手柄->Wifi->适配器->当前Xow接收处理

Tracing 接收Wifi packet data的数据流向是:

Attention当以下flow开始执行时,Dongle已完成初始化。所以在此之前,xowDongle之间并没有进行USB批量读取;

-->自定义类Dongle的构造函数中,在其Vector容器threads中添加了两个元素readBulkPackets

 -->执行到readBulkPackets,里面周期调用自定义类的函数bulkRead,其通过USB批量传输形式进行数据读取;

 

 -->调用方法handleBulkData进行数据解析;

Note:这里解析所有从适配器读取的data,然后基于不同类型进行处理,其中就包括WlanPacket(Wifi 数据);

 

-->当检测到当前端口为“WLAN_PORT”,或当前端口为“CPU_RX_PORT&&event类型为EVT_PACKET_RX”时,执行函数handleWlanPacket

 

 

 当满足条件“else if (type == MT_WLAN_DATA && subtype == MT_WLAN_QOS_DATA)”时,在函数内部执行函数handleControllerPacket;

-->执行函数handleControllerPacket

Note:这里有一个细节需注意:一个适配器可同时连接多个游戏手柄,这里的管控就是controllers数组;

 

 -->执行到对应controllers对象方法handlePacket,这里解析具体的操作数据; 其数据的操作类型是结构体Frame,先通过Frame获取到具体的command类型(FrameCommand),然后再把偏移sizeof(Frame)后的data转换为具体command对应的结构体;

 

 实际测试发现:当手柄按键操作时,此时收到的CMD_INPUT,最终执行的APIinputReceived

 

问题点8:xow如何把反馈告知手柄(如启动手柄的震动), 以及其数据格式;

关于手柄的震动实现,可在Source Code中搜索“magnitude”字样,相关API inputFeedbackReceived,相关结构体是RumbleData,关联的发送API performRumble,其内部调用的是API sendPacket

 

 

问题点9:当前xow中附带的测试rumble(其实就是震动)的实现方式;

 实现的核心是:每当连接上时,就执行“Controller::initInput” 初始化一个线程

processRumble 进行rumble 数据等待;

-->每当手柄和适配器连接上时,函数“Controller::initInput”都会被执行,里面将创建线程processRumble

-->线程processRumble内部将执行while循环,并使用“rumbleCondition.wait(lock);”进行阻塞等待;

 -->当获取到data时,将执行API performRumble进行发送;

Note:发送的data基于微软的GIP格式,而并非HID, 而且还需注意的是:最终的数据发送前,其会加上frame 部分,而非直接发送GIP data

 Attention:当前xow中提供数据组织的实现API inputFeedbackReceived(但其目前实测没有触发),我们只要关注其里面实现的rumble数据添加以及notify_one(用于唤醒阻塞);后续我们可以自行编写测试程式进行发送震动测试;

 Note:笔者自行编写测试震动程式,分别测试了xbox 的一代和二代无线适配器,均能实现手柄的4个马达震动;

问题点10:libusb中定义的宏定义具体代表的值;

LIBUSB_ENDPOINT_IN 0x80 (bit7位为1),表示设备到主机

LIBUSB_ENDPOINT_OUT: 0x00,表示主机到设备;

LIBUSB_REQUEST_TYPE_VENDOR:0x40 (bit6位为1)

LIBUSB_RECIPIENT_DEVICE:0x00

以上定义符合《USB开发大全》第4Page78的描述;

问题点11:xow编译成功后如何操作;

安装及启动xow:

sudo make install

sudo systemctl enable xow

sudo systemctl start xow

当执行“sudo systemctl start xow”后,再执行“sudo systemctl status now”,通过log确认xow是否已经跑起来(建议使用 make BUILD=DEBUG 编译xow);

停止及卸载xow:

sudo systemctl stop xow

sudo systemctl disable xow

sudo make uninstall

 Xow如何进入配对模式

xow的描述中,其是通过在Ubuntu中敲指令发送 SIGUSR1xow;

sudo systemctl kill -s SIGUSR1 xow

-->Xow中使用“sigaddset”进行信号设置:

 当其收到信号“SIGUSR1”时,触发以下flow:

-->执行API setPairingStatus, 其使用Wifi beacon进行信息推送,并设置无线适配器的Led等闪烁;

当前为何能使用无线适配器连接的原因是:当手柄进入到搜索模式(西瓜灯闪烁),同时无线适配器灯闪烁时,无线适配器执行802.11(Wifi) Beacon帧 ,使得手柄收到了Wifi信号(目前猜测手柄本身并不广播Wifi信号,而是等待特定广播并主动连接无线适配器);

 

问题点12:在手柄和适配器连接后,它们之间如何保持心跳来确认当前还处于连接状态;

通过实测发现,当手柄和适配器连接后,如果不进行任何的按键操作,适配器将每20收到一个CMD_STATUS 指令;

 

问题点13:实测xow驱动下的适配器与手柄(xbox one S)的稳定连接实况;

实测:第一次连接上后15分钟内,手柄没有任何连接操作,手柄断开并自动关机;第二次连接上后在15分钟内有操作过按键,所以一直持续要>17分钟,手动关闭手柄;

       发现在公司这种Wifi 环境复杂(公司NWifi 路由)下,断线就会容易出现(断开后手柄自动重连);在家里Wifi 环境比较干净的话,倒是不会断线;

最后,只要把xow的整个执行flow及微软bin文件解析,就能随意移植到其他平台;笔者曾经移植到STM32 测试,能正常和xbox one S 手柄进行连接,行为、功能和在Linux平台无异;

 

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值