Zoltan Hudak / UsbHostMAX3421E

Dependents:   UsbHostMAX3421E_Hello

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers XBOXONE.cpp Source File

XBOXONE.cpp

00001 /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
00002    Copyright (C) 2015 guruthree
00003 
00004  This software may be distributed and modified under the terms of the GNU
00005  General Public License version 2 (GPL2) as published by the Free Software
00006  Foundation and appearing in the file GPL2.TXT included in the packaging of
00007  this file. Please note that GPL2 Section 2[b] requires that all works based
00008  on this software must also be made publicly available under the terms of
00009  the GPL2 ("Copyleft").
00010 
00011  Contact information
00012  -------------------
00013 
00014  Kristian Lauszus, TKJ Electronics
00015  Web      :  http://www.tkjelectronics.com
00016  e-mail   :  kristianl@tkjelectronics.com
00017 
00018  guruthree
00019  Web      :  https://github.com/guruthree/
00020  */
00021 
00022 #include "XBOXONE.h"
00023 // To enable serial debugging see "settings.h"
00024 //#define EXTRADEBUG // Uncomment to get even more debugging data
00025 //#define PRINTREPORT // Uncomment to print the report send by the Xbox ONE Controller
00026 
00027 XBOXONE::XBOXONE(Usb *p) :
00028 pUsb(p), // pointer to USB class instance - mandatory
00029 bAddress(0), // device address - mandatory
00030 bNumEP(1), // If config descriptor needs to be parsed
00031 qNextPollTime(0), // Reset NextPollTime
00032 pollInterval(0),
00033 bPollEnable(false) { // don't start polling before dongle is connected
00034         for(uint8_t i = 0; i < XBOX_ONE_MAX_ENDPOINTS; i++) {
00035                 epInfo[i].epAddr = 0;
00036                 epInfo[i].maxPktSize = (i) ? 0 : 8;
00037                 epInfo[i].bmSndToggle = 0;
00038                 epInfo[i].bmRcvToggle = 0;
00039                 epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
00040         }
00041 
00042         if(pUsb) // register in USB subsystem
00043                 pUsb->RegisterDeviceClass(this); //set devConfig[] entry
00044 }
00045 
00046 uint8_t XBOXONE::Init(uint8_t parent, uint8_t port, bool lowspeed) {
00047         uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
00048         USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
00049         uint8_t rcode;
00050         UsbDevice *p = NULL;
00051         EpInfo *oldep_ptr = NULL;
00052         uint16_t PID, VID;
00053         uint8_t num_of_conf; // Number of configurations
00054 
00055         // get memory address of USB device address pool
00056         AddressPool &addrPool = pUsb->GetAddressPool();
00057 #ifdef EXTRADEBUG
00058         Notify(PSTR("\r\nXBOXONE Init"), 0x80);
00059 #endif
00060         // check if address has already been assigned to an instance
00061         if(bAddress) {
00062 #ifdef DEBUG_USB_HOST
00063                 Notify(PSTR("\r\nAddress in use"), 0x80);
00064 #endif
00065                 return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
00066         }
00067 
00068         // Get pointer to pseudo device with address 0 assigned
00069         p = addrPool.GetUsbDevicePtr(0);
00070 
00071         if(!p) {
00072 #ifdef DEBUG_USB_HOST
00073                 Notify(PSTR("\r\nAddress not found"), 0x80);
00074 #endif
00075                 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
00076         }
00077 
00078         if(!p->epinfo) {
00079 #ifdef DEBUG_USB_HOST
00080                 Notify(PSTR("\r\nepinfo is null"), 0x80);
00081 #endif
00082                 return USB_ERROR_EPINFO_IS_NULL;
00083         }
00084 
00085         // Save old pointer to EP_RECORD of address 0
00086         oldep_ptr = p->epinfo;
00087 
00088         // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
00089         p->epinfo = epInfo;
00090 
00091         p->lowspeed = lowspeed;
00092 
00093         // Get device descriptor
00094         rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
00095         // Restore p->epinfo
00096         p->epinfo = oldep_ptr;
00097 
00098         if(rcode)
00099                 goto FailGetDevDescr;
00100 
00101         VID = udd->idVendor;
00102         PID = udd->idProduct;
00103 
00104         if(!VIDPIDOK(VID, PID)) // Check VID
00105                 goto FailUnknownDevice;
00106 
00107         // Allocate new address according to device class
00108         bAddress = addrPool.AllocAddress(parent, false, port);
00109 
00110         if(!bAddress)
00111                 return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
00112 
00113         // Extract Max Packet Size from device descriptor
00114         epInfo[0].maxPktSize = udd->bMaxPacketSize0;
00115 
00116         // Assign new address to the device
00117         rcode = pUsb->setAddr(0, 0, bAddress);
00118         if(rcode) {
00119                 p->lowspeed = false;
00120                 addrPool.FreeAddress(bAddress);
00121                 bAddress = 0;
00122 #ifdef DEBUG_USB_HOST
00123                 Notify(PSTR("\r\nsetAddr: "), 0x80);
00124                 D_PrintHex<uint8_t > (rcode, 0x80);
00125 #endif
00126                 return rcode;
00127         }
00128 #ifdef EXTRADEBUG
00129         Notify(PSTR("\r\nAddr: "), 0x80);
00130         D_PrintHex<uint8_t > (bAddress, 0x80);
00131 #endif
00132         //wait_ms(300); // Spec says you should wait at least 200ms
00133 
00134         p->lowspeed = false;
00135 
00136         //get pointer to assigned address record
00137         p = addrPool.GetUsbDevicePtr(bAddress);
00138         if(!p)
00139                 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
00140 
00141         p->lowspeed = lowspeed;
00142 
00143         // Assign epInfo to epinfo pointer - only EP0 is known
00144         rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
00145         if(rcode)
00146                 goto FailSetDevTblEntry;
00147 
00148         num_of_conf = udd->bNumConfigurations; // Number of configurations
00149 
00150         USBTRACE2("NC:", num_of_conf);
00151 
00152         // Check if attached device is a Xbox One controller and fill endpoint data structure
00153         for(uint8_t i = 0; i < num_of_conf; i++) {
00154                 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
00155                 rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
00156                 if(rcode) // Check error code
00157                         goto FailGetConfDescr;
00158                 if(bNumEP >= XBOX_ONE_MAX_ENDPOINTS) // All endpoints extracted
00159                         break;
00160         }
00161 
00162         if(bNumEP < XBOX_ONE_MAX_ENDPOINTS)
00163                 goto FailUnknownDevice;
00164 
00165         rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
00166         if(rcode)
00167                 goto FailSetDevTblEntry;
00168 
00169         wait_ms(200); // Give time for address change
00170 
00171         rcode = pUsb->setConf(bAddress, epInfo[ XBOX_ONE_CONTROL_PIPE ].epAddr, bConfNum);
00172         if(rcode)
00173                 goto FailSetConfDescr;
00174 
00175 #ifdef DEBUG_USB_HOST
00176         Notify(PSTR("\r\nXbox One Controller Connected\r\n"), 0x80);
00177 #endif
00178 
00179         wait_ms(200); // let things settle
00180 
00181         // Initialize the controller for input
00182         cmdCounter = 0; // Reset the counter used when sending out the commands
00183         uint8_t writeBuf[5];
00184         writeBuf[0] = 0x05;
00185         writeBuf[1] = 0x20;
00186         // Byte 2 is set in "XboxCommand"
00187         writeBuf[3] = 0x01;
00188         writeBuf[4] = 0x00;
00189         rcode = XboxCommand(writeBuf, 5);
00190         if (rcode)
00191                 goto Fail;
00192 
00193         onInit();
00194         XboxOneConnected = true;
00195         bPollEnable = true;
00196         return 0; // Successful configuration
00197 
00198         /* Diagnostic messages */
00199 FailGetDevDescr:
00200 #ifdef DEBUG_USB_HOST
00201         NotifyFailGetDevDescr();
00202         goto Fail;
00203 #endif
00204 
00205 FailSetDevTblEntry:
00206 #ifdef DEBUG_USB_HOST
00207         NotifyFailSetDevTblEntry();
00208         goto Fail;
00209 #endif
00210 
00211 FailGetConfDescr:
00212 #ifdef DEBUG_USB_HOST
00213         NotifyFailGetConfDescr();
00214         goto Fail;
00215 #endif
00216 
00217 FailSetConfDescr:
00218 #ifdef DEBUG_USB_HOST
00219         NotifyFailSetConfDescr();
00220 #endif
00221         goto Fail;
00222 
00223 FailUnknownDevice:
00224 #ifdef DEBUG_USB_HOST
00225         NotifyFailUnknownDevice(VID, PID);
00226 #endif
00227         rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
00228 
00229 Fail:
00230 #ifdef DEBUG_USB_HOST
00231         Notify(PSTR("\r\nXbox One Init Failed, error code: "), 0x80);
00232         NotifyFail(rcode);
00233 #endif
00234         Release();
00235         return rcode;
00236 }
00237 
00238 /* Extracts endpoint information from config descriptor */
00239 void XBOXONE::EndpointXtract(uint8_t conf,
00240         uint8_t iface __attribute__((unused)),
00241         uint8_t alt __attribute__((unused)),
00242         uint8_t proto __attribute__((unused)),
00243         const USB_ENDPOINT_DESCRIPTOR *pep)
00244 {
00245         
00246     bConfNum = conf;
00247         uint8_t index;
00248 
00249         if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT) { // Interrupt endpoint
00250                 index = (pep->bEndpointAddress & 0x80) == 0x80 ? XBOX_ONE_INPUT_PIPE : XBOX_ONE_OUTPUT_PIPE; // Set the endpoint index
00251         } else
00252                 return;
00253 
00254         // Fill the rest of endpoint data structure
00255         epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
00256         epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
00257 #ifdef EXTRADEBUG
00258         PrintEndpointDescriptor(pep);
00259 #endif
00260         if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints
00261                 pollInterval = pep->bInterval;
00262         bNumEP++;
00263 }
00264 
00265 void XBOXONE::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr
00266     __attribute__((unused)))
00267 {
00268 #ifdef EXTRADEBUG
00269         Notify(PSTR("\r\nEndpoint descriptor:"), 0x80);
00270         Notify(PSTR("\r\nLength:\t\t"), 0x80);
00271         D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);
00272         Notify(PSTR("\r\nType:\t\t"), 0x80);
00273         D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);
00274         Notify(PSTR("\r\nAddress:\t"), 0x80);
00275         D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);
00276         Notify(PSTR("\r\nAttributes:\t"), 0x80);
00277         D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);
00278         Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
00279         D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);
00280         Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
00281         D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);
00282 #endif
00283 }
00284 
00285 /* Performs a cleanup after failed Init() attempt */
00286 uint8_t XBOXONE::Release() {
00287         XboxOneConnected = false;
00288         pUsb->GetAddressPool().FreeAddress(bAddress);
00289         bAddress = 0; // Clear device address
00290         bNumEP = 1; // Must have to be reset to 1
00291         qNextPollTime = 0; // Reset next poll time
00292         pollInterval = 0;
00293         bPollEnable = false;
00294 #ifdef DEBUG_USB_HOST
00295         Notify(PSTR("\r\nXbox One Controller Disconnected\r\n"), 0x80);
00296 #endif
00297         return 0;
00298 }
00299 
00300 uint8_t XBOXONE::Poll() {
00301         uint8_t rcode = 0;
00302 
00303         if(!bPollEnable)
00304                 return 0;
00305 
00306         if((int32_t)((uint32_t)millis() - qNextPollTime) >= 0L) { // Do not poll if shorter than polling interval
00307                 qNextPollTime = (uint32_t)millis() + pollInterval; // Set new poll time
00308                 uint16_t length =  (uint16_t)epInfo[ XBOX_ONE_INPUT_PIPE ].maxPktSize; // Read the maximum packet size from the endpoint
00309                 uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ XBOX_ONE_INPUT_PIPE ].epAddr, &length, readBuf, pollInterval);
00310                 if(!rcode) {
00311                         readReport();
00312 #ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send by the Xbox ONE Controller
00313                         for(uint8_t i = 0; i < length; i++) {
00314                                 D_PrintHex<uint8_t > (readBuf[i], 0x80);
00315                                 Notify(PSTR(" "), 0x80);
00316                         }
00317                         Notify(PSTR("\r\n"), 0x80);
00318 #endif
00319                 }
00320 #ifdef DEBUG_USB_HOST
00321                 else if(rcode != hrNAK) { // Not a matter of no update to send
00322                         Notify(PSTR("\r\nXbox One Poll Failed, error code: "), 0x80);
00323                         NotifyFail(rcode);
00324                 }
00325 #endif
00326     }
00327     return rcode;
00328 }
00329 
00330 void XBOXONE::readReport() {
00331         if(readBuf[0] == 0x07) {
00332                 // The XBOX button has a separate message
00333                 if(readBuf[4] == 1)
00334                         ButtonState |= pgm_read_word(&XBOX_BUTTONS[XBOX]);
00335                 else
00336                         ButtonState &= ~pgm_read_word(&XBOX_BUTTONS[XBOX]);
00337 
00338                 if(ButtonState != OldButtonState) {
00339                     ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
00340                     OldButtonState = ButtonState;
00341                 }
00342         }
00343         if(readBuf[0] != 0x20) { // Check if it's the correct report, otherwise return - the controller also sends different status reports
00344 #ifdef EXTRADEBUG
00345                 Notify(PSTR("\r\nXbox Poll: "), 0x80);
00346                 D_PrintHex<uint8_t > (readBuf[0], 0x80); // 0x03 is a heart beat report!
00347 #endif
00348                 return;
00349         }
00350 
00351         uint16_t xbox = ButtonState & pgm_read_word(&XBOX_BUTTONS[XBOX]); // Since the XBOX button is separate, save it and add it back in
00352         // xbox button from before, dpad, abxy, start/back, sync, stick click, shoulder buttons
00353         ButtonState = xbox | (((uint16_t)readBuf[5] & 0xF) << 8) | (readBuf[4] & 0xF0)  | (((uint16_t)readBuf[4] & 0x0C) << 10) | ((readBuf[4] & 0x01) << 3) | (((uint16_t)readBuf[5] & 0xC0) << 8) | ((readBuf[5] & 0x30) >> 4);
00354 
00355         triggerValue[0] = (uint16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
00356         triggerValue[1] = (uint16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
00357 
00358         hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
00359         hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
00360         hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);
00361         hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);
00362 
00363         //Notify(PSTR("\r\nButtonState"), 0x80);
00364         //PrintHex<uint16_t>(ButtonState, 0x80);
00365 
00366         if(ButtonState != OldButtonState) {
00367                 ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
00368                 OldButtonState = ButtonState;
00369         }
00370 
00371         // Handle click detection for triggers
00372         if(triggerValue[0] != 0 && triggerValueOld[0] == 0)
00373                 L2Clicked = true;
00374         triggerValueOld[0] = triggerValue[0];
00375         if(triggerValue[1] != 0 && triggerValueOld[1] == 0)
00376                 R2Clicked = true;
00377         triggerValueOld[1] = triggerValue[1];
00378 }
00379 
00380 uint16_t XBOXONE::getButtonPress(ButtonEnum b) {
00381         if(b == L2) // These are analog buttons
00382                 return triggerValue[0];
00383         else if(b == R2)
00384                 return triggerValue[1];
00385         return (bool)(ButtonState & ((uint16_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b])));
00386 }
00387 
00388 bool XBOXONE::getButtonClick(ButtonEnum b) {
00389         if(b == L2) {
00390                 if(L2Clicked) {
00391                         L2Clicked = false;
00392                         return true;
00393                 }
00394                 return false;
00395         } else if(b == R2) {
00396                 if(R2Clicked) {
00397                         R2Clicked = false;
00398                         return true;
00399                 }
00400                 return false;
00401         }
00402         uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
00403         bool click = (ButtonClickState & button);
00404         ButtonClickState &= ~button; // Clear "click" event
00405         return click;
00406 }
00407 
00408 int16_t XBOXONE::getAnalogHat(AnalogHatEnum a) {
00409         return hatValue[a];
00410 }
00411 
00412 /* Xbox Controller commands */
00413 uint8_t XBOXONE::XboxCommand(uint8_t* data, uint16_t nbytes) {
00414         data[2] = cmdCounter++; // Increment the output command counter
00415         uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_ONE_OUTPUT_PIPE ].epAddr, nbytes, data);
00416 #ifdef DEBUG_USB_HOST
00417         Notify(PSTR("\r\nXboxCommand, Return: "), 0x80);
00418         D_PrintHex<uint8_t > (rcode, 0x80);
00419 #endif
00420         return rcode;
00421 }
00422 
00423 // The Xbox One packets are described at: https://github.com/quantus/xbox-one-controller-protocol
00424 void XBOXONE::onInit() {
00425         // A short buzz to show the controller is active
00426         uint8_t writeBuf[13];
00427 
00428         // Activate rumble
00429         writeBuf[0] = 0x09;
00430         writeBuf[1] = 0x00;
00431         // Byte 2 is set in "XboxCommand"
00432 
00433         // Single rumble effect
00434         writeBuf[3] = 0x09; // Substructure (what substructure rest of this packet has)
00435         writeBuf[4] = 0x00; // Mode
00436         writeBuf[5] = 0x0F; // Rumble mask (what motors are activated) (0000 lT rT L R)
00437         writeBuf[6] = 0x04; // lT force
00438         writeBuf[7] = 0x04; // rT force
00439         writeBuf[8] = 0x20; // L force
00440         writeBuf[9] = 0x20; // R force
00441         writeBuf[10] = 0x80; // Length of pulse
00442         writeBuf[11] = 0x00; // Off period
00443         writeBuf[12] = 0x00; // Repeat count
00444         XboxCommand(writeBuf, 13);
00445 
00446         if(pFuncOnInit)
00447                 pFuncOnInit(); // Call the user function
00448 }
00449 
00450 void XBOXONE::setRumbleOff() {
00451         uint8_t writeBuf[13];
00452 
00453         // Activate rumble
00454         writeBuf[0] = 0x09;
00455         writeBuf[1] = 0x00;
00456         // Byte 2 is set in "XboxCommand"
00457 
00458         // Continuous rumble effect
00459         writeBuf[3] = 0x09; // Substructure (what substructure rest of this packet has)
00460         writeBuf[4] = 0x00; // Mode
00461         writeBuf[5] = 0x0F; // Rumble mask (what motors are activated) (0000 lT rT L R)
00462         writeBuf[6] = 0x00; // lT force
00463         writeBuf[7] = 0x00; // rT force
00464         writeBuf[8] = 0x00; // L force
00465         writeBuf[9] = 0x00; // R force
00466         writeBuf[10] = 0x00; // On period
00467         writeBuf[11] = 0x00; // Off period
00468         writeBuf[12] = 0x00; // Repeat count
00469         XboxCommand(writeBuf, 13);
00470 }
00471 
00472 void XBOXONE::setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint8_t leftMotor, uint8_t rightMotor) {
00473         uint8_t writeBuf[13];
00474 
00475         // Activate rumble
00476         writeBuf[0] = 0x09;
00477         writeBuf[1] = 0x00;
00478         // Byte 2 is set in "XboxCommand"
00479 
00480         // Continuous rumble effect
00481         writeBuf[3] = 0x09; // Substructure (what substructure rest of this packet has)
00482         writeBuf[4] = 0x00; // Mode
00483         writeBuf[5] = 0x0F; // Rumble mask (what motors are activated) (0000 lT rT L R)
00484         writeBuf[6] = leftTrigger; // lT force
00485         writeBuf[7] = rightTrigger; // rT force
00486         writeBuf[8] = leftMotor; // L force
00487         writeBuf[9] = rightMotor; // R force
00488         writeBuf[10] = 0xFF; // On period
00489         writeBuf[11] = 0x00; // Off period
00490         writeBuf[12] = 0xFF; // Repeat count
00491         XboxCommand(writeBuf, 13);
00492 }