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 see "settings.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].bmSndToggle = 0;
31  epInfo[i].bmRcvToggle = 0;
33  }
34 
35  if(pUsb) // register in USB subsystem
36  pUsb->RegisterDeviceClass(this); //set devConfig[] entry
37 }
38 
39 uint8_t XBOXUSB::Init(uint8_t parent, uint8_t port, bool lowspeed) {
40  uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
41  USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
42  uint8_t rcode;
43  UsbDevice *p = NULL;
44  EpInfo *oldep_ptr = NULL;
45  uint16_t PID;
46  uint16_t VID;
47 
48  // get memory address of USB device address pool
49  AddressPool &addrPool = pUsb->GetAddressPool();
50 #ifdef EXTRADEBUG
51  Notify(PSTR("\r\nXBOXUSB Init"), 0x80);
52 #endif
53  // check if address has already been assigned to an instance
54  if(bAddress) {
55 #ifdef DEBUG_USB_HOST
56  Notify(PSTR("\r\nAddress in use"), 0x80);
57 #endif
59  }
60 
61  // Get pointer to pseudo device with address 0 assigned
62  p = addrPool.GetUsbDevicePtr(0);
63 
64  if(!p) {
65 #ifdef DEBUG_USB_HOST
66  Notify(PSTR("\r\nAddress not found"), 0x80);
67 #endif
69  }
70 
71  if(!p->epinfo) {
72 #ifdef DEBUG_USB_HOST
73  Notify(PSTR("\r\nepinfo is null"), 0x80);
74 #endif
76  }
77 
78  // Save old pointer to EP_RECORD of address 0
79  oldep_ptr = p->epinfo;
80 
81  // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
82  p->epinfo = epInfo;
83 
84  p->lowspeed = lowspeed;
85 
86  // Get device descriptor
87  rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
88  // Restore p->epinfo
89  p->epinfo = oldep_ptr;
90 
91  if(rcode)
92  goto FailGetDevDescr;
93 
94  VID = udd->idVendor;
95  PID = udd->idProduct;
96 
97  if(VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID && VID != GAMESTOP_VID) // Check VID
98  goto FailUnknownDevice;
99  if(PID == XBOX_WIRELESS_PID) {
100 #ifdef DEBUG_USB_HOST
101  Notify(PSTR("\r\nYou have plugged in a wireless Xbox 360 controller - it doesn't support USB communication"), 0x80);
102 #endif
103  goto FailUnknownDevice;
105 #ifdef DEBUG_USB_HOST
106  Notify(PSTR("\r\nThis library only supports Xbox 360 controllers via USB"), 0x80);
107 #endif
108  goto FailUnknownDevice;
109  } else if(PID != XBOX_WIRED_PID && PID != MADCATZ_WIRED_PID && PID != GAMESTOP_WIRED_PID && PID != AFTERGLOW_WIRED_PID && PID != JOYTECH_WIRED_PID) // Check PID
110  goto FailUnknownDevice;
111 
112  // Allocate new address according to device class
113  bAddress = addrPool.AllocAddress(parent, false, port);
114 
115  if(!bAddress)
117 
118  // Extract Max Packet Size from device descriptor
119  epInfo[0].maxPktSize = udd->bMaxPacketSize0;
120 
121  // Assign new address to the device
122  rcode = pUsb->setAddr(0, 0, bAddress);
123  if(rcode) {
124  p->lowspeed = false;
125  addrPool.FreeAddress(bAddress);
126  bAddress = 0;
127 #ifdef DEBUG_USB_HOST
128  Notify(PSTR("\r\nsetAddr: "), 0x80);
129  D_PrintHex<uint8_t > (rcode, 0x80);
130 #endif
131  return rcode;
132  }
133 #ifdef EXTRADEBUG
134  Notify(PSTR("\r\nAddr: "), 0x80);
135  D_PrintHex<uint8_t > (bAddress, 0x80);
136 #endif
137  //delay(300); // Spec says you should wait at least 200ms
138 
139  p->lowspeed = false;
140 
141  //get pointer to assigned address record
142  p = addrPool.GetUsbDevicePtr(bAddress);
143  if(!p)
145 
146  p->lowspeed = lowspeed;
147 
148  // Assign epInfo to epinfo pointer - only EP0 is known
149  rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
150  if(rcode)
151  goto FailSetDevTblEntry;
152 
153  /* The application will work in reduced host mode, so we can save program and data
154  memory space. After verifying the VID we will use known values for the
155  configuration values for device, interface, endpoints and HID for the XBOX360 Controllers */
156 
157  /* Initialize data structures for endpoints of device */
158  epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX 360 report endpoint
160  epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
164  epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX 360 output endpoint
166  epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
170 
171  rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
172  if(rcode)
173  goto FailSetDevTblEntry;
174 
175  delay(200); // Give time for address change
176 
177  rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
178  if(rcode)
179  goto FailSetConfDescr;
180 
181 #ifdef DEBUG_USB_HOST
182  Notify(PSTR("\r\nXbox 360 Controller Connected\r\n"), 0x80);
183 #endif
184  onInit();
185  Xbox360Connected = true;
186  bPollEnable = true;
187  return 0; // Successful configuration
188 
189  /* Diagnostic messages */
190 FailGetDevDescr:
191 #ifdef DEBUG_USB_HOST
193  goto Fail;
194 #endif
195 
196 FailSetDevTblEntry:
197 #ifdef DEBUG_USB_HOST
199  goto Fail;
200 #endif
201 
202 FailSetConfDescr:
203 #ifdef DEBUG_USB_HOST
205 #endif
206  goto Fail;
207 
208 FailUnknownDevice:
209 #ifdef DEBUG_USB_HOST
210  NotifyFailUnknownDevice(VID, PID);
211 #endif
213 
214 Fail:
215 #ifdef DEBUG_USB_HOST
216  Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
217  NotifyFail(rcode);
218 #endif
219  Release();
220  return rcode;
221 }
222 
223 /* Performs a cleanup after failed Init() attempt */
224 uint8_t XBOXUSB::Release() {
225  Xbox360Connected = false;
227  bAddress = 0;
228  bPollEnable = false;
229  return 0;
230 }
231 
232 uint8_t XBOXUSB::Poll() {
233  if(!bPollEnable)
234  return 0;
235  uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
236  pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
237  readReport();
238 #ifdef PRINTREPORT
239  printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
240 #endif
241  return 0;
242 }
243 
244 void XBOXUSB::readReport() {
245  if(readBuf == NULL)
246  return;
247  if(readBuf[0] != 0x00 || readBuf[1] != 0x14) { // Check if it's the correct report - the controller also sends different status reports
248  return;
249  }
250 
251  ButtonState = (uint32_t)(readBuf[5] | ((uint16_t)readBuf[4] << 8) | ((uint32_t)readBuf[3] << 16) | ((uint32_t)readBuf[2] << 24));
252 
253  hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
254  hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
255  hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
256  hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
257 
258  //Notify(PSTR("\r\nButtonState"), 0x80);
259  //PrintHex<uint32_t>(ButtonState, 0x80);
260 
261  if(ButtonState != OldButtonState) {
262  ButtonClickState = (ButtonState >> 16) & ((~OldButtonState) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
263  if(((uint8_t)OldButtonState) == 0 && ((uint8_t)ButtonState) != 0) // The L2 and R2 buttons are special as they are analog buttons
264  R2Clicked = true;
265  if((uint8_t)(OldButtonState >> 8) == 0 && (uint8_t)(ButtonState >> 8) != 0)
266  L2Clicked = true;
267  OldButtonState = ButtonState;
268  }
269 }
270 
271 void XBOXUSB::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
272 #ifdef PRINTREPORT
273  if(readBuf == NULL)
274  return;
275  for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {
276  D_PrintHex<uint8_t > (readBuf[i], 0x80);
277  Notify(PSTR(" "), 0x80);
278  }
279  Notify(PSTR("\r\n"), 0x80);
280 #endif
281 }
282 
284  if(b == L2) // These are analog buttons
285  return (uint8_t)(ButtonState >> 8);
286  else if(b == R2)
287  return (uint8_t)ButtonState;
288  return (bool)(ButtonState & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16));
289 }
290 
292  if(b == L2) {
293  if(L2Clicked) {
294  L2Clicked = false;
295  return true;
296  }
297  return false;
298  } else if(b == R2) {
299  if(R2Clicked) {
300  R2Clicked = false;
301  return true;
302  }
303  return false;
304  }
305  uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
306  bool click = (ButtonClickState & button);
307  ButtonClickState &= ~button; // clear "click" event
308  return click;
309 }
310 
312  return hatValue[a];
313 }
314 
315 /* Xbox Controller commands */
316 void XBOXUSB::XboxCommand(uint8_t* data, uint16_t nbytes) {
317  //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)
318  pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
319 }
320 
321 void XBOXUSB::setLedRaw(uint8_t value) {
322  writeBuf[0] = 0x01;
323  writeBuf[1] = 0x03;
324  writeBuf[2] = value;
325 
326  XboxCommand(writeBuf, 3);
327 }
328 
330  if(led == OFF)
331  setLedRaw(0);
332  else if(led != ALL) // All LEDs can't be on a the same time
333  setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]) + 4);
334 }
335 
337  setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]));
338 }
339 
340 void XBOXUSB::setLedMode(LEDModeEnum ledMode) { // This function is used to do some special LED stuff the controller supports
341  setLedRaw((uint8_t)ledMode);
342 }
343 
344 void XBOXUSB::setRumbleOn(uint8_t lValue, uint8_t rValue) {
345  writeBuf[0] = 0x00;
346  writeBuf[1] = 0x08;
347  writeBuf[2] = 0x00;
348  writeBuf[3] = lValue; // big weight
349  writeBuf[4] = rValue; // small weight
350  writeBuf[5] = 0x00;
351  writeBuf[6] = 0x00;
352  writeBuf[7] = 0x00;
353 
354  XboxCommand(writeBuf, 8);
355 }
356 
357 void XBOXUSB::onInit() {
358  if(pFuncOnInit)
359  pFuncOnInit(); // Call the user function
360  else
361  setLedOn(static_cast<LEDEnum>(LED1));
362 }
#define XBOX_WIRED_PID
Definition: XBOXUSB.h:39
uint8_t bmRcvToggle
Definition: address.h:48
#define XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID
Definition: XBOXRECV.h:46
LEDModeEnum
Definition: xboxEnums.h:24
EpInfo * epinfo
Definition: address.h:83
bool lowspeed
Definition: address.h:86
#define USB_ERROR_EPINFO_IS_NULL
Definition: UsbCore.h:94
uint8_t bmNakPower
Definition: address.h:49
#define XBOX_WIRELESS_RECEIVER_PID
Definition: XBOXRECV.h:45
void setLedOn(LEDEnum l)
Definition: XBOXUSB.cpp:329
#define pgm_read_word(addr)
#define JOYTECH_WIRED_PID
Definition: XBOXUSB.h:44
#define NotifyFail(...)
Definition: message.h:62
AnalogHatEnum
USB * pUsb
Definition: XBOXUSB.h:189
#define XBOX_REPORT_BUFFER_SIZE
Definition: XBOXUSB.h:48
uint8_t setConf(uint8_t addr, uint8_t ep, uint8_t conf_value)
Definition: Usb.cpp:823
uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed)
Definition: XBOXUSB.cpp:39
#define USB_TRANSFER_TYPE_INTERRUPT
Definition: usb_ch9.h:93
int16_t getAnalogHat(AnalogHatEnum a)
Definition: XBOXUSB.cpp:311
#define bmREQ_HID_OUT
Definition: usbhid.h:63
void setLedBlink(LEDEnum l)
Definition: XBOXUSB.cpp:336
#define pgm_read_byte(addr)
#define NotifyFailGetDevDescr(...)
Definition: message.h:57
#define AFTERGLOW_WIRED_PID
Definition: XBOXUSB.h:46
uint8_t setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo *eprecord_ptr)
Definition: Usb.cpp:71
#define EP_MAXPKTSIZE
Definition: PS3USB.h:26
virtual void FreeAddress(uint8_t addr)=0
LEDEnum
uint8_t epAttribs
Definition: address.h:44
uint8_t ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi, uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t *dataptr, USBReadParser *p)
Definition: Usb.cpp:133
virtual UsbDevice * GetUsbDevicePtr(uint8_t addr)=0
#define Notify(...)
Definition: message.h:51
uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr)
Definition: Usb.cpp:814
uint8_t epAddr
Definition: address.h:40
#define NotifyFailUnknownDevice(...)
Definition: message.h:61
#define USB_NAK_MAX_POWER
Definition: address.h:34
#define XBOX_VID
Definition: XBOXOLD.h:34
#define XBOX_INPUT_PIPE
Definition: XBOXOLD.h:30
uint8_t bAddress
Definition: XBOXUSB.h:191
uint8_t getButtonPress(ButtonEnum b)
Definition: XBOXUSB.cpp:283
Definition: address.h:39
#define XBOX_WIRELESS_PID
Definition: XBOXUSB.h:40
ButtonEnum
bool Xbox360Connected
Definition: XBOXUSB.h:181
#define JOYTECH_VID
Definition: XBOXOLD.h:36
virtual uint8_t AllocAddress(uint8_t parent, bool is_hub=false, uint8_t port=0)=0
uint8_t bmSndToggle
Definition: address.h:47
#define USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE
Definition: UsbCore.h:96
#define PSTR(str)
void setRumbleOn(uint8_t lValue, uint8_t rValue)
Definition: XBOXUSB.cpp:344
XBOXUSB(USB *pUsb)
Definition: XBOXUSB.cpp:23
#define MADCATZ_VID
Definition: XBOXOLD.h:35
#define XBOX_OUTPUT_PIPE
Definition: XBOXOLD.h:31
#define USB_NAK_NOWAIT
Definition: address.h:36
#define USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL
Definition: UsbCore.h:93
#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED
Definition: UsbCore.h:88
#define GAMESTOP_VID
Definition: XBOXUSB.h:37
const uint8_t XBOX_LEDS[]
Definition: xboxEnums.h:32
bool getButtonClick(ButtonEnum b)
Definition: XBOXUSB.cpp:291
#define XBOX_MAX_ENDPOINTS
Definition: XBOXOLD.h:43
uint8_t inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t *data, uint8_t bInterval=0)
Definition: Usb.cpp:213
void setLedRaw(uint8_t value)
Definition: XBOXUSB.cpp:321
#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL
Definition: UsbCore.h:91
void setLedMode(LEDModeEnum lm)
Definition: XBOXUSB.cpp:340
#define MADCATZ_WIRED_PID
Definition: XBOXUSB.h:43
uint8_t maxPktSize
Definition: address.h:41
AddressPool & GetAddressPool()
Definition: UsbCore.h:224
Definition: UsbCore.h:208
#define XBOX_CONTROL_PIPE
Definition: XBOXOLD.h:29
uint8_t Release()
Definition: XBOXUSB.cpp:224
#define HID_REQUEST_SET_REPORT
Definition: usbhid.h:72
const uint16_t XBOX_BUTTONS[]
Definition: xboxEnums.h:41
uint8_t RegisterDeviceClass(USBDeviceConfig *pdev)
Definition: UsbCore.h:228
#define NotifyFailSetConfDescr(...)
Definition: message.h:60
uint8_t getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *dataptr)
defined(USB_METHODS_INLINE)
Definition: Usb.cpp:779
uint8_t Poll()
Definition: XBOXUSB.cpp:232
#define NotifyFailSetDevTblEntry(...)
Definition: message.h:58
EpInfo epInfo[XBOX_MAX_ENDPOINTS]
Definition: XBOXUSB.h:193
#define GAMESTOP_WIRED_PID
Definition: XBOXUSB.h:45