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"), 0x80);
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"), 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
64  Notify(PSTR("\r\nAddress not found"), 0x80);
65 #endif
67  }
68 
69  if (!p->epinfo) {
70 #ifdef DEBUG
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
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
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
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
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:
187  goto Fail;
188 
189 FailSetDevTblEntry:
191  goto Fail;
192 
193 FailSetConfDescr:
195  goto Fail;
196 FailUnknownDevice:
197  NotifyFailUnknownDevice(VID,PID);
199 
200 Fail:
201 #ifdef DEBUG
202  Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
203 #endif
204  NotifyFail(rcode);
205  Release();
206  return rcode;
207 }
208 
209 /* Performs a cleanup after failed Init() attempt */
210 uint8_t XBOXUSB::Release() {
211  Xbox360Connected = false;
213  bAddress = 0;
214  bPollEnable = false;
215  return 0;
216 }
217 
218 uint8_t XBOXUSB::Poll() {
219  if (!bPollEnable)
220  return 0;
221  uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
222  pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
223  readReport();
224 #ifdef PRINTREPORT
225  printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
226 #endif
227  return 0;
228 }
229 
230 void XBOXUSB::readReport() {
231  if (readBuf == NULL)
232  return;
233  if (readBuf[0] != 0x00 || readBuf[1] != 0x14) { // Check if it's the correct report - the controller also sends different status reports
234  return;
235  }
236 
237  ButtonState = (uint32_t)(readBuf[5] | ((uint16_t)readBuf[4] << 8) | ((uint32_t)readBuf[3] << 16) | ((uint32_t)readBuf[2] << 24));
238 
239  hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
240  hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
241  hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
242  hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
243 
244  //Notify(PSTR("\r\nButtonState"), 0x80);
245  //PrintHex<uint32_t>(ButtonState, 0x80);
246 
247  if (ButtonState != OldButtonState) {
248  ButtonClickState = (ButtonState >> 16) & ((~OldButtonState) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
249  if (((uint8_t)OldButtonState) == 0 && ((uint8_t)ButtonState) != 0) // The L2 and R2 buttons are special as they are analog buttons
250  R2Clicked = true;
251  if ((uint8_t)(OldButtonState >> 8) == 0 && (uint8_t)(ButtonState >> 8) != 0)
252  L2Clicked = true;
253  OldButtonState = ButtonState;
254  }
255 }
256 
257 void XBOXUSB::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
258 #ifdef PRINTREPORT
259  if (readBuf == NULL)
260  return;
261  for (uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {
262  PrintHex<uint8_t > (readBuf[i], 0x80);
263  Notify(PSTR(" "), 0x80);
264  }
265  Notify(PSTR("\r\n"), 0x80);
266 #endif
267 }
268 
270  if (b == L2) // These are analog buttons
271  return (uint8_t)(ButtonState >> 8);
272  else if (b == R2)
273  return (uint8_t)ButtonState;
274  return (ButtonState & ((uint32_t)pgm_read_word(&XBOXBUTTONS[(uint8_t)b]) << 16));
275 }
276 
278  if (b == L2) {
279  if (L2Clicked) {
280  L2Clicked = false;
281  return true;
282  }
283  return false;
284  } else if (b == R2) {
285  if (R2Clicked) {
286  R2Clicked = false;
287  return true;
288  }
289  return false;
290  }
291  uint16_t button = pgm_read_word(&XBOXBUTTONS[(uint8_t)b]);
292  bool click = (ButtonClickState & button);
293  ButtonClickState &= ~button; // clear "click" event
294  return click;
295 }
296 
298  return hatValue[a];
299 }
300 
301 /* Xbox Controller commands */
302 void XBOXUSB::XboxCommand(uint8_t* data, uint16_t nbytes) {
303  //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)
304  pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
305 }
306 
307 void XBOXUSB::setLedRaw(uint8_t value) {
308  writeBuf[0] = 0x01;
309  writeBuf[1] = 0x03;
310  writeBuf[2] = value;
311 
312  XboxCommand(writeBuf, 3);
313 }
314 
316  if (led != ALL) // All LEDs can't be on a the same time
317  setLedRaw((pgm_read_byte(&XBOXLEDS[(uint8_t)led])) + 4);
318 }
319 
321  setLedRaw(pgm_read_byte(&XBOXLEDS[(uint8_t)led]));
322 }
323 
324 void XBOXUSB::setLedMode(LEDMode ledMode) { // This function is used to do some speciel LED stuff the controller supports
325  setLedRaw((uint8_t)ledMode);
326 }
327 
328 void XBOXUSB::setRumbleOn(uint8_t lValue, uint8_t rValue) {
329  writeBuf[0] = 0x00;
330  writeBuf[1] = 0x08;
331  writeBuf[2] = 0x00;
332  writeBuf[3] = lValue; // big weight
333  writeBuf[4] = rValue; // small weight
334  writeBuf[5] = 0x00;
335  writeBuf[6] = 0x00;
336  writeBuf[7] = 0x00;
337 
338  XboxCommand(writeBuf, 8);
339 }