USB Host Shield 2.0
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  const int8_t index = getButtonIndexXbox(b); if (index < 0) return 0;
285  if(index == ButtonIndex(L2)) // These are analog buttons
286  return (uint8_t)(ButtonState >> 8);
287  else if(index == ButtonIndex(R2))
288  return (uint8_t)ButtonState;
289  return (bool)(ButtonState & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[index]) << 16));
290 }
291 
293  const int8_t index = getButtonIndexXbox(b); if (index < 0) return 0;
294  if(index == ButtonIndex(L2)) {
295  if(L2Clicked) {
296  L2Clicked = false;
297  return true;
298  }
299  return false;
300  } else if(index == ButtonIndex(R2)) {
301  if(R2Clicked) {
302  R2Clicked = false;
303  return true;
304  }
305  return false;
306  }
307  uint16_t button = pgm_read_word(&XBOX_BUTTONS[index]);
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 XBOXUSB::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 XBOXUSB::setLedRaw(uint8_t value) {
324  writeBuf[0] = 0x01;
325  writeBuf[1] = 0x03;
326  writeBuf[2] = value;
327 
328  XboxCommand(writeBuf, 3);
329 }
330 
332  if(led == OFF)
333  setLedRaw(0);
334  else if(led != ALL) // All LEDs can't be on a the same time
335  setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]) + 4);
336 }
337 
339  setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]));
340 }
341 
342 void XBOXUSB::setLedMode(LEDModeEnum ledMode) { // This function is used to do some special LED stuff the controller supports
343  setLedRaw((uint8_t)ledMode);
344 }
345 
346 void XBOXUSB::setRumbleOn(uint8_t lValue, uint8_t rValue) {
347  writeBuf[0] = 0x00;
348  writeBuf[1] = 0x08;
349  writeBuf[2] = 0x00;
350  writeBuf[3] = lValue; // big weight
351  writeBuf[4] = rValue; // small weight
352  writeBuf[5] = 0x00;
353  writeBuf[6] = 0x00;
354  writeBuf[7] = 0x00;
355 
356  XboxCommand(writeBuf, 8);
357 }
358 
359 void XBOXUSB::onInit() {
360  if(pFuncOnInit)
361  pFuncOnInit(); // Call the user function
362  else
363  setLedOn(static_cast<LEDEnum>(LED1));
364 }
#define EP_MAXPKTSIZE
Definition: PS3USB.h:26
#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL
Definition: UsbCore.h:95
#define USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE
Definition: UsbCore.h:100
#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED
Definition: UsbCore.h:92
#define USB_ERROR_EPINFO_IS_NULL
Definition: UsbCore.h:98
#define USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL
Definition: UsbCore.h:97
#define MADCATZ_VID
Definition: XBOXOLD.h:35
#define XBOX_INPUT_PIPE
Definition: XBOXOLD.h:30
#define XBOX_VID
Definition: XBOXOLD.h:34
#define XBOX_CONTROL_PIPE
Definition: XBOXOLD.h:29
#define XBOX_OUTPUT_PIPE
Definition: XBOXOLD.h:31
#define JOYTECH_VID
Definition: XBOXOLD.h:36
#define XBOX_MAX_ENDPOINTS
Definition: XBOXOLD.h:45
#define XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID
Definition: XBOXRECV.h:47
#define XBOX_WIRELESS_PID
Definition: XBOXUSB.h:40
#define XBOX_WIRELESS_RECEIVER_PID
Definition: XBOXUSB.h:41
#define JOYTECH_WIRED_PID
Definition: XBOXUSB.h:44
#define GAMESTOP_WIRED_PID
Definition: XBOXUSB.h:45
#define MADCATZ_WIRED_PID
Definition: XBOXUSB.h:43
#define AFTERGLOW_WIRED_PID
Definition: XBOXUSB.h:46
#define XBOX_REPORT_BUFFER_SIZE
Definition: XBOXUSB.h:48
#define GAMESTOP_VID
Definition: XBOXUSB.h:37
#define XBOX_WIRED_PID
Definition: XBOXUSB.h:39
#define USB_NAK_MAX_POWER
Definition: address.h:34
#define USB_NAK_NOWAIT
Definition: address.h:36
virtual void FreeAddress(uint8_t addr)=0
virtual uint8_t AllocAddress(uint8_t parent, bool is_hub=false, uint8_t port=0)=0
virtual UsbDevice * GetUsbDevicePtr(uint8_t addr)=0
Definition: UsbCore.h:212
uint8_t getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *dataptr)
defined(USB_METHODS_INLINE)
Definition: Usb.cpp:801
uint8_t setConf(uint8_t addr, uint8_t ep, uint8_t conf_value)
Definition: Usb.cpp:845
uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr)
Definition: Usb.cpp:836
uint8_t RegisterDeviceClass(USBDeviceConfig *pdev)
Definition: UsbCore.h:232
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:126
AddressPool & GetAddressPool()
Definition: UsbCore.h:228
uint8_t setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo *eprecord_ptr)
Definition: Usb.cpp:64
uint8_t inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t *data, uint8_t bInterval=0)
Definition: Usb.cpp:209
void setLedMode(LEDModeEnum lm)
Definition: XBOXUSB.cpp:342
uint8_t getButtonPress(ButtonEnum b)
Definition: XBOXUSB.cpp:283
bool Xbox360Connected
Definition: XBOXUSB.h:181
void setLedRaw(uint8_t value)
Definition: XBOXUSB.cpp:323
uint8_t Poll()
Definition: XBOXUSB.cpp:232
void setLedBlink(LEDEnum l)
Definition: XBOXUSB.cpp:338
XBOXUSB(USB *pUsb)
Definition: XBOXUSB.cpp:23
uint8_t bAddress
Definition: XBOXUSB.h:191
void setLedOn(LEDEnum l)
Definition: XBOXUSB.cpp:331
bool getButtonClick(ButtonEnum b)
Definition: XBOXUSB.cpp:292
uint8_t Release()
Definition: XBOXUSB.cpp:224
USB * pUsb
Definition: XBOXUSB.h:189
uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed)
Definition: XBOXUSB.cpp:39
int16_t getAnalogHat(AnalogHatEnum a)
Definition: XBOXUSB.cpp:313
EpInfo epInfo[XBOX_MAX_ENDPOINTS]
Definition: XBOXUSB.h:193
void setRumbleOn(uint8_t lValue, uint8_t rValue)
Definition: XBOXUSB.cpp:346
constexpr int8_t ButtonIndex(ButtonEnum key)
AnalogHatEnum
@ LeftHatX
@ RightHatY
@ RightHatX
@ LeftHatY
LEDEnum
@ OFF
@ ALL
@ LED1
ButtonEnum
@ L2
@ R2
#define NotifyFailSetConfDescr(...)
Definition: message.h:60
#define NotifyFailUnknownDevice(...)
Definition: message.h:61
#define NotifyFail(...)
Definition: message.h:62
#define Notify(...)
Definition: message.h:51
#define NotifyFailSetDevTblEntry(...)
Definition: message.h:58
#define NotifyFailGetDevDescr(...)
Definition: message.h:57
Definition: address.h:39
uint8_t epAttribs
Definition: address.h:44
uint8_t bmNakPower
Definition: address.h:49
uint8_t bmRcvToggle
Definition: address.h:48
uint8_t epAddr
Definition: address.h:40
uint8_t maxPktSize
Definition: address.h:41
uint8_t bmSndToggle
Definition: address.h:47
EpInfo * epinfo
Definition: address.h:83
bool lowspeed
Definition: address.h:86
#define USB_TRANSFER_TYPE_INTERRUPT
Definition: usb_ch9.h:93
#define bmREQ_HID_OUT
Definition: usbhid.h:63
#define HID_REQUEST_SET_REPORT
Definition: usbhid.h:72
#define pgm_read_byte(addr)
#define PSTR(str)
#define pgm_read_word(addr)
const uint8_t XBOX_LEDS[]
Definition: xboxEnums.h:32
const uint16_t XBOX_BUTTONS[]
Definition: xboxEnums.h:41
int8_t getButtonIndexXbox(ButtonEnum b)
Definition: xboxEnums.h:65
LEDModeEnum
Definition: xboxEnums.h:24