Zoltan Hudak / UsbHostMAX3421E

Dependents:   UsbHostMAX3421E_Hello

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers XBOXUSB.cpp Source File

XBOXUSB.cpp

00001 /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
00002 
00003  This software may be distributed and modified under the terms of the GNU
00004  General Public License version 2 (GPL2) as published by the Free Software
00005  Foundation and appearing in the file GPL2.TXT included in the packaging of
00006  this file. Please note that GPL2 Section 2[b] requires that all works based
00007  on this software must also be made publicly available under the terms of
00008  the GPL2 ("Copyleft").
00009 
00010  Contact information
00011  -------------------
00012 
00013  Kristian Lauszus, TKJ Electronics
00014  Web      :  http://www.tkjelectronics.com
00015  e-mail   :  kristianl@tkjelectronics.com
00016  */
00017 
00018 #include "XBOXUSB.h"
00019 // To enable serial debugging see "settings.h"
00020 //#define EXTRADEBUG // Uncomment to get even more debugging data
00021 //#define PRINTREPORT // Uncomment to print the report send by the Xbox 360 Controller
00022 
00023 XBOXUSB::XBOXUSB(Usb *p) :
00024 pUsb(p), // pointer to USB class instance - mandatory
00025 bAddress(0), // device address - mandatory
00026 bPollEnable(false) { // don't start polling before dongle is connected
00027         for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
00028                 epInfo[i].epAddr = 0;
00029                 epInfo[i].maxPktSize = (i) ? 0 : 8;
00030                 epInfo[i].bmSndToggle = 0;
00031                 epInfo[i].bmRcvToggle = 0;
00032                 epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
00033         }
00034 
00035         if(pUsb) // register in USB subsystem
00036                 pUsb->RegisterDeviceClass(this); //set devConfig[] entry
00037 }
00038 
00039 uint8_t XBOXUSB::Init(uint8_t parent, uint8_t port, bool lowspeed) {
00040         uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
00041         USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
00042         uint8_t rcode;
00043         UsbDevice *p = NULL;
00044         EpInfo *oldep_ptr = NULL;
00045         uint16_t PID;
00046         uint16_t VID;
00047 
00048         // get memory address of USB device address pool
00049         AddressPool &addrPool = pUsb->GetAddressPool();
00050 #ifdef EXTRADEBUG
00051         Notify(PSTR("\r\nXBOXUSB Init"), 0x80);
00052 #endif
00053         // check if address has already been assigned to an instance
00054         if(bAddress) {
00055 #ifdef DEBUG_USB_HOST
00056                 Notify(PSTR("\r\nAddress in use"), 0x80);
00057 #endif
00058                 return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
00059         }
00060 
00061         // Get pointer to pseudo device with address 0 assigned
00062         p = addrPool.GetUsbDevicePtr(0);
00063 
00064         if(!p) {
00065 #ifdef DEBUG_USB_HOST
00066                 Notify(PSTR("\r\nAddress not found"), 0x80);
00067 #endif
00068                 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
00069         }
00070 
00071         if(!p->epinfo) {
00072 #ifdef DEBUG_USB_HOST
00073                 Notify(PSTR("\r\nepinfo is null"), 0x80);
00074 #endif
00075                 return USB_ERROR_EPINFO_IS_NULL;
00076         }
00077 
00078         // Save old pointer to EP_RECORD of address 0
00079         oldep_ptr = p->epinfo;
00080 
00081         // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
00082         p->epinfo = epInfo;
00083 
00084         p->lowspeed = lowspeed;
00085 
00086         // Get device descriptor
00087         rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
00088         // Restore p->epinfo
00089         p->epinfo = oldep_ptr;
00090 
00091         if(rcode)
00092                 goto FailGetDevDescr;
00093 
00094         VID = udd->idVendor;
00095         PID = udd->idProduct;
00096 
00097         if(VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID && VID != GAMESTOP_VID) // Check VID
00098                 goto FailUnknownDevice;
00099         if(PID == XBOX_WIRELESS_PID) {
00100 #ifdef DEBUG_USB_HOST
00101                 Notify(PSTR("\r\nYou have plugged in a wireless Xbox 360 controller - it doesn't support USB communication"), 0x80);
00102 #endif
00103                 goto FailUnknownDevice;
00104         } else if(PID == XBOX_WIRELESS_RECEIVER_PID || PID == XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID) {
00105 #ifdef DEBUG_USB_HOST
00106                 Notify(PSTR("\r\nThis library only supports Xbox 360 controllers via USB"), 0x80);
00107 #endif
00108                 goto FailUnknownDevice;
00109         } else if(PID != XBOX_WIRED_PID && PID != MADCATZ_WIRED_PID && PID != GAMESTOP_WIRED_PID && PID != AFTERGLOW_WIRED_PID && PID != JOYTECH_WIRED_PID) // Check PID
00110                 goto FailUnknownDevice;
00111 
00112         // Allocate new address according to device class
00113         bAddress = addrPool.AllocAddress(parent, false, port);
00114 
00115         if(!bAddress)
00116                 return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
00117 
00118         // Extract Max Packet Size from device descriptor
00119         epInfo[0].maxPktSize = udd->bMaxPacketSize0;
00120 
00121         // Assign new address to the device
00122         rcode = pUsb->setAddr(0, 0, bAddress);
00123         if(rcode) {
00124                 p->lowspeed = false;
00125                 addrPool.FreeAddress(bAddress);
00126                 bAddress = 0;
00127 #ifdef DEBUG_USB_HOST
00128                 Notify(PSTR("\r\nsetAddr: "), 0x80);
00129                 D_PrintHex<uint8_t > (rcode, 0x80);
00130 #endif
00131                 return rcode;
00132         }
00133 #ifdef EXTRADEBUG
00134         Notify(PSTR("\r\nAddr: "), 0x80);
00135         D_PrintHex<uint8_t > (bAddress, 0x80);
00136 #endif
00137         //wait_ms(300); // Spec says you should wait at least 200ms
00138 
00139         p->lowspeed = false;
00140 
00141         //get pointer to assigned address record
00142         p = addrPool.GetUsbDevicePtr(bAddress);
00143         if(!p)
00144                 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
00145 
00146         p->lowspeed = lowspeed;
00147 
00148         // Assign epInfo to epinfo pointer - only EP0 is known
00149         rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
00150         if(rcode)
00151                 goto FailSetDevTblEntry;
00152 
00153         /* The application will work in reduced host mode, so we can save program and data
00154            memory space. After verifying the VID we will use known values for the
00155            configuration values for device, interface, endpoints and HID for the XBOX360 Controllers */
00156 
00157         /* Initialize data structures for endpoints of device */
00158         epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX 360 report endpoint
00159         epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
00160         epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
00161         epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
00162         epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0;
00163         epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0;
00164         epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX 360 output endpoint
00165         epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
00166         epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
00167         epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
00168         epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0;
00169         epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0;
00170 
00171         rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
00172         if(rcode)
00173                 goto FailSetDevTblEntry;
00174 
00175         wait_ms(200); // Give time for address change
00176 
00177         rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
00178         if(rcode)
00179                 goto FailSetConfDescr;
00180 
00181 #ifdef DEBUG_USB_HOST
00182         Notify(PSTR("\r\nXbox 360 Controller Connected\r\n"), 0x80);
00183 #endif
00184         onInit();
00185         Xbox360Connected = true;
00186         bPollEnable = true;
00187         return 0; // Successful configuration
00188 
00189         /* Diagnostic messages */
00190 FailGetDevDescr:
00191 #ifdef DEBUG_USB_HOST
00192         NotifyFailGetDevDescr();
00193         goto Fail;
00194 #endif
00195 
00196 FailSetDevTblEntry:
00197 #ifdef DEBUG_USB_HOST
00198         NotifyFailSetDevTblEntry();
00199         goto Fail;
00200 #endif
00201 
00202 FailSetConfDescr:
00203 #ifdef DEBUG_USB_HOST
00204         NotifyFailSetConfDescr();
00205 #endif
00206         goto Fail;
00207 
00208 FailUnknownDevice:
00209 #ifdef DEBUG_USB_HOST
00210         NotifyFailUnknownDevice(VID, PID);
00211 #endif
00212         rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
00213 
00214 Fail:
00215 #ifdef DEBUG_USB_HOST
00216         Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
00217         NotifyFail(rcode);
00218 #endif
00219         Release();
00220         return rcode;
00221 }
00222 
00223 /* Performs a cleanup after failed Init() attempt */
00224 uint8_t XBOXUSB::Release() {
00225         Xbox360Connected = false;
00226         pUsb->GetAddressPool().FreeAddress(bAddress);
00227         bAddress = 0;
00228         bPollEnable = false;
00229         return 0;
00230 }
00231 
00232 uint8_t XBOXUSB::Poll() {
00233         if(!bPollEnable)
00234                 return 0;
00235         uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
00236         pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
00237         readReport();
00238 #ifdef PRINTREPORT
00239         printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
00240 #endif
00241         return 0;
00242 }
00243 
00244 void XBOXUSB::readReport() {
00245         if(readBuf == NULL)
00246                 return;
00247         if(readBuf[0] != 0x00 || readBuf[1] != 0x14) { // Check if it's the correct report - the controller also sends different status reports
00248                 return;
00249         }
00250 
00251         ButtonState = (uint32_t)(readBuf[5] | ((uint16_t)readBuf[4] << 8) | ((uint32_t)readBuf[3] << 16) | ((uint32_t)readBuf[2] << 24));
00252 
00253         hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
00254         hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
00255         hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
00256         hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
00257 
00258         //Notify(PSTR("\r\nButtonState"), 0x80);
00259         //PrintHex<uint32_t>(ButtonState, 0x80);
00260 
00261         if(ButtonState != OldButtonState) {
00262                 ButtonClickState = (ButtonState >> 16) & ((~OldButtonState) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
00263                 if(((uint8_t)OldButtonState) == 0 && ((uint8_t)ButtonState) != 0) // The L2 and R2 buttons are special as they are analog buttons
00264                         R2Clicked = true;
00265                 if((uint8_t)(OldButtonState >> 8) == 0 && (uint8_t)(ButtonState >> 8) != 0)
00266                         L2Clicked = true;
00267                 OldButtonState = ButtonState;
00268         }
00269 }
00270 
00271 void XBOXUSB::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
00272 #ifdef PRINTREPORT
00273         if(readBuf == NULL)
00274                 return;
00275         for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {
00276                 D_PrintHex<uint8_t > (readBuf[i], 0x80);
00277                 Notify(PSTR(" "), 0x80);
00278         }
00279         Notify(PSTR("\r\n"), 0x80);
00280 #endif
00281 }
00282 
00283 uint8_t XBOXUSB::getButtonPress(ButtonEnum b) {
00284         if(b == L2) // These are analog buttons
00285                 return (uint8_t)(ButtonState >> 8);
00286         else if(b == R2)
00287                 return (uint8_t)ButtonState;
00288         return (bool)(ButtonState & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16));
00289 }
00290 
00291 bool XBOXUSB::getButtonClick(ButtonEnum b) {
00292         if(b == L2) {
00293                 if(L2Clicked) {
00294                         L2Clicked = false;
00295                         return true;
00296                 }
00297                 return false;
00298         } else if(b == R2) {
00299                 if(R2Clicked) {
00300                         R2Clicked = false;
00301                         return true;
00302                 }
00303                 return false;
00304         }
00305         uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
00306         bool click = (ButtonClickState & button);
00307         ButtonClickState &= ~button; // clear "click" event
00308         return click;
00309 }
00310 
00311 int16_t XBOXUSB::getAnalogHat(AnalogHatEnum a) {
00312         return hatValue[a];
00313 }
00314 
00315 /* Xbox Controller commands */
00316 void XBOXUSB::XboxCommand(uint8_t* data, uint16_t nbytes) {
00317         //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x00), Report Type (Output 0x02), interface (0x00), datalength, datalength, data)
00318         pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
00319 }
00320 
00321 void XBOXUSB::setLedRaw(uint8_t value) {
00322         writeBuf[0] = 0x01;
00323         writeBuf[1] = 0x03;
00324         writeBuf[2] = value;
00325 
00326         XboxCommand(writeBuf, 3);
00327 }
00328 
00329 void XBOXUSB::setLedOn(LEDEnum led) {
00330         if(led == OFF)
00331                 setLedRaw(0);
00332         else if(led != ALL) // All LEDs can't be on a the same time
00333                 setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]) + 4);
00334 }
00335 
00336 void XBOXUSB::setLedBlink(LEDEnum led) {
00337         setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]));
00338 }
00339 
00340 void XBOXUSB::setLedMode(LEDModeEnum ledMode) { // This function is used to do some special LED stuff the controller supports
00341         setLedRaw((uint8_t)ledMode);
00342 }
00343 
00344 void XBOXUSB::setRumbleOn(uint8_t lValue, uint8_t rValue) {
00345         writeBuf[0] = 0x00;
00346         writeBuf[1] = 0x08;
00347         writeBuf[2] = 0x00;
00348         writeBuf[3] = lValue; // big weight
00349         writeBuf[4] = rValue; // small weight
00350         writeBuf[5] = 0x00;
00351         writeBuf[6] = 0x00;
00352         writeBuf[7] = 0x00;
00353 
00354         XboxCommand(writeBuf, 8);
00355 }
00356 
00357 void XBOXUSB::onInit() {
00358         if(pFuncOnInit)
00359                 pFuncOnInit(); // Call the user function
00360         else
00361                 setLedOn(static_cast<LEDEnum>(LED1));
00362 }