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 
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
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
66  Notify(PSTR("\r\nAddress not found"), 0x80);
67 #endif
69  }
70 
71  if (!p->epinfo) {
72 #ifdef DEBUG
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;
100 #ifdef DEBUG
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
122  Notify(PSTR("\r\nsetAddr: "), 0x80);
123 #endif
124  PrintHex<uint8_t > (rcode, 0x80);
125  return rcode;
126  }
127 #ifdef EXTRADEBUG
128  Notify(PSTR("\r\nAddr: "), 0x80);
129  PrintHex<uint8_t > (bAddress, 0x80);
130 #endif
131  p->lowspeed = false;
132 
133  //get pointer to assigned address record
134  p = addrPool.GetUsbDevicePtr(bAddress);
135  if (!p)
137 
138  p->lowspeed = lowspeed;
139 
140  // Assign epInfo to epinfo pointer - only EP0 is known
141  rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
142  if (rcode)
143  goto FailSetDevTblEntry;
144 
145  /* The application will work in reduced host mode, so we can save program and data
146  memory space. After verifying the VID we will use known values for the
147  configuration values for device, interface, endpoints and HID for the XBOX360 Wireless receiver */
148 
149  /* Initialize data structures for endpoints of device */
150  epInfo[ XBOX_INPUT_PIPE_1 ].epAddr = 0x01; // XBOX 360 report endpoint - poll interval 1ms
152  epInfo[ XBOX_INPUT_PIPE_1 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
156  epInfo[ XBOX_OUTPUT_PIPE_1 ].epAddr = 0x01; // XBOX 360 output endpoint - poll interval 8ms
158  epInfo[ XBOX_OUTPUT_PIPE_1 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
162 
163  epInfo[ XBOX_INPUT_PIPE_2 ].epAddr = 0x03; // XBOX 360 report endpoint - poll interval 1ms
165  epInfo[ XBOX_INPUT_PIPE_2 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
169  epInfo[ XBOX_OUTPUT_PIPE_2 ].epAddr = 0x03; // XBOX 360 output endpoint - poll interval 8ms
171  epInfo[ XBOX_OUTPUT_PIPE_2 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
175 
176  epInfo[ XBOX_INPUT_PIPE_3 ].epAddr = 0x05; // XBOX 360 report endpoint - poll interval 1ms
178  epInfo[ XBOX_INPUT_PIPE_3 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
182  epInfo[ XBOX_OUTPUT_PIPE_3 ].epAddr = 0x05; // XBOX 360 output endpoint - poll interval 8ms
184  epInfo[ XBOX_OUTPUT_PIPE_3 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
188 
189  epInfo[ XBOX_INPUT_PIPE_4 ].epAddr = 0x07; // XBOX 360 report endpoint - poll interval 1ms
191  epInfo[ XBOX_INPUT_PIPE_4 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
195  epInfo[ XBOX_OUTPUT_PIPE_4 ].epAddr = 0x07; // XBOX 360 output endpoint - poll interval 8ms
197  epInfo[ XBOX_OUTPUT_PIPE_4 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
201 
202  rcode = pUsb->setEpInfoEntry(bAddress, 9, epInfo);
203  if (rcode)
204  goto FailSetDevTblEntry;
205 
206  delay(200); //Give time for address change
207 
208  rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
209  if (rcode)
210  goto FailSetConfDescr;
211 
212 #ifdef DEBUG
213  Notify(PSTR("\r\nXbox Wireless Receiver Connected\r\n"), 0x80);
214 #endif
215  XboxReceiverConnected = true;
216  bPollEnable = true;
217  return 0; // successful configuration
218 
219  /* diagnostic messages */
220 FailGetDevDescr:
222  goto Fail;
223 
224 FailSetDevTblEntry:
226  goto Fail;
227 
228 FailSetConfDescr:
230  goto Fail;
231 
232 FailUnknownDevice:
233  NotifyFailUnknownDevice(VID,PID);
235 
236 Fail:
237 #ifdef DEBUG
238  Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
239 #endif
240  NotifyFail(rcode);
241  Release();
242  return rcode;
243 }
244 
245 /* Performs a cleanup after failed Init() attempt */
246 uint8_t XBOXRECV::Release() {
247  XboxReceiverConnected = false;
248  for (uint8_t i = 0; i < 4; i++)
249  Xbox360Connected[i] = 0x00;
251  bAddress = 0;
252  bPollEnable = false;
253  return 0;
254 }
255 
256 uint8_t XBOXRECV::Poll() {
257  if (!bPollEnable)
258  return 0;
259  if (!timer || ((millis() - timer) > 3000)) { // Run checkStatus every 3 seconds
260  timer = millis();
261  checkStatus();
262  }
263  uint8_t inputPipe;
264  uint16_t bufferSize;
265  for (uint8_t i = 0; i < 4; i++) {
266  switch (i) {
267  case 0: inputPipe = XBOX_INPUT_PIPE_1;
268  break;
269  case 1: inputPipe = XBOX_INPUT_PIPE_2;
270  break;
271  case 2: inputPipe = XBOX_INPUT_PIPE_3;
272  break;
273  case 3: inputPipe = XBOX_INPUT_PIPE_4;
274  break;
275  }
276  bufferSize = EP_MAXPKTSIZE; // This is the maximum number of bytes we want to receive
277  pUsb->inTransfer(bAddress, epInfo[ inputPipe ].epAddr, &bufferSize, readBuf);
278  if (bufferSize > 0) { // The number of received bytes
279 #ifdef EXTRADEBUG
280  Notify(PSTR("Bytes Received: "), 0x80);
281  PrintHex<uint16_t > (bufferSize, 0x80);
282  Notify(PSTR("\r\n"), 0x80);
283 #endif
284  readReport(i);
285 #ifdef PRINTREPORT
286  printReport(i, bufferSize); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
287 #endif
288  }
289  }
290  return 0;
291 }
292 
293 void XBOXRECV::readReport(uint8_t controller) {
294  if (readBuf == NULL)
295  return;
296  // This report is send when a controller is connected and disconnected
297  if (readBuf[0] == 0x08 && readBuf[1] != Xbox360Connected[controller]) {
298  Xbox360Connected[controller] = readBuf[1];
299 #ifdef DEBUG
300  Notify(PSTR("Controller "), 0x80);
301  Notify(controller, 0x80);
302 #endif
303  if (Xbox360Connected[controller]) {
304 #ifdef DEBUG
305  char* str = 0;
306  switch (readBuf[1]) {
307  case 0x80: str = PSTR(" as controller\r\n");
308  break;
309  case 0x40: str = PSTR(" as headset\r\n");
310  break;
311  case 0xC0: str = PSTR(" as controller+headset\r\n");
312  break;
313  }
314  Notify(PSTR(": connected"), 0x80);
315  Notify(str, 0x80);
316 #endif
317  LED led;
318  switch (controller) {
319  case 0: led = LED1;
320  break;
321  case 1: led = LED2;
322  break;
323  case 2: led = LED3;
324  break;
325  case 3: led = LED4;
326  break;
327  }
328  setLedOn(controller, led);
329  }
330 #ifdef DEBUG
331  else
332  Notify(PSTR(": disconnected\r\n"), 0x80);
333 #endif
334  return;
335  }
336  // Controller status report
337  if (readBuf[1] == 0x00 && readBuf[3] & 0x13 && readBuf[4] >= 0x22) {
338  controllerStatus[controller] = ((uint16_t)readBuf[3] << 8) | readBuf[4];
339  return;
340  }
341  if (readBuf[1] != 0x01) // Check if it's the correct report - the receiver also sends different status reports
342  return;
343 
344  // A controller must be connected if it's sending data
345  if (!Xbox360Connected[controller])
346  Xbox360Connected[controller] |= 0x80;
347 
348  ButtonState[controller] = (uint32_t)(readBuf[9] | ((uint16_t)readBuf[8] << 8) | ((uint32_t)readBuf[7] << 16) | ((uint32_t)readBuf[6] << 24));
349 
350  hatValue[controller][LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
351  hatValue[controller][LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
352  hatValue[controller][RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);
353  hatValue[controller][RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);
354 
355  //Notify(PSTR("\r\nButtonState: "), 0x80);
356  //PrintHex<uint32_t>(ButtonState[controller], 0x80);
357 
358  if (ButtonState[controller] != OldButtonState[controller]) {
359  buttonStateChanged[controller] = true;
360  ButtonClickState[controller] = (ButtonState[controller] >> 16) & ((~OldButtonState[controller]) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
361  if (((uint8_t)OldButtonState[controller]) == 0 && ((uint8_t)ButtonState[controller]) != 0) // The L2 and R2 buttons are special as they are analog buttons
362  R2Clicked[controller] = true;
363  if ((uint8_t)(OldButtonState[controller] >> 8) == 0 && (uint8_t)(ButtonState[controller] >> 8) != 0)
364  L2Clicked[controller] = true;
365  OldButtonState[controller] = ButtonState[controller];
366  }
367 }
368 
369 void XBOXRECV::printReport(uint8_t controller, uint8_t nBytes) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
370 #ifdef PRINTREPORT
371  if (readBuf == NULL)
372  return;
373  Notify(PSTR("Controller "), 0x80);
374  Notify(controller, 0x80);
375  Notify(PSTR(": "), 0x80);
376  for (uint8_t i = 0; i < nBytes; i++) {
377  PrintHex<uint8_t > (readBuf[i], 0x80);
378  Notify(PSTR(" "), 0x80);
379  }
380  Notify(PSTR("\r\n"), 0x80);
381 #endif
382 }
383 
384 uint8_t XBOXRECV::getButtonPress(uint8_t controller, Button b) {
385  if (b == L2) // These are analog buttons
386  return (uint8_t)(ButtonState[controller] >> 8);
387  else if (b == R2)
388  return (uint8_t)ButtonState[controller];
389  return (bool)(ButtonState[controller] & ((uint32_t)pgm_read_word(&XBOXBUTTONS[(uint8_t)b]) << 16));
390 }
391 
392 bool XBOXRECV::getButtonClick(uint8_t controller, Button b) {
393  if (b == L2) {
394  if (L2Clicked[controller]) {
395  L2Clicked[controller] = false;
396  return true;
397  }
398  return false;
399  } else if (b == R2) {
400  if (R2Clicked[controller]) {
401  R2Clicked[controller] = false;
402  return true;
403  }
404  return false;
405  }
406  uint16_t button = pgm_read_word(&XBOXBUTTONS[(uint8_t)b]);
407  bool click = (ButtonClickState[controller] & button);
408  ButtonClickState[controller] &= ~button; // clear "click" event
409  return click;
410 }
411 
412 int16_t XBOXRECV::getAnalogHat(uint8_t controller, AnalogHat a) {
413  return hatValue[controller][a];
414 }
415 
416 bool XBOXRECV::buttonChanged(uint8_t controller) {
417  bool state = buttonStateChanged[controller];
418  buttonStateChanged[controller] = false;
419  return state;
420 }
421 
422 /*
423 ControllerStatus Breakdown
424  ControllerStatus[controller] & 0x0001 // 0
425  ControllerStatus[controller] & 0x0002 // normal batteries, no rechargeable battery pack
426  ControllerStatus[controller] & 0x0004 // controller starting up / settling
427  ControllerStatus[controller] & 0x0008 // headset adapter plugged in, but no headphones connected (mute?)
428  ControllerStatus[controller] & 0x0010 // 0
429  ControllerStatus[controller] & 0x0020 // 1
430  ControllerStatus[controller] & 0x0040 // battery level (high bit)
431  ControllerStatus[controller] & 0x0080 // battery level (low bit)
432  ControllerStatus[controller] & 0x0100 // 1
433  ControllerStatus[controller] & 0x0200 // 1
434  ControllerStatus[controller] & 0x0400 // headset adapter plugged in
435  ControllerStatus[controller] & 0x0800 // 0
436  ControllerStatus[controller] & 0x1000 // 1
437  ControllerStatus[controller] & 0x2000 // 0
438  ControllerStatus[controller] & 0x4000 // 0
439  ControllerStatus[controller] & 0x8000 // 0
440  */
441 uint8_t XBOXRECV::getBatteryLevel(uint8_t controller) {
442  return ((controllerStatus[controller] & 0x00C0) >> 6);
443 }
444 
445 void XBOXRECV::XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes) {
446  uint8_t rcode;
447  uint8_t outputPipe;
448  switch (controller) {
449  case 0: outputPipe = XBOX_OUTPUT_PIPE_1;
450  break;
451  case 1: outputPipe = XBOX_OUTPUT_PIPE_2;
452  break;
453  case 2: outputPipe = XBOX_OUTPUT_PIPE_3;
454  break;
455  case 3: outputPipe = XBOX_OUTPUT_PIPE_4;
456  break;
457  }
458  rcode = pUsb->outTransfer(bAddress, epInfo[ outputPipe ].epAddr, nbytes, data);
459 #ifdef EXTRADEBUG
460  if (rcode)
461  Notify(PSTR("Error sending Xbox message\r\n"), 0x80);
462 #endif
463 }
464 
465 void XBOXRECV::setLedRaw(uint8_t controller, uint8_t value) {
466  writeBuf[0] = 0x00;
467  writeBuf[1] = 0x00;
468  writeBuf[2] = 0x08;
469  writeBuf[3] = value | 0x40;
470 
471  XboxCommand(controller, writeBuf, 4);
472 }
473 
474 void XBOXRECV::setLedOn(uint8_t controller, LED led) {
475  if (led != ALL) // All LEDs can't be on a the same time
476  setLedRaw(controller, (pgm_read_byte(&XBOXLEDS[(uint8_t)led])) + 4);
477 }
478 
479 void XBOXRECV::setLedBlink(uint8_t controller, LED led) {
480  setLedRaw(controller, pgm_read_byte(&XBOXLEDS[(uint8_t)led]));
481 }
482 
483 void XBOXRECV::setLedMode(uint8_t controller, LEDMode ledMode) { // This function is used to do some speciel LED stuff the controller supports
484  setLedRaw(controller, (uint8_t)ledMode);
485 }
486 
487 /* PC runs this at interval of approx 2 seconds
488 Thanks to BusHound from Perisoft.net for the Windows USB Analysis output
489 Found by timstamp.co.uk
490  */
491 void XBOXRECV::checkStatus() {
492  if (!bPollEnable)
493  return;
494  // Get controller info
495  writeBuf[0] = 0x08;
496  writeBuf[1] = 0x00;
497  writeBuf[2] = 0x0f;
498  writeBuf[3] = 0xc0;
499  for (uint8_t i = 0; i < 4; i++) {
500  XboxCommand(i, writeBuf, 4);
501  }
502  // Get battery status
503  writeBuf[0] = 0x00;
504  writeBuf[1] = 0x00;
505  writeBuf[2] = 0x00;
506  writeBuf[3] = 0x40;
507  for (uint8_t i = 0; i < 4; i++) {
508  if (Xbox360Connected[i])
509  XboxCommand(i, writeBuf, 4);
510  }
511 }
512 
513 void XBOXRECV::setRumbleOn(uint8_t controller, uint8_t lValue, uint8_t rValue) {
514  writeBuf[0] = 0x00;
515  writeBuf[1] = 0x01;
516  writeBuf[2] = 0x0f;
517  writeBuf[3] = 0xc0;
518  writeBuf[4] = 0x00;
519  writeBuf[5] = lValue; // big weight
520  writeBuf[6] = rValue; // small weight
521 
522  XboxCommand(controller, writeBuf, 7);
523 }