USB Host Shield 2.0
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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].epAttribs = 0;
56  }
57 
58  if(pUsb) // register in USB subsystem
59  pUsb->RegisterDeviceClass(this); //set devConfig[] entry
60 }
61 
62 uint8_t XBOXOLD::Init(uint8_t parent, uint8_t port, bool lowspeed) {
63  uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
64  USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
65  uint8_t rcode;
66  UsbDevice *p = NULL;
67  EpInfo *oldep_ptr = NULL;
68  uint16_t PID;
69  uint16_t VID;
70 
71  // get memory address of USB device address pool
72  AddressPool &addrPool = pUsb->GetAddressPool();
73 #ifdef EXTRADEBUG
74  Notify(PSTR("\r\nXBOXUSB Init"), 0x80);
75 #endif
76  // check if address has already been assigned to an instance
77  if(bAddress) {
78 #ifdef DEBUG_USB_HOST
79  Notify(PSTR("\r\nAddress in use"), 0x80);
80 #endif
82  }
83 
84  // Get pointer to pseudo device with address 0 assigned
85  p = addrPool.GetUsbDevicePtr(0);
86 
87  if(!p) {
88 #ifdef DEBUG_USB_HOST
89  Notify(PSTR("\r\nAddress not found"), 0x80);
90 #endif
92  }
93 
94  if(!p->epinfo) {
95 #ifdef DEBUG_USB_HOST
96  Notify(PSTR("\r\nepinfo is null"), 0x80);
97 #endif
99  }
100 
101  // Save old pointer to EP_RECORD of address 0
102  oldep_ptr = p->epinfo;
103 
104  // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
105  p->epinfo = epInfo;
106 
107  p->lowspeed = lowspeed;
108 
109  // Get device descriptor
110  rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
111  // Restore p->epinfo
112  p->epinfo = oldep_ptr;
113 
114  if(rcode)
115  goto FailGetDevDescr;
116 
117  VID = udd->idVendor;
118  PID = udd->idProduct;
119 
120  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
121  goto FailUnknownDevice;
122 
123  // Allocate new address according to device class
124  bAddress = addrPool.AllocAddress(parent, false, port);
125 
126  if(!bAddress)
128 
129  // Extract Max Packet Size from device descriptor
130  epInfo[0].maxPktSize = udd->bMaxPacketSize0;
131 
132  // Assign new address to the device
133  rcode = pUsb->setAddr(0, 0, bAddress);
134  if(rcode) {
135  p->lowspeed = false;
136  addrPool.FreeAddress(bAddress);
137  bAddress = 0;
138 #ifdef DEBUG_USB_HOST
139  Notify(PSTR("\r\nsetAddr: "), 0x80);
140  D_PrintHex<uint8_t > (rcode, 0x80);
141 #endif
142  return rcode;
143  }
144 #ifdef EXTRADEBUG
145  Notify(PSTR("\r\nAddr: "), 0x80);
146  D_PrintHex<uint8_t > (bAddress, 0x80);
147 #endif
148  //delay(300); // Spec says you should wait at least 200ms
149 
150  p->lowspeed = false;
151 
152  //get pointer to assigned address record
153  p = addrPool.GetUsbDevicePtr(bAddress);
154  if(!p)
156 
157  p->lowspeed = lowspeed;
158 
159  // Assign epInfo to epinfo pointer - only EP0 is known
160  rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
161  if(rcode)
162  goto FailSetDevTblEntry;
163 
164  /* The application will work in reduced host mode, so we can save program and data
165  memory space. After verifying the VID we will use known values for the
166  configuration values for device, interface, endpoints and HID for the XBOX controllers */
167 
168  /* Initialize data structures for endpoints of device */
169  epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX report endpoint
171  epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
175  epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX output endpoint
177  epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
181 
182  rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
183  if(rcode)
184  goto FailSetDevTblEntry;
185 
186  delay(200); // Give time for address change
187 
188  rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
189  if(rcode)
190  goto FailSetConfDescr;
191 
192 #ifdef DEBUG_USB_HOST
193  Notify(PSTR("\r\nXbox Controller Connected\r\n"), 0x80);
194 #endif
195  if(pFuncOnInit)
196  pFuncOnInit(); // Call the user function
197  XboxConnected = true;
198  bPollEnable = true;
199  return 0; // Successful configuration
200 
201  /* Diagnostic messages */
202 FailGetDevDescr:
203 #ifdef DEBUG_USB_HOST
205  goto Fail;
206 #endif
207 
208 FailSetDevTblEntry:
209 #ifdef DEBUG_USB_HOST
211  goto Fail;
212 #endif
213 
214 FailSetConfDescr:
215 #ifdef DEBUG_USB_HOST
217 #endif
218  goto Fail;
219 
220 FailUnknownDevice:
221 #ifdef DEBUG_USB_HOST
222  NotifyFailUnknownDevice(VID, PID);
223 #endif
225 
226 Fail:
227 #ifdef DEBUG_USB_HOST
228  Notify(PSTR("\r\nXbox Init Failed, error code: "), 0x80);
229  NotifyFail(rcode);
230 #endif
231  Release();
232  return rcode;
233 }
234 
235 /* Performs a cleanup after failed Init() attempt */
236 uint8_t XBOXOLD::Release() {
237  XboxConnected = false;
239  bAddress = 0;
240  bPollEnable = false;
241  return 0;
242 }
243 
244 uint8_t XBOXOLD::Poll() {
245  if(!bPollEnable)
246  return 0;
247  uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
248  pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
249  readReport();
250 #ifdef PRINTREPORT
251  printReport(BUFFER_SIZE); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
252 #endif
253  return 0;
254 }
255 
256 void XBOXOLD::readReport() {
257  ButtonState = readBuf[2];
258 
259  for(uint8_t i = 0; i < sizeof (buttonValues); i++)
260  buttonValues[i] = readBuf[i + 4]; // A, B, X, Y, BLACK, WHITE, L1, and R1
261 
262  hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[12] << 8) | readBuf[13]);
263  hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[14] << 8) | readBuf[15]);
264  hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[16] << 8) | readBuf[17]);
265  hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[18] << 8) | readBuf[19]);
266 
267  //Notify(PSTR("\r\nButtonState"), 0x80);
268  //PrintHex<uint8_t>(ButtonState, 0x80);
269 
270  if(ButtonState != OldButtonState || memcmp(buttonValues, oldButtonValues, sizeof (buttonValues)) != 0) {
271  ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
272  OldButtonState = ButtonState;
273 
274  for(uint8_t i = 0; i < sizeof (buttonValues); i++) {
275  if(oldButtonValues[i] == 0 && buttonValues[i] != 0)
276  buttonClicked[i] = true; // Update A, B, X, Y, BLACK, WHITE, L1, and R1 click state
277  oldButtonValues[i] = buttonValues[i];
278  }
279  }
280 }
281 
282 void XBOXOLD::printReport(uint16_t length) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
283 #ifdef PRINTREPORT
284  if(readBuf == NULL)
285  return;
286  for(uint8_t i = 0; i < length; i++) {
287  D_PrintHex<uint8_t > (readBuf[i], 0x80);
288  Notify(PSTR(" "), 0x80);
289  }
290  Notify(PSTR("\r\n"), 0x80);
291 #endif
292 }
293 
295  uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]);
296  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
297  return buttonValues[button]; // Analog buttons
298  return (ButtonState & button); // Digital buttons
299 }
300 
302  uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]);
303  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
304  if(buttonClicked[button]) {
305  buttonClicked[button] = false;
306  return true;
307  }
308  return false;
309  }
310 
311  bool click = (ButtonClickState & button);
312  ButtonClickState &= ~button; // clear "click" event
313  return click;
314 }
315 
317  return hatValue[a];
318 }
319 
320 /* Xbox Controller commands */
321 void XBOXOLD::XboxCommand(uint8_t* data, uint16_t nbytes) {
322  //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)
323  pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
324 }
325 
326 void XBOXOLD::setRumbleOn(uint8_t lValue, uint8_t rValue) {
327  uint8_t writeBuf[6];
328 
329  writeBuf[0] = 0x00;
330  writeBuf[1] = 0x06;
331  writeBuf[2] = 0x00;
332  writeBuf[3] = rValue; // small weight
333  writeBuf[4] = 0x00;
334  writeBuf[5] = lValue; // big weight
335 
336  XboxCommand(writeBuf, 6);
337 }
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:67
uint8_t bmNakPower
Definition: address.h:42
virtual uint8_t Release()
Definition: XBOXOLD.cpp:236
#define NotifyFail(...)
Definition: message.h:55
AnalogHatEnum
uint8_t setConf(uint8_t addr, uint8_t ep, uint8_t conf_value)
Definition: Usb.cpp:805
void setRumbleOn(uint8_t lValue, uint8_t rValue)
Definition: XBOXOLD.cpp:326
#define NotifyFailGetDevDescr(...)
Definition: message.h:50
uint8_t setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo *eprecord_ptr)
Definition: Usb.cpp:64
#define XBOX_OLD_PID4
Definition: XBOXOLD.h:43
EpInfo epInfo[XBOX_MAX_ENDPOINTS]
Definition: XBOXOLD.h:159
#define EP_MAXPKTSIZE
Definition: PS3USB.h:25
virtual void FreeAddress(uint8_t addr)=0
uint8_t epAttribs
Definition: address.h:37
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:44
uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr)
Definition: Usb.cpp:796
uint8_t epAddr
Definition: address.h:33
#define NotifyFailUnknownDevice(...)
Definition: message.h:54
#define USB_NAK_MAX_POWER
Definition: address.h:27
int16_t getAnalogHat(AnalogHatEnum a)
Definition: XBOXOLD.cpp:316
#define XBOX_VID
Definition: XBOXOLD.h:36
#define XBOX_INPUT_PIPE
Definition: XBOXOLD.h:32
XBOXOLD(USB *pUsb)
Definition: XBOXOLD.cpp:47
#define EP_INTERRUPT
Definition: PS3USB.h:28
#define XBOX_OLD_PID1
Definition: XBOXOLD.h:40
Definition: address.h:32
ButtonEnum
#define HID_REQUEST_SET_REPORT
Definition: BTD.h:39
#define JOYTECH_VID
Definition: XBOXOLD.h:38
USB * pUsb
Definition: XBOXOLD.h:155
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:69
virtual uint8_t Poll()
Definition: XBOXOLD.cpp:244
#define XBOX_OLD_PID2
Definition: XBOXOLD.h:41
#define MADCATZ_VID
Definition: XBOXOLD.h:37
#define XBOX_OUTPUT_PIPE
Definition: XBOXOLD.h:33
#define USB_NAK_NOWAIT
Definition: address.h:29
#define USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL
Definition: UsbCore.h:66
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:61
virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed)
Definition: XBOXOLD.cpp:62
#define XBOX_MAX_ENDPOINTS
Definition: XBOXOLD.h:49
#define XBOX_OLD_PID3
Definition: XBOXOLD.h:42
#define bmREQ_HID_OUT
Definition: BTD.h:38
#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL
Definition: UsbCore.h:64
bool XboxConnected
Definition: XBOXOLD.h:147
uint8_t maxPktSize
Definition: address.h:34
AddressPool & GetAddressPool()
Definition: UsbCore.h:192
Definition: UsbCore.h:176
bool getButtonClick(ButtonEnum b)
Definition: XBOXOLD.cpp:301
uint8_t getButtonPress(ButtonEnum b)
Definition: XBOXOLD.cpp:294
#define XBOX_CONTROL_PIPE
Definition: XBOXOLD.h:31
uint8_t bAddress
Definition: XBOXOLD.h:157
uint8_t RegisterDeviceClass(USBDeviceConfig *pdev)
Definition: UsbCore.h:196
#define NotifyFailSetConfDescr(...)
Definition: message.h:53
uint8_t getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *dataptr)
defined(USB_METHODS_INLINE)
Definition: Usb.cpp:761
#define NotifyFailSetDevTblEntry(...)
Definition: message.h:51