USB Host Shield 2.0
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 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 controller
22 
24 const uint8_t XBOXOLD_BUTTONS[] 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].bmSndToggle = 0;
55  epInfo[i].bmRcvToggle = 0;
57  }
58 
59  if(pUsb) // register in USB subsystem
60  pUsb->RegisterDeviceClass(this); //set devConfig[] entry
61 }
62 
63 uint8_t XBOXOLD::Init(uint8_t parent, uint8_t port, bool lowspeed) {
64  uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
65  USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
66  uint8_t rcode;
67  UsbDevice *p = NULL;
68  EpInfo *oldep_ptr = NULL;
69  uint16_t PID;
70  uint16_t VID;
71 
72  // get memory address of USB device address pool
73  AddressPool &addrPool = pUsb->GetAddressPool();
74 #ifdef EXTRADEBUG
75  Notify(PSTR("\r\nXBOXUSB Init"), 0x80);
76 #endif
77  // check if address has already been assigned to an instance
78  if(bAddress) {
79 #ifdef DEBUG_USB_HOST
80  Notify(PSTR("\r\nAddress in use"), 0x80);
81 #endif
83  }
84 
85  // Get pointer to pseudo device with address 0 assigned
86  p = addrPool.GetUsbDevicePtr(0);
87 
88  if(!p) {
89 #ifdef DEBUG_USB_HOST
90  Notify(PSTR("\r\nAddress not found"), 0x80);
91 #endif
93  }
94 
95  if(!p->epinfo) {
96 #ifdef DEBUG_USB_HOST
97  Notify(PSTR("\r\nepinfo is null"), 0x80);
98 #endif
100  }
101 
102  // Save old pointer to EP_RECORD of address 0
103  oldep_ptr = p->epinfo;
104 
105  // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
106  p->epinfo = epInfo;
107 
108  p->lowspeed = lowspeed;
109 
110  // Get device descriptor
111  rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
112  // Restore p->epinfo
113  p->epinfo = oldep_ptr;
114 
115  if(rcode)
116  goto FailGetDevDescr;
117 
118  VID = udd->idVendor;
119  PID = udd->idProduct;
120 
121  if(!VIDPIDOK(VID, PID)) // Check if VID and PID match
122  goto FailUnknownDevice;
123 
124  // Allocate new address according to device class
125  bAddress = addrPool.AllocAddress(parent, false, port);
126 
127  if(!bAddress)
129 
130  // Extract Max Packet Size from device descriptor
131  epInfo[0].maxPktSize = udd->bMaxPacketSize0;
132 
133  // Assign new address to the device
134  rcode = pUsb->setAddr(0, 0, bAddress);
135  if(rcode) {
136  p->lowspeed = false;
137  addrPool.FreeAddress(bAddress);
138  bAddress = 0;
139 #ifdef DEBUG_USB_HOST
140  Notify(PSTR("\r\nsetAddr: "), 0x80);
141  D_PrintHex<uint8_t > (rcode, 0x80);
142 #endif
143  return rcode;
144  }
145 #ifdef EXTRADEBUG
146  Notify(PSTR("\r\nAddr: "), 0x80);
147  D_PrintHex<uint8_t > (bAddress, 0x80);
148 #endif
149  //delay(300); // Spec says you should wait at least 200ms
150 
151  p->lowspeed = false;
152 
153  //get pointer to assigned address record
154  p = addrPool.GetUsbDevicePtr(bAddress);
155  if(!p)
157 
158  p->lowspeed = lowspeed;
159 
160  // Assign epInfo to epinfo pointer - only EP0 is known
161  rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
162  if(rcode)
163  goto FailSetDevTblEntry;
164 
165  /* The application will work in reduced host mode, so we can save program and data
166  memory space. After verifying the VID we will use known values for the
167  configuration values for device, interface, endpoints and HID for the XBOX controllers */
168 
169  /* Initialize data structures for endpoints of device */
170  epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX report endpoint
172  epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
176  epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX output endpoint
178  epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
182 
183  rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
184  if(rcode)
185  goto FailSetDevTblEntry;
186 
187  delay(200); // Give time for address change
188 
189  rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
190  if(rcode)
191  goto FailSetConfDescr;
192 
193 #ifdef DEBUG_USB_HOST
194  Notify(PSTR("\r\nXbox Controller Connected\r\n"), 0x80);
195 #endif
196  if(pFuncOnInit)
197  pFuncOnInit(); // Call the user function
198  XboxConnected = true;
199  bPollEnable = true;
200  return 0; // Successful configuration
201 
202  /* Diagnostic messages */
203 FailGetDevDescr:
204 #ifdef DEBUG_USB_HOST
206  goto Fail;
207 #endif
208 
209 FailSetDevTblEntry:
210 #ifdef DEBUG_USB_HOST
212  goto Fail;
213 #endif
214 
215 FailSetConfDescr:
216 #ifdef DEBUG_USB_HOST
218 #endif
219  goto Fail;
220 
221 FailUnknownDevice:
222 #ifdef DEBUG_USB_HOST
223  NotifyFailUnknownDevice(VID, PID);
224 #endif
226 
227 Fail:
228 #ifdef DEBUG_USB_HOST
229  Notify(PSTR("\r\nXbox Init Failed, error code: "), 0x80);
230  NotifyFail(rcode);
231 #endif
232  Release();
233  return rcode;
234 }
235 
236 /* Performs a cleanup after failed Init() attempt */
237 uint8_t XBOXOLD::Release() {
238  XboxConnected = false;
240  bAddress = 0;
241  bPollEnable = false;
242  return 0;
243 }
244 
245 uint8_t XBOXOLD::Poll() {
246  if(!bPollEnable)
247  return 0;
248  uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
249  pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
250  readReport();
251 #ifdef PRINTREPORT
252  printReport(BUFFER_SIZE); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
253 #endif
254  return 0;
255 }
256 
257 void XBOXOLD::readReport() {
258  ButtonState = readBuf[2];
259 
260  for(uint8_t i = 0; i < sizeof (buttonValues); i++)
261  buttonValues[i] = readBuf[i + 4]; // A, B, X, Y, BLACK, WHITE, L1, and R1
262 
263  hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[12] << 8) | readBuf[13]);
264  hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[14] << 8) | readBuf[15]);
265  hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[16] << 8) | readBuf[17]);
266  hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[18] << 8) | readBuf[19]);
267 
268  //Notify(PSTR("\r\nButtonState"), 0x80);
269  //PrintHex<uint8_t>(ButtonState, 0x80);
270 
271  if(ButtonState != OldButtonState || memcmp(buttonValues, oldButtonValues, sizeof (buttonValues)) != 0) {
272  ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
273  OldButtonState = ButtonState;
274 
275  for(uint8_t i = 0; i < sizeof (buttonValues); i++) {
276  if(oldButtonValues[i] == 0 && buttonValues[i] != 0)
277  buttonClicked[i] = true; // Update A, B, X, Y, BLACK, WHITE, L1, and R1 click state
278  oldButtonValues[i] = buttonValues[i];
279  }
280  }
281 }
282 
283 void XBOXOLD::printReport(uint16_t length __attribute__((unused))) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
284 #ifdef PRINTREPORT
285  if(readBuf == NULL)
286  return;
287  for(uint8_t i = 0; i < length; i++) {
288  D_PrintHex<uint8_t > (readBuf[i], 0x80);
289  Notify(PSTR(" "), 0x80);
290  }
291  Notify(PSTR("\r\n"), 0x80);
292 #endif
293 }
294 
295 int8_t XBOXOLD::getAnalogIndex(ButtonEnum b) {
296  // For legacy reasons these mapping indices not match up,
297  // as the original code uses L1/R1 for the triggers and
298  // L2/R2 for the white/black buttons. To fix these new enums
299  // we have to transpose the keys before passing them through
300  // the button index function
301  switch (b) {
302  case(LT): b = L1; break; // normally L2
303  case(RT): b = R1; break; // normally R2
304  case(LB): b = WHITE; break; // normally L1
305  case(RB): b = BLACK; break; // normally R1
306  default: break;
307  }
308 
309  // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
310  const int8_t index = ButtonIndex(b);
311 
312  switch (index) {
313  case ButtonIndex(A):
314  case ButtonIndex(B):
315  case ButtonIndex(X):
316  case ButtonIndex(Y):
317  case ButtonIndex(BLACK):
318  case ButtonIndex(WHITE):
319  case ButtonIndex(L1):
320  case ButtonIndex(R1):
321  return index;
322  default: break;
323  }
324 
325  return -1;
326 }
327 
328 int8_t XBOXOLD::getDigitalIndex(ButtonEnum b) {
329  // UP, DOWN, LEFT, RIGHT, START, BACK, L3, and R3 are digital buttons
330  const int8_t index = ButtonIndex(b);
331 
332  switch (index) {
333  case ButtonIndex(UP):
334  case ButtonIndex(DOWN):
335  case ButtonIndex(LEFT):
336  case ButtonIndex(RIGHT):
337  case ButtonIndex(START):
338  case ButtonIndex(BACK):
339  case ButtonIndex(L3):
340  case ButtonIndex(R3):
341  return index;
342  default: break;
343  }
344 
345  return -1;
346 }
347 
349  const int8_t analogIndex = getAnalogIndex(b);
350  if (analogIndex >= 0) {
351  const uint8_t buttonIndex = pgm_read_byte(&XBOXOLD_BUTTONS[analogIndex]);
352  return buttonValues[buttonIndex];
353  }
354  const int8_t digitalIndex = getDigitalIndex(b);
355  if (digitalIndex >= 0) {
356  const uint8_t buttonMask = pgm_read_byte(&XBOXOLD_BUTTONS[digitalIndex]);
357  return (ButtonState & buttonMask);
358  }
359  return 0;
360 }
361 
363  const int8_t analogIndex = getAnalogIndex(b);
364  if (analogIndex >= 0) {
365  const uint8_t buttonIndex = pgm_read_byte(&XBOXOLD_BUTTONS[analogIndex]);
366  if (buttonClicked[buttonIndex]) {
367  buttonClicked[buttonIndex] = false;
368  return true;
369  }
370  return false;
371  }
372  const int8_t digitalIndex = getDigitalIndex(b);
373  if (digitalIndex >= 0) {
374  const uint8_t mask = pgm_read_byte(&XBOXOLD_BUTTONS[digitalIndex]);
375  const bool click = (ButtonClickState & mask);
376  ButtonClickState &= ~mask;
377  return click;
378  }
379  return 0;
380 }
381 
383  return hatValue[a];
384 }
385 
386 /* Xbox Controller commands */
387 void XBOXOLD::XboxCommand(uint8_t* data, uint16_t nbytes) {
388  //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)
389  pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
390 }
391 
392 void XBOXOLD::setRumbleOn(uint8_t lValue, uint8_t rValue) {
393  uint8_t writeBuf[6];
394 
395  writeBuf[0] = 0x00;
396  writeBuf[1] = 0x06;
397  writeBuf[2] = 0x00;
398  writeBuf[3] = rValue; // small weight
399  writeBuf[4] = 0x00;
400  writeBuf[5] = lValue; // big weight
401 
402  XboxCommand(writeBuf, 6);
403 }
#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
const uint8_t XBOXOLD_BUTTONS[]
Definition: XBOXOLD.cpp:24
#define XBOX_INPUT_PIPE
Definition: XBOXOLD.h:30
#define XBOX_CONTROL_PIPE
Definition: XBOXOLD.h:29
#define XBOX_OUTPUT_PIPE
Definition: XBOXOLD.h:31
#define XBOX_MAX_ENDPOINTS
Definition: XBOXOLD.h:45
#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
EpInfo epInfo[XBOX_MAX_ENDPOINTS]
Definition: XBOXOLD.h:155
XBOXOLD(USB *pUsb)
Definition: XBOXOLD.cpp:47
USB * pUsb
Definition: XBOXOLD.h:151
uint8_t Poll()
Definition: XBOXOLD.cpp:245
bool getButtonClick(ButtonEnum b)
Definition: XBOXOLD.cpp:362
virtual bool VIDPIDOK(uint16_t vid, uint16_t pid)
Definition: XBOXOLD.h:98
uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed)
Definition: XBOXOLD.cpp:63
uint8_t getButtonPress(ButtonEnum b)
Definition: XBOXOLD.cpp:348
uint8_t Release()
Definition: XBOXOLD.cpp:237
bool XboxConnected
Definition: XBOXOLD.h:143
void setRumbleOn(uint8_t lValue, uint8_t rValue)
Definition: XBOXOLD.cpp:392
uint8_t bAddress
Definition: XBOXOLD.h:153
int16_t getAnalogHat(AnalogHatEnum a)
Definition: XBOXOLD.cpp:382
constexpr int8_t ButtonIndex(ButtonEnum key)
AnalogHatEnum
@ LeftHatX
@ RightHatY
@ RightHatX
@ LeftHatY
ButtonEnum
@ START
@ WHITE
@ B
@ A
@ LT
@ Y
@ X
@ RB
@ L3
@ DOWN
@ R3
@ UP
@ BACK
@ LEFT
@ L1
@ LB
@ RIGHT
@ RT
@ BLACK
@ R1
#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)