]> git.friedersdorff.com Git - max/tmk_keyboard.git/blob - tmk_core/protocol/usb_hid/USB_Host_Shield_2.0/XBOXUSB.cpp
lufa: usb-usb: Use LUFA startup instead of cusotom
[max/tmk_keyboard.git] / tmk_core / protocol / usb_hid / USB_Host_Shield_2.0 / XBOXUSB.cpp
1 /* Copyright (C) 2012 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 "XBOXUSB.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 360 Controller
22
23 XBOXUSB::XBOXUSB(USB *p) :
24 pUsb(p), // pointer to USB class instance - mandatory
25 bAddress(0), // device address - mandatory
26 bPollEnable(false) { // don't start polling before dongle is connected
27         for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
28                 epInfo[i].epAddr = 0;
29                 epInfo[i].maxPktSize = (i) ? 0 : 8;
30                 epInfo[i].epAttribs = 0;
31                 epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
32         }
33
34         if(pUsb) // register in USB subsystem
35                 pUsb->RegisterDeviceClass(this); //set devConfig[] entry
36 }
37
38 uint8_t XBOXUSB::Init(uint8_t parent, uint8_t port, bool lowspeed) {
39         uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
40         USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
41         uint8_t rcode;
42         UsbDevice *p = NULL;
43         EpInfo *oldep_ptr = NULL;
44         uint16_t PID;
45         uint16_t VID;
46
47         // get memory address of USB device address pool
48         AddressPool &addrPool = pUsb->GetAddressPool();
49 #ifdef EXTRADEBUG
50         Notify(PSTR("\r\nXBOXUSB Init"), 0x80);
51 #endif
52         // check if address has already been assigned to an instance
53         if(bAddress) {
54 #ifdef DEBUG_USB_HOST
55                 Notify(PSTR("\r\nAddress in use"), 0x80);
56 #endif
57                 return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
58         }
59
60         // Get pointer to pseudo device with address 0 assigned
61         p = addrPool.GetUsbDevicePtr(0);
62
63         if(!p) {
64 #ifdef DEBUG_USB_HOST
65                 Notify(PSTR("\r\nAddress not found"), 0x80);
66 #endif
67                 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
68         }
69
70         if(!p->epinfo) {
71 #ifdef DEBUG_USB_HOST
72                 Notify(PSTR("\r\nepinfo is null"), 0x80);
73 #endif
74                 return USB_ERROR_EPINFO_IS_NULL;
75         }
76
77         // Save old pointer to EP_RECORD of address 0
78         oldep_ptr = p->epinfo;
79
80         // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
81         p->epinfo = epInfo;
82
83         p->lowspeed = lowspeed;
84
85         // Get device descriptor
86         rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
87         // Restore p->epinfo
88         p->epinfo = oldep_ptr;
89
90         if(rcode)
91                 goto FailGetDevDescr;
92
93         VID = udd->idVendor;
94         PID = udd->idProduct;
95
96         if(VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID && VID != GAMESTOP_VID) // Check VID
97                 goto FailUnknownDevice;
98         if(PID == XBOX_WIRELESS_PID) {
99 #ifdef DEBUG_USB_HOST
100                 Notify(PSTR("\r\nYou have plugged in a wireless Xbox 360 controller - it doesn't support USB communication"), 0x80);
101 #endif
102                 goto FailUnknownDevice;
103         } else if(PID == XBOX_WIRELESS_RECEIVER_PID || PID == XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID) {
104 #ifdef DEBUG_USB_HOST
105                 Notify(PSTR("\r\nThis library only supports Xbox 360 controllers via USB"), 0x80);
106 #endif
107                 goto FailUnknownDevice;
108         } else if(PID != XBOX_WIRED_PID && PID != MADCATZ_WIRED_PID && PID != GAMESTOP_WIRED_PID && PID != AFTERGLOW_WIRED_PID && PID != JOYTECH_WIRED_PID) // Check PID
109                 goto FailUnknownDevice;
110
111         // Allocate new address according to device class
112         bAddress = addrPool.AllocAddress(parent, false, port);
113
114         if(!bAddress)
115                 return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
116
117         // Extract Max Packet Size from device descriptor
118         epInfo[0].maxPktSize = udd->bMaxPacketSize0;
119
120         // Assign new address to the device
121         rcode = pUsb->setAddr(0, 0, bAddress);
122         if(rcode) {
123                 p->lowspeed = false;
124                 addrPool.FreeAddress(bAddress);
125                 bAddress = 0;
126 #ifdef DEBUG_USB_HOST
127                 Notify(PSTR("\r\nsetAddr: "), 0x80);
128                 D_PrintHex<uint8_t > (rcode, 0x80);
129 #endif
130                 return rcode;
131         }
132 #ifdef EXTRADEBUG
133         Notify(PSTR("\r\nAddr: "), 0x80);
134         D_PrintHex<uint8_t > (bAddress, 0x80);
135 #endif
136         //delay(300); // Spec says you should wait at least 200ms
137
138         p->lowspeed = false;
139
140         //get pointer to assigned address record
141         p = addrPool.GetUsbDevicePtr(bAddress);
142         if(!p)
143                 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
144
145         p->lowspeed = lowspeed;
146
147         // Assign epInfo to epinfo pointer - only EP0 is known
148         rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
149         if(rcode)
150                 goto FailSetDevTblEntry;
151
152         /* The application will work in reduced host mode, so we can save program and data
153            memory space. After verifying the VID we will use known values for the
154            configuration values for device, interface, endpoints and HID for the XBOX360 Controllers */
155
156         /* Initialize data structures for endpoints of device */
157         epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX 360 report endpoint
158         epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
159         epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
160         epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
161         epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0;
162         epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0;
163         epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX 360 output endpoint
164         epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
165         epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
166         epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
167         epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0;
168         epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0;
169
170         rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
171         if(rcode)
172                 goto FailSetDevTblEntry;
173
174         delay(200); // Give time for address change
175
176         rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
177         if(rcode)
178                 goto FailSetConfDescr;
179
180 #ifdef DEBUG_USB_HOST
181         Notify(PSTR("\r\nXbox 360 Controller Connected\r\n"), 0x80);
182 #endif
183         onInit();
184         Xbox360Connected = true;
185         bPollEnable = true;
186         return 0; // Successful configuration
187
188         /* Diagnostic messages */
189 FailGetDevDescr:
190 #ifdef DEBUG_USB_HOST
191         NotifyFailGetDevDescr();
192         goto Fail;
193 #endif
194
195 FailSetDevTblEntry:
196 #ifdef DEBUG_USB_HOST
197         NotifyFailSetDevTblEntry();
198         goto Fail;
199 #endif
200
201 FailSetConfDescr:
202 #ifdef DEBUG_USB_HOST
203         NotifyFailSetConfDescr();
204 #endif
205         goto Fail;
206
207 FailUnknownDevice:
208 #ifdef DEBUG_USB_HOST
209         NotifyFailUnknownDevice(VID, PID);
210 #endif
211         rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
212
213 Fail:
214 #ifdef DEBUG_USB_HOST
215         Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
216         NotifyFail(rcode);
217 #endif
218         Release();
219         return rcode;
220 }
221
222 /* Performs a cleanup after failed Init() attempt */
223 uint8_t XBOXUSB::Release() {
224         Xbox360Connected = false;
225         pUsb->GetAddressPool().FreeAddress(bAddress);
226         bAddress = 0;
227         bPollEnable = false;
228         return 0;
229 }
230
231 uint8_t XBOXUSB::Poll() {
232         if(!bPollEnable)
233                 return 0;
234         uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
235         pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
236         readReport();
237 #ifdef PRINTREPORT
238         printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
239 #endif
240         return 0;
241 }
242
243 void XBOXUSB::readReport() {
244         if(readBuf == NULL)
245                 return;
246         if(readBuf[0] != 0x00 || readBuf[1] != 0x14) { // Check if it's the correct report - the controller also sends different status reports
247                 return;
248         }
249
250         ButtonState = (uint32_t)(readBuf[5] | ((uint16_t)readBuf[4] << 8) | ((uint32_t)readBuf[3] << 16) | ((uint32_t)readBuf[2] << 24));
251
252         hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
253         hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
254         hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
255         hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
256
257         //Notify(PSTR("\r\nButtonState"), 0x80);
258         //PrintHex<uint32_t>(ButtonState, 0x80);
259
260         if(ButtonState != OldButtonState) {
261                 ButtonClickState = (ButtonState >> 16) & ((~OldButtonState) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
262                 if(((uint8_t)OldButtonState) == 0 && ((uint8_t)ButtonState) != 0) // The L2 and R2 buttons are special as they are analog buttons
263                         R2Clicked = true;
264                 if((uint8_t)(OldButtonState >> 8) == 0 && (uint8_t)(ButtonState >> 8) != 0)
265                         L2Clicked = true;
266                 OldButtonState = ButtonState;
267         }
268 }
269
270 void XBOXUSB::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
271 #ifdef PRINTREPORT
272         if(readBuf == NULL)
273                 return;
274         for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {
275                 D_PrintHex<uint8_t > (readBuf[i], 0x80);
276                 Notify(PSTR(" "), 0x80);
277         }
278         Notify(PSTR("\r\n"), 0x80);
279 #endif
280 }
281
282 uint8_t XBOXUSB::getButtonPress(ButtonEnum b) {
283         if(b == L2) // These are analog buttons
284                 return (uint8_t)(ButtonState >> 8);
285         else if(b == R2)
286                 return (uint8_t)ButtonState;
287         return (bool)(ButtonState & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16));
288 }
289
290 bool XBOXUSB::getButtonClick(ButtonEnum b) {
291         if(b == L2) {
292                 if(L2Clicked) {
293                         L2Clicked = false;
294                         return true;
295                 }
296                 return false;
297         } else if(b == R2) {
298                 if(R2Clicked) {
299                         R2Clicked = false;
300                         return true;
301                 }
302                 return false;
303         }
304         uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
305         bool click = (ButtonClickState & button);
306         ButtonClickState &= ~button; // clear "click" event
307         return click;
308 }
309
310 int16_t XBOXUSB::getAnalogHat(AnalogHatEnum a) {
311         return hatValue[a];
312 }
313
314 /* Xbox Controller commands */
315 void XBOXUSB::XboxCommand(uint8_t* data, uint16_t nbytes) {
316         //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)
317         pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
318 }
319
320 void XBOXUSB::setLedRaw(uint8_t value) {
321         writeBuf[0] = 0x01;
322         writeBuf[1] = 0x03;
323         writeBuf[2] = value;
324
325         XboxCommand(writeBuf, 3);
326 }
327
328 void XBOXUSB::setLedOn(LEDEnum led) {
329         if(led == OFF)
330                 setLedRaw(0);
331         else if(led != ALL) // All LEDs can't be on a the same time
332                 setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]) + 4);
333 }
334
335 void XBOXUSB::setLedBlink(LEDEnum led) {
336         setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]));
337 }
338
339 void XBOXUSB::setLedMode(LEDModeEnum ledMode) { // This function is used to do some special LED stuff the controller supports
340         setLedRaw((uint8_t)ledMode);
341 }
342
343 void XBOXUSB::setRumbleOn(uint8_t lValue, uint8_t rValue) {
344         writeBuf[0] = 0x00;
345         writeBuf[1] = 0x08;
346         writeBuf[2] = 0x00;
347         writeBuf[3] = lValue; // big weight
348         writeBuf[4] = rValue; // small weight
349         writeBuf[5] = 0x00;
350         writeBuf[6] = 0x00;
351         writeBuf[7] = 0x00;
352
353         XboxCommand(writeBuf, 8);
354 }
355
356 void XBOXUSB::onInit() {
357         if(pFuncOnInit)
358                 pFuncOnInit(); // Call the user function
359         else
360                 setLedOn(LED1);
361 }