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;
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  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_USB_HOST
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:
221 #ifdef DEBUG_USB_HOST
223  goto Fail;
224 #endif
225 
226 FailSetDevTblEntry:
227 #ifdef DEBUG_USB_HOST
229  goto Fail;
230 #endif
231 
232 FailSetConfDescr:
233 #ifdef DEBUG_USB_HOST
235 #endif
236  goto Fail;
237 
238 FailUnknownDevice:
239 #ifdef DEBUG_USB_HOST
240  NotifyFailUnknownDevice(VID,PID);
241 #endif
243 
244 Fail:
245 #ifdef DEBUG_USB_HOST
246  Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
247  NotifyFail(rcode);
248 #endif
249  Release();
250  return rcode;
251 }
252 
253 /* Performs a cleanup after failed Init() attempt */
254 uint8_t XBOXRECV::Release() {
255  XboxReceiverConnected = false;
256  for (uint8_t i = 0; i < 4; i++)
257  Xbox360Connected[i] = 0x00;
259  bAddress = 0;
260  bPollEnable = false;
261  return 0;
262 }
263 
264 uint8_t XBOXRECV::Poll() {
265  if (!bPollEnable)
266  return 0;
267  if (!timer || ((millis() - timer) > 3000)) { // Run checkStatus every 3 seconds
268  timer = millis();
269  checkStatus();
270  }
271  uint8_t inputPipe;
272  uint16_t bufferSize;
273  for (uint8_t i = 0; i < 4; i++) {
274  switch (i) {
275  case 0: inputPipe = XBOX_INPUT_PIPE_1;
276  break;
277  case 1: inputPipe = XBOX_INPUT_PIPE_2;
278  break;
279  case 2: inputPipe = XBOX_INPUT_PIPE_3;
280  break;
281  case 3: inputPipe = XBOX_INPUT_PIPE_4;
282  break;
283  }
284  bufferSize = EP_MAXPKTSIZE; // This is the maximum number of bytes we want to receive
285  pUsb->inTransfer(bAddress, epInfo[ inputPipe ].epAddr, &bufferSize, readBuf);
286  if (bufferSize > 0) { // The number of received bytes
287 #ifdef EXTRADEBUG
288  Notify(PSTR("Bytes Received: "), 0x80);
289  D_PrintHex<uint16_t > (bufferSize, 0x80);
290  Notify(PSTR("\r\n"), 0x80);
291 #endif
292  readReport(i);
293 #ifdef PRINTREPORT
294  printReport(i, bufferSize); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
295 #endif
296  }
297  }
298  return 0;
299 }
300 
301 void XBOXRECV::readReport(uint8_t controller) {
302  if (readBuf == NULL)
303  return;
304  // This report is send when a controller is connected and disconnected
305  if (readBuf[0] == 0x08 && readBuf[1] != Xbox360Connected[controller]) {
306  Xbox360Connected[controller] = readBuf[1];
307 #ifdef DEBUG_USB_HOST
308  Notify(PSTR("Controller "), 0x80);
309  Notify(controller, 0x80);
310 #endif
311  if (Xbox360Connected[controller]) {
312 #ifdef DEBUG_USB_HOST
313  char* str = 0;
314  switch (readBuf[1]) {
315  case 0x80: str = PSTR(" as controller\r\n");
316  break;
317  case 0x40: str = PSTR(" as headset\r\n");
318  break;
319  case 0xC0: str = PSTR(" as controller+headset\r\n");
320  break;
321  }
322  Notify(PSTR(": connected"), 0x80);
323  Notify(str, 0x80);
324 #endif
325  LED led;
326  switch (controller) {
327  case 0: led = LED1;
328  break;
329  case 1: led = LED2;
330  break;
331  case 2: led = LED3;
332  break;
333  case 3: led = LED4;
334  break;
335  }
336  setLedOn(led, controller);
337  }
338 #ifdef DEBUG_USB_HOST
339  else
340  Notify(PSTR(": disconnected\r\n"), 0x80);
341 #endif
342  return;
343  }
344  // Controller status report
345  if (readBuf[1] == 0x00 && readBuf[3] & 0x13 && readBuf[4] >= 0x22) {
346  controllerStatus[controller] = ((uint16_t)readBuf[3] << 8) | readBuf[4];
347  return;
348  }
349  if (readBuf[1] != 0x01) // Check if it's the correct report - the receiver also sends different status reports
350  return;
351 
352  // A controller must be connected if it's sending data
353  if (!Xbox360Connected[controller])
354  Xbox360Connected[controller] |= 0x80;
355 
356  ButtonState[controller] = (uint32_t)(readBuf[9] | ((uint16_t)readBuf[8] << 8) | ((uint32_t)readBuf[7] << 16) | ((uint32_t)readBuf[6] << 24));
357 
358  hatValue[controller][LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
359  hatValue[controller][LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
360  hatValue[controller][RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);
361  hatValue[controller][RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);
362 
363  //Notify(PSTR("\r\nButtonState: "), 0x80);
364  //PrintHex<uint32_t>(ButtonState[controller], 0x80);
365 
366  if (ButtonState[controller] != OldButtonState[controller]) {
367  buttonStateChanged[controller] = true;
368  ButtonClickState[controller] = (ButtonState[controller] >> 16) & ((~OldButtonState[controller]) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
369  if (((uint8_t)OldButtonState[controller]) == 0 && ((uint8_t)ButtonState[controller]) != 0) // The L2 and R2 buttons are special as they are analog buttons
370  R2Clicked[controller] = true;
371  if ((uint8_t)(OldButtonState[controller] >> 8) == 0 && (uint8_t)(ButtonState[controller] >> 8) != 0)
372  L2Clicked[controller] = true;
373  OldButtonState[controller] = ButtonState[controller];
374  }
375 }
376 
377 void XBOXRECV::printReport(uint8_t controller, uint8_t nBytes) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
378 #ifdef PRINTREPORT
379  if (readBuf == NULL)
380  return;
381  Notify(PSTR("Controller "), 0x80);
382  Notify(controller, 0x80);
383  Notify(PSTR(": "), 0x80);
384  for (uint8_t i = 0; i < nBytes; i++) {
385  D_PrintHex<uint8_t > (readBuf[i], 0x80);
386  Notify(PSTR(" "), 0x80);
387  }
388  Notify(PSTR("\r\n"), 0x80);
389 #endif
390 }
391 
392 uint8_t XBOXRECV::getButtonPress(Button b, uint8_t controller) {
393  if (b == L2) // These are analog buttons
394  return (uint8_t)(ButtonState[controller] >> 8);
395  else if (b == R2)
396  return (uint8_t)ButtonState[controller];
397  return (bool)(ButtonState[controller] & ((uint32_t)pgm_read_word(&XBOXBUTTONS[(uint8_t)b]) << 16));
398 }
399 
400 bool XBOXRECV::getButtonClick(Button b, uint8_t controller) {
401  if (b == L2) {
402  if (L2Clicked[controller]) {
403  L2Clicked[controller] = false;
404  return true;
405  }
406  return false;
407  } else if (b == R2) {
408  if (R2Clicked[controller]) {
409  R2Clicked[controller] = false;
410  return true;
411  }
412  return false;
413  }
414  uint16_t button = pgm_read_word(&XBOXBUTTONS[(uint8_t)b]);
415  bool click = (ButtonClickState[controller] & button);
416  ButtonClickState[controller] &= ~button; // clear "click" event
417  return click;
418 }
419 
420 int16_t XBOXRECV::getAnalogHat(AnalogHat a, uint8_t controller) {
421  return hatValue[controller][a];
422 }
423 
424 bool XBOXRECV::buttonChanged(uint8_t controller) {
425  bool state = buttonStateChanged[controller];
426  buttonStateChanged[controller] = false;
427  return state;
428 }
429 
430 /*
431 ControllerStatus Breakdown
432  ControllerStatus[controller] & 0x0001 // 0
433  ControllerStatus[controller] & 0x0002 // normal batteries, no rechargeable battery pack
434  ControllerStatus[controller] & 0x0004 // controller starting up / settling
435  ControllerStatus[controller] & 0x0008 // headset adapter plugged in, but no headphones connected (mute?)
436  ControllerStatus[controller] & 0x0010 // 0
437  ControllerStatus[controller] & 0x0020 // 1
438  ControllerStatus[controller] & 0x0040 // battery level (high bit)
439  ControllerStatus[controller] & 0x0080 // battery level (low bit)
440  ControllerStatus[controller] & 0x0100 // 1
441  ControllerStatus[controller] & 0x0200 // 1
442  ControllerStatus[controller] & 0x0400 // headset adapter plugged in
443  ControllerStatus[controller] & 0x0800 // 0
444  ControllerStatus[controller] & 0x1000 // 1
445  ControllerStatus[controller] & 0x2000 // 0
446  ControllerStatus[controller] & 0x4000 // 0
447  ControllerStatus[controller] & 0x8000 // 0
448  */
449 uint8_t XBOXRECV::getBatteryLevel(uint8_t controller) {
450  return ((controllerStatus[controller] & 0x00C0) >> 6);
451 }
452 
453 void XBOXRECV::XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes) {
454  uint8_t rcode;
455  uint8_t outputPipe;
456  switch (controller) {
457  case 0: outputPipe = XBOX_OUTPUT_PIPE_1;
458  break;
459  case 1: outputPipe = XBOX_OUTPUT_PIPE_2;
460  break;
461  case 2: outputPipe = XBOX_OUTPUT_PIPE_3;
462  break;
463  case 3: outputPipe = XBOX_OUTPUT_PIPE_4;
464  break;
465  }
466  rcode = pUsb->outTransfer(bAddress, epInfo[ outputPipe ].epAddr, nbytes, data);
467 #ifdef EXTRADEBUG
468  if (rcode)
469  Notify(PSTR("Error sending Xbox message\r\n"), 0x80);
470 #endif
471 }
472 
473 void XBOXRECV::setLedRaw(uint8_t value, uint8_t controller) {
474  writeBuf[0] = 0x00;
475  writeBuf[1] = 0x00;
476  writeBuf[2] = 0x08;
477  writeBuf[3] = value | 0x40;
478 
479  XboxCommand(controller, writeBuf, 4);
480 }
481 
482 void XBOXRECV::setLedOn(LED led, uint8_t controller) {
483  if (led != ALL) // All LEDs can't be on a the same time
484  setLedRaw(pgm_read_byte(&XBOXLEDS[(uint8_t)led]) + 4, controller);
485 }
486 
487 void XBOXRECV::setLedBlink(LED led, uint8_t controller) {
488  setLedRaw(pgm_read_byte(&XBOXLEDS[(uint8_t)led]), controller);
489 }
490 
491 void XBOXRECV::setLedMode(LEDMode ledMode, uint8_t controller) { // This function is used to do some speciel LED stuff the controller supports
492  setLedRaw((uint8_t)ledMode, controller);
493 }
494 
495 /* PC runs this at interval of approx 2 seconds
496 Thanks to BusHound from Perisoft.net for the Windows USB Analysis output
497 Found by timstamp.co.uk
498  */
499 void XBOXRECV::checkStatus() {
500  if (!bPollEnable)
501  return;
502  // Get controller info
503  writeBuf[0] = 0x08;
504  writeBuf[1] = 0x00;
505  writeBuf[2] = 0x0f;
506  writeBuf[3] = 0xc0;
507  for (uint8_t i = 0; i < 4; i++) {
508  XboxCommand(i, writeBuf, 4);
509  }
510  // Get battery status
511  writeBuf[0] = 0x00;
512  writeBuf[1] = 0x00;
513  writeBuf[2] = 0x00;
514  writeBuf[3] = 0x40;
515  for (uint8_t i = 0; i < 4; i++) {
516  if (Xbox360Connected[i])
517  XboxCommand(i, writeBuf, 4);
518  }
519 }
520 
521 void XBOXRECV::setRumbleOn(uint8_t lValue, uint8_t rValue, uint8_t controller) {
522  writeBuf[0] = 0x00;
523  writeBuf[1] = 0x01;
524  writeBuf[2] = 0x0f;
525  writeBuf[3] = 0xc0;
526  writeBuf[4] = 0x00;
527  writeBuf[5] = lValue; // big weight
528  writeBuf[6] = rValue; // small weight
529 
530  XboxCommand(controller, writeBuf, 7);
531 }