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