]> git.gir.st - tmk_keyboard.git/blob - tmk_core/protocol/usb_hid/USB_Host_Shield_2.0/XBOXOLD.cpp
Add ChibiOS support (USB stack + support files).
[tmk_keyboard.git] / tmk_core / 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 }
Imprint / Impressum