Update TinyUSB to latest / Xbox One Auth fix (#1002)

* Update tinyusb to latest

* TinyUSB update to latest (removed hack for get_report)

* Moving a single shared 1024 byte auth buffer to a queue system in-case of race condition (which seemed to be happening)

* Fixed a legit bug with Xbox One authentication.

.init, .reset, and .open are called multiple times during USB discovery

initial announcement code was moved from .init to .open AFTER the xbox one interface type is declared.

Also moved shared buffers to more sane areas and used two instead of one buffer for sharing.

* Update xgip_protocol.h

Extra spaces
This commit is contained in:
Luke A
2024-05-28 11:18:17 -04:00
committed by GitHub
parent 43a8446245
commit b2fb7fecf3
12 changed files with 140 additions and 139 deletions

View File

@@ -110,9 +110,9 @@ private:
bool chunkEnded; // did we hit the end of the chunk successfully?
uint8_t packet[64]; // for output packets
uint16_t packetLength; // LAST SENT packet length
uint8_t * data; // Total data in this packet
uint8_t data[1024]; // Total data in this packet
uint16_t dataLength; // actual length of data
bool isValidPacket; // is this a valid packet or did we get an error?
};
#endif
#endif

View File

@@ -101,7 +101,7 @@ TU_ATTR_WEAK void tuh_xinput_report_sent_cb(uint8_t dev_addr, uint8_t instance,
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void xinputh_init(void);
bool xinputh_init(void);
bool xinputh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len);
bool xinputh_set_config(uint8_t dev_addr, uint8_t itf_num);
bool xinputh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);

View File

