USB Host Shield 2.0
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
XBOXOLD.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2013 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 "XBOXOLD.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 controller
22 
24 const uint8_t XBOXOLDBUTTONS[] PROGMEM = {
25  0x01, // UP
26  0x08, // RIGHT
27  0x02, // DOWN
28  0x04, // LEFT
29 
30  0x20, // BACK
31  0x10, // START
32  0x40, // L3
33  0x80, // R3
34 
35  // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
36  4, // BLACK
37  5, // WHTIE
38  6, // L1
39  7, // R1
40 
41  1, // B
42  0, // A
43  2, // X
44  3, // Y
45 };
46 
48 pUsb(p), // pointer to USB class instance - mandatory
49 bAddress(0), // device address - mandatory
50 bPollEnable(false) { // don't start polling before dongle is connected
51  for (uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
52  epInfo[i].epAddr = 0;
53  epInfo[i].maxPktSize = (i) ? 0 : 8;
54  epInfo[i].epAttribs = 0;
56  }
57 
58  if (pUsb) // register in USB subsystem
59  pUsb->RegisterDeviceClass(this); //set devConfig[] entry
60 }
61 
62 uint8_t XBOXOLD::Init(uint8_t parent, uint8_t port, bool lowspeed) {
63  uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
64  uint8_t rcode;
65  UsbDevice *p = NULL;
66  EpInfo *oldep_ptr = NULL;
67  uint16_t PID;
68  uint16_t VID;
69 
70  // get memory address of USB device address pool
71  AddressPool &addrPool = pUsb->GetAddressPool();
72 #ifdef EXTRADEBUG
73  Notify(PSTR("\r\nXBOXUSB Init"), 0x80);
74 #endif
75  // check if address has already been assigned to an instance
76  if (bAddress) {
77 #ifdef DEBUG_USB_HOST
78  Notify(PSTR("\r\nAddress in use"), 0x80);
79 #endif
81  }
82 
83  // Get pointer to pseudo device with address 0 assigned
84  p = addrPool.GetUsbDevicePtr(0);
85 
86  if (!p) {
87 #ifdef DEBUG_USB_HOST
88  Notify(PSTR("\r\nAddress not found"), 0x80);
89 #endif
91  }
92 
93  if (!p->epinfo) {
94 #ifdef DEBUG_USB_HOST
95  Notify(PSTR("\r\nepinfo is null"), 0x80);
96 #endif
98  }
99 
100  // Save old pointer to EP_RECORD of address 0
101  oldep_ptr = p->epinfo;
102 
103  // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
104  p->epinfo = epInfo;
105 
106  p->lowspeed = lowspeed;
107 
108  // Get device descriptor
109  rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
110  // Restore p->epinfo
111  p->epinfo = oldep_ptr;
112 
113  if (rcode)
114  goto FailGetDevDescr;
115 
116  VID = ((USB_DEVICE_DESCRIPTOR*)buf)->idVendor;
117  PID = ((USB_DEVICE_DESCRIPTOR*)buf)->idProduct;
118 
119  if ((VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID) || (PID != XBOX_OLD_PID1 && PID != XBOX_OLD_PID2 && PID != XBOX_OLD_PID3 && PID != XBOX_OLD_PID4)) // Check if VID and PID match
120  goto FailUnknownDevice;
121 
122  // Allocate new address according to device class
123  bAddress = addrPool.AllocAddress(parent, false, port);
124 
125  if (!bAddress)
127 
128  // Extract Max Packet Size from device descriptor
129  epInfo[0].maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0;
130 
131  // Assign new address to the device
132  rcode = pUsb->setAddr(0, 0, bAddress);
133  if (rcode) {
134  p->lowspeed = false;
135  addrPool.FreeAddress(bAddress);
136  bAddress = 0;
137 #ifdef DEBUG_USB_HOST
138  Notify(PSTR("\r\nsetAddr: "), 0x80);
139  D_PrintHex<uint8_t > (rcode, 0x80);
140 #endif
141  return rcode;
142  }
143 #ifdef EXTRADEBUG
144  Notify(PSTR("\r\nAddr: "), 0x80);
145  D_PrintHex<uint8_t > (bAddress, 0x80);
146 #endif
147  delay(300); // Spec says you should wait at least 200ms
148 
149  p->lowspeed = false;
150 
151  //get pointer to assigned address record
152  p = addrPool.GetUsbDevicePtr(bAddress);
153  if (!p)
155 
156  p->lowspeed = lowspeed;
157 
158  // Assign epInfo to epinfo pointer - only EP0 is known
159  rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
160  if (rcode)
161  goto FailSetDevTblEntry;
162 
163  /* The application will work in reduced host mode, so we can save program and data
164  memory space. After verifying the VID we will use known values for the
165  configuration values for device, interface, endpoints and HID for the XBOX controllers */
166 
167  /* Initialize data structures for endpoints of device */
168  epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX report endpoint
170  epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
174  epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX output endpoint
176  epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
180 
181  rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
182  if (rcode)
183  goto FailSetDevTblEntry;
184 
185  delay(200); // Give time for address change
186 
187  rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
188  if (rcode)
189  goto FailSetConfDescr;
190 
191 #ifdef DEBUG_USB_HOST
192  Notify(PSTR("\r\nXbox Controller Connected\r\n"), 0x80);
193 #endif
194  if (pFuncOnInit)
195  pFuncOnInit(); // Call the user function
196  XboxConnected = true;
197  bPollEnable = true;
198  return 0; // Successful configuration
199 
200  /* Diagnostic messages */
201 FailGetDevDescr:
202 #ifdef DEBUG_USB_HOST
204  goto Fail;
205 #endif
206 
207 FailSetDevTblEntry:
208 #ifdef DEBUG_USB_HOST
210  goto Fail;
211 #endif
212 
213 FailSetConfDescr:
214 #ifdef DEBUG_USB_HOST
216  goto Fail;
217 #endif
218 FailUnknownDevice:
219 #ifdef DEBUG_USB_HOST
220  NotifyFailUnknownDevice(VID, PID);
221 #endif
223 
224 Fail:
225 #ifdef DEBUG_USB_HOST
226  Notify(PSTR("\r\nXbox Init Failed, error code: "), 0x80);
227  NotifyFail(rcode);
228 #endif
229  Release();
230  return rcode;
231 }
232 
233 /* Performs a cleanup after failed Init() attempt */
234 uint8_t XBOXOLD::Release() {
235  XboxConnected = false;
237  bAddress = 0;
238  bPollEnable = false;
239  return 0;
240 }
241 
242 uint8_t XBOXOLD::Poll() {
243  if (!bPollEnable)
244  return 0;
245  uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
246  pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
247  readReport();
248 #ifdef PRINTREPORT
249  printReport(BUFFER_SIZE); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
250 #endif
251  return 0;
252 }
253 
254 void XBOXOLD::readReport() {
255  ButtonState = readBuf[2];
256 
257  for (uint8_t i = 0; i < sizeof(buttonValues); i++)
258  buttonValues[i] = readBuf[i + 4]; // A, B, X, Y, BLACK, WHITE, L1, and R1
259 
260  hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[12] << 8) | readBuf[13]);
261  hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[14] << 8) | readBuf[15]);
262  hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[16] << 8) | readBuf[17]);
263  hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[18] << 8) | readBuf[19]);
264 
265  //Notify(PSTR("\r\nButtonState"), 0x80);
266  //PrintHex<uint8_t>(ButtonState, 0x80);
267 
268  if (ButtonState != OldButtonState || memcmp(buttonValues, oldButtonValues, sizeof(buttonValues)) != 0) {
269  ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
270  OldButtonState = ButtonState;
271 
272  for (uint8_t i = 0; i < sizeof(buttonValues); i++) {
273  if (oldButtonValues[i] == 0 && buttonValues[i] != 0)
274  buttonClicked[i] = true; // Update A, B, X, Y, BLACK, WHITE, L1, and R1 click state
275  oldButtonValues[i] = buttonValues[i];
276  }
277  }
278 }
279 
280 void XBOXOLD::printReport(uint16_t length) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
281 #ifdef PRINTREPORT
282  if (readBuf == NULL)
283  return;
284  for (uint8_t i = 0; i < length; i++) {
285  D_PrintHex<uint8_t > (readBuf[i], 0x80);
286  Notify(PSTR(" "), 0x80);
287  }
288  Notify(PSTR("\r\n"), 0x80);
289 #endif
290 }
291 
293  if (b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
294  return buttonValues[pgm_read_byte(&XBOXOLDBUTTONS[(uint8_t)b])]; // Analog buttons
295  return (ButtonState & pgm_read_byte(&XBOXOLDBUTTONS[(uint8_t)b])); // Digital buttons
296 }
297 
299  uint8_t button;
300  if (b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) { // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
301  button = pgm_read_byte(&XBOXOLDBUTTONS[(uint8_t)b]);
302  if (buttonClicked[button])
303  buttonClicked[button] = false;
304  return buttonClicked[button];
305  }
306 
307  button = pgm_read_byte(&XBOXOLDBUTTONS[(uint8_t)b]); // Digital buttons
308  bool click = (ButtonClickState & button);
309  ButtonClickState &= ~button; // clear "click" event
310  return click;
311 }
312 
314  return hatValue[a];
315 }
316 
317 /* Xbox Controller commands */
318 void XBOXOLD::XboxCommand(uint8_t* data, uint16_t nbytes) {
319  //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)
320  pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
321 }
322 
323 void XBOXOLD::setRumbleOn(uint8_t lValue, uint8_t rValue) {
324  uint8_t writeBuf[6];
325 
326  writeBuf[0] = 0x00;
327  writeBuf[1] = 0x06;
328  writeBuf[2] = 0x00;
329  writeBuf[3] = rValue; // small weight
330  writeBuf[4] = 0x00;
331  writeBuf[5] = lValue; // big weight
332 
333  XboxCommand(writeBuf, 6);
334 }