1 /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
3 This software may be distributed and modified under the terms of the GNU
4 General Public License version 2 (GPL2) as published by the Free Software
5 Foundation and appearing in the file GPL2.TXT included in the packaging of
6 this file. Please note that GPL2 Section 2[b] requires that all works based
7 on this software must also be made publicly available under the terms of
13 Kristian Lauszus, TKJ Electronics
14 Web : http://www.tkjelectronics.com
15 e-mail : kristianl@tkjelectronics.com
17 getBatteryLevel and checkStatus functions made by timstamp.co.uk found using BusHound from Perisoft.net
21 // To enable serial debugging see "settings.h"
22 //#define EXTRADEBUG // Uncomment to get even more debugging data
23 //#define PRINTREPORT // Uncomment to print the report send by the Xbox 360 Controller
25 XBOXRECV::XBOXRECV(USB *p) :
26 pUsb(p), // pointer to USB class instance - mandatory
27 bAddress(0), // device address - mandatory
28 bPollEnable(false) { // don't start polling before dongle is connected
29 for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
31 epInfo[i].maxPktSize = (i) ? 0 : 8;
32 epInfo[i].epAttribs = 0;
33 epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
36 if(pUsb) // register in USB subsystem
37 pUsb->RegisterDeviceClass(this); //set devConfig[] entry
40 uint8_t XBOXRECV::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {
41 const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
42 uint8_t buf[constBufSize];
43 USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
46 EpInfo *oldep_ptr = NULL;
49 AddressPool &addrPool = pUsb->GetAddressPool(); // Get memory address of USB device address pool
51 Notify(PSTR("\r\nXBOXRECV Init"), 0x80);
54 if(bAddress) { // Check if address has already been assigned to an instance
56 Notify(PSTR("\r\nAddress in use"), 0x80);
58 return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
61 p = addrPool.GetUsbDevicePtr(0); // Get pointer to pseudo device with address 0 assigned
65 Notify(PSTR("\r\nAddress not found"), 0x80);
67 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
72 Notify(PSTR("\r\nepinfo is null"), 0x80);
74 return USB_ERROR_EPINFO_IS_NULL;
77 oldep_ptr = p->epinfo; // Save old pointer to EP_RECORD of address 0
78 p->epinfo = epInfo; // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
79 p->lowspeed = lowspeed;
81 rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
83 p->epinfo = oldep_ptr; // Restore p->epinfo
91 if((VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID) || (PID != XBOX_WIRELESS_RECEIVER_PID && PID != XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID)) { // Check if it's a Xbox receiver using the Vendor ID and Product ID
93 Notify(PSTR("\r\nYou'll need a wireless receiver for this libary to work"), 0x80);
95 goto FailUnknownDevice;
98 bAddress = addrPool.AllocAddress(parent, false, port); // Allocate new address according to device class
101 #ifdef DEBUG_USB_HOST
102 Notify(PSTR("\r\nOut of address space"), 0x80);
104 return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
107 epInfo[0].maxPktSize = udd->bMaxPacketSize0; // Extract Max Packet Size from device descriptor
109 delay(20); // Wait a little before resetting device
111 return USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET;
113 /* Diagnostic messages */
115 #ifdef DEBUG_USB_HOST
116 NotifyFailGetDevDescr(rcode);
119 rcode = USB_ERROR_FailGetDevDescr;
123 #ifdef DEBUG_USB_HOST
124 NotifyFailUnknownDevice(VID, PID);
126 rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
129 #ifdef DEBUG_USB_HOST
130 Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
137 uint8_t XBOXRECV::Init(uint8_t parent, uint8_t port, bool lowspeed) {
140 AddressPool &addrPool = pUsb->GetAddressPool();
142 Notify(PSTR("\r\nBTD Init"), 0x80);
144 UsbDevice *p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record
147 #ifdef DEBUG_USB_HOST
148 Notify(PSTR("\r\nAddress not found"), 0x80);
150 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
153 delay(300); // Assign new address to the device
155 rcode = pUsb->setAddr(0, 0, bAddress); // Assign new address to the device
157 #ifdef DEBUG_USB_HOST
158 Notify(PSTR("\r\nsetAddr: "), 0x80);
159 D_PrintHex<uint8_t > (rcode, 0x80);
165 Notify(PSTR("\r\nAddr: "), 0x80);
166 D_PrintHex<uint8_t > (bAddress, 0x80);
171 p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record
173 #ifdef DEBUG_USB_HOST
174 Notify(PSTR("\r\nAddress not found"), 0x80);
176 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
179 p->lowspeed = lowspeed;
181 rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); // Assign epInfo to epinfo pointer - only EP0 is known
183 goto FailSetDevTblEntry;
185 /* The application will work in reduced host mode, so we can save program and data
186 memory space. After verifying the VID we will use known values for the
187 configuration values for device, interface, endpoints and HID for the XBOX360 Wireless receiver */
189 /* Initialize data structures for endpoints of device */
190 epInfo[ XBOX_INPUT_PIPE_1 ].epAddr = 0x01; // XBOX 360 report endpoint - poll interval 1ms
191 epInfo[ XBOX_INPUT_PIPE_1 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
192 epInfo[ XBOX_INPUT_PIPE_1 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
193 epInfo[ XBOX_INPUT_PIPE_1 ].maxPktSize = EP_MAXPKTSIZE;
194 epInfo[ XBOX_INPUT_PIPE_1 ].bmSndToggle = 0;
195 epInfo[ XBOX_INPUT_PIPE_1 ].bmRcvToggle = 0;
196 epInfo[ XBOX_OUTPUT_PIPE_1 ].epAddr = 0x01; // XBOX 360 output endpoint - poll interval 8ms
197 epInfo[ XBOX_OUTPUT_PIPE_1 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
198 epInfo[ XBOX_OUTPUT_PIPE_1 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
199 epInfo[ XBOX_OUTPUT_PIPE_1 ].maxPktSize = EP_MAXPKTSIZE;
200 epInfo[ XBOX_OUTPUT_PIPE_1 ].bmSndToggle = 0;
201 epInfo[ XBOX_OUTPUT_PIPE_1 ].bmRcvToggle = 0;
203 epInfo[ XBOX_INPUT_PIPE_2 ].epAddr = 0x03; // XBOX 360 report endpoint - poll interval 1ms
204 epInfo[ XBOX_INPUT_PIPE_2 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
205 epInfo[ XBOX_INPUT_PIPE_2 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
206 epInfo[ XBOX_INPUT_PIPE_2 ].maxPktSize = EP_MAXPKTSIZE;
207 epInfo[ XBOX_INPUT_PIPE_2 ].bmSndToggle = 0;
208 epInfo[ XBOX_INPUT_PIPE_2 ].bmRcvToggle = 0;
209 epInfo[ XBOX_OUTPUT_PIPE_2 ].epAddr = 0x03; // XBOX 360 output endpoint - poll interval 8ms
210 epInfo[ XBOX_OUTPUT_PIPE_2 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
211 epInfo[ XBOX_OUTPUT_PIPE_2 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
212 epInfo[ XBOX_OUTPUT_PIPE_2 ].maxPktSize = EP_MAXPKTSIZE;
213 epInfo[ XBOX_OUTPUT_PIPE_2 ].bmSndToggle = 0;
214 epInfo[ XBOX_OUTPUT_PIPE_2 ].bmRcvToggle = 0;
216 epInfo[ XBOX_INPUT_PIPE_3 ].epAddr = 0x05; // XBOX 360 report endpoint - poll interval 1ms
217 epInfo[ XBOX_INPUT_PIPE_3 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
218 epInfo[ XBOX_INPUT_PIPE_3 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
219 epInfo[ XBOX_INPUT_PIPE_3 ].maxPktSize = EP_MAXPKTSIZE;
220 epInfo[ XBOX_INPUT_PIPE_3 ].bmSndToggle = 0;
221 epInfo[ XBOX_INPUT_PIPE_3 ].bmRcvToggle = 0;
222 epInfo[ XBOX_OUTPUT_PIPE_3 ].epAddr = 0x05; // XBOX 360 output endpoint - poll interval 8ms
223 epInfo[ XBOX_OUTPUT_PIPE_3 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
224 epInfo[ XBOX_OUTPUT_PIPE_3 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
225 epInfo[ XBOX_OUTPUT_PIPE_3 ].maxPktSize = EP_MAXPKTSIZE;
226 epInfo[ XBOX_OUTPUT_PIPE_3 ].bmSndToggle = 0;
227 epInfo[ XBOX_OUTPUT_PIPE_3 ].bmRcvToggle = 0;
229 epInfo[ XBOX_INPUT_PIPE_4 ].epAddr = 0x07; // XBOX 360 report endpoint - poll interval 1ms
230 epInfo[ XBOX_INPUT_PIPE_4 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
231 epInfo[ XBOX_INPUT_PIPE_4 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
232 epInfo[ XBOX_INPUT_PIPE_4 ].maxPktSize = EP_MAXPKTSIZE;
233 epInfo[ XBOX_INPUT_PIPE_4 ].bmSndToggle = 0;
234 epInfo[ XBOX_INPUT_PIPE_4 ].bmRcvToggle = 0;
235 epInfo[ XBOX_OUTPUT_PIPE_4 ].epAddr = 0x07; // XBOX 360 output endpoint - poll interval 8ms
236 epInfo[ XBOX_OUTPUT_PIPE_4 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
237 epInfo[ XBOX_OUTPUT_PIPE_4 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
238 epInfo[ XBOX_OUTPUT_PIPE_4 ].maxPktSize = EP_MAXPKTSIZE;
239 epInfo[ XBOX_OUTPUT_PIPE_4 ].bmSndToggle = 0;
240 epInfo[ XBOX_OUTPUT_PIPE_4 ].bmRcvToggle = 0;
242 rcode = pUsb->setEpInfoEntry(bAddress, 9, epInfo);
244 goto FailSetDevTblEntry;
246 delay(200); //Give time for address change
248 rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
250 goto FailSetConfDescr;
252 #ifdef DEBUG_USB_HOST
253 Notify(PSTR("\r\nXbox Wireless Receiver Connected\r\n"), 0x80);
255 XboxReceiverConnected = true;
257 checkStatusTimer = 0; // Reset timer
258 return 0; // Successful configuration
260 /* Diagnostic messages */
262 #ifdef DEBUG_USB_HOST
263 NotifyFailSetDevTblEntry();
268 #ifdef DEBUG_USB_HOST
269 NotifyFailSetConfDescr();
273 #ifdef DEBUG_USB_HOST
274 Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
281 /* Performs a cleanup after failed Init() attempt */
282 uint8_t XBOXRECV::Release() {
283 XboxReceiverConnected = false;
284 for(uint8_t i = 0; i < 4; i++)
285 Xbox360Connected[i] = 0x00;
286 pUsb->GetAddressPool().FreeAddress(bAddress);
292 uint8_t XBOXRECV::Poll() {
295 if(!checkStatusTimer || ((millis() - checkStatusTimer) > 3000)) { // Run checkStatus every 3 seconds
296 checkStatusTimer = millis();
302 for(uint8_t i = 0; i < 4; i++) {
304 inputPipe = XBOX_INPUT_PIPE_1;
306 inputPipe = XBOX_INPUT_PIPE_2;
308 inputPipe = XBOX_INPUT_PIPE_3;
310 inputPipe = XBOX_INPUT_PIPE_4;
312 bufferSize = EP_MAXPKTSIZE; // This is the maximum number of bytes we want to receive
313 pUsb->inTransfer(bAddress, epInfo[ inputPipe ].epAddr, &bufferSize, readBuf);
314 if(bufferSize > 0) { // The number of received bytes
316 Notify(PSTR("Bytes Received: "), 0x80);
317 D_PrintHex<uint16_t > (bufferSize, 0x80);
318 Notify(PSTR("\r\n"), 0x80);
322 printReport(i, bufferSize); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
329 void XBOXRECV::readReport(uint8_t controller) {
332 // This report is send when a controller is connected and disconnected
333 if(readBuf[0] == 0x08 && readBuf[1] != Xbox360Connected[controller]) {
334 Xbox360Connected[controller] = readBuf[1];
335 #ifdef DEBUG_USB_HOST
336 Notify(PSTR("Controller "), 0x80);
337 Notify(controller, 0x80);
339 if(Xbox360Connected[controller]) {
340 #ifdef DEBUG_USB_HOST
343 case 0x80: str = PSTR(" as controller\r\n");
345 case 0x40: str = PSTR(" as headset\r\n");
347 case 0xC0: str = PSTR(" as controller+headset\r\n");
350 Notify(PSTR(": connected"), 0x80);
355 #ifdef DEBUG_USB_HOST
357 Notify(PSTR(": disconnected\r\n"), 0x80);
361 // Controller status report
362 if(readBuf[1] == 0x00 && readBuf[3] & 0x13 && readBuf[4] >= 0x22) {
363 controllerStatus[controller] = ((uint16_t)readBuf[3] << 8) | readBuf[4];
366 if(readBuf[1] != 0x01) // Check if it's the correct report - the receiver also sends different status reports
369 // A controller must be connected if it's sending data
370 if(!Xbox360Connected[controller])
371 Xbox360Connected[controller] |= 0x80;
373 ButtonState[controller] = (uint32_t)(readBuf[9] | ((uint16_t)readBuf[8] << 8) | ((uint32_t)readBuf[7] << 16) | ((uint32_t)readBuf[6] << 24));
375 hatValue[controller][LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
376 hatValue[controller][LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
377 hatValue[controller][RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);
378 hatValue[controller][RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);
380 //Notify(PSTR("\r\nButtonState: "), 0x80);
381 //PrintHex<uint32_t>(ButtonState[controller], 0x80);
383 if(ButtonState[controller] != OldButtonState[controller]) {
384 buttonStateChanged[controller] = true;
385 ButtonClickState[controller] = (ButtonState[controller] >> 16) & ((~OldButtonState[controller]) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
386 if(((uint8_t)OldButtonState[controller]) == 0 && ((uint8_t)ButtonState[controller]) != 0) // The L2 and R2 buttons are special as they are analog buttons
387 R2Clicked[controller] = true;
388 if((uint8_t)(OldButtonState[controller] >> 8) == 0 && (uint8_t)(ButtonState[controller] >> 8) != 0)
389 L2Clicked[controller] = true;
390 OldButtonState[controller] = ButtonState[controller];
394 void XBOXRECV::printReport(uint8_t controller, uint8_t nBytes) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
398 Notify(PSTR("Controller "), 0x80);
399 Notify(controller, 0x80);
400 Notify(PSTR(": "), 0x80);
401 for(uint8_t i = 0; i < nBytes; i++) {
402 D_PrintHex<uint8_t > (readBuf[i], 0x80);
403 Notify(PSTR(" "), 0x80);
405 Notify(PSTR("\r\n"), 0x80);
409 uint8_t XBOXRECV::getButtonPress(ButtonEnum b, uint8_t controller) {
410 if(b == L2) // These are analog buttons
411 return (uint8_t)(ButtonState[controller] >> 8);
413 return (uint8_t)ButtonState[controller];
414 return (bool)(ButtonState[controller] & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16));
417 bool XBOXRECV::getButtonClick(ButtonEnum b, uint8_t controller) {
419 if(L2Clicked[controller]) {
420 L2Clicked[controller] = false;
425 if(R2Clicked[controller]) {
426 R2Clicked[controller] = false;
431 uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
432 bool click = (ButtonClickState[controller] & button);
433 ButtonClickState[controller] &= ~button; // clear "click" event
437 int16_t XBOXRECV::getAnalogHat(AnalogHatEnum a, uint8_t controller) {
438 return hatValue[controller][a];
441 bool XBOXRECV::buttonChanged(uint8_t controller) {
442 bool state = buttonStateChanged[controller];
443 buttonStateChanged[controller] = false;
448 ControllerStatus Breakdown
449 ControllerStatus[controller] & 0x0001 // 0
450 ControllerStatus[controller] & 0x0002 // normal batteries, no rechargeable battery pack
451 ControllerStatus[controller] & 0x0004 // controller starting up / settling
452 ControllerStatus[controller] & 0x0008 // headset adapter plugged in, but no headphones connected (mute?)
453 ControllerStatus[controller] & 0x0010 // 0
454 ControllerStatus[controller] & 0x0020 // 1
455 ControllerStatus[controller] & 0x0040 // battery level (high bit)
456 ControllerStatus[controller] & 0x0080 // battery level (low bit)
457 ControllerStatus[controller] & 0x0100 // 1
458 ControllerStatus[controller] & 0x0200 // 1
459 ControllerStatus[controller] & 0x0400 // headset adapter plugged in
460 ControllerStatus[controller] & 0x0800 // 0
461 ControllerStatus[controller] & 0x1000 // 1
462 ControllerStatus[controller] & 0x2000 // 0
463 ControllerStatus[controller] & 0x4000 // 0
464 ControllerStatus[controller] & 0x8000 // 0
466 uint8_t XBOXRECV::getBatteryLevel(uint8_t controller) {
467 return ((controllerStatus[controller] & 0x00C0) >> 6);
470 void XBOXRECV::XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes) {
476 case 0: outputPipe = XBOX_OUTPUT_PIPE_1;
478 case 1: outputPipe = XBOX_OUTPUT_PIPE_2;
480 case 2: outputPipe = XBOX_OUTPUT_PIPE_3;
482 case 3: outputPipe = XBOX_OUTPUT_PIPE_4;
490 pUsb->outTransfer(bAddress, epInfo[ outputPipe ].epAddr, nbytes, data);
493 Notify(PSTR("Error sending Xbox message\r\n"), 0x80);
497 void XBOXRECV::disconnect(uint8_t controller) {
503 XboxCommand(controller, writeBuf, 4);
506 void XBOXRECV::setLedRaw(uint8_t value, uint8_t controller) {
510 writeBuf[3] = value | 0x40;
512 XboxCommand(controller, writeBuf, 4);
515 void XBOXRECV::setLedOn(LEDEnum led, uint8_t controller) {
517 setLedRaw(0, controller);
518 else if(led != ALL) // All LEDs can't be on a the same time
519 setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]) + 4, controller);
522 void XBOXRECV::setLedBlink(LEDEnum led, uint8_t controller) {
523 setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]), controller);
526 void XBOXRECV::setLedMode(LEDModeEnum ledMode, uint8_t controller) { // This function is used to do some speciel LED stuff the controller supports
527 setLedRaw((uint8_t)ledMode, controller);
530 /* PC runs this at interval of approx 2 seconds
531 Thanks to BusHound from Perisoft.net for the Windows USB Analysis output
532 Found by timstamp.co.uk
534 void XBOXRECV::checkStatus() {
537 // Get controller info
542 for(uint8_t i = 0; i < 4; i++) {
543 XboxCommand(i, writeBuf, 4);
545 // Get battery status
550 for(uint8_t i = 0; i < 4; i++) {
551 if(Xbox360Connected[i])
552 XboxCommand(i, writeBuf, 4);
556 void XBOXRECV::setRumbleOn(uint8_t lValue, uint8_t rValue, uint8_t controller) {
562 writeBuf[5] = lValue; // big weight
563 writeBuf[6] = rValue; // small weight
565 XboxCommand(controller, writeBuf, 7);
568 void XBOXRECV::onInit(uint8_t controller) {
570 pFuncOnInit(); // Call the user function
575 else if(controller == 1)
577 else if(controller == 2)
581 setLedOn(led, controller);