USB Host Shield 2.0
XBOXONE.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
2  Copyright (C) 2015 guruthree
3 
4  This software may be distributed and modified under the terms of the GNU
5  General Public License version 2 (GPL2) as published by the Free Software
6  Foundation and appearing in the file GPL2.TXT included in the packaging of
7  this file. Please note that GPL2 Section 2[b] requires that all works based
8  on this software must also be made publicly available under the terms of
9  the GPL2 ("Copyleft").
10 
11  Contact information
12  -------------------
13 
14  Kristian Lauszus, TKJ Electronics
15  Web : http://www.tkjelectronics.com
16  e-mail : kristianl@tkjelectronics.com
17 
18  guruthree
19  Web : https://github.com/guruthree/
20  */
21 
22 #include "XBOXONE.h"
23 // To enable serial debugging see "settings.h"
24 //#define EXTRADEBUG // Uncomment to get even more debugging data
25 //#define PRINTREPORT // Uncomment to print the report send by the Xbox ONE Controller
26 
28 pUsb(p), // pointer to USB class instance - mandatory
29 bAddress(0), // device address - mandatory
30 bPollEnable(false) { // don't start polling before dongle is connected
31  for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
32  epInfo[i].epAddr = 0;
33  epInfo[i].maxPktSize = (i) ? 0 : 8;
34  epInfo[i].epAttribs = 0;
36  }
37 
38  if(pUsb) // register in USB subsystem
39  pUsb->RegisterDeviceClass(this); //set devConfig[] entry
40 }
41 
42 uint8_t XBOXONE::Init(uint8_t parent, uint8_t port, bool lowspeed) {
43  uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
44  USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
45  uint8_t rcode;
46  UsbDevice *p = NULL;
47  EpInfo *oldep_ptr = NULL;
48  uint16_t PID;
49  uint16_t VID;
50 
51  // get memory address of USB device address pool
52  AddressPool &addrPool = pUsb->GetAddressPool();
53 #ifdef EXTRADEBUG
54  Notify(PSTR("\r\nXBOXONE Init"), 0x80);
55 #endif
56  // check if address has already been assigned to an instance
57  if(bAddress) {
58 #ifdef DEBUG_USB_HOST
59  Notify(PSTR("\r\nAddress in use"), 0x80);
60 #endif
62  }
63 
64  // Get pointer to pseudo device with address 0 assigned
65  p = addrPool.GetUsbDevicePtr(0);
66 
67  if(!p) {
68 #ifdef DEBUG_USB_HOST
69  Notify(PSTR("\r\nAddress not found"), 0x80);
70 #endif
72  }
73 
74  if(!p->epinfo) {
75 #ifdef DEBUG_USB_HOST
76  Notify(PSTR("\r\nepinfo is null"), 0x80);
77 #endif
79  }
80 
81  // Save old pointer to EP_RECORD of address 0
82  oldep_ptr = p->epinfo;
83 
84  // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
85  p->epinfo = epInfo;
86 
87  p->lowspeed = lowspeed;
88 
89  // Get device descriptor
90  rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
91  // Restore p->epinfo
92  p->epinfo = oldep_ptr;
93 
94  if(rcode)
95  goto FailGetDevDescr;
96 
97  VID = udd->idVendor;
98  PID = udd->idProduct;
99 
100  if(!VIDPIDOK(VID, PID)) // Check VID
101  goto FailUnknownDevice;
102 
103  // Allocate new address according to device class
104  bAddress = addrPool.AllocAddress(parent, false, port);
105 
106  if(!bAddress)
108 
109  // Extract Max Packet Size from device descriptor
110  epInfo[0].maxPktSize = udd->bMaxPacketSize0;
111 
112  // Assign new address to the device
113  rcode = pUsb->setAddr(0, 0, bAddress);
114  if(rcode) {
115  p->lowspeed = false;
116  addrPool.FreeAddress(bAddress);
117  bAddress = 0;
118 #ifdef DEBUG_USB_HOST
119  Notify(PSTR("\r\nsetAddr: "), 0x80);
120  D_PrintHex<uint8_t > (rcode, 0x80);
121 #endif
122  return rcode;
123  }
124 #ifdef EXTRADEBUG
125  Notify(PSTR("\r\nAddr: "), 0x80);
126  D_PrintHex<uint8_t > (bAddress, 0x80);
127 #endif
128  //delay(300); // Spec says you should wait at least 200ms
129 
130  p->lowspeed = false;
131 
132  //get pointer to assigned address record
133  p = addrPool.GetUsbDevicePtr(bAddress);
134  if(!p)
136 
137  p->lowspeed = lowspeed;
138 
139  // Assign epInfo to epinfo pointer - only EP0 is known
140  rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
141  if(rcode)
142  goto FailSetDevTblEntry;
143 
144  /* The application will work in reduced host mode, so we can save program and data
145  memory space. After verifying the VID we will use known values for the
146  configuration values for device, interface, endpoints and HID for the XBOXONE Controllers */
147 
148  /* Initialize data structures for endpoints of device */
149  epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x01; // XBOX one output endpoint
151  epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
155  epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX one input endpoint
157  epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
161 
162  rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
163  if(rcode)
164  goto FailSetDevTblEntry;
165 
166  delay(200); // Give time for address change
167 
168  rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
169  if(rcode)
170  goto FailSetConfDescr;
171 
172 #ifdef DEBUG_USB_HOST
173  Notify(PSTR("\r\nXbox One Controller Connected\r\n"), 0x80);
174 #endif
175 
176  delay(200); // let things settle
177 
178  // initialize the controller for input
179  writeBuf[0] = 0x05;
180  writeBuf[1] = 0x20;
181  rcode = XboxCommand(writeBuf, 2);
182  if (rcode)
183  goto Fail;
184 
185  onInit();
186  XboxOneConnected = true;
187  bPollEnable = true;
188  return 0; // Successful configuration
189 
190  /* Diagnostic messages */
191 FailGetDevDescr:
192 #ifdef DEBUG_USB_HOST
194  goto Fail;
195 #endif
196 
197 FailSetDevTblEntry:
198 #ifdef DEBUG_USB_HOST
200  goto Fail;
201 #endif
202 
203 FailSetConfDescr:
204 #ifdef DEBUG_USB_HOST
206 #endif
207  goto Fail;
208 
209 FailUnknownDevice:
210 #ifdef DEBUG_USB_HOST
211  NotifyFailUnknownDevice(VID, PID);
212 #endif
214 
215 Fail:
216 #ifdef DEBUG_USB_HOST
217  Notify(PSTR("\r\nXbox One Init Failed, error code: "), 0x80);
218  NotifyFail(rcode);
219 #endif
220  Release();
221  return rcode;
222 }
223 
224 /* Performs a cleanup after failed Init() attempt */
225 uint8_t XBOXONE::Release() {
226  XboxOneConnected = false;
228  bAddress = 0;
229  bPollEnable = false;
230 #ifdef DEBUG_USB_HOST
231  Notify(PSTR("\r\nXbox One Controller Disconnected\r\n"), 0x80);
232 #endif
233  return 0;
234 }
235 
236 uint8_t XBOXONE::Poll() {
237  if(!bPollEnable)
238  return 0;
239  uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
240  uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf);
241  if (!rcode) {
242  readReport();
243 #ifdef PRINTREPORT
244  printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox ONE Controller
245 #endif
246  }
247 #ifdef DEBUG_USB_HOST
248  else if (rcode != 0x04) { // not a matter of no update to send
249  Notify(PSTR("\r\nXbox One Poll Failed, error code: "), 0x80);
250  NotifyFail(rcode);
251  }
252 #endif
253  return rcode;
254 }
255 
256 void XBOXONE::readReport() {
257  if(readBuf == NULL)
258  return;
259  if(readBuf[0] == 0x07) {
260  // The XBOX button has a separate message
261  if(readBuf[4] == 1)
262  ButtonState |= XBOX_BUTTONS[XBOX];
263  else
264  ButtonState &= ~XBOX_BUTTONS[XBOX];
265  }
266  if(readBuf[0] != 0x20) { // Check if it's the correct report, otherwise return - the controller also sends different status reports
267 #ifdef EXTRADEBUG
268  Notify(PSTR("\r\nXbox Poll: "), 0x80);
269  D_PrintHex<uint8_t > (readBuf[0], 0x80); // 0x03 is a heart beat report!
270 #endif
271  return;
272  }
273 
274  uint16_t xbox = ButtonState & XBOX_BUTTONS[XBOX]; // Since the XBOX button is separate, save it and add it back in
275  // xbox button from before, dpad, abxy, start/back, sync, stick click, shoulder buttons
276  ButtonState = xbox | (((uint16_t)readBuf[5] & 0xF) << 8) | (readBuf[4] & 0xF0) | (((uint16_t)readBuf[4] & 0x0C) << 10) | ((readBuf[4] & 0x01) << 3) | (((uint16_t)readBuf[5] & 0xC0) << 8) | ((readBuf[5] & 0x30) >> 4);
277 
278  triggerValue[0] = (uint16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
279  triggerValue[1] = (uint16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
280 
281  hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
282  hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
283  hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);
284  hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);
285 
286  //Notify(PSTR("\r\nButtonState"), 0x80);
287  //PrintHex<uint16_t>(ButtonState, 0x80);
288 
289  if(ButtonState != OldButtonState) {
290  ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
291  OldButtonState = ButtonState;
292  }
293 
294  // Handle click detection for triggers
295  if(triggerValue[0] != 0 && triggerValueOld[0] == 0)
296  L2Clicked = true;
297  triggerValueOld[0] = triggerValue[0];
298  if(triggerValue[1] != 0 && triggerValueOld[1] == 0)
299  R2Clicked = true;
300  triggerValueOld[1] = triggerValue[1];
301 }
302 
303 void XBOXONE::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox ONE Controller
304 #ifdef PRINTREPORT
305  if(readBuf == NULL)
306  return;
307  for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {
308  D_PrintHex<uint8_t > (readBuf[i], 0x80);
309  Notify(PSTR(" "), 0x80);
310  }
311  Notify(PSTR("\r\n"), 0x80);
312 #endif
313 }
314 
316  if(b == L2) // These are analog buttons
317  return triggerValue[0];
318  else if(b == R2)
319  return triggerValue[1];
320  return (bool)(ButtonState & ((uint16_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b])));
321 }
322 
324  if(b == L2) {
325  if(L2Clicked) {
326  L2Clicked = false;
327  return true;
328  }
329  return false;
330  } else if(b == R2) {
331  if(R2Clicked) {
332  R2Clicked = false;
333  return true;
334  }
335  return false;
336  }
337  uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
338  bool click = (ButtonClickState & button);
339  ButtonClickState &= ~button; // clear "click" event
340  return click;
341 }
342 
344  return hatValue[a];
345 }
346 
347 /* Xbox Controller commands */
348 uint8_t XBOXONE::XboxCommand(uint8_t* data, uint16_t nbytes) {
349  uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_OUTPUT_PIPE ].epAddr, nbytes, data);
350 #ifdef DEBUG_USB_HOST
351  Notify(PSTR("\r\nXboxCommand, Return: "), 0x80);
352  D_PrintHex<uint8_t > (rcode, 0x80);
353 #endif
354  return rcode;
355 }
356 
357 void XBOXONE::onInit() {
358  // a short buzz to show the controller is active
359  writeBuf[0] = 0x09;
360  writeBuf[1] = 0x08;
361  writeBuf[2] = 0x00;
362  writeBuf[3] = 0x09;
363  writeBuf[4] = 0x00;
364  writeBuf[5] = 0x0f;
365  writeBuf[6] = 0x04;
366  writeBuf[7] = 0x04;
367  writeBuf[8] = 0x20;
368  writeBuf[9] = 0x20;
369  writeBuf[10] = 0x80;
370  XboxCommand(writeBuf, 11);
371 
372  if(pFuncOnInit)
373  pFuncOnInit(); // Call the user function
374 }
uint8_t bmRcvToggle
Definition: address.h:41
EpInfo * epinfo
Definition: address.h:76
bool lowspeed
Definition: address.h:79
#define USB_ERROR_EPINFO_IS_NULL
Definition: UsbCore.h:83
uint8_t bmNakPower
Definition: address.h:42
#define pgm_read_word(addr)
USB * pUsb
Definition: XBOXONE.h:136
#define NotifyFail(...)
Definition: message.h:55
AnalogHatEnum
uint8_t setConf(uint8_t addr, uint8_t ep, uint8_t conf_value)
Definition: Usb.cpp:808
uint8_t bAddress
Definition: XBOXONE.h:138
EpInfo epInfo[XBOX_MAX_ENDPOINTS]
Definition: XBOXONE.h:140
XBOXONE(USB *pUsb)
Definition: XBOXONE.cpp:27
#define USB_TRANSFER_TYPE_INTERRUPT
Definition: usb_ch9.h:86
#define NotifyFailGetDevDescr(...)
Definition: message.h:50
uint8_t setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo *eprecord_ptr)
Definition: Usb.cpp:64
#define EP_MAXPKTSIZE
Definition: PS3USB.h:26
virtual void FreeAddress(uint8_t addr)=0
uint8_t epAttribs
Definition: address.h:37
virtual UsbDevice * GetUsbDevicePtr(uint8_t addr)=0
#define Notify(...)
Definition: message.h:44
uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr)
Definition: Usb.cpp:799
uint8_t epAddr
Definition: address.h:33
bool XboxOneConnected
Definition: XBOXONE.h:128
#define NotifyFailUnknownDevice(...)
Definition: message.h:54
#define USB_NAK_MAX_POWER
Definition: address.h:27
virtual uint8_t Release()
Definition: XBOXONE.cpp:225
#define XBOX_INPUT_PIPE
Definition: XBOXOLD.h:30
Definition: address.h:32
uint8_t outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *data)
Definition: Usb.cpp:292
ButtonEnum
virtual boolean VIDPIDOK(uint16_t vid, uint16_t pid)
Definition: XBOXONE.h:96
uint16_t getButtonPress(ButtonEnum b)
Definition: XBOXONE.cpp:315
virtual uint8_t AllocAddress(uint8_t parent, bool is_hub=false, uint8_t port=0)=0
uint8_t bmSndToggle
Definition: address.h:40
#define USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE
Definition: UsbCore.h:85
#define PSTR(str)
bool getButtonClick(ButtonEnum b)
Definition: XBOXONE.cpp:323
virtual uint8_t Poll()
Definition: XBOXONE.cpp:236
#define XBOX_OUTPUT_PIPE
Definition: XBOXOLD.h:31
#define USB_NAK_NOWAIT
Definition: address.h:29
#define USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL
Definition: UsbCore.h:82
uint8_t inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t *data)
Definition: Usb.cpp:206
#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED
Definition: UsbCore.h:77
#define XBOX_MAX_ENDPOINTS
Definition: XBOXOLD.h:43
virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed)
Definition: XBOXONE.cpp:42
#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL
Definition: UsbCore.h:80
uint8_t maxPktSize
Definition: address.h:34
AddressPool & GetAddressPool()
Definition: UsbCore.h:213
Definition: UsbCore.h:197
#define XBOX_CONTROL_PIPE
Definition: XBOXOLD.h:29
int16_t getAnalogHat(AnalogHatEnum a)
Definition: XBOXONE.cpp:343
const uint16_t XBOX_BUTTONS[]
Definition: xboxEnums.h:41
uint8_t RegisterDeviceClass(USBDeviceConfig *pdev)
Definition: UsbCore.h:217
#define NotifyFailSetConfDescr(...)
Definition: message.h:53
#define XBOX_REPORT_BUFFER_SIZE
Definition: XBOXONE.h:41
uint8_t getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *dataptr)
defined(USB_METHODS_INLINE)
Definition: Usb.cpp:764
#define NotifyFailSetDevTblEntry(...)
Definition: message.h:51