USB Host Shield 2.0
Loading...
Searching...
No Matches
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
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
48pUsb(p), // pointer to USB class instance - mandatory
49bAddress(0), // device address - mandatory
50bNumEP(1), // If config descriptor needs to be parsed
51qNextPollTime(0), // Reset NextPollTime
52pollInterval(0),
53bPollEnable(false) { // don't start polling before dongle is connected
54 for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
55 epInfo[i].epAddr = 0;
56 epInfo[i].maxPktSize = (i) ? 0 : 8;
57 epInfo[i].bmSndToggle = 0;
58 epInfo[i].bmRcvToggle = 0;
60 }
61
62 if(pUsb) // register in USB subsystem
63 pUsb->RegisterDeviceClass(this); //set devConfig[] entry
64}
65
68 USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
70 UsbDevice *p = NULL;
72 uint16_t PID;
73 uint16_t VID;
74 uint8_t num_of_conf; // Number of configurations
75
76 // get memory address of USB device address pool
77 AddressPool &addrPool = pUsb->GetAddressPool();
78#ifdef EXTRADEBUG
79 Notify(PSTR("\r\nXBOXUSB Init"), 0x80);
80#endif
81 // check if address has already been assigned to an instance
82 if(bAddress) {
83#ifdef DEBUG_USB_HOST
84 Notify(PSTR("\r\nAddress in use"), 0x80);
85#endif
87 }
88
89 // Get pointer to pseudo device with address 0 assigned
90 p = addrPool.GetUsbDevicePtr(0);
91
92 if(!p) {
93#ifdef DEBUG_USB_HOST
94 Notify(PSTR("\r\nAddress not found"), 0x80);
95#endif
97 }
98
99 if(!p->epinfo) {
100#ifdef DEBUG_USB_HOST
101 Notify(PSTR("\r\nepinfo is null"), 0x80);
102#endif
104 }
105
106 // Save old pointer to EP_RECORD of address 0
107 oldep_ptr = p->epinfo;
108
109 // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
110 p->epinfo = epInfo;
111
112 p->lowspeed = lowspeed;
113
114 // Get device descriptor
115 rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
116 // Restore p->epinfo
117 p->epinfo = oldep_ptr;
118
119 if(rcode)
120 goto FailGetDevDescr;
121
122 VID = udd->idVendor;
123 PID = udd->idProduct;
124
125 if(!VIDPIDOK(VID, PID)) // Check if VID and PID match
127
128 // Allocate new address according to device class
129 bAddress = addrPool.AllocAddress(parent, false, port);
130
131 if(!bAddress)
133
134 // Extract Max Packet Size from device descriptor
135 epInfo[0].maxPktSize = udd->bMaxPacketSize0;
136
137 // Assign new address to the device
138 rcode = pUsb->setAddr(0, 0, bAddress);
139 if(rcode) {
140 p->lowspeed = false;
141 addrPool.FreeAddress(bAddress);
142 bAddress = 0;
143#ifdef DEBUG_USB_HOST
144 Notify(PSTR("\r\nsetAddr: "), 0x80);
146#endif
147 return rcode;
148 }
149#ifdef EXTRADEBUG
150 Notify(PSTR("\r\nAddr: "), 0x80);
152#endif
153 //delay(300); // Spec says you should wait at least 200ms
154
155 p->lowspeed = false;
156
157 //get pointer to assigned address record
158 p = addrPool.GetUsbDevicePtr(bAddress);
159 if(!p)
161
162 p->lowspeed = lowspeed;
163
164 // Assign epInfo to epinfo pointer - only EP0 is known
166 if(rcode)
168
169 /*
170 We better go and parse configuration values, as there are at least two kind of controllers that use different endpoints,
171 so using hardcoded values cause the usb host to miss all input reports from those controllers.
172
173 As an example:
174 - 045e:0289 uses EP 1 for IN and EP 2 for OUT
175 - but 045e:0202 uses both EP 2 for IN and OUT
176 */
177 num_of_conf = udd->bNumConfigurations; // Number of configurations
178
179 USBTRACE2("NC:", num_of_conf);
180
181 // Check if attached device is a Xbox controller and fill endpoint data structure
182 for(uint8_t i = 0; i < num_of_conf; i++) {
183 ConfigDescParser<0, 0, 0, 0> confDescrParser(this); // Allow all devices, as we have already verified that it is a Xbox controller from the VID and PID
185 if(rcode) // Check error code
186 goto FailGetConfDescr;
187 if(bNumEP >= XBOX_MAX_ENDPOINTS) // All endpoints extracted
188 break;
189 }
190
193
195 if(rcode)
197
198 delay(200); // Give time for address change
199
201 if(rcode)
202 goto FailSetConfDescr;
203
204#ifdef DEBUG_USB_HOST
205 Notify(PSTR("\r\nXbox Controller Connected\r\n"), 0x80);
206#endif
207 if(pFuncOnInit)
208 pFuncOnInit(); // Call the user function
209 XboxConnected = true;
210 bPollEnable = true;
211 return 0; // Successful configuration
212
213 /* Diagnostic messages */
215#ifdef DEBUG_USB_HOST
217 goto Fail;
218#endif
219
221#ifdef DEBUG_USB_HOST
223 goto Fail;
224#endif
225
227#ifdef DEBUG_USB_HOST
229 goto Fail;
230#endif
231
233#ifdef DEBUG_USB_HOST
235#endif
236 goto Fail;
237
239#ifdef DEBUG_USB_HOST
240 NotifyFailUnknownDevice(VID, PID);
241#endif
243
244Fail:
245#ifdef DEBUG_USB_HOST
246 Notify(PSTR("\r\nXbox Init Failed, error code: "), 0x80);
248#endif
249 Release();
250 return rcode;
251}
252
253/* Extracts endpoint information from config descriptor */
255 uint8_t iface __attribute__((unused)),
256 uint8_t alt __attribute__((unused)),
257 uint8_t proto __attribute__((unused)),
259{
260 bConfNum = conf;
261 uint8_t index;
262
263 if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT) { // Interrupt endpoint
264 index = (pep->bEndpointAddress & 0x80) == 0x80 ? XBOX_INPUT_PIPE : XBOX_OUTPUT_PIPE; // Set the endpoint index
265 } else
266 return;
267
268 // Fill the rest of endpoint data structure
269 epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
270 epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
271#ifdef EXTRADEBUG
273#endif
274 if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints
275 pollInterval = pep->bInterval;
276 bNumEP++;
277}
278
280 __attribute__((unused)))
281{
282#ifdef EXTRADEBUG
283 Notify(PSTR("\r\nEndpoint descriptor:"), 0x80);
284 Notify(PSTR("\r\nLength:\t\t"), 0x80);
285 D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);
286 Notify(PSTR("\r\nType:\t\t"), 0x80);
287 D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);
288 Notify(PSTR("\r\nAddress:\t"), 0x80);
289 D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);
290 Notify(PSTR("\r\nAttributes:\t"), 0x80);
291 D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);
292 Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
293 D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);
294 Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
295 D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);
296#endif
297}
298
299/* Performs a cleanup after failed Init() attempt */
301 XboxConnected = false;
303 bAddress = 0;
304 bNumEP = 1; // Must have to be reset to 1
305 qNextPollTime = 0; // Reset next poll time
306 pollInterval = 0;
307 bPollEnable = false;
308#ifdef DEBUG_USB_HOST
309 Notify(PSTR("\r\nXbox Controller Disconnected\r\n"), 0x80);
310#endif
311 return 0;
312}
313
315 uint8_t rcode = 0;
316
317 if(!bPollEnable)
318 return 0;
319
320 if((int32_t)((uint32_t)millis() - qNextPollTime) >= 0L) { // Do not poll if shorter than polling interval
321 qNextPollTime = (uint32_t)millis() + pollInterval; // Set new poll time
322 uint16_t length = (uint16_t)epInfo[ XBOX_INPUT_PIPE ].maxPktSize; // Read the maximum packet size from the endpoint
323 uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &length, readBuf, pollInterval);
324 if(!rcode) {
325 readReport();
326#ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send by the Xbox ONE Controller
327 printReport(length);
328#endif
329 }
330#ifdef DEBUG_USB_HOST
331 else if(rcode != hrNAK) { // Not a matter of no update to send
332 Notify(PSTR("\r\nXbox Poll Failed, error code: "), 0x80);
334 }
335#endif
336 }
337 return rcode;
338}
339
340void XBOXOLD::readReport() {
341 ButtonState = readBuf[2];
342
343 for(uint8_t i = 0; i < sizeof (buttonValues); i++)
344 buttonValues[i] = readBuf[i + 4]; // A, B, X, Y, BLACK, WHITE, L1, and R1
345
346 hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
347 hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);
348 hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);
349 hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[19] << 8) | readBuf[18]);
350
351 //Notify(PSTR("\r\nButtonState"), 0x80);
352 //PrintHex<uint8_t>(ButtonState, 0x80);
353
354 if(ButtonState != OldButtonState || memcmp(buttonValues, oldButtonValues, sizeof (buttonValues)) != 0) {
355 ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
356 OldButtonState = ButtonState;
357
358 for(uint8_t i = 0; i < sizeof (buttonValues); i++) {
359 if(oldButtonValues[i] == 0 && buttonValues[i] != 0)
360 buttonClicked[i] = true; // Update A, B, X, Y, BLACK, WHITE, L1, and R1 click state
361 oldButtonValues[i] = buttonValues[i];
362 }
363 }
364}
365
366void XBOXOLD::printReport(uint16_t length __attribute__((unused))) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
367#ifdef PRINTREPORT
368 if(readBuf == NULL || !length)
369 return;
370 for(uint8_t i = 0; i < length; i++) {
371 D_PrintHex<uint8_t > (readBuf[i], 0x80);
372 Notify(PSTR(" "), 0x80);
373 }
374 Notify(PSTR("\r\n"), 0x80);
375#endif
376}
377
378int8_t XBOXOLD::getAnalogIndex(ButtonEnum b) {
379 // For legacy reasons these mapping indices not match up,
380 // as the original code uses L1/R1 for the triggers and
381 // L2/R2 for the white/black buttons. To fix these new enums
382 // we have to transpose the keys before passing them through
383 // the button index function
384 switch (b) {
385 case(LT): b = L1; break; // normally L2
386 case(RT): b = R1; break; // normally R2
387 case(LB): b = WHITE; break; // normally L1
388 case(RB): b = BLACK; break; // normally R1
389 default: break;
390 }
391
392 // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
393 const int8_t index = ButtonIndex(b);
394
395 switch (index) {
396 case ButtonIndex(A):
404 return index;
405 default: break;
406 }
407
408 return -1;
409}
410
411int8_t XBOXOLD::getDigitalIndex(ButtonEnum b) {
412 // UP, DOWN, LEFT, RIGHT, START, BACK, L3, and R3 are digital buttons
413 const int8_t index = ButtonIndex(b);
414
415 switch (index) {
416 case ButtonIndex(UP):
424 return index;
425 default: break;
426 }
427
428 return -1;
429}
430
432 const int8_t analogIndex = getAnalogIndex(b);
433 if (analogIndex >= 0) {
435 return buttonValues[buttonIndex];
436 }
437 const int8_t digitalIndex = getDigitalIndex(b);
438 if (digitalIndex >= 0) {
440 return (ButtonState & buttonMask);
441 }
442 return 0;
443}
444
446 const int8_t analogIndex = getAnalogIndex(b);
447 if (analogIndex >= 0) {
449 if (buttonClicked[buttonIndex]) {
450 buttonClicked[buttonIndex] = false;
451 return true;
452 }
453 return false;
454 }
455 const int8_t digitalIndex = getDigitalIndex(b);
456 if (digitalIndex >= 0) {
458 const bool click = (ButtonClickState & mask);
459 ButtonClickState &= ~mask;
460 return click;
461 }
462 return 0;
463}
464
466 return hatValue[a];
467}
468
469/* Xbox Controller commands */
470void XBOXOLD::XboxCommand(uint8_t* data, uint16_t nbytes) {
471 //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)
473}
474
476 uint8_t writeBuf[6];
477
478 writeBuf[0] = 0x00;
479 writeBuf[1] = 0x06;
480 writeBuf[2] = 0x00;
481 writeBuf[3] = rValue; // small weight
482 writeBuf[4] = 0x00;
483 writeBuf[5] = lValue; // big weight
484
485 XboxCommand(writeBuf, 6);
486}
#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL
Definition UsbCore.h:103
#define USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE
Definition UsbCore.h:108
#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED
Definition UsbCore.h:100
#define USB_ERROR_EPINFO_IS_NULL
Definition UsbCore.h:106
#define USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL
Definition UsbCore.h:105
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 UsbDevice * GetUsbDevicePtr(uint8_t addr)=0
virtual uint8_t AllocAddress(uint8_t parent, bool is_hub=false, uint8_t port=0)=0
Definition UsbCore.h:220
AddressPool & GetAddressPool()
Definition UsbCore.h:236
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:850
uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr)
Definition Usb.cpp:841
uint8_t RegisterDeviceClass(USBDeviceConfig *pdev)
Definition UsbCore.h:240
uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t *dataptr)
Definition Usb.cpp:806
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
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:163
XBOXOLD(USB *pUsb)
Definition XBOXOLD.cpp:47
USB * pUsb
Definition XBOXOLD.h:159
void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep)
Definition XBOXOLD.cpp:254
void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR *ep_ptr)
Definition XBOXOLD.cpp:279
uint8_t Poll()
Definition XBOXOLD.cpp:314
bool getButtonClick(ButtonEnum b)
Definition XBOXOLD.cpp:445
virtual bool VIDPIDOK(uint16_t vid, uint16_t pid)
Definition XBOXOLD.h:106
uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed)
Definition XBOXOLD.cpp:66
uint8_t getButtonPress(ButtonEnum b)
Definition XBOXOLD.cpp:431
uint8_t Release()
Definition XBOXOLD.cpp:300
uint8_t bConfNum
Definition XBOXOLD.h:166
bool XboxConnected
Definition XBOXOLD.h:155
void setRumbleOn(uint8_t lValue, uint8_t rValue)
Definition XBOXOLD.cpp:475
uint8_t bAddress
Definition XBOXOLD.h:161
uint32_t qNextPollTime
Definition XBOXOLD.h:170
uint8_t bNumEP
Definition XBOXOLD.h:168
int16_t getAnalogHat(AnalogHatEnum a)
Definition XBOXOLD.cpp:465
constexpr int8_t ButtonIndex(ButtonEnum key)
AnalogHatEnum
@ LeftHatX
@ RightHatY
@ RightHatX
@ LeftHatY
ButtonEnum
@ START
@ WHITE
@ L3
@ DOWN
@ R3
@ UP
@ BACK
@ LEFT
@ RIGHT
@ BLACK
#define USBTRACE2(s, r)
Definition macros.h:84
#define hrNAK
Definition max3421e.h:218
#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 NotifyFailGetConfDescr(...)
Definition message.h:56
#define NotifyFailSetDevTblEntry(...)
Definition message.h:55
#define NotifyFailGetDevDescr(...)
Definition message.h:54
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
#define bmUSB_TRANSFER_TYPE
Definition usb_ch9.h:94
#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)