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 // To enable serial debugging uncomment "#define DEBUG_USB_HOST" in message.h
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"), 0x80);
50 #endif
51  // check if address has already been assigned to an instance
52  if (bAddress) {
53 #ifdef DEBUG_USB_HOST
54  Notify(PSTR("\r\nAddress in use"), 0x80);
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_USB_HOST
64  Notify(PSTR("\r\nAddress not found"), 0x80);
65 #endif
67  }
68 
69  if (!p->epinfo) {
70 #ifdef DEBUG_USB_HOST
71  Notify(PSTR("\r\nepinfo is null"), 0x80);
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) // Check VID
96  goto FailUnknownDevice;
97  if (PID == XBOX_WIRELESS_PID) {
98 #ifdef DEBUG_USB_HOST
99  Notify(PSTR("\r\nYou have plugged in a wireless Xbox 360 controller - it doesn't support USB communication"), 0x80);
100 #endif
101  goto FailUnknownDevice;
103 #ifdef DEBUG_USB_HOST
104  Notify(PSTR("\r\nThis library only supports Xbox 360 controllers via USB"), 0x80);
105 #endif
106  goto FailUnknownDevice;
107  } else if (PID != XBOX_WIRED_PID) // Check PID
108  goto FailUnknownDevice;
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_USB_HOST
126  Notify(PSTR("\r\nsetAddr: "), 0x80);
127  D_PrintHex<uint8_t > (rcode, 0x80);
128 #endif
129  return rcode;
130  }
131 #ifdef EXTRADEBUG
132  Notify(PSTR("\r\nAddr: "), 0x80);
133  D_PrintHex<uint8_t > (bAddress, 0x80);
134 #endif
135  delay(300); // Spec says you should wait at least 200ms
136 
137  p->lowspeed = false;
138 
139  //get pointer to assigned address record
140  p = addrPool.GetUsbDevicePtr(bAddress);
141  if (!p)
143 
144  p->lowspeed = lowspeed;
145 
146  // Assign epInfo to epinfo pointer - only EP0 is known
147  rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
148  if (rcode)
149  goto FailSetDevTblEntry;
150 
151  /* The application will work in reduced host mode, so we can save program and data
152  memory space. After verifying the VID we will use known values for the
153  configuration values for device, interface, endpoints and HID for the XBOX360 Controllers */
154 
155  /* Initialize data structures for endpoints of device */
156  epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX 360 report endpoint
158  epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
162  epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX 360 output endpoint
164  epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
168 
169  rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
170  if (rcode)
171  goto FailSetDevTblEntry;
172 
173  delay(200); // Give time for address change
174 
175  rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
176  if (rcode)
177  goto FailSetConfDescr;
178 
179 #ifdef DEBUG_USB_HOST
180  Notify(PSTR("\r\nXbox 360 Controller Connected\r\n"), 0x80);
181 #endif
182  onInit();
183  Xbox360Connected = true;
184  bPollEnable = true;
185  return 0; // Successful configuration
186 
187  /* Diagnostic messages */
188 FailGetDevDescr:
189 #ifdef DEBUG_USB_HOST
191  goto Fail;
192 #endif
193 
194 FailSetDevTblEntry:
195 #ifdef DEBUG_USB_HOST
197  goto Fail;
198 #endif
199 
200 FailSetConfDescr:
201 #ifdef DEBUG_USB_HOST
203  goto Fail;
204 #endif
205 FailUnknownDevice:
206 #ifdef DEBUG_USB_HOST
207  NotifyFailUnknownDevice(VID, PID);
208 #endif
210 
211 Fail:
212 #ifdef DEBUG_USB_HOST
213  Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
214  NotifyFail(rcode);
215 #endif
216  Release();
217  return rcode;
218 }
219 
220 /* Performs a cleanup after failed Init() attempt */
221 uint8_t XBOXUSB::Release() {
222  Xbox360Connected = false;
224  bAddress = 0;
225  bPollEnable = false;
226  return 0;
227 }
228 
229 uint8_t XBOXUSB::Poll() {
230  if (!bPollEnable)
231  return 0;
232  uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
233  pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
234  readReport();
235 #ifdef PRINTREPORT
236  printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
237 #endif
238  return 0;
239 }
240 
241 void XBOXUSB::readReport() {
242  if (readBuf == NULL)
243  return;
244  if (readBuf[0] != 0x00 || readBuf[1] != 0x14) { // Check if it's the correct report - the controller also sends different status reports
245  return;
246  }
247 
248  ButtonState = (uint32_t)(readBuf[5] | ((uint16_t)readBuf[4] << 8) | ((uint32_t)readBuf[3] << 16) | ((uint32_t)readBuf[2] << 24));
249 
250  hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
251  hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
252  hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
253  hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
254 
255  //Notify(PSTR("\r\nButtonState"), 0x80);
256  //PrintHex<uint32_t>(ButtonState, 0x80);
257 
258  if (ButtonState != OldButtonState) {
259  ButtonClickState = (ButtonState >> 16) & ((~OldButtonState) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
260  if (((uint8_t)OldButtonState) == 0 && ((uint8_t)ButtonState) != 0) // The L2 and R2 buttons are special as they are analog buttons
261  R2Clicked = true;
262  if ((uint8_t)(OldButtonState >> 8) == 0 && (uint8_t)(ButtonState >> 8) != 0)
263  L2Clicked = true;
264  OldButtonState = ButtonState;
265  }
266 }
267 
268 void XBOXUSB::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
269 #ifdef PRINTREPORT
270  if (readBuf == NULL)
271  return;
272  for (uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {
273  D_PrintHex<uint8_t > (readBuf[i], 0x80);
274  Notify(PSTR(" "), 0x80);
275  }
276  Notify(PSTR("\r\n"), 0x80);
277 #endif
278 }
279 
281  if (b == L2) // These are analog buttons
282  return (uint8_t)(ButtonState >> 8);
283  else if (b == R2)
284  return (uint8_t)ButtonState;
285  return (ButtonState & ((uint32_t)pgm_read_word(&XBOXBUTTONS[(uint8_t)b]) << 16));
286 }
287 
289  if (b == L2) {
290  if (L2Clicked) {
291  L2Clicked = false;
292  return true;
293  }
294  return false;
295  } else if (b == R2) {
296  if (R2Clicked) {
297  R2Clicked = false;
298  return true;
299  }
300  return false;
301  }
302  uint16_t button = pgm_read_word(&XBOXBUTTONS[(uint8_t)b]);
303  bool click = (ButtonClickState & button);
304  ButtonClickState &= ~button; // clear "click" event
305  return click;
306 }
307 
309  return hatValue[a];
310 }
311 
312 /* Xbox Controller commands */
313 void XBOXUSB::XboxCommand(uint8_t* data, uint16_t nbytes) {
314  //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)
315  pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
316 }
317 
318 void XBOXUSB::setLedRaw(uint8_t value) {
319  writeBuf[0] = 0x01;
320  writeBuf[1] = 0x03;
321  writeBuf[2] = value;
322 
323  XboxCommand(writeBuf, 3);
324 }
325 
327  if (led != ALL) // All LEDs can't be on a the same time
328  setLedRaw((pgm_read_byte(&XBOXLEDS[(uint8_t)led])) + 4);
329 }
330 
332  setLedRaw(pgm_read_byte(&XBOXLEDS[(uint8_t)led]));
333 }
334 
335 void XBOXUSB::setLedMode(LEDMode ledMode) { // This function is used to do some speciel LED stuff the controller supports
336  setLedRaw((uint8_t)ledMode);
337 }
338 
339 void XBOXUSB::setRumbleOn(uint8_t lValue, uint8_t rValue) {
340  writeBuf[0] = 0x00;
341  writeBuf[1] = 0x08;
342  writeBuf[2] = 0x00;
343  writeBuf[3] = lValue; // big weight
344  writeBuf[4] = rValue; // small weight
345  writeBuf[5] = 0x00;
346  writeBuf[6] = 0x00;
347  writeBuf[7] = 0x00;
348 
349  XboxCommand(writeBuf, 8);
350 }
351 
352 void XBOXUSB::onInit() {
353  if (pFuncOnInit)
354  pFuncOnInit(); // Call the user function
355  else
356  setLedOn(LED1);
357 }