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) // 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_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  }
108 
109  // Allocate new address according to device class
110  bAddress = addrPool.AllocAddress(parent, false, port);
111 
112  if (!bAddress)
114 
115  // Extract Max Packet Size from device descriptor
116  epInfo[0].maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0;
117 
118  // Assign new address to the device
119  rcode = pUsb->setAddr(0, 0, bAddress);
120  if (rcode) {
121  p->lowspeed = false;
122  addrPool.FreeAddress(bAddress);
123  bAddress = 0;
124 #ifdef DEBUG_USB_HOST
125  Notify(PSTR("\r\nsetAddr: "), 0x80);
126 #endif
127  PrintHex<uint8_t > (rcode, 0x80);
128  return rcode;
129  }
130 #ifdef EXTRADEBUG
131  Notify(PSTR("\r\nAddr: "), 0x80);
132  PrintHex<uint8_t > (bAddress, 0x80);
133 #endif
134  p->lowspeed = false;
135 
136  //get pointer to assigned address record
137  p = addrPool.GetUsbDevicePtr(bAddress);
138  if (!p)
140 
141  p->lowspeed = lowspeed;
142 
143  // Assign epInfo to epinfo pointer - only EP0 is known
144  rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
145  if (rcode)
146  goto FailSetDevTblEntry;
147 
148  /* The application will work in reduced host mode, so we can save program and data
149  memory space. After verifying the VID we will use known values for the
150  configuration values for device, interface, endpoints and HID for the XBOX360 Controllers */
151 
152  /* Initialize data structures for endpoints of device */
153  epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX 360 report endpoint
155  epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
159  epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX 360 output endpoint
161  epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
165 
166  rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
167  if (rcode)
168  goto FailSetDevTblEntry;
169 
170  delay(200); //Give time for address change
171 
172  rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
173  if (rcode)
174  goto FailSetConfDescr;
175 
176 #ifdef DEBUG_USB_HOST
177  Notify(PSTR("\r\nXbox 360 Controller Connected\r\n"), 0x80);
178 #endif
179  setLedOn(LED1);
180  Xbox360Connected = true;
181  bPollEnable = true;
182  return 0; // successful configuration
183 
184  /* diagnostic messages */
185 FailGetDevDescr:
186 #ifdef DEBUG_USB_HOST
188  goto Fail;
189 #endif
190 
191 FailSetDevTblEntry:
192 #ifdef DEBUG_USB_HOST
194  goto Fail;
195 #endif
196 
197 FailSetConfDescr:
198 #ifdef DEBUG_USB_HOST
200  goto Fail;
201 #endif
202 FailUnknownDevice:
203 #ifdef DEBUG_USB_HOST
204  NotifyFailUnknownDevice(VID, PID);
205 #endif
207 
208 Fail:
209 #ifdef DEBUG_USB_HOST
210  Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
211  NotifyFail(rcode);
212 #endif
213  Release();
214  return rcode;
215 }
216 
217 /* Performs a cleanup after failed Init() attempt */
218 uint8_t XBOXUSB::Release() {
219  Xbox360Connected = false;
221  bAddress = 0;
222  bPollEnable = false;
223  return 0;
224 }
225 
226 uint8_t XBOXUSB::Poll() {
227  if (!bPollEnable)
228  return 0;
229  uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
230  pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
231  readReport();
232 #ifdef PRINTREPORT
233  printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
234 #endif
235  return 0;
236 }
237 
238 void XBOXUSB::readReport() {
239  if (readBuf == NULL)
240  return;
241  if (readBuf[0] != 0x00 || readBuf[1] != 0x14) { // Check if it's the correct report - the controller also sends different status reports
242  return;
243  }
244 
245  ButtonState = (uint32_t)(readBuf[5] | ((uint16_t)readBuf[4] << 8) | ((uint32_t)readBuf[3] << 16) | ((uint32_t)readBuf[2] << 24));
246 
247  hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
248  hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
249  hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
250  hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
251 
252  //Notify(PSTR("\r\nButtonState"), 0x80);
253  //PrintHex<uint32_t>(ButtonState, 0x80);
254 
255  if (ButtonState != OldButtonState) {
256  ButtonClickState = (ButtonState >> 16) & ((~OldButtonState) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
257  if (((uint8_t)OldButtonState) == 0 && ((uint8_t)ButtonState) != 0) // The L2 and R2 buttons are special as they are analog buttons
258  R2Clicked = true;
259  if ((uint8_t)(OldButtonState >> 8) == 0 && (uint8_t)(ButtonState >> 8) != 0)
260  L2Clicked = true;
261  OldButtonState = ButtonState;
262  }
263 }
264 
265 void XBOXUSB::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
266 #ifdef PRINTREPORT
267  if (readBuf == NULL)
268  return;
269  for (uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {
270  PrintHex<uint8_t > (readBuf[i], 0x80);
271  Notify(PSTR(" "), 0x80);
272  }
273  Notify(PSTR("\r\n"), 0x80);
274 #endif
275 }
276 
278  if (b == L2) // These are analog buttons
279  return (uint8_t)(ButtonState >> 8);
280  else if (b == R2)
281  return (uint8_t)ButtonState;
282  return (ButtonState & ((uint32_t)pgm_read_word(&XBOXBUTTONS[(uint8_t)b]) << 16));
283 }
284 
286  if (b == L2) {
287  if (L2Clicked) {
288  L2Clicked = false;
289  return true;
290  }
291  return false;
292  } else if (b == R2) {
293  if (R2Clicked) {
294  R2Clicked = false;
295  return true;
296  }
297  return false;
298  }
299  uint16_t button = pgm_read_word(&XBOXBUTTONS[(uint8_t)b]);
300  bool click = (ButtonClickState & button);
301  ButtonClickState &= ~button; // clear "click" event
302  return click;
303 }
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 
315 void XBOXUSB::setLedRaw(uint8_t value) {
316  writeBuf[0] = 0x01;
317  writeBuf[1] = 0x03;
318  writeBuf[2] = value;
319 
320  XboxCommand(writeBuf, 3);
321 }
322 
324  if (led != ALL) // All LEDs can't be on a the same time
325  setLedRaw((pgm_read_byte(&XBOXLEDS[(uint8_t)led])) + 4);
326 }
327 
329  setLedRaw(pgm_read_byte(&XBOXLEDS[(uint8_t)led]));
330 }
331 
332 void XBOXUSB::setLedMode(LEDMode ledMode) { // This function is used to do some speciel LED stuff the controller supports
333  setLedRaw((uint8_t)ledMode);
334 }
335 
336 void XBOXUSB::setRumbleOn(uint8_t lValue, uint8_t rValue) {
337  writeBuf[0] = 0x00;
338  writeBuf[1] = 0x08;
339  writeBuf[2] = 0x00;
340  writeBuf[3] = lValue; // big weight
341  writeBuf[4] = rValue; // small weight
342  writeBuf[5] = 0x00;
343  writeBuf[6] = 0x00;
344  writeBuf[7] = 0x00;
345 
346  XboxCommand(writeBuf, 8);
347 }