USB Host Shield 2.0
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
XBOXUSB.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 
18 #include "XBOXUSB.h"
19 #define DEBUG // Uncomment to print data for debugging
20 //#define EXTRADEBUG // Uncomment to get even more debugging data
21 //#define PRINTREPORT // Uncomment to print the report send by the Xbox 360 Controller
22 
24 pUsb(p), // pointer to USB class instance - mandatory
25 bAddress(0), // device address - mandatory
26 bPollEnable(false) { // don't start polling before dongle is connected
27  for(uint8_t i=0; i<XBOX_MAX_ENDPOINTS; i++) {
28  epInfo[i].epAddr = 0;
29  epInfo[i].maxPktSize = (i) ? 0 : 8;
30  epInfo[i].epAttribs = 0;
32  }
33 
34  if (pUsb) // register in USB subsystem
35  pUsb->RegisterDeviceClass(this); //set devConfig[] entry
36 }
37 
38 uint8_t XBOXUSB::Init(uint8_t parent, uint8_t port, bool lowspeed) {
39  uint8_t buf[sizeof(USB_DEVICE_DESCRIPTOR)];
40  uint8_t rcode;
41  UsbDevice *p = NULL;
42  EpInfo *oldep_ptr = NULL;
43  uint16_t PID;
44  uint16_t VID;
45 
46  // get memory address of USB device address pool
47  AddressPool &addrPool = pUsb->GetAddressPool();
48 #ifdef EXTRADEBUG
49  Notify(PSTR("\r\nXBOXUSB Init"));
50 #endif
51  // check if address has already been assigned to an instance
52  if (bAddress) {
53 #ifdef DEBUG
54  Notify(PSTR("\r\nAddress in use"));
55 #endif
57  }
58 
59  // Get pointer to pseudo device with address 0 assigned
60  p = addrPool.GetUsbDevicePtr(0);
61 
62  if (!p) {
63 #ifdef DEBUG
64  Notify(PSTR("\r\nAddress not found"));
65 #endif
67  }
68 
69  if (!p->epinfo) {
70 #ifdef DEBUG
71  Notify(PSTR("\r\nepinfo is null"));
72 #endif
74  }
75 
76  // Save old pointer to EP_RECORD of address 0
77  oldep_ptr = p->epinfo;
78 
79  // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
80  p->epinfo = epInfo;
81 
82  p->lowspeed = lowspeed;
83 
84  // Get device descriptor
85  rcode = pUsb->getDevDescr(0, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);// Get device descriptor - addr, ep, nbytes, data
86  // Restore p->epinfo
87  p->epinfo = oldep_ptr;
88 
89  if(rcode)
90  goto FailGetDevDescr;
91 
92  VID = ((USB_DEVICE_DESCRIPTOR*)buf)->idVendor;
93  PID = ((USB_DEVICE_DESCRIPTOR*)buf)->idProduct;
94 
95  if(VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID) // We just check if it's a xbox controller using the Vendor ID
96  goto FailUnknownDevice;
97  if(PID == XBOX_WIRELESS_PID) {
98 #ifdef DEBUG
99  Notify(PSTR("\r\nYou have plugged in a wireless Xbox 360 controller - it doesn't support USB communication"));
100 #endif
101  goto FailUnknownDevice;
102  }
104 #ifdef DEBUG
105  Notify(PSTR("\r\nThis library only supports Xbox 360 controllers via USB"));
106 #endif
107  goto FailUnknownDevice;
108  }
109 
110  // Allocate new address according to device class
111  bAddress = addrPool.AllocAddress(parent, false, port);
112 
113  if (!bAddress)
115 
116  // Extract Max Packet Size from device descriptor
117  epInfo[0].maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0;
118 
119  // Assign new address to the device
120  rcode = pUsb->setAddr( 0, 0, bAddress );
121  if (rcode) {
122  p->lowspeed = false;
123  addrPool.FreeAddress(bAddress);
124  bAddress = 0;
125 #ifdef DEBUG
126  Notify(PSTR("\r\nsetAddr: "));
127 #endif
128  PrintHex<uint8_t>(rcode);
129  return rcode;
130  }
131 #ifdef EXTRADEBUG
132  Notify(PSTR("\r\nAddr: "));
133  PrintHex<uint8_t>(bAddress);
134 #endif
135  p->lowspeed = false;
136 
137  //get pointer to assigned address record
138  p = addrPool.GetUsbDevicePtr(bAddress);
139  if (!p)
141 
142  p->lowspeed = lowspeed;
143 
144  // Assign epInfo to epinfo pointer - only EP0 is known
145  rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
146  if (rcode)
147  goto FailSetDevTblEntry;
148 
149  /* The application will work in reduced host mode, so we can save program and data
150  memory space. After verifying the VID we will use known values for the
151  configuration values for device, interface, endpoints and HID for the XBOX360 Controllers */
152 
153  /* Initialize data structures for endpoints of device */
154  epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX 360 report endpoint
156  epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
160  epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX 360 output endpoint
162  epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
166 
167  rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
168  if( rcode )
169  goto FailSetDevTblEntry;
170 
171  delay(200);//Give time for address change
172 
173  rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
174  if( rcode )
175  goto FailSetConf;
176 
177 #ifdef DEBUG
178  Notify(PSTR("\r\nXbox 360 Controller Connected\r\n"));
179 #endif
180  setLedOn(LED1);
181  Xbox360Connected = true;
182  bPollEnable = true;
183  return 0; // successful configuration
184 
185  /* diagnostic messages */
186 FailGetDevDescr:
187 #ifdef DEBUG
188  Notify(PSTR("\r\ngetDevDescr:"));
189 #endif
190  goto Fail;
191 FailSetDevTblEntry:
192 #ifdef DEBUG
193  Notify(PSTR("\r\nsetDevTblEn:"));
194 #endif
195  goto Fail;
196 FailSetConf:
197 #ifdef DEBUG
198  Notify(PSTR("\r\nsetConf:"));
199 #endif
200  goto Fail;
201 FailUnknownDevice:
202 #ifdef DEBUG
203  Notify(PSTR("\r\nUnknown Device Connected - VID: "));
204  PrintHex<uint16_t>(VID);
205  Notify(PSTR(" PID: "));
206  PrintHex<uint16_t>(PID);
207 #endif
209  goto Fail;
210 Fail:
211 #ifdef DEBUG
212  Notify(PSTR("\r\nXbox 360 Init Failed, error code: "));
213  Serial.print(rcode,HEX);
214 #endif
215  Release();
216  return rcode;
217 }
218 
219 /* Performs a cleanup after failed Init() attempt */
220 uint8_t XBOXUSB::Release() {
221  Xbox360Connected = false;
223  bAddress = 0;
224  bPollEnable = false;
225  return 0;
226 }
227 uint8_t XBOXUSB::Poll() {
228  if (!bPollEnable)
229  return 0;
230  uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
231  pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
232  readReport();
233 #ifdef PRINTREPORT
234  printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
235 #endif
236  return 0;
237 }
238 
239 void XBOXUSB::readReport() {
240  if (readBuf == NULL)
241  return;
242  if(readBuf[0] != 0x00 || readBuf[1] != 0x14) { // Check if it's the correct report - the controller also sends different status reports
243  return;
244  }
245 
246  ButtonState = (uint32_t)(readBuf[5] | ((uint16_t)readBuf[4] << 8) | ((uint32_t)readBuf[3] << 16) | ((uint32_t)readBuf[2] << 24));
247 
248  hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
249  hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
250  hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
251  hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
252 
253  //Notify(PSTR("\r\nButtonState"));
254  //PrintHex<uint32_t>(ButtonState);
255 
256  if(ButtonState != OldButtonState) {
257  ButtonClickState = (ButtonState >> 16) & ((~OldButtonState) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
258  if(((uint8_t)OldButtonState) == 0 && ((uint8_t)ButtonState) != 0) // The L2 and R2 buttons are special as they are analog buttons
259  R2Clicked = true;
260  if((uint8_t)(OldButtonState >> 8) == 0 && (uint8_t)(ButtonState >> 8) != 0)
261  L2Clicked = true;
262  OldButtonState = ButtonState;
263  }
264 }
265 
266 void XBOXUSB::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
267 #ifdef PRINTREPORT
268  if (readBuf == NULL)
269  return;
270  for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE;i++) {
271  PrintHex<uint8_t>(readBuf[i]);
272  Serial.print(" ");
273  }
274  Serial.println();
275 #endif
276 }
277 
279  if(b == L2) // These are analog buttons
280  return (uint8_t)(ButtonState >> 8);
281  else if(b == R2)
282  return (uint8_t)ButtonState;
283  return (ButtonState & ((uint32_t)pgm_read_word(&BUTTONS[(uint8_t)b]) << 16));
284 }
286  if(b == L2) {
287  if(L2Clicked) {
288  L2Clicked = false;
289  return true;
290  }
291  return false;
292  }
293  else if(b == R2) {
294  if(R2Clicked) {
295  R2Clicked = false;
296  return true;
297  }
298  return false;
299  }
300  uint16_t button = pgm_read_word(&BUTTONS[(uint8_t)b]);
301  bool click = (ButtonClickState & button);
302  ButtonClickState &= ~button; // clear "click" event
303  return click;
304 }
306  return hatValue[a];
307 }
308 
309 /* Xbox Controller commands */
310 void XBOXUSB::XboxCommand(uint8_t* data, uint16_t nbytes) {
311  //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x00), Report Type (Output 0x02), interface (0x00), datalength, datalength, data)
312  pUsb->ctrlReq(bAddress,epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
313 }
314 void XBOXUSB::setLedRaw(uint8_t value) {
315  writeBuf[0] = 0x01;
316  writeBuf[1] = 0x03;
317  writeBuf[2] = value;
318 
319  XboxCommand(writeBuf, 3);
320 }
322  if(led != ALL) // All LEDs can't be on a the same time
323  setLedRaw((pgm_read_byte(&LEDS[(uint8_t)led]))+4);
324 }
326  setLedRaw(pgm_read_byte(&LEDS[(uint8_t)led]));
327 }
328 void XBOXUSB::setLedMode(LEDMode ledMode) { // This function is used to do some speciel LED stuff the controller supports
329  setLedRaw((uint8_t)ledMode);
330 }
331 void XBOXUSB::setRumbleOn(uint8_t lValue, uint8_t rValue) {
332  writeBuf[0] = 0x00;
333  writeBuf[1] = 0x08;
334  writeBuf[2] = 0x00;
335  writeBuf[3] = lValue; // big weight
336  writeBuf[4] = rValue; // small weight
337  writeBuf[5] = 0x00;
338  writeBuf[6] = 0x00;
339  writeBuf[7] = 0x00;
340 
341  XboxCommand(writeBuf, 8);
342 }