@@ -12,6 +12,44 @@ typedef enum {
wait_auth_dongle_to_console = 4,
} XboxOneState;
class XBOneAuthBuffer {
public:
XBOneAuthBuffer() {
data = nullptr;
sequence = 0;
length = 0;
type = 0;
}
~XBOneAuthBuffer(){
if ( data != nullptr ) {
delete [] data;
}
}
void setBuffer(uint8_t * inData, uint16_t inLen, uint8_t inSeq, uint8_t inType) {
data = new uint8_t[inLen];
length = inLen;
sequence = inSeq;
type = inType;
memcpy(data, inData, inLen);
}
void reset() {
if ( data != nullptr ) {
delete [] data;
}
data = nullptr;
sequence = 0;
length = 0;
type = 0;
}
uint8_t * data;
uint8_t sequence;
uint16_t length;
uint8_t type;
};
typedef struct {
XboxOneState xboneState;
@@ -19,11 +57,9 @@ typedef struct {
// Note: the Xbox One Passthrough can call send_xbone_report() directly but not the other way around
bool authCompleted;
// Auth Buffer
uint8_t authBuffer[1024];
uint8_t authSequence;
uint16_t authLen;
uint8_t authType;
// Auth Buffer Queue
XBOneAuthBuffer consoleBuffer;
XBOneAuthBuffer dongleBuffer;
} XboxOneAuthData;
class XBOneAuth : public GPAuthDriver {

View File

@@ -30,6 +30,7 @@ public:
bool getAuthSent();
private:
virtual void update();
void process_report_queue(uint32_t now);
bool send_xbone_usb(uint8_t const *buffer, uint16_t bufsize);
void set_ack_wait();
uint8_t last_report[CFG_TUD_ENDPOINT0_SIZE] = { };

View File

@@ -12,9 +12,6 @@
// USB Host manager decides on TinyUSB Host driver
usbh_class_driver_t const* usbh_app_driver_get_cb(uint8_t *driver_count);
// Missing TinyUSB call
bool tuh_hid_get_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
class USBHostManager {
public:
USBHostManager(USBHostManager const&) = delete;

View File

@@ -28,15 +28,11 @@
// Default Constructor
XGIPProtocol::XGIPProtocol() {
data = nullptr;
reset();
}
// Default Destructor
XGIPProtocol::~XGIPProtocol() {
if ( data != nullptr ) {
delete [] data;
}
}
// Reset packet information
@@ -50,10 +46,7 @@ void XGIPProtocol::reset() {
numberOfChunksSent = 0; // How many actual chunks have we sent?
chunkEnded = false; // Are we at the end of the chunk?
isValidPacket = false; // Is this a valid packet?
if ( data != nullptr ) { // Delete our data if its not null
delete [] data;
}
data = nullptr;
memset(data, 0, 1024);
dataLength = 0; // Set data length to 0
memset(packet, 0, sizeof(packet)); // Set our packet to 0
packetLength = 0; // Set packet length to 0
@@ -116,12 +109,8 @@ bool XGIPProtocol::parse(const uint8_t * buffer, uint16_t len) {
dataLength = dataLength - ((dataLength / 0x100)*0x80);
}
// Ensure we clear data if its set to something else
if ( data != nullptr )
delete [] data;
data = new uint8_t[dataLength];
actualDataReceived = 0; // haven't received anything yet
totalChunkReceived = header.length; //
// Set our chunk received to the header length
totalChunkReceived = header.length;
} else {
totalChunkReceived += header.length; // not actual data length, but chunk value
}
@@ -137,9 +126,6 @@ bool XGIPProtocol::parse(const uint8_t * buffer, uint16_t len) {
reset();
memcpy((void*)&header, buffer, sizeof(GipHeader_t));
if ( header.length > 0 ) {
if (data != nullptr)
delete [] data;
data = new uint8_t[header.length];
memcpy(data, &buffer[4], header.length); // copy incoming data
}
actualDataReceived = header.length;
@@ -177,9 +163,6 @@ bool XGIPProtocol::setData(const uint8_t * buffer, uint16_t len) {
if ( len > 0x3000) { // arbitrary but this should cover us if something bad happens
return false;
}
if ( data != nullptr )
delete [] data;
data = new uint8_t[len];
memcpy(data, buffer, len);
dataLength = len;
return true;

View File

@@ -150,8 +150,9 @@ bool tuh_xinput_ready(uint8_t dev_addr, uint8_t instance) {
//--------------------------------------------------------------------+
// USBH API
//--------------------------------------------------------------------+
void xinputh_init(void) {
bool xinputh_init(void) {
tu_memclr(_xinputh_dev, sizeof(_xinputh_dev));
return true;
}
bool xinputh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {

View File

@@ -15,8 +15,6 @@ void XBOneAuth::initialize() {
if ( available() ) {
listener = new XBOneAuthUSBListener();
xboxOneAuthData.xboneState = auth_idle_state;
xboxOneAuthData.authLen = 0;
xboxOneAuthData.authSequence = 0;
xboxOneAuthData.authCompleted = false;
((XBOneAuthUSBListener*)listener)->setup();
((XBOneAuthUSBListener*)listener)->setAuthData(&xboxOneAuthData);

View File

@@ -24,7 +24,7 @@ typedef struct {
} report_queue_t;
static std::queue<report_queue_t> report_queue;
static uint32_t lastReportQueueSent = 0;
static uint32_t lastReportQueue = 0;
#define REPORT_QUEUE_INTERVAL 15
void XBOneAuthUSBListener::setup() {
@@ -38,27 +38,36 @@ void XBOneAuthUSBListener::setAuthData(XboxOneAuthData * authData ) {
}
void XBOneAuthUSBListener::process() {
if ( mounted == false || xboxOneAuthData == nullptr) // do nothing if we have not mounted an xbox one dongle
// Process the report queue
process_report_queue();
// Do nothing if auth data or dongle are not ready
if ( xboxOneAuthData == nullptr || dongle_ready == false) // do nothing if we have not mounted an xbox one dongle
return;
// Do not begin processing console auth unless we have the dongle ready
if ( dongle_ready == true ) {
if ( xboxOneAuthData->xboneState == XboxOneState::send_auth_console_to_dongle ) {
uint8_t isChunked = ( xboxOneAuthData->authLen > GIP_MAX_CHUNK_SIZE );
uint8_t needsAck = (xboxOneAuthData->authLen > 2);
outgoingXGIP.reset();
outgoingXGIP.setAttributes(xboxOneAuthData->authType, xboxOneAuthData->authSequence, 1, isChunked, needsAck);
outgoingXGIP.setData(xboxOneAuthData->authBuffer, xboxOneAuthData->authLen);
xboxOneAuthData->xboneState = XboxOneState::wait_auth_console_to_dongle;
} else if ( xboxOneAuthData->xboneState == XboxOneState::wait_auth_console_to_dongle) {
queue_host_report(outgoingXGIP.generatePacket(), outgoingXGIP.getPacketLength());
if ( outgoingXGIP.getChunked() == false || outgoingXGIP.endOfChunk() == true ) {
xboxOneAuthData->xboneState = XboxOneState::auth_idle_state;
}
}
// Received a packet from the console (or Windows) to dongle
if ( xboxOneAuthData->xboneState == XboxOneState::send_auth_console_to_dongle ) {
uint8_t * buffer = xboxOneAuthData->consoleBuffer.data;
uint8_t type = xboxOneAuthData->consoleBuffer.type;
uint16_t len = xboxOneAuthData->consoleBuffer.length;
uint8_t sequence = xboxOneAuthData->consoleBuffer.sequence;
uint8_t isChunked = ( len > GIP_MAX_CHUNK_SIZE );
uint8_t needsAck = (len > 2);
outgoingXGIP.reset();
outgoingXGIP.setAttributes(type, sequence, 1, isChunked, needsAck);
outgoingXGIP.setData(buffer, len);
xboxOneAuthData->consoleBuffer.reset();
xboxOneAuthData->xboneState = XboxOneState::wait_auth_console_to_dongle;
}
process_report_queue();
// Process waiting (always on first frame)
if ( xboxOneAuthData->xboneState == XboxOneState::wait_auth_console_to_dongle) {
queue_host_report(outgoingXGIP.generatePacket(), outgoingXGIP.getPacketLength());
if ( outgoingXGIP.getChunked() == false ||
(outgoingXGIP.getChunked() == true && outgoingXGIP.endOfChunk() == true) ) {
xboxOneAuthData->xboneState = XboxOneState::auth_idle_state;
}
}
}
void XBOneAuthUSBListener::xmount(uint8_t dev_addr, uint8_t instance, uint8_t controllerType, uint8_t subtype) {
@@ -74,11 +83,16 @@ void XBOneAuthUSBListener::xmount(uint8_t dev_addr, uint8_t instance, uint8_t co
void XBOneAuthUSBListener::unmount(uint8_t dev_addr) {
// Do not reset dongle_ready on unmount (Magic-X will remount but still be ready)
mounted = false;
incomingXGIP.reset();
outgoingXGIP.reset();
dongle_ready = false; // not ready for auth if we unmounted
xboxOneAuthData->xboneState = XboxOneState::auth_idle_state;
}
void XBOneAuthUSBListener::report_received(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) {
if ( mounted == false || xboxOneAuthData == nullptr)
if ( mounted == false || xboxOneAuthData == nullptr) {
return;
}
incomingXGIP.parse(report, len);
if ( incomingXGIP.validate() == false ) {
@@ -104,26 +118,28 @@ void XBOneAuthUSBListener::report_received(uint8_t dev_addr, uint8_t instance, u
outgoingXGIP.setAttributes(GIP_POWER_MODE_DEVICE_CONFIG, 2, 1, false, 0);
outgoingXGIP.setData(xb1_power_on, sizeof(xb1_power_on));
queue_host_report((uint8_t*)outgoingXGIP.generatePacket(), outgoingXGIP.getPacketLength());
outgoingXGIP.reset(); // Power-on with 0x00
outgoingXGIP.setAttributes(GIP_POWER_MODE_DEVICE_CONFIG, 3, 1, false, 0);
outgoingXGIP.setData(xb1_power_on_single, sizeof(xb1_power_on_single));
queue_host_report((uint8_t*)outgoingXGIP.generatePacket(), outgoingXGIP.getPacketLength());
outgoingXGIP.reset(); // Rumble Support to enable dongle
outgoingXGIP.setAttributes(GIP_CMD_RUMBLE, 1, 0, false, 0); // not internal function
outgoingXGIP.setData(xb1_rumble_on, sizeof(xb1_rumble_on));
queue_host_report((uint8_t*)outgoingXGIP.generatePacket(), outgoingXGIP.getPacketLength());
// Dongle is ready!
dongle_ready = true;
}
break;
case GIP_AUTH:
case GIP_FINAL_AUTH:
if ( incomingXGIP.getChunked() == false ||
(incomingXGIP.getChunked() == true && incomingXGIP.endOfChunk() == true )) {
memcpy(xboxOneAuthData->authBuffer, incomingXGIP.getData(), incomingXGIP.getDataLength());
xboxOneAuthData->authLen = incomingXGIP.getDataLength();
xboxOneAuthData->authType = incomingXGIP.getCommand();
xboxOneAuthData->authSequence = incomingXGIP.getSequence();
xboxOneAuthData->xboneState = XboxOneState::send_auth_dongle_to_console;
if ( incomingXGIP.getChunked() == false ||
(incomingXGIP.getChunked() == true && incomingXGIP.endOfChunk() == true )) {
xboxOneAuthData->dongleBuffer.setBuffer(incomingXGIP.getData(), incomingXGIP.getDataLength(),
incomingXGIP.getSequence(), incomingXGIP.getCommand());
xboxOneAuthData->xboneState = XboxOneState::send_auth_dongle_to_console;
incomingXGIP.reset();
}
break;
@@ -142,12 +158,10 @@ void XBOneAuthUSBListener::queue_host_report(void* report, uint16_t len) {
void XBOneAuthUSBListener::process_report_queue() {
uint32_t now = to_ms_since_boot(get_absolute_time());
if ( !report_queue.empty() && (now - lastReportQueueSent) > REPORT_QUEUE_INTERVAL ) {
if ( !report_queue.empty() && (now - lastReportQueue) > REPORT_QUEUE_INTERVAL ) {
if ( tuh_xinput_send_report(xbone_dev_addr, xbone_instance, report_queue.front().report, report_queue.front().len) ) {
report_queue.pop();
lastReportQueueSent = now;
} else { // FAILED: Keeping it on the queue to send again
sleep_ms(REPORT_QUEUE_INTERVAL);
lastReportQueue = now; // last time we checked report queue
}
}
}

View File

@@ -21,9 +21,9 @@
#define DESC_EXTENDED_PROPERTIES_DESCRIPTOR 0x0005
#define REQ_GET_XGIP_HEADER 0x90
// Sent report queue every 15 milliseconds
static uint32_t lastReportQueueSent = 0;
#define REPORT_QUEUE_INTERVAL 15
// Check report queue every 35 milliseconds
static uint32_t lastReportQueue = 0;
#define REPORT_QUEUE_INTERVAL 35
typedef enum {
IDLE_STATE = 0,
@@ -150,7 +150,8 @@ static void xbone_reset(uint8_t rhport) {
while(!report_queue.empty())
report_queue.pop();
xboneDriverState = XboxOneDriverState::READY_ANNOUNCE;
// make sure xbox driver state is doing nothing
xboneDriverState = XboxOneDriverState::IDLE_STATE;
// close any endpoints that are open
tu_memclr(&_xboned_itf, sizeof(_xboned_itf));
@@ -160,7 +161,6 @@ static void xbone_init(void) {
xbone_reset(TUD_OPT_RHPORT);
}
static uint16_t xbone_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
uint16_t drv_len = 0;
if (TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) {
@@ -197,6 +197,13 @@ static uint16_t xbone_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc
TU_BREAKPOINT();
}
}
// Setting up XGIPs and driver state
if (incomingXGIP != nullptr && outgoingXGIP != nullptr ) {
xboneDriverState = XboxOneDriverState::READY_ANNOUNCE;
incomingXGIP->reset();
outgoingXGIP->reset();
}
}
}
@@ -270,14 +277,13 @@ bool xbone_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
} else if ( command == GIP_POWER_MODE_DEVICE_CONFIG || command == GIP_CMD_WAKEUP || command == GIP_CMD_RUMBLE ) {
xbox_one_powered_on = true;
} else if ( command == GIP_AUTH || command == GIP_FINAL_AUTH) {
if (incomingXGIP->getDataLength() == 2 && memcmp(incomingXGIP->getData(), authReady, sizeof(authReady))==0 )
if (incomingXGIP->getDataLength() == 2 && memcmp(incomingXGIP->getData(), authReady, sizeof(authReady))==0 ) {
xboxOneAuthData->authCompleted = true;
}
if ( (incomingXGIP->getChunked() == true && incomingXGIP->endOfChunk() == true) ||
(incomingXGIP->getChunked() == false )) {
memcpy(xboxOneAuthData->authBuffer, incomingXGIP->getData(), incomingXGIP->getDataLength());
xboxOneAuthData->authLen = incomingXGIP->getDataLength();
xboxOneAuthData->authType = incomingXGIP->getCommand();
xboxOneAuthData->authSequence = incomingXGIP->getSequence();
xboxOneAuthData->consoleBuffer.setBuffer(incomingXGIP->getData(), incomingXGIP->getDataLength(),
incomingXGIP->getSequence(), incomingXGIP->getCommand());
xboxOneAuthData->xboneState = XboxOneState::send_auth_console_to_dongle;
incomingXGIP->reset();
}
@@ -585,20 +591,8 @@ void XBOneDriver::set_ack_wait() {
void XBOneDriver::update() {
uint32_t now = to_ms_since_boot(get_absolute_time());
if ( !report_queue.empty() ) {
if ( (now - lastReportQueueSent) > REPORT_QUEUE_INTERVAL ) {
report_queue_t & report_front = report_queue.front();
uint16_t xboneReportSize = report_front.len;
if ( send_xbone_usb(report_front.report, xboneReportSize) ) {
lastReportQueueSent = now;
// Set last report queue sent to our report sent by the queue
memcpy(last_report, &report_front.report, xboneReportSize);
report_queue.pop();
} else {
sleep_ms(REPORT_QUEUE_INTERVAL);
}
}
}
// Process our report queue
process_report_queue(now);
// Do not add logic until our ACK returns
if ( waiting_ack == true ) {
@@ -617,7 +611,7 @@ void XBOneDriver::update() {
outgoingXGIP->setAttributes(GIP_ANNOUNCE, 1, 1, 0, 0);
outgoingXGIP->setData(announcePacket, sizeof(announcePacket));
queue_xbone_report(outgoingXGIP->generatePacket(), outgoingXGIP->getPacketLength());
xboneDriverState = WAIT_DESCRIPTOR_REQUEST;
xboneDriverState = IDLE_STATE;
}
break;
case SEND_DESCRIPTOR:
@@ -630,13 +624,22 @@ void XBOneDriver::update() {
}
break;
case SETUP_AUTH:
// Received packet from dongle to console / PC
if ( xboxOneAuthData->xboneState == XboxOneState::send_auth_dongle_to_console ) {
bool isChunked = (xboxOneAuthData->authLen > GIP_MAX_CHUNK_SIZE);
uint16_t len = xboxOneAuthData->dongleBuffer.length;
uint8_t type = xboxOneAuthData->dongleBuffer.type;
uint8_t sequence = xboxOneAuthData->dongleBuffer.sequence;
uint8_t * buffer = xboxOneAuthData->dongleBuffer.data;
bool isChunked = (len > GIP_MAX_CHUNK_SIZE);
outgoingXGIP->reset();
outgoingXGIP->setAttributes(xboxOneAuthData->authType, xboxOneAuthData->authSequence, 1, isChunked, 1);
outgoingXGIP->setData(xboxOneAuthData->authBuffer, xboxOneAuthData->authLen);
outgoingXGIP->setAttributes(type, sequence, 1, isChunked, 1);
outgoingXGIP->setData(buffer, len);
xboxOneAuthData->xboneState = wait_auth_dongle_to_console;
} else if ( xboxOneAuthData->xboneState == XboxOneState::wait_auth_dongle_to_console ) {
xboxOneAuthData->dongleBuffer.reset();
}
// Process auth dongle to console
if ( xboxOneAuthData->xboneState == XboxOneState::wait_auth_dongle_to_console ) {
queue_xbone_report(outgoingXGIP->generatePacket(), outgoingXGIP->getPacketLength());
if ( outgoingXGIP->getChunked() == false || outgoingXGIP->endOfChunk() == true ) {
xboxOneAuthData->xboneState = XboxOneState::auth_idle_state;
@@ -652,6 +655,19 @@ void XBOneDriver::update() {
};
}
void XBOneDriver::process_report_queue(uint32_t now) {
if ( !report_queue.empty() && (now - lastReportQueue) > REPORT_QUEUE_INTERVAL ) {
if ( send_xbone_usb(report_queue.front().report, report_queue.front().len) ) {
memcpy(last_report, &report_queue.front().report, report_queue.front().len);
report_queue.pop();
lastReportQueue = now;
} else {
// THIS IS REQUIRED FOR TIMING ON PC / CONSOLE
sleep_ms(REPORT_QUEUE_INTERVAL); // sleep while we wait, never happens during input only auth
}
}
}
uint16_t XBOneDriver::GetJoystickMidValue() {
return GAMEPAD_JOYSTICK_MID;
}

View File

@@ -249,48 +249,3 @@ usbh_class_driver_t const *usbh_app_driver_get_cb(uint8_t *driver_count) {
*driver_count = 1;
return driver_host;
}
// Request for HID_REQ_CONTROL_GET_REPORT missing from TinyUSB
static void get_report_complete(tuh_xfer_t* xfer)
{
if (tuh_hid_get_report_complete_cb)
{
uint8_t const instance = 0;
uint8_t const report_type = tu_u16_high(xfer->setup->wValue);
uint8_t const report_id = tu_u16_low(xfer->setup->wValue);
tuh_hid_get_report_complete_cb(xfer->daddr, instance, report_id, report_type,
(xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0);
}
}
bool tuh_hid_get_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, void* report, uint16_t len)
{
tusb_control_request_t const request =
{
.bmRequestType_bit =
{
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_IN
},
.bRequest = HID_REQ_CONTROL_GET_REPORT,
.wValue = tu_u16(report_type, report_id),
.wIndex = _intf_num, // pulled in from tuh_hid_mount_cb()
.wLength = len
};
tuh_xfer_t xfer =
{
.daddr = dev_addr,
.ep_addr = 0,
.setup = &request,
.buffer = (uint8_t*)report,
.complete_cb = get_report_complete,
.user_data = 0
};
TU_ASSERT( tuh_control_xfer(&xfer) );
return true;
}