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