USB_Host_Shield_2.0
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
XBOXRECV.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
2 
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
8  the GPL2 ("Copyleft").
9 
10  Contact information
11  -------------------
12 
13  Kristian Lauszus, TKJ Electronics
14  Web : http://www.tkjelectronics.com
15  e-mail : kristianl@tkjelectronics.com
16 
17  getBatteryLevel and checkStatus functions made by timstamp.co.uk found using BusHound from Perisoft.net
18  */
19 
20 #include "XBOXRECV.h"
21 #define DEBUG // Uncomment to print data for debugging
22 //#define EXTRADEBUG // Uncomment to get even more debugging data
23 //#define PRINTREPORT // Uncomment to print the report send by the Xbox 360 Controller
24 
25 const uint8_t LEDS[] PROGMEM = {
26  0x02, // LED1
27  0x03, // LED2
28  0x04, // LED3
29  0x05, // LED4
30  0x01 // ALL - Used to blink all LEDs
31  };
32 const uint16_t BUTTONS[] PROGMEM = {
33  0x0100, // UP
34  0x0800, // RIGHT
35  0x0200, // DOWN
36  0x0400, // LEFT
37 
38  0x2000, // BACK
39  0x1000, // START
40  0x4000, // L3
41  0x8000, // R3
42 
43  0,0, // Skip L2 and R2 as these are analog buttons
44  0x0001, // L1
45  0x0002, // R1
46 
47  0x0020, // B
48  0x0010, // A
49  0x0040, // X
50  0x0080, // Y
51 
52  0x0004, // XBOX
53  0x0008 // SYNC
54 };
55 
57 pUsb(p), // pointer to USB class instance - mandatory
58 bAddress(0), // device address - mandatory
59 bPollEnable(false) { // don't start polling before dongle is connected
60  for(uint8_t i=0; i<XBOX_MAX_ENDPOINTS; i++) {
61  epInfo[i].epAddr = 0;
62  epInfo[i].maxPktSize = (i) ? 0 : 8;
63  epInfo[i].epAttribs = 0;
65  }
66 
67  if (pUsb) // register in USB subsystem
68  pUsb->RegisterDeviceClass(this); //set devConfig[] entry
69 }
70 
71 uint8_t XBOXRECV::Init(uint8_t parent, uint8_t port, bool lowspeed) {
72  uint8_t buf[sizeof(USB_DEVICE_DESCRIPTOR)];
73  uint8_t rcode;
74  UsbDevice *p = NULL;
75  EpInfo *oldep_ptr = NULL;
76  uint16_t PID;
77  uint16_t VID;
78 
79  // get memory address of USB device address pool
80  AddressPool &addrPool = pUsb->GetAddressPool();
81 #ifdef EXTRADEBUG
82  Notify(PSTR("\r\nXBOXRECV Init"));
83 #endif
84  // check if address has already been assigned to an instance
85  if (bAddress) {
86 #ifdef DEBUG
87  Notify(PSTR("\r\nAddress in use"));
88 #endif
90  }
91 
92  // Get pointer to pseudo device with address 0 assigned
93  p = addrPool.GetUsbDevicePtr(0);
94 
95  if (!p) {
96 #ifdef DEBUG
97  Notify(PSTR("\r\nAddress not found"));
98 #endif
100  }
101 
102  if (!p->epinfo) {
103 #ifdef DEBUG
104  Notify(PSTR("\r\nepinfo is null"));
105 #endif
107  }
108 
109  // Save old pointer to EP_RECORD of address 0
110  oldep_ptr = p->epinfo;
111 
112  // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
113  p->epinfo = epInfo;
114 
115  p->lowspeed = lowspeed;
116 
117  // Get device descriptor
118  rcode = pUsb->getDevDescr(0, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);// Get device descriptor - addr, ep, nbytes, data
119  // Restore p->epinfo
120  p->epinfo = oldep_ptr;
121 
122  if(rcode)
123  goto FailGetDevDescr;
124 
125  VID = ((USB_DEVICE_DESCRIPTOR*)buf)->idVendor;
126  PID = ((USB_DEVICE_DESCRIPTOR*)buf)->idProduct;
127 
128  if(VID != XBOX_VID && VID != MADCATZ_VID) // We just check if it's a xbox receiver using the Vendor ID
129  goto FailUnknownDevice;
131 #ifdef DEBUG
132  Notify(PSTR("\r\nYou'll need a wireless receiver for this libary to work"));
133 #endif
134  goto FailUnknownDevice;
135  }
136 
137  // Allocate new address according to device class
138  bAddress = addrPool.AllocAddress(parent, false, port);
139 
140  if (!bAddress)
142 
143  // Extract Max Packet Size from device descriptor
144  epInfo[0].maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0;
145 
146  // Assign new address to the device
147  rcode = pUsb->setAddr( 0, 0, bAddress );
148  if (rcode) {
149  p->lowspeed = false;
150  addrPool.FreeAddress(bAddress);
151  bAddress = 0;
152 #ifdef DEBUG
153  Notify(PSTR("\r\nsetAddr: "));
154 #endif
155  PrintHex<uint8_t>(rcode);
156  return rcode;
157  }
158 #ifdef EXTRADEBUG
159  Notify(PSTR("\r\nAddr: "));
160  PrintHex<uint8_t>(bAddress);
161 #endif
162  p->lowspeed = false;
163 
164  //get pointer to assigned address record
165  p = addrPool.GetUsbDevicePtr(bAddress);
166  if (!p)
168 
169  p->lowspeed = lowspeed;
170 
171  // Assign epInfo to epinfo pointer - only EP0 is known
172  rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
173  if (rcode)
174  goto FailSetDevTblEntry;
175 
176  /* The application will work in reduced host mode, so we can save program and data
177  memory space. After verifying the VID we will use known values for the
178  configuration values for device, interface, endpoints and HID for the XBOX360 Wireless receiver */
179 
180  /* Initialize data structures for endpoints of device */
181  epInfo[ XBOX_INPUT_PIPE_1 ].epAddr = 0x01; // XBOX 360 report endpoint - poll interval 1ms
183  epInfo[ XBOX_INPUT_PIPE_1 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
187  epInfo[ XBOX_OUTPUT_PIPE_1 ].epAddr = 0x01; // XBOX 360 output endpoint - poll interval 8ms
189  epInfo[ XBOX_OUTPUT_PIPE_1 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
193 
194  epInfo[ XBOX_INPUT_PIPE_2 ].epAddr = 0x03; // XBOX 360 report endpoint - poll interval 1ms
196  epInfo[ XBOX_INPUT_PIPE_2 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
200  epInfo[ XBOX_OUTPUT_PIPE_2 ].epAddr = 0x03; // XBOX 360 output endpoint - poll interval 8ms
202  epInfo[ XBOX_OUTPUT_PIPE_2 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
206 
207  epInfo[ XBOX_INPUT_PIPE_3 ].epAddr = 0x05; // XBOX 360 report endpoint - poll interval 1ms
209  epInfo[ XBOX_INPUT_PIPE_3 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
213  epInfo[ XBOX_OUTPUT_PIPE_3 ].epAddr = 0x05; // XBOX 360 output endpoint - poll interval 8ms
215  epInfo[ XBOX_OUTPUT_PIPE_3 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
219 
220  epInfo[ XBOX_INPUT_PIPE_4 ].epAddr = 0x07; // XBOX 360 report endpoint - poll interval 1ms
222  epInfo[ XBOX_INPUT_PIPE_4 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
226  epInfo[ XBOX_OUTPUT_PIPE_4 ].epAddr = 0x07; // XBOX 360 output endpoint - poll interval 8ms
228  epInfo[ XBOX_OUTPUT_PIPE_4 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
232 
233  rcode = pUsb->setEpInfoEntry(bAddress, 9, epInfo);
234  if( rcode )
235  goto FailSetDevTblEntry;
236 
237  delay(200);//Give time for address change
238 
239  rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
240  if( rcode )
241  goto FailSetConf;
242 
243 #ifdef DEBUG
244  Notify(PSTR("\r\nXbox Wireless Receiver Connected\r\n"));
245 #endif
246  XboxReceiverConnected = true;
247  bPollEnable = true;
248  return 0; // successful configuration
249 
250  /* diagnostic messages */
251 FailGetDevDescr:
252 #ifdef DEBUG
253  Notify(PSTR("\r\ngetDevDescr:"));
254 #endif
255  goto Fail;
256 FailSetDevTblEntry:
257 #ifdef DEBUG
258  Notify(PSTR("\r\nsetDevTblEn:"));
259 #endif
260  goto Fail;
261 FailSetConf:
262 #ifdef DEBUG
263  Notify(PSTR("\r\nsetConf:"));
264 #endif
265  goto Fail;
266 FailUnknownDevice:
267 #ifdef DEBUG
268  Notify(PSTR("\r\nUnknown Device Connected - VID: "));
269  PrintHex<uint16_t>(VID);
270  Notify(PSTR(" PID: "));
271  PrintHex<uint16_t>(PID);
272 #endif
274  goto Fail;
275 Fail:
276 #ifdef DEBUG
277  Notify(PSTR("\r\nXbox 360 Init Failed, error code: "));
278  Serial.print(rcode,HEX);
279 #endif
280  Release();
281  return rcode;
282 }
283 
284 /* Performs a cleanup after failed Init() attempt */
285 uint8_t XBOXRECV::Release() {
286  XboxReceiverConnected = false;
287  for(uint8_t i=0;i<4;i++)
288  Xbox360Connected[i] = 0x00;
290  bAddress = 0;
291  bPollEnable = false;
292  return 0;
293 }
294 uint8_t XBOXRECV::Poll() {
295  if (!bPollEnable)
296  return 0;
297  if(!timer || ((millis() - timer) > 3000)) { // Run checkStatus every 3 seconds
298  timer = millis();
299  checkStatus();
300  }
301  uint8_t inputPipe;
302  uint16_t bufferSize;
303  for(uint8_t i=0;i<4;i++) {
304  switch (i) {
305  case 0: inputPipe = XBOX_INPUT_PIPE_1; break;
306  case 1: inputPipe = XBOX_INPUT_PIPE_2; break;
307  case 2: inputPipe = XBOX_INPUT_PIPE_3; break;
308  case 3: inputPipe = XBOX_INPUT_PIPE_4; break;
309  }
310  bufferSize = EP_MAXPKTSIZE; // This is the maximum number of bytes we want to receive
311  pUsb->inTransfer(bAddress, epInfo[ inputPipe ].epAddr, &bufferSize, readBuf);
312  if(bufferSize > 0) { // The number of received bytes
313 #ifdef EXTRADEBUG
314  Notify(PSTR("Bytes Received: "));
315  Serial.print(bufferSize);
316  Notify(PSTR("\r\n"));
317 #endif
318  readReport(i);
319 #ifdef PRINTREPORT
320  printReport(i,bufferSize); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
321 #endif
322  }
323  }
324  return 0;
325 }
326 
327 void XBOXRECV::readReport(uint8_t controller) {
328  if (readBuf == NULL)
329  return;
330  // This report is send when a controller is connected and disconnected
331  if(readBuf[0] == 0x08 && readBuf[1] != Xbox360Connected[controller]) {
332  Xbox360Connected[controller] = readBuf[1];
333 #ifdef DEBUG
334  Notify(PSTR("Controller "));
335  Serial.print(controller);
336 #endif
337  if(Xbox360Connected[controller]) {
338 #ifdef DEBUG
339  char* str = 0;
340  switch(readBuf[1]) {
341  case 0x80: str = PSTR(" as controller\r\n"); break;
342  case 0x40: str = PSTR(" as headset\r\n"); break;
343  case 0xC0: str = PSTR(" as controller+headset\r\n"); break;
344  }
345  Notify(PSTR(": connected"));
346  Notify(str);
347 #endif
348  LED led;
349  switch (controller) {
350  case 0: led = LED1; break;
351  case 1: led = LED2; break;
352  case 2: led = LED3; break;
353  case 3: led = LED4; break;
354  }
355  setLedOn(controller,led);
356  }
357 #ifdef DEBUG
358  else
359  Notify(PSTR(": disconnected\r\n"));
360 #endif
361  return;
362  }
363  // Controller status report
364  if(readBuf[1] == 0x00 && readBuf[3] & 0x13 && readBuf[4] >= 0x22) {
365  controllerStatus[controller] = ((uint16_t)readBuf[3] << 8) | readBuf[4];
366  return;
367  }
368  if(readBuf[1] != 0x01) // Check if it's the correct report - the receiver also sends different status reports
369  return;
370 
371  // A controller must be connected if it's sending data
372  if(!Xbox360Connected[controller])
373  Xbox360Connected[controller] |= 0x80;
374 
375  ButtonState[controller] = (uint32_t)(readBuf[9] | ((uint16_t)readBuf[8] << 8) | ((uint32_t)readBuf[7] << 16) | ((uint32_t)readBuf[6] << 24));
376 
377  hatValue[controller][LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
378  hatValue[controller][LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
379  hatValue[controller][RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);
380  hatValue[controller][RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);
381 
382  //Notify(PSTR("\r\nButtonState: "));
383  //PrintHex<uint32_t>(ButtonState[controller]);
384 
385  if(ButtonState[controller] != OldButtonState[controller]) {
386  buttonStateChanged[controller] = true;
387  ButtonClickState[controller] = (ButtonState[controller] >> 16) & ((~OldButtonState[controller]) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
388  if(((uint8_t)OldButtonState[controller]) == 0 && ((uint8_t)ButtonState[controller]) != 0) // The L2 and R2 buttons are special as they are analog buttons
389  R2Clicked[controller] = true;
390  if((uint8_t)(OldButtonState[controller] >> 8) == 0 && (uint8_t)(ButtonState[controller] >> 8) != 0)
391  L2Clicked[controller] = true;
392  OldButtonState[controller] = ButtonState[controller];
393  }
394 }
395 
396 void XBOXRECV::printReport(uint8_t controller, uint8_t nBytes) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
397 #ifdef PRINTREPORT
398  if (readBuf == NULL)
399  return;
400  Notify(PSTR("Controller "));
401  Serial.print(controller);
402  Notify(PSTR(": "));
403  for(uint8_t i = 0; i < nBytes;i++) {
404  PrintHex<uint8_t>(readBuf[i]);
405  Serial.print(" ");
406  }
407  Serial.println();
408 #endif
409 }
410 uint8_t XBOXRECV::getButtonPress(uint8_t controller, Button b) {
411  if(b == L2) // These are analog buttons
412  return (uint8_t)(ButtonState[controller] >> 8);
413  else if(b == R2)
414  return (uint8_t)ButtonState[controller];
415  return (ButtonState[controller] & ((uint32_t)pgm_read_word(&BUTTONS[(uint8_t)b]) << 16));
416 }
417 bool XBOXRECV::getButtonClick(uint8_t controller, Button b) {
418  if(b == L2) {
419  if(L2Clicked[controller]) {
420  L2Clicked[controller] = false;
421  return true;
422  }
423  return false;
424  }
425  else if(b == R2) {
426  if(R2Clicked[controller]) {
427  R2Clicked[controller] = false;
428  return true;
429  }
430  return false;
431  }
432  uint16_t button = pgm_read_word(&BUTTONS[(uint8_t)b]);
433  bool click = (ButtonClickState[controller] & button);
434  ButtonClickState[controller] &= ~button; // clear "click" event
435  return click;
436 }
437 int16_t XBOXRECV::getAnalogHat(uint8_t controller, AnalogHat a) {
438  return hatValue[controller][a];
439 }
440 bool XBOXRECV::buttonChanged(uint8_t controller) {
441  bool state = buttonStateChanged[controller];
442  buttonStateChanged[controller] = false;
443  return state;
444 }
445 /*
446 ControllerStatus Breakdown
447  ControllerStatus[controller] & 0x0001 // 0
448  ControllerStatus[controller] & 0x0002 // normal batteries, no rechargeable battery pack
449  ControllerStatus[controller] & 0x0004 // controller starting up / settling
450  ControllerStatus[controller] & 0x0008 // headset adapter plugged in, but no headphones connected (mute?)
451  ControllerStatus[controller] & 0x0010 // 0
452  ControllerStatus[controller] & 0x0020 // 1
453  ControllerStatus[controller] & 0x0040 // battery level (high bit)
454  ControllerStatus[controller] & 0x0080 // battery level (low bit)
455  ControllerStatus[controller] & 0x0100 // 1
456  ControllerStatus[controller] & 0x0200 // 1
457  ControllerStatus[controller] & 0x0400 // headset adapter plugged in
458  ControllerStatus[controller] & 0x0800 // 0
459  ControllerStatus[controller] & 0x1000 // 1
460  ControllerStatus[controller] & 0x2000 // 0
461  ControllerStatus[controller] & 0x4000 // 0
462  ControllerStatus[controller] & 0x8000 // 0
463 */
464 uint8_t XBOXRECV::getBatteryLevel(uint8_t controller) {
465  uint8_t batteryLevel = ((controllerStatus[controller] & 0x00C0) >> 6) * 33;
466  if(batteryLevel == 99)
467  batteryLevel = 100;
468  return batteryLevel;
469 }
470 
471 void XBOXRECV::XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes) {
472  uint8_t rcode;
473  uint8_t outputPipe;
474  switch (controller) {
475  case 0: outputPipe = XBOX_OUTPUT_PIPE_1; break;
476  case 1: outputPipe = XBOX_OUTPUT_PIPE_2; break;
477  case 2: outputPipe = XBOX_OUTPUT_PIPE_3; break;
478  case 3: outputPipe = XBOX_OUTPUT_PIPE_4; break;
479  }
480  rcode = pUsb->outTransfer(bAddress, epInfo[ outputPipe ].epAddr, nbytes, data);
481 #ifdef EXTRADEBUG
482  if(rcode)
483  Notify(PSTR("Error sending Xbox message\r\n"));
484 #endif
485 }
486 void XBOXRECV::setLedRaw(uint8_t controller, uint8_t value) {
487  writeBuf[0] = 0x00;
488  writeBuf[1] = 0x00;
489  writeBuf[2] = 0x08;
490  writeBuf[3] = value | 0x40;
491 
492  XboxCommand(controller, writeBuf, 4);
493 }
494 void XBOXRECV::setLedOn(uint8_t controller, LED led) {
495  if(led != ALL) // All LEDs can't be on a the same time
496  setLedRaw(controller,(pgm_read_byte(&LEDS[(uint8_t)led]))+4);
497 }
498 void XBOXRECV::setLedBlink(uint8_t controller, LED led) {
499  setLedRaw(controller,pgm_read_byte(&LEDS[(uint8_t)led]));
500 }
501 void XBOXRECV::setLedMode(uint8_t controller, LEDMode ledMode) { // This function is used to do some speciel LED stuff the controller supports
502  setLedRaw(controller,(uint8_t)ledMode);
503 }
504 /* PC runs this at interval of approx 2 seconds
505 Thanks to BusHound from Perisoft.net for the Windows USB Analysis output
506 Found by timstamp.co.uk
507 */
508 void XBOXRECV::checkStatus() {
509  if(!bPollEnable)
510  return;
511  // Get controller info
512  writeBuf[0] = 0x08;
513  writeBuf[1] = 0x00;
514  writeBuf[2] = 0x0f;
515  writeBuf[3] = 0xc0;
516  for(uint8_t i=0; i<4; i++) {
517  XboxCommand(i, writeBuf, 4);
518  }
519  // Get battery status
520  writeBuf[0] = 0x00;
521  writeBuf[1] = 0x00;
522  writeBuf[2] = 0x00;
523  writeBuf[3] = 0x40;
524  for(uint8_t i=0; i<4; i++) {
525  if(Xbox360Connected[i])
526  XboxCommand(i, writeBuf, 4);
527  }
528 }
529 
530 void XBOXRECV::setRumbleOn(uint8_t controller, uint8_t lValue, uint8_t rValue) {
531  writeBuf[0] = 0x00;
532  writeBuf[1] = 0x01;
533  writeBuf[2] = 0x0f;
534  writeBuf[3] = 0xc0;
535  writeBuf[4] = 0x00;
536  writeBuf[5] = lValue; // big weight
537  writeBuf[6] = rValue; // small weight
538 
539  XboxCommand(controller, writeBuf, 7);
540 }