mirror of
https://github.com/felis/USB_Host_Shield_2.0.git
synced 2024-03-22 11:31:26 +01:00
Extract the endpoints from the Xbox One controller instead of hardcoding them
Also cleaned up the code a bit Fixes #305
This commit is contained in:
parent
1308773eb8
commit
7078ea9f32
2 changed files with 147 additions and 69 deletions
140
XBOXONE.cpp
140
XBOXONE.cpp
|
@ -27,8 +27,11 @@
|
|||
XBOXONE::XBOXONE(USB *p) :
|
||||
pUsb(p), // pointer to USB class instance - mandatory
|
||||
bAddress(0), // device address - mandatory
|
||||
bNumEP(1), // If config descriptor needs to be parsed
|
||||
qNextPollTime(0), // Reset NextPollTime
|
||||
pollInterval(0),
|
||||
bPollEnable(false) { // don't start polling before dongle is connected
|
||||
for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
|
||||
for(uint8_t i = 0; i < XBOX_ONE_MAX_ENDPOINTS; i++) {
|
||||
epInfo[i].epAddr = 0;
|
||||
epInfo[i].maxPktSize = (i) ? 0 : 8;
|
||||
epInfo[i].bmSndToggle = 0;
|
||||
|
@ -46,8 +49,8 @@ uint8_t XBOXONE::Init(uint8_t parent, uint8_t port, bool lowspeed) {
|
|||
uint8_t rcode;
|
||||
UsbDevice *p = NULL;
|
||||
EpInfo *oldep_ptr = NULL;
|
||||
uint16_t PID;
|
||||
uint16_t VID;
|
||||
uint16_t PID, VID;
|
||||
uint8_t num_of_conf; // Number of configurations
|
||||
|
||||
// get memory address of USB device address pool
|
||||
AddressPool &addrPool = pUsb->GetAddressPool();
|
||||
|
@ -142,31 +145,30 @@ uint8_t XBOXONE::Init(uint8_t parent, uint8_t port, bool lowspeed) {
|
|||
if(rcode)
|
||||
goto FailSetDevTblEntry;
|
||||
|
||||
/* The application will work in reduced host mode, so we can save program and data
|
||||
memory space. After verifying the VID we will use known values for the
|
||||
configuration values for device, interface, endpoints and HID for the XBOXONE Controllers */
|
||||
num_of_conf = udd->bNumConfigurations; // Number of configurations
|
||||
|
||||
/* Initialize data structures for endpoints of device */
|
||||
epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x01; // XBOX one output endpoint
|
||||
epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
|
||||
epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
|
||||
epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
|
||||
epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0;
|
||||
epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0;
|
||||
epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX one input endpoint
|
||||
epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
|
||||
epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
|
||||
epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
|
||||
epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0;
|
||||
epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0;
|
||||
USBTRACE2("NC:", num_of_conf);
|
||||
|
||||
rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
|
||||
// Check if attached device is a Xbox One controller and fill endpoint data structure
|
||||
for(uint8_t i = 0; i < num_of_conf; i++) {
|
||||
ConfigDescParser<0, 0, 0, 0> confDescrParser(this); // Allow all devices, as we have already verified that it is a Xbox One controller from the VID and PID
|
||||
rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
|
||||
if(rcode) // Check error code
|
||||
goto FailGetConfDescr;
|
||||
if(bNumEP >= XBOX_ONE_MAX_ENDPOINTS) // All endpoints extracted
|
||||
break;
|
||||
}
|
||||
|
||||
if(bNumEP < XBOX_ONE_MAX_ENDPOINTS)
|
||||
goto FailUnknownDevice;
|
||||
|
||||
rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
|
||||
if(rcode)
|
||||
goto FailSetDevTblEntry;
|
||||
|
||||
delay(200); // Give time for address change
|
||||
|
||||
rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
|
||||
rcode = pUsb->setConf(bAddress, epInfo[ XBOX_ONE_CONTROL_PIPE ].epAddr, bConfNum);
|
||||
if(rcode)
|
||||
goto FailSetConfDescr;
|
||||
|
||||
|
@ -176,7 +178,8 @@ uint8_t XBOXONE::Init(uint8_t parent, uint8_t port, bool lowspeed) {
|
|||
|
||||
delay(200); // let things settle
|
||||
|
||||
// initialize the controller for input
|
||||
// Initialize the controller for input
|
||||
uint8_t writeBuf[5];
|
||||
writeBuf[0] = 0x05;
|
||||
writeBuf[1] = 0x20;
|
||||
writeBuf[2] = 0x00;
|
||||
|
@ -204,6 +207,12 @@ FailSetDevTblEntry:
|
|||
goto Fail;
|
||||
#endif
|
||||
|
||||
FailGetConfDescr:
|
||||
#ifdef DEBUG_USB_HOST
|
||||
NotifyFailGetConfDescr();
|
||||
goto Fail;
|
||||
#endif
|
||||
|
||||
FailSetConfDescr:
|
||||
#ifdef DEBUG_USB_HOST
|
||||
NotifyFailSetConfDescr();
|
||||
|
@ -225,11 +234,53 @@ Fail:
|
|||
return rcode;
|
||||
}
|
||||
|
||||
/* Extracts endpoint information from config descriptor */
|
||||
void XBOXONE::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) {
|
||||
bConfNum = conf;
|
||||
uint8_t index;
|
||||
|
||||
if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT) { // Interrupt endpoint
|
||||
index = (pep->bEndpointAddress & 0x80) == 0x80 ? XBOX_ONE_INPUT_PIPE : XBOX_ONE_OUTPUT_PIPE; // Set the endpoint index
|
||||
} else
|
||||
return;
|
||||
|
||||
// Fill the rest of endpoint data structure
|
||||
epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
|
||||
epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
|
||||
#ifdef EXTRADEBUG
|
||||
PrintEndpointDescriptor(pep);
|
||||
#endif
|
||||
if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints
|
||||
pollInterval = pep->bInterval;
|
||||
bNumEP++;
|
||||
}
|
||||
|
||||
void XBOXONE::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) {
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nEndpoint descriptor:"), 0x80);
|
||||
Notify(PSTR("\r\nLength:\t\t"), 0x80);
|
||||
D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);
|
||||
Notify(PSTR("\r\nType:\t\t"), 0x80);
|
||||
D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);
|
||||
Notify(PSTR("\r\nAddress:\t"), 0x80);
|
||||
D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);
|
||||
Notify(PSTR("\r\nAttributes:\t"), 0x80);
|
||||
D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);
|
||||
Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
|
||||
D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);
|
||||
Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
|
||||
D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Performs a cleanup after failed Init() attempt */
|
||||
uint8_t XBOXONE::Release() {
|
||||
XboxOneConnected = false;
|
||||
pUsb->GetAddressPool().FreeAddress(bAddress);
|
||||
bAddress = 0;
|
||||
bAddress = 0; // Clear device address
|
||||
bNumEP = 1; // Must have to be reset to 1
|
||||
qNextPollTime = 0; // Reset next poll time
|
||||
pollInterval = 0;
|
||||
bPollEnable = false;
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nXbox One Controller Disconnected\r\n"), 0x80);
|
||||
|
@ -238,28 +289,36 @@ uint8_t XBOXONE::Release() {
|
|||
}
|
||||
|
||||
uint8_t XBOXONE::Poll() {
|
||||
uint8_t rcode = 0;
|
||||
|
||||
if(!bPollEnable)
|
||||
return 0;
|
||||
uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
|
||||
uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf);
|
||||
if (!rcode) {
|
||||
|
||||
if((int32_t)((uint32_t)millis() - qNextPollTime) >= 0L) { // Do not poll if shorter than polling interval
|
||||
qNextPollTime = (uint32_t)millis() + pollInterval; // Set new poll time
|
||||
uint16_t length = (uint16_t)epInfo[ XBOX_ONE_INPUT_PIPE ].maxPktSize; // Read the maximum packet size from the endpoint
|
||||
uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ XBOX_ONE_INPUT_PIPE ].epAddr, &length, readBuf, pollInterval);
|
||||
if(!rcode) {
|
||||
readReport();
|
||||
#ifdef PRINTREPORT
|
||||
printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox ONE Controller
|
||||
#ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send by the Xbox ONE Controller
|
||||
for(uint8_t i = 0; i < length; i++) {
|
||||
D_PrintHex<uint8_t > (readBuf[i], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
Notify(PSTR("\r\n"), 0x80);
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG_USB_HOST
|
||||
else if (rcode != 0x04) { // not a matter of no update to send
|
||||
else if(rcode != hrNAK) { // Not a matter of no update to send
|
||||
Notify(PSTR("\r\nXbox One Poll Failed, error code: "), 0x80);
|
||||
NotifyFail(rcode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return rcode;
|
||||
}
|
||||
|
||||
void XBOXONE::readReport() {
|
||||
if(readBuf == NULL)
|
||||
return;
|
||||
if(readBuf[0] == 0x07) {
|
||||
// The XBOX button has a separate message
|
||||
if(readBuf[4] == 1)
|
||||
|
@ -309,18 +368,6 @@ void XBOXONE::readReport() {
|
|||
triggerValueOld[1] = triggerValue[1];
|
||||
}
|
||||
|
||||
void XBOXONE::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox ONE Controller
|
||||
#ifdef PRINTREPORT
|
||||
if(readBuf == NULL)
|
||||
return;
|
||||
for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {
|
||||
D_PrintHex<uint8_t > (readBuf[i], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
Notify(PSTR("\r\n"), 0x80);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t XBOXONE::getButtonPress(ButtonEnum b) {
|
||||
if(b == L2) // These are analog buttons
|
||||
return triggerValue[0];
|
||||
|
@ -345,7 +392,7 @@ bool XBOXONE::getButtonClick(ButtonEnum b) {
|
|||
}
|
||||
uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
|
||||
bool click = (ButtonClickState & button);
|
||||
ButtonClickState &= ~button; // clear "click" event
|
||||
ButtonClickState &= ~button; // Clear "click" event
|
||||
return click;
|
||||
}
|
||||
|
||||
|
@ -355,7 +402,7 @@ int16_t XBOXONE::getAnalogHat(AnalogHatEnum a) {
|
|||
|
||||
/* Xbox Controller commands */
|
||||
uint8_t XBOXONE::XboxCommand(uint8_t* data, uint16_t nbytes) {
|
||||
uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_OUTPUT_PIPE ].epAddr, nbytes, data);
|
||||
uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_ONE_OUTPUT_PIPE ].epAddr, nbytes, data);
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nXboxCommand, Return: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rcode, 0x80);
|
||||
|
@ -364,7 +411,8 @@ uint8_t XBOXONE::XboxCommand(uint8_t* data, uint16_t nbytes) {
|
|||
}
|
||||
|
||||
void XBOXONE::onInit() {
|
||||
// a short buzz to show the controller is active
|
||||
// A short buzz to show the controller is active
|
||||
uint8_t writeBuf[11];
|
||||
writeBuf[0] = 0x09;
|
||||
writeBuf[1] = 0x08;
|
||||
writeBuf[2] = 0x00;
|
||||
|
|
62
XBOXONE.h
62
XBOXONE.h
|
@ -26,15 +26,17 @@
|
|||
#include "Usb.h"
|
||||
#include "xboxEnums.h"
|
||||
|
||||
/* Data Xbox ONE taken from descriptors */
|
||||
#define EP_MAXPKTSIZE 32 // max size for data via USB
|
||||
/* Xbox One data taken from descriptors */
|
||||
#define XBOX_ONE_EP_MAXPKTSIZE 64 // Max size for data via USB
|
||||
|
||||
/* Names we give to the 3 XboxONE pipes */
|
||||
#define XBOX_CONTROL_PIPE 0
|
||||
#define XBOX_OUTPUT_PIPE 1
|
||||
#define XBOX_INPUT_PIPE 2
|
||||
#define XBOX_ONE_CONTROL_PIPE 0
|
||||
#define XBOX_ONE_OUTPUT_PIPE 1
|
||||
#define XBOX_ONE_INPUT_PIPE 2
|
||||
|
||||
// PID and VID of the different devices - see: https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c
|
||||
#define XBOX_ONE_MAX_ENDPOINTS 3
|
||||
|
||||
// PID and VID of the different versions of the controller - see: https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c
|
||||
|
||||
// Official controllers
|
||||
#define XBOX_VID1 0x045E // Microsoft Corporation
|
||||
|
@ -59,12 +61,8 @@
|
|||
#define XBOX_ONE_PID11 0x542A // Xbox ONE spectra
|
||||
#define XBOX_ONE_PID12 0x543A // PowerA Xbox One wired controller
|
||||
|
||||
#define XBOX_REPORT_BUFFER_SIZE 14 // Size of the input report buffer
|
||||
|
||||
#define XBOX_MAX_ENDPOINTS 3
|
||||
|
||||
/** This class implements support for a Xbox ONE controller connected via USB. */
|
||||
class XBOXONE : public USBDeviceConfig {
|
||||
class XBOXONE : public USBDeviceConfig, public UsbConfigXtracter {
|
||||
public:
|
||||
/**
|
||||
* Constructor for the XBOXONE class.
|
||||
|
@ -108,6 +106,14 @@ public:
|
|||
return bPollEnable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read the poll interval taken from the endpoint descriptors.
|
||||
* @return The poll interval in ms.
|
||||
*/
|
||||
uint8_t readPollInterval() {
|
||||
return pollInterval;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used by the USB core to check what this driver support.
|
||||
* @param vid The device's VID.
|
||||
|
@ -161,7 +167,32 @@ protected:
|
|||
/** Device address. */
|
||||
uint8_t bAddress;
|
||||
/** Endpoint info structure. */
|
||||
EpInfo epInfo[XBOX_MAX_ENDPOINTS];
|
||||
EpInfo epInfo[XBOX_ONE_MAX_ENDPOINTS];
|
||||
|
||||
/** Configuration number. */
|
||||
uint8_t bConfNum;
|
||||
/** Total number of endpoints in the configuration. */
|
||||
uint8_t bNumEP;
|
||||
/** Next poll time based on poll interval taken from the USB descriptor. */
|
||||
uint32_t qNextPollTime;
|
||||
|
||||
/** @name UsbConfigXtracter implementation */
|
||||
/**
|
||||
* UsbConfigXtracter implementation, used to extract endpoint information.
|
||||
* @param conf Configuration value.
|
||||
* @param iface Interface number.
|
||||
* @param alt Alternate setting.
|
||||
* @param proto Interface Protocol.
|
||||
* @param ep Endpoint Descriptor.
|
||||
*/
|
||||
void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
|
||||
/**@}*/
|
||||
|
||||
/**
|
||||
* Used to print the USB Endpoint Descriptor.
|
||||
* @param ep_ptr Pointer to USB Endpoint Descriptor.
|
||||
*/
|
||||
void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
|
||||
|
||||
private:
|
||||
/**
|
||||
|
@ -171,6 +202,7 @@ private:
|
|||
void onInit();
|
||||
void (*pFuncOnInit)(void); // Pointer to function called in onInit()
|
||||
|
||||
uint8_t pollInterval;
|
||||
bool bPollEnable;
|
||||
|
||||
/* Variables to store the buttons */
|
||||
|
@ -184,11 +216,9 @@ private:
|
|||
bool L2Clicked; // These buttons are analog, so we use we use these bools to check if they where clicked or not
|
||||
bool R2Clicked;
|
||||
|
||||
uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data
|
||||
uint8_t writeBuf[12]; // General purpose buffer for output data
|
||||
uint8_t readBuf[XBOX_ONE_EP_MAXPKTSIZE]; // General purpose buffer for input data
|
||||
|
||||
void readReport(); // read incoming data
|
||||
void printReport(); // print incoming date - Uncomment for debugging
|
||||
void readReport(); // Used to read the incoming data
|
||||
|
||||
/* Private commands */
|
||||
uint8_t XboxCommand(uint8_t* data, uint16_t nbytes);
|
||||
|
|
Loading…
Reference in a new issue