Zoltan Hudak / UsbHostMAX3421E

Dependents:   UsbHostMAX3421E_Hello

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers XBOXOLD.cpp Source File

XBOXOLD.cpp

00001 /* Copyright (C) 2013 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 "XBOXOLD.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 controller
00022 
00023 /** Buttons on the controllers */
00024 const uint8_t XBOXOLD_BUTTONS[] PROGMEM = {
00025         0x01, // UP
00026         0x08, // RIGHT
00027         0x02, // DOWN
00028         0x04, // LEFT
00029 
00030         0x20, // BACK
00031         0x10, // START
00032         0x40, // L3
00033         0x80, // R3
00034 
00035         // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
00036         4, // BLACK
00037         5, // WHTIE
00038         6, // L1
00039         7, // R1
00040 
00041         1, // B
00042         0, // A
00043         2, // X
00044         3, // Y
00045 };
00046 
00047 XBOXOLD::XBOXOLD(Usb *p) :
00048 pUsb(p), // pointer to USB class instance - mandatory
00049 bAddress(0), // device address - mandatory
00050 bPollEnable(false) { // don't start polling before dongle is connected
00051         for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
00052                 epInfo[i].epAddr = 0;
00053                 epInfo[i].maxPktSize = (i) ? 0 : 8;
00054                 epInfo[i].bmSndToggle = 0;
00055                 epInfo[i].bmRcvToggle = 0;
00056                 epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
00057         }
00058 
00059         if(pUsb) // register in USB subsystem
00060                 pUsb->RegisterDeviceClass(this); //set devConfig[] entry
00061 }
00062 
00063 uint8_t XBOXOLD::Init(uint8_t parent, uint8_t port, bool lowspeed) {
00064         uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
00065         USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
00066         uint8_t rcode;
00067         UsbDevice *p = NULL;
00068         EpInfo *oldep_ptr = NULL;
00069         uint16_t PID;
00070         uint16_t VID;
00071 
00072         // get memory address of USB device address pool
00073         AddressPool &addrPool = pUsb->GetAddressPool();
00074 #ifdef EXTRADEBUG
00075         Notify(PSTR("\r\nXBOXUSB Init"), 0x80);
00076 #endif
00077         // check if address has already been assigned to an instance
00078         if(bAddress) {
00079 #ifdef DEBUG_USB_HOST
00080                 Notify(PSTR("\r\nAddress in use"), 0x80);
00081 #endif
00082                 return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
00083         }
00084 
00085         // Get pointer to pseudo device with address 0 assigned
00086         p = addrPool.GetUsbDevicePtr(0);
00087 
00088         if(!p) {
00089 #ifdef DEBUG_USB_HOST
00090                 Notify(PSTR("\r\nAddress not found"), 0x80);
00091 #endif
00092                 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
00093         }
00094 
00095         if(!p->epinfo) {
00096 #ifdef DEBUG_USB_HOST
00097                 Notify(PSTR("\r\nepinfo is null"), 0x80);
00098 #endif
00099                 return USB_ERROR_EPINFO_IS_NULL;
00100         }
00101 
00102         // Save old pointer to EP_RECORD of address 0
00103         oldep_ptr = p->epinfo;
00104 
00105         // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
00106         p->epinfo = epInfo;
00107 
00108         p->lowspeed = lowspeed;
00109 
00110         // Get device descriptor
00111         rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
00112         // Restore p->epinfo
00113         p->epinfo = oldep_ptr;
00114 
00115         if(rcode)
00116                 goto FailGetDevDescr;
00117 
00118         VID = udd->idVendor;
00119         PID = udd->idProduct;
00120 
00121         if((VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID) || (PID != XBOX_OLD_PID1 && PID != XBOX_OLD_PID2 && PID != XBOX_OLD_PID3 && PID != XBOX_OLD_PID4)) // Check if VID and PID match
00122                 goto FailUnknownDevice;
00123 
00124         // Allocate new address according to device class
00125         bAddress = addrPool.AllocAddress(parent, false, port);
00126 
00127         if(!bAddress)
00128                 return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
00129 
00130         // Extract Max Packet Size from device descriptor
00131         epInfo[0].maxPktSize = udd->bMaxPacketSize0;
00132 
00133         // Assign new address to the device
00134         rcode = pUsb->setAddr(0, 0, bAddress);
00135         if(rcode) {
00136                 p->lowspeed = false;
00137                 addrPool.FreeAddress(bAddress);
00138                 bAddress = 0;
00139 #ifdef DEBUG_USB_HOST
00140                 Notify(PSTR("\r\nsetAddr: "), 0x80);
00141                 D_PrintHex<uint8_t > (rcode, 0x80);
00142 #endif
00143                 return rcode;
00144         }
00145 #ifdef EXTRADEBUG
00146         Notify(PSTR("\r\nAddr: "), 0x80);
00147         D_PrintHex<uint8_t > (bAddress, 0x80);
00148 #endif
00149         //wait_ms(300); // Spec says you should wait at least 200ms
00150 
00151         p->lowspeed = false;
00152 
00153         //get pointer to assigned address record
00154         p = addrPool.GetUsbDevicePtr(bAddress);
00155         if(!p)
00156                 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
00157 
00158         p->lowspeed = lowspeed;
00159 
00160         // Assign epInfo to epinfo pointer - only EP0 is known
00161         rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
00162         if(rcode)
00163                 goto FailSetDevTblEntry;
00164 
00165         /* The application will work in reduced host mode, so we can save program and data
00166            memory space. After verifying the VID we will use known values for the
00167            configuration values for device, interface, endpoints and HID for the XBOX controllers */
00168 
00169         /* Initialize data structures for endpoints of device */
00170         epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX report endpoint
00171         epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
00172         epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
00173         epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
00174         epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0;
00175         epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0;
00176         epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX output endpoint
00177         epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
00178         epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
00179         epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
00180         epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0;
00181         epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0;
00182 
00183         rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
00184         if(rcode)
00185                 goto FailSetDevTblEntry;
00186 
00187         wait_ms(200); // Give time for address change
00188 
00189         rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
00190         if(rcode)
00191                 goto FailSetConfDescr;
00192 
00193 #ifdef DEBUG_USB_HOST
00194         Notify(PSTR("\r\nXbox Controller Connected\r\n"), 0x80);
00195 #endif
00196         if(pFuncOnInit)
00197                 pFuncOnInit(); // Call the user function
00198         XboxConnected = true;
00199         bPollEnable = true;
00200         return 0; // Successful configuration
00201 
00202         /* Diagnostic messages */
00203 FailGetDevDescr:
00204 #ifdef DEBUG_USB_HOST
00205         NotifyFailGetDevDescr();
00206         goto Fail;
00207 #endif
00208 
00209 FailSetDevTblEntry:
00210 #ifdef DEBUG_USB_HOST
00211         NotifyFailSetDevTblEntry();
00212         goto Fail;
00213 #endif
00214 
00215 FailSetConfDescr:
00216 #ifdef DEBUG_USB_HOST
00217         NotifyFailSetConfDescr();
00218 #endif
00219         goto Fail;
00220 
00221 FailUnknownDevice:
00222 #ifdef DEBUG_USB_HOST
00223         NotifyFailUnknownDevice(VID, PID);
00224 #endif
00225         rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
00226 
00227 Fail:
00228 #ifdef DEBUG_USB_HOST
00229         Notify(PSTR("\r\nXbox Init Failed, error code: "), 0x80);
00230         NotifyFail(rcode);
00231 #endif
00232         Release();
00233         return rcode;
00234 }
00235 
00236 /* Performs a cleanup after failed Init() attempt */
00237 uint8_t XBOXOLD::Release() {
00238         XboxConnected = false;
00239         pUsb->GetAddressPool().FreeAddress(bAddress);
00240         bAddress = 0;
00241         bPollEnable = false;
00242         return 0;
00243 }
00244 
00245 uint8_t XBOXOLD::Poll() {
00246         if(!bPollEnable)
00247                 return 0;
00248         uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
00249         pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
00250         readReport();
00251 #ifdef PRINTREPORT
00252         printReport(BUFFER_SIZE); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
00253 #endif
00254         return 0;
00255 }
00256 
00257 void XBOXOLD::readReport() {
00258         ButtonState = readBuf[2];
00259 
00260         for(uint8_t i = 0; i < sizeof (buttonValues); i++)
00261                 buttonValues[i] = readBuf[i + 4]; // A, B, X, Y, BLACK, WHITE, L1, and R1
00262 
00263         hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[12] << 8) | readBuf[13]);
00264         hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[14] << 8) | readBuf[15]);
00265         hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[16] << 8) | readBuf[17]);
00266         hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[18] << 8) | readBuf[19]);
00267 
00268         //Notify(PSTR("\r\nButtonState"), 0x80);
00269         //PrintHex<uint8_t>(ButtonState, 0x80);
00270 
00271         if(ButtonState != OldButtonState || memcmp(buttonValues, oldButtonValues, sizeof (buttonValues)) != 0) {
00272                 ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
00273                 OldButtonState = ButtonState;
00274 
00275                 for(uint8_t i = 0; i < sizeof (buttonValues); i++) {
00276                         if(oldButtonValues[i] == 0 && buttonValues[i] != 0)
00277                                 buttonClicked[i] = true; // Update A, B, X, Y, BLACK, WHITE, L1, and R1 click state
00278                         oldButtonValues[i] = buttonValues[i];
00279                 }
00280         }
00281 }
00282 
00283 void XBOXOLD::printReport(uint16_t length __attribute__((unused))) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
00284 #ifdef PRINTREPORT
00285         if(readBuf == NULL)
00286                 return;
00287         for(uint8_t i = 0; i < length; i++) {
00288                 D_PrintHex<uint8_t > (readBuf[i], 0x80);
00289                 Notify(PSTR(" "), 0x80);
00290         }
00291         Notify(PSTR("\r\n"), 0x80);
00292 #endif
00293 }
00294 
00295 uint8_t XBOXOLD::getButtonPress(ButtonEnum b) {
00296         uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]);
00297         if(b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
00298                 return buttonValues[button]; // Analog buttons
00299         return (ButtonState & button); // Digital buttons
00300 }
00301 
00302 bool XBOXOLD::getButtonClick(ButtonEnum b) {
00303         uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]);
00304         if(b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) { // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
00305                 if(buttonClicked[button]) {
00306                         buttonClicked[button] = false;
00307                         return true;
00308                 }
00309                 return false;
00310         }
00311 
00312         bool click = (ButtonClickState & button);
00313         ButtonClickState &= ~button; // clear "click" event
00314         return click;
00315 }
00316 
00317 int16_t XBOXOLD::getAnalogHat(AnalogHatEnum a) {
00318         return hatValue[a];
00319 }
00320 
00321 /* Xbox Controller commands */
00322 void XBOXOLD::XboxCommand(uint8_t* data, uint16_t nbytes) {
00323         //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)
00324         pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
00325 }
00326 
00327 void XBOXOLD::setRumbleOn(uint8_t lValue, uint8_t rValue) {
00328         uint8_t writeBuf[6];
00329 
00330         writeBuf[0] = 0x00;
00331         writeBuf[1] = 0x06;
00332         writeBuf[2] = 0x00;
00333         writeBuf[3] = rValue; // small weight
00334         writeBuf[4] = 0x00;
00335         writeBuf[5] = lValue; // big weight
00336 
00337         XboxCommand(writeBuf, 6);
00338 }