]> git.donarmstrong.com Git - qmk_firmware.git/blob - protocol/usb_hid/USB_Host_Shield_2.0/XBOXOLD.cpp
Squashed 'tmk_core/' changes from caca2c0..dc0e46e
[qmk_firmware.git] / protocol / usb_hid / USB_Host_Shield_2.0 / XBOXOLD.cpp
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
23 /** Buttons on the controllers */
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
47 XBOXOLD::XBOXOLD(USB *p) :
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;
55                 epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
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
81                 return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
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
91                 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
92         }
93
94         if(!p->epinfo) {
95 #ifdef DEBUG_USB_HOST
96                 Notify(PSTR("\r\nepinfo is null"), 0x80);
97 #endif
98                 return USB_ERROR_EPINFO_IS_NULL;
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)
127                 return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
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)
155                 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
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
170         epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
171         epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
172         epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
173         epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0;
174         epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0;
175         epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX output endpoint
176         epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
177         epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
178         epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
179         epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0;
180         epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0;
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
204         NotifyFailGetDevDescr();
205         goto Fail;
206 #endif
207
208 FailSetDevTblEntry:
209 #ifdef DEBUG_USB_HOST
210         NotifyFailSetDevTblEntry();
211         goto Fail;
212 #endif
213
214 FailSetConfDescr:
215 #ifdef DEBUG_USB_HOST
216         NotifyFailSetConfDescr();
217 #endif
218         goto Fail;
219
220 FailUnknownDevice:
221 #ifdef DEBUG_USB_HOST
222         NotifyFailUnknownDevice(VID, PID);
223 #endif
224         rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
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;
238         pUsb->GetAddressPool().FreeAddress(bAddress);
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
294 uint8_t XBOXOLD::getButtonPress(ButtonEnum b) {
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
301 bool XBOXOLD::getButtonClick(ButtonEnum b) {
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
316 int16_t XBOXOLD::getAnalogHat(AnalogHatEnum a) {
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 }