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 
296  uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]);
297  if(b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
298  return buttonValues[button]; // Analog buttons
299  return (ButtonState & button); // Digital buttons
300 }
301 
303  uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]);
304  if(b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) { // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
305  if(buttonClicked[button]) {
306  buttonClicked[button] = false;
307  return true;
308  }
309  return false;
310  }
311 
312  bool click = (ButtonClickState & button);
313  ButtonClickState &= ~button; // clear "click" event
314  return click;
315 }
316 
318  return hatValue[a];
319 }
320 
321 /* Xbox Controller commands */
322 void XBOXOLD::XboxCommand(uint8_t* data, uint16_t nbytes) {
323  //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)
324  pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
325 }
326 
327 void XBOXOLD::setRumbleOn(uint8_t lValue, uint8_t rValue) {
328  uint8_t writeBuf[6];
329 
330  writeBuf[0] = 0x00;
331  writeBuf[1] = 0x06;
332  writeBuf[2] = 0x00;
333  writeBuf[3] = rValue; // small weight
334  writeBuf[4] = 0x00;
335  writeBuf[5] = lValue; // big weight
336 
337  XboxCommand(writeBuf, 6);
338 }
uint8_t bmRcvToggle
Definition: address.h:48
EpInfo * epinfo
Definition: address.h:83
bool lowspeed
Definition: address.h:86
#define USB_ERROR_EPINFO_IS_NULL
Definition: UsbCore.h:96
uint8_t bmNakPower
Definition: address.h:49
uint8_t Release()
Definition: XBOXOLD.cpp:237
#define NotifyFail(...)
Definition: message.h:62
AnalogHatEnum
uint8_t setConf(uint8_t addr, uint8_t ep, uint8_t conf_value)
Definition: Usb.cpp:840
#define USB_TRANSFER_TYPE_INTERRUPT
Definition: usb_ch9.h:93
#define bmREQ_HID_OUT
Definition: usbhid.h:63
#define pgm_read_byte(addr)
void setRumbleOn(uint8_t lValue, uint8_t rValue)
Definition: XBOXOLD.cpp:327
#define NotifyFailGetDevDescr(...)
Definition: message.h:57
uint8_t setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo *eprecord_ptr)
Definition: Usb.cpp:64
#define XBOX_OLD_PID4
Definition: XBOXOLD.h:41
EpInfo epInfo[XBOX_MAX_ENDPOINTS]
Definition: XBOXOLD.h:153
#define EP_MAXPKTSIZE
Definition: PS3USB.h:26
virtual void FreeAddress(uint8_t addr)=0
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:126
virtual UsbDevice * GetUsbDevicePtr(uint8_t addr)=0
const uint8_t XBOXOLD_BUTTONS[]
Definition: XBOXOLD.cpp:24
#define Notify(...)
Definition: message.h:51
uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr)
Definition: Usb.cpp:831
uint8_t epAddr
Definition: address.h:40
#define NotifyFailUnknownDevice(...)
Definition: message.h:61
#define USB_NAK_MAX_POWER
Definition: address.h:34
int16_t getAnalogHat(AnalogHatEnum a)
Definition: XBOXOLD.cpp:317
#define XBOX_VID
Definition: XBOXOLD.h:34
#define XBOX_INPUT_PIPE
Definition: XBOXOLD.h:30
XBOXOLD(USB *pUsb)
Definition: XBOXOLD.cpp:47
#define XBOX_OLD_PID1
Definition: XBOXOLD.h:38
Definition: address.h:39
ButtonEnum
#define JOYTECH_VID
Definition: XBOXOLD.h:36
USB * pUsb
Definition: XBOXOLD.h:149
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:98
#define PSTR(str)
uint8_t Poll()
Definition: XBOXOLD.cpp:245
#define XBOX_OLD_PID2
Definition: XBOXOLD.h:39
#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:95
#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED
Definition: UsbCore.h:90
uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed)
Definition: XBOXOLD.cpp:63
#define XBOX_MAX_ENDPOINTS
Definition: XBOXOLD.h:43
#define XBOX_OLD_PID3
Definition: XBOXOLD.h:40
uint8_t inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t *data, uint8_t bInterval=0)
Definition: Usb.cpp:209
#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL
Definition: UsbCore.h:93
bool XboxConnected
Definition: XBOXOLD.h:141
uint8_t maxPktSize
Definition: address.h:41
AddressPool & GetAddressPool()
Definition: UsbCore.h:226
Definition: UsbCore.h:210
bool getButtonClick(ButtonEnum b)
Definition: XBOXOLD.cpp:302
uint8_t getButtonPress(ButtonEnum b)
Definition: XBOXOLD.cpp:295
#define XBOX_CONTROL_PIPE
Definition: XBOXOLD.h:29
uint8_t bAddress
Definition: XBOXOLD.h:151
#define HID_REQUEST_SET_REPORT
Definition: usbhid.h:72
uint8_t RegisterDeviceClass(USBDeviceConfig *pdev)
Definition: UsbCore.h:230
#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:796
#define NotifyFailSetDevTblEntry(...)
Definition: message.h:58