diff options
Diffstat (limited to 'lib/usbhost/USB_Host_Shield_2.0/XBOXOLD.cpp')
-rw-r--r-- | lib/usbhost/USB_Host_Shield_2.0/XBOXOLD.cpp | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/lib/usbhost/USB_Host_Shield_2.0/XBOXOLD.cpp b/lib/usbhost/USB_Host_Shield_2.0/XBOXOLD.cpp new file mode 100644 index 000000000..78e6e9a5f --- /dev/null +++ b/lib/usbhost/USB_Host_Shield_2.0/XBOXOLD.cpp | |||
@@ -0,0 +1,337 @@ | |||
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 | } | ||