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((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
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  // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
297  const int8_t index = ButtonIndex(b);
298 
299  switch (index) {
300  case ButtonIndex(A):
301  case ButtonIndex(B):
302  case ButtonIndex(X):
303  case ButtonIndex(Y):
304  case ButtonIndex(BLACK):
305  case ButtonIndex(WHITE):
306  case ButtonIndex(L1):
307  case ButtonIndex(R1):
308  return index;
309  default: break;
310  }
311 
312  return -1;
313 }
314 
315 int8_t XBOXOLD::getDigitalIndex(ButtonEnum b) {
316  // UP, DOWN, LEFT, RIGHT, START, BACK, L3, and R3 are digital buttons
317  const int8_t index = ButtonIndex(b);
318 
319  switch (index) {
320  case ButtonIndex(UP):
321  case ButtonIndex(DOWN):
322  case ButtonIndex(LEFT):
323  case ButtonIndex(RIGHT):
324  case ButtonIndex(START):
325  case ButtonIndex(BACK):
326  case ButtonIndex(L3):
327  case ButtonIndex(R3):
328  return index;
329  default: break;
330  }
331 
332  return -1;
333 }
334 
336  const int8_t analogIndex = getAnalogIndex(b);
337  if (analogIndex >= 0) {
338  const uint8_t buttonIndex = pgm_read_byte(&XBOXOLD_BUTTONS[analogIndex]);
339  return buttonValues[buttonIndex];
340  }
341  const int8_t digitalIndex = getDigitalIndex(b);
342  if (digitalIndex >= 0) {
343  const uint8_t buttonMask = pgm_read_byte(&XBOXOLD_BUTTONS[digitalIndex]);
344  return (ButtonState & buttonMask);
345  }
346  return 0;
347 }
348 
350  const int8_t analogIndex = getAnalogIndex(b);
351  if (analogIndex >= 0) {
352  const uint8_t buttonIndex = pgm_read_byte(&XBOXOLD_BUTTONS[analogIndex]);
353  if (buttonClicked[buttonIndex]) {
354  buttonClicked[buttonIndex] = false;
355  return true;
356  }
357  return false;
358  }
359  const int8_t digitalIndex = getDigitalIndex(b);
360  if (digitalIndex >= 0) {
361  const uint8_t mask = pgm_read_byte(&XBOXOLD_BUTTONS[digitalIndex]);
362  const bool click = (ButtonClickState & mask);
363  ButtonClickState &= ~mask;
364  return click;
365  }
366  return 0;
367 }
368 
370  return hatValue[a];
371 }
372 
373 /* Xbox Controller commands */
374 void XBOXOLD::XboxCommand(uint8_t* data, uint16_t nbytes) {
375  //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)
376  pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
377 }
378 
379 void XBOXOLD::setRumbleOn(uint8_t lValue, uint8_t rValue) {
380  uint8_t writeBuf[6];
381 
382  writeBuf[0] = 0x00;
383  writeBuf[1] = 0x06;
384  writeBuf[2] = 0x00;
385  writeBuf[3] = rValue; // small weight
386  writeBuf[4] = 0x00;
387  writeBuf[5] = lValue; // big weight
388 
389  XboxCommand(writeBuf, 6);
390 }
AddressPool::GetUsbDevicePtr
virtual UsbDevice * GetUsbDevicePtr(uint8_t addr)=0
XBOXOLD.h
bmREQ_HID_OUT
#define bmREQ_HID_OUT
Definition: usbhid.h:63
R3
@ R3
Definition: controllerEnums.h:98
XBOXOLD::getAnalogHat
int16_t getAnalogHat(AnalogHatEnum a)
Definition: XBOXOLD.cpp:369
AddressPool
Definition: address.h:90
EpInfo::bmSndToggle
uint8_t bmSndToggle
Definition: address.h:47
MADCATZ_VID
#define MADCATZ_VID
Definition: XBOXOLD.h:35
NotifyFail
#define NotifyFail(...)
Definition: message.h:62
UsbDevice::lowspeed
bool lowspeed
Definition: address.h:86
USB_TRANSFER_TYPE_INTERRUPT
#define USB_TRANSFER_TYPE_INTERRUPT
Definition: usb_ch9.h:93
USB::RegisterDeviceClass
uint8_t RegisterDeviceClass(USBDeviceConfig *pdev)
Definition: UsbCore.h:230
AddressPool::AllocAddress
virtual uint8_t AllocAddress(uint8_t parent, bool is_hub=false, uint8_t port=0)=0
USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE
#define USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE
Definition: UsbCore.h:98
NotifyFailSetDevTblEntry
#define NotifyFailSetDevTblEntry(...)
Definition: message.h:58
XBOXOLD::XboxConnected
bool XboxConnected
Definition: XBOXOLD.h:141
USB::getDevDescr
uint8_t getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *dataptr)
defined(USB_METHODS_INLINE)
Definition: Usb.cpp:796
ButtonIndex
constexpr int8_t ButtonIndex(ButtonEnum key)
Definition: controllerEnums.h:186
Y
@ Y
Definition: controllerEnums.h:141
WHITE
@ WHITE
Definition: controllerEnums.h:155
XBOX_INPUT_PIPE
#define XBOX_INPUT_PIPE
Definition: XBOXOLD.h:30
EpInfo::epAddr
uint8_t epAddr
Definition: address.h:40
X
@ X
Definition: controllerEnums.h:140
Notify
#define Notify(...)
Definition: message.h:51
BLACK
@ BLACK
Definition: controllerEnums.h:154
XBOXOLD::getButtonPress
uint8_t getButtonPress(ButtonEnum b)
Definition: XBOXOLD.cpp:335
BACK
@ BACK
Definition: controllerEnums.h:143
UP
@ UP
Definition: controllerEnums.h:81
A
@ A
Definition: controllerEnums.h:138
XBOX_OLD_PID1
#define XBOX_OLD_PID1
Definition: XBOXOLD.h:38
NotifyFailGetDevDescr
#define NotifyFailGetDevDescr(...)
Definition: message.h:57
HID_REQUEST_SET_REPORT
#define HID_REQUEST_SET_REPORT
Definition: usbhid.h:72
XBOXOLD::getButtonClick
bool getButtonClick(ButtonEnum b)
Definition: XBOXOLD.cpp:349
R1
@ R1
Definition: controllerEnums.h:101
USB_DEVICE_DESCRIPTOR
Definition: usb_ch9.h:105
EpInfo::bmNakPower
uint8_t bmNakPower
Definition: address.h:49
XBOXOLD::epInfo
EpInfo epInfo[XBOX_MAX_ENDPOINTS]
Definition: XBOXOLD.h:153
RIGHT
@ RIGHT
Definition: controllerEnums.h:82
XBOX_OLD_PID3
#define XBOX_OLD_PID3
Definition: XBOXOLD.h:40
B
@ B
Definition: controllerEnums.h:139
DOWN
@ DOWN
Definition: controllerEnums.h:83
EP_MAXPKTSIZE
#define EP_MAXPKTSIZE
Definition: PS3USB.h:26
XBOX_OUTPUT_PIPE
#define XBOX_OUTPUT_PIPE
Definition: XBOXOLD.h:31
USB_NAK_MAX_POWER
#define USB_NAK_MAX_POWER
Definition: address.h:34
XBOXOLD::Release
uint8_t Release()
Definition: XBOXOLD.cpp:237
RightHatY
@ RightHatY
Definition: controllerEnums.h:221
XBOXOLD::Poll
uint8_t Poll()
Definition: XBOXOLD.cpp:245
EpInfo
Definition: address.h:39
JOYTECH_VID
#define JOYTECH_VID
Definition: XBOXOLD.h:36
USB_NAK_NOWAIT
#define USB_NAK_NOWAIT
Definition: address.h:36
EpInfo::bmRcvToggle
uint8_t bmRcvToggle
Definition: address.h:48
XBOXOLD_BUTTONS
const uint8_t XBOXOLD_BUTTONS[]
Definition: XBOXOLD.cpp:24
USB::ctrlReq
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
NotifyFailUnknownDevice
#define NotifyFailUnknownDevice(...)
Definition: message.h:61
XBOXOLD::XBOXOLD
XBOXOLD(USB *pUsb)
Definition: XBOXOLD.cpp:47
USB
Definition: UsbCore.h:210
EpInfo::epAttribs
uint8_t epAttribs
Definition: address.h:44
USB::inTransfer
uint8_t inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t *data, uint8_t bInterval=0)
Definition: Usb.cpp:209
USB_ERROR_EPINFO_IS_NULL
#define USB_ERROR_EPINFO_IS_NULL
Definition: UsbCore.h:96
XBOXOLD::Init
uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed)
Definition: XBOXOLD.cpp:63
UsbDevice
Definition: address.h:82
XBOXOLD::setRumbleOn
void setRumbleOn(uint8_t lValue, uint8_t rValue)
Definition: XBOXOLD.cpp:379
pgm_read_byte
#define pgm_read_byte(addr)
Definition: version_helper.h:161
USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL
#define USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL
Definition: UsbCore.h:95
XBOX_OLD_PID4
#define XBOX_OLD_PID4
Definition: XBOXOLD.h:41
USB::setConf
uint8_t setConf(uint8_t addr, uint8_t ep, uint8_t conf_value)
Definition: Usb.cpp:840
XBOXOLD::pUsb
USB * pUsb
Definition: XBOXOLD.h:149
XBOX_MAX_ENDPOINTS
#define XBOX_MAX_ENDPOINTS
Definition: XBOXOLD.h:43
L1
@ L1
Definition: controllerEnums.h:100
XBOX_OLD_PID2
#define XBOX_OLD_PID2
Definition: XBOXOLD.h:39
XBOXOLD::bAddress
uint8_t bAddress
Definition: XBOXOLD.h:151
PSTR
#define PSTR(str)
Definition: version_helper.h:54
USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL
#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL
Definition: UsbCore.h:93
NotifyFailSetConfDescr
#define NotifyFailSetConfDescr(...)
Definition: message.h:60
LeftHatX
@ LeftHatX
Definition: controllerEnums.h:215
AddressPool::FreeAddress
virtual void FreeAddress(uint8_t addr)=0
L3
@ L3
Definition: controllerEnums.h:97
USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED
#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED
Definition: UsbCore.h:90
USB::setAddr
uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr)
Definition: Usb.cpp:831
EpInfo::maxPktSize
uint8_t maxPktSize
Definition: address.h:41
USB::setEpInfoEntry
uint8_t setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo *eprecord_ptr)
Definition: Usb.cpp:64
XBOX_VID
#define XBOX_VID
Definition: XBOXOLD.h:34
ButtonEnum
ButtonEnum
Definition: controllerEnums.h:78
LEFT
@ LEFT
Definition: controllerEnums.h:84
AnalogHatEnum
AnalogHatEnum
Definition: controllerEnums.h:213
LeftHatY
@ LeftHatY
Definition: controllerEnums.h:217
RightHatX
@ RightHatX
Definition: controllerEnums.h:219
START
@ START
Definition: controllerEnums.h:95
XBOX_CONTROL_PIPE
#define XBOX_CONTROL_PIPE
Definition: XBOXOLD.h:29
USB::GetAddressPool
AddressPool & GetAddressPool()
Definition: UsbCore.h:226
UsbDevice::epinfo
EpInfo * epinfo
Definition: address.h:83