]> git.gir.st - tmk_keyboard.git/blob - protocol/usb_hid/USB_Host_Shield_2.0/XBOXONE.cpp
Squashed 'tmk_core/' changes from caca2c0..dc0e46e
[tmk_keyboard.git] / protocol / usb_hid / USB_Host_Shield_2.0 / XBOXONE.cpp
1 /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
2 Copyright (C) 2015 guruthree
3
4 This software may be distributed and modified under the terms of the GNU
5 General Public License version 2 (GPL2) as published by the Free Software
6 Foundation and appearing in the file GPL2.TXT included in the packaging of
7 this file. Please note that GPL2 Section 2[b] requires that all works based
8 on this software must also be made publicly available under the terms of
9 the GPL2 ("Copyleft").
10
11 Contact information
12 -------------------
13
14 Kristian Lauszus, TKJ Electronics
15 Web : http://www.tkjelectronics.com
16 e-mail : kristianl@tkjelectronics.com
17
18 guruthree
19 Web : https://github.com/guruthree/
20 */
21
22 #include "XBOXONE.h"
23 // To enable serial debugging see "settings.h"
24 //#define EXTRADEBUG // Uncomment to get even more debugging data
25 //#define PRINTREPORT // Uncomment to print the report send by the Xbox ONE Controller
26
27 XBOXONE::XBOXONE(USB *p) :
28 pUsb(p), // pointer to USB class instance - mandatory
29 bAddress(0), // device address - mandatory
30 bPollEnable(false) { // don't start polling before dongle is connected
31 for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
32 epInfo[i].epAddr = 0;
33 epInfo[i].maxPktSize = (i) ? 0 : 8;
34 epInfo[i].epAttribs = 0;
35 epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
36 }
37
38 if(pUsb) // register in USB subsystem
39 pUsb->RegisterDeviceClass(this); //set devConfig[] entry
40 }
41
42 uint8_t XBOXONE::Init(uint8_t parent, uint8_t port, bool lowspeed) {
43 uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
44 USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
45 uint8_t rcode;
46 UsbDevice *p = NULL;
47 EpInfo *oldep_ptr = NULL;
48 uint16_t PID;
49 uint16_t VID;
50
51 // get memory address of USB device address pool
52 AddressPool &addrPool = pUsb->GetAddressPool();
53 #ifdef EXTRADEBUG
54 Notify(PSTR("\r\nXBOXONE Init"), 0x80);
55 #endif
56 // check if address has already been assigned to an instance
57 if(bAddress) {
58 #ifdef DEBUG_USB_HOST
59 Notify(PSTR("\r\nAddress in use"), 0x80);
60 #endif
61 return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
62 }
63
64 // Get pointer to pseudo device with address 0 assigned
65 p = addrPool.GetUsbDevicePtr(0);
66
67 if(!p) {
68 #ifdef DEBUG_USB_HOST
69 Notify(PSTR("\r\nAddress not found"), 0x80);
70 #endif
71 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
72 }
73
74 if(!p->epinfo) {
75 #ifdef DEBUG_USB_HOST
76 Notify(PSTR("\r\nepinfo is null"), 0x80);
77 #endif
78 return USB_ERROR_EPINFO_IS_NULL;
79 }
80
81 // Save old pointer to EP_RECORD of address 0
82 oldep_ptr = p->epinfo;
83
84 // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
85 p->epinfo = epInfo;
86
87 p->lowspeed = lowspeed;
88
89 // Get device descriptor
90 rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
91 // Restore p->epinfo
92 p->epinfo = oldep_ptr;
93
94 if(rcode)
95 goto FailGetDevDescr;
96
97 VID = udd->idVendor;
98 PID = udd->idProduct;
99
100 if(!VIDPIDOK(VID, PID)) // Check VID
101 goto FailUnknownDevice;
102
103 // Allocate new address according to device class
104 bAddress = addrPool.AllocAddress(parent, false, port);
105
106 if(!bAddress)
107 return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
108
109 // Extract Max Packet Size from device descriptor
110 epInfo[0].maxPktSize = udd->bMaxPacketSize0;
111
112 // Assign new address to the device
113 rcode = pUsb->setAddr(0, 0, bAddress);
114 if(rcode) {
115 p->lowspeed = false;
116 addrPool.FreeAddress(bAddress);
117 bAddress = 0;
118 #ifdef DEBUG_USB_HOST
119 Notify(PSTR("\r\nsetAddr: "), 0x80);
120 D_PrintHex<uint8_t > (rcode, 0x80);
121 #endif
122 return rcode;
123 }
124 #ifdef EXTRADEBUG
125 Notify(PSTR("\r\nAddr: "), 0x80);
126 D_PrintHex<uint8_t > (bAddress, 0x80);
127 #endif
128 //delay(300); // Spec says you should wait at least 200ms
129
130 p->lowspeed = false;
131
132 //get pointer to assigned address record
133 p = addrPool.GetUsbDevicePtr(bAddress);
134 if(!p)
135 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
136
137 p->lowspeed = lowspeed;
138
139 // Assign epInfo to epinfo pointer - only EP0 is known
140 rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
141 if(rcode)
142 goto FailSetDevTblEntry;
143
144 /* The application will work in reduced host mode, so we can save program and data
145 memory space. After verifying the VID we will use known values for the
146 configuration values for device, interface, endpoints and HID for the XBOXONE Controllers */
147
148 /* Initialize data structures for endpoints of device */
149 epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x01; // XBOX one output endpoint
150 epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
151 epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
152 epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
153 epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0;
154 epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0;
155 epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX one input endpoint
156 epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
157 epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
158 epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
159 epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0;
160 epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0;
161
162 rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
163 if(rcode)
164 goto FailSetDevTblEntry;
165
166 delay(200); // Give time for address change
167
168 rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
169 if(rcode)
170 goto FailSetConfDescr;
171
172 #ifdef DEBUG_USB_HOST
173 Notify(PSTR("\r\nXbox One Controller Connected\r\n"), 0x80);
174 #endif
175
176 delay(200); // let things settle
177
178 // initialize the controller for input
179 writeBuf[0] = 0x05;
180 writeBuf[1] = 0x20;
181 rcode = XboxCommand(writeBuf, 2);
182 if (rcode)
183 goto Fail;
184
185 onInit();
186 XboxOneConnected = true;
187 bPollEnable = true;
188 return 0; // Successful configuration
189
190 /* Diagnostic messages */
191 FailGetDevDescr:
192 #ifdef DEBUG_USB_HOST
193 NotifyFailGetDevDescr();
194 goto Fail;
195 #endif
196
197 FailSetDevTblEntry:
198 #ifdef DEBUG_USB_HOST
199 NotifyFailSetDevTblEntry();
200 goto Fail;
201 #endif
202
203 FailSetConfDescr:
204 #ifdef DEBUG_USB_HOST
205 NotifyFailSetConfDescr();
206 #endif
207 goto Fail;
208
209 FailUnknownDevice:
210 #ifdef DEBUG_USB_HOST
211 NotifyFailUnknownDevice(VID, PID);
212 #endif
213 rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
214
215 Fail:
216 #ifdef DEBUG_USB_HOST
217 Notify(PSTR("\r\nXbox One Init Failed, error code: "), 0x80);
218 NotifyFail(rcode);
219 #endif
220 Release();
221 return rcode;
222 }
223
224 /* Performs a cleanup after failed Init() attempt */
225 uint8_t XBOXONE::Release() {
226 XboxOneConnected = false;
227 pUsb->GetAddressPool().FreeAddress(bAddress);
228 bAddress = 0;
229 bPollEnable = false;
230 #ifdef DEBUG_USB_HOST
231 Notify(PSTR("\r\nXbox One Controller Disconnected\r\n"), 0x80);
232 #endif
233 return 0;
234 }
235
236 uint8_t XBOXONE::Poll() {
237 if(!bPollEnable)
238 return 0;
239 uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
240 uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf);
241 if (!rcode) {
242 readReport();
243 #ifdef PRINTREPORT
244 printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox ONE Controller
245 #endif
246 }
247 #ifdef DEBUG_USB_HOST
248 else if (rcode != 0x04) { // not a matter of no update to send
249 Notify(PSTR("\r\nXbox One Poll Failed, error code: "), 0x80);
250 NotifyFail(rcode);
251 }
252 #endif
253 return rcode;
254 }
255
256 void XBOXONE::readReport() {
257 if(readBuf == NULL)
258 return;
259 if(readBuf[0] == 0x07) {
260 // The XBOX button has a separate message
261 if(readBuf[4] == 1)
262 ButtonState |= XBOX_BUTTONS[XBOX];
263 else
264 ButtonState &= ~XBOX_BUTTONS[XBOX];
265 }
266 if(readBuf[0] != 0x20) { // Check if it's the correct report, otherwise return - the controller also sends different status reports
267 #ifdef EXTRADEBUG
268 Notify(PSTR("\r\nXbox Poll: "), 0x80);
269 D_PrintHex<uint8_t > (readBuf[0], 0x80); // 0x03 is a heart beat report!
270 #endif
271 return;
272 }
273
274 uint16_t xbox = ButtonState & XBOX_BUTTONS[XBOX]; // Since the XBOX button is separate, save it and add it back in
275 // xbox button from before, dpad, abxy, start/back, sync, stick click, shoulder buttons
276 ButtonState = xbox | (((uint16_t)readBuf[5] & 0xF) << 8) | (readBuf[4] & 0xF0) | (((uint16_t)readBuf[4] & 0x0C) << 10) | ((readBuf[4] & 0x01) << 3) | (((uint16_t)readBuf[5] & 0xC0) << 8) | ((readBuf[5] & 0x30) >> 4);
277
278 triggerValue[0] = (uint16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
279 triggerValue[1] = (uint16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
280
281 hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
282 hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
283 hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);
284 hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);
285
286 //Notify(PSTR("\r\nButtonState"), 0x80);
287 //PrintHex<uint16_t>(ButtonState, 0x80);
288
289 if(ButtonState != OldButtonState) {
290 ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
291 OldButtonState = ButtonState;
292 }
293
294 // Handle click detection for triggers
295 if(triggerValue[0] != 0 && triggerValueOld[0] == 0)
296 L2Clicked = true;
297 triggerValueOld[0] = triggerValue[0];
298 if(triggerValue[1] != 0 && triggerValueOld[1] == 0)
299 R2Clicked = true;
300 triggerValueOld[1] = triggerValue[1];
301 }
302
303 void XBOXONE::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox ONE Controller
304 #ifdef PRINTREPORT
305 if(readBuf == NULL)
306 return;
307 for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {
308 D_PrintHex<uint8_t > (readBuf[i], 0x80);
309 Notify(PSTR(" "), 0x80);
310 }
311 Notify(PSTR("\r\n"), 0x80);
312 #endif
313 }
314
315 uint16_t XBOXONE::getButtonPress(ButtonEnum b) {
316 if(b == L2) // These are analog buttons
317 return triggerValue[0];
318 else if(b == R2)
319 return triggerValue[1];
320 return (bool)(ButtonState & ((uint16_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b])));
321 }
322
323 bool XBOXONE::getButtonClick(ButtonEnum b) {
324 if(b == L2) {
325 if(L2Clicked) {
326 L2Clicked = false;
327 return true;
328 }
329 return false;
330 } else if(b == R2) {
331 if(R2Clicked) {
332 R2Clicked = false;
333 return true;
334 }
335 return false;
336 }
337 uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
338 bool click = (ButtonClickState & button);
339 ButtonClickState &= ~button; // clear "click" event
340 return click;
341 }
342
343 int16_t XBOXONE::getAnalogHat(AnalogHatEnum a) {
344 return hatValue[a];
345 }
346
347 /* Xbox Controller commands */
348 uint8_t XBOXONE::XboxCommand(uint8_t* data, uint16_t nbytes) {
349 uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_OUTPUT_PIPE ].epAddr, nbytes, data);
350 #ifdef DEBUG_USB_HOST
351 Notify(PSTR("\r\nXboxCommand, Return: "), 0x80);
352 D_PrintHex<uint8_t > (rcode, 0x80);
353 #endif
354 return rcode;
355 }
356
357 void XBOXONE::onInit() {
358 // a short buzz to show the controller is active
359 writeBuf[0] = 0x09;
360 writeBuf[1] = 0x08;
361 writeBuf[2] = 0x00;
362 writeBuf[3] = 0x09;
363 writeBuf[4] = 0x00;
364 writeBuf[5] = 0x0f;
365 writeBuf[6] = 0x04;
366 writeBuf[7] = 0x04;
367 writeBuf[8] = 0x20;
368 writeBuf[9] = 0x20;
369 writeBuf[10] = 0x80;
370 XboxCommand(writeBuf, 11);
371
372 if(pFuncOnInit)
373 pFuncOnInit(); // Call the user function
374 }
Imprint / Impressum