]> git.donarmstrong.com Git - qmk_firmware.git/blob - tmk_core/protocol/usb_hid/USB_Host_Shield_2.0/XBOXONE.cpp
Merge commit 'f6d56675f9f981c5464f0ca7a1fbb0162154e8c5'
[qmk_firmware.git] / tmk_core / 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 }