1 /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
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
13 Kristian Lauszus, TKJ Electronics
14 Web : http://www.tkjelectronics.com
15 e-mail : kristianl@tkjelectronics.com
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
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++) {
29 epInfo[i].maxPktSize = (i) ? 0 : 8;
30 epInfo[i].epAttribs = 0;
31 epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
34 if(pUsb) // register in USB subsystem
35 pUsb->RegisterDeviceClass(this); //set devConfig[] entry
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);
43 EpInfo *oldep_ptr = NULL;
47 // get memory address of USB device address pool
48 AddressPool &addrPool = pUsb->GetAddressPool();
50 Notify(PSTR("\r\nXBOXUSB Init"), 0x80);
52 // check if address has already been assigned to an instance
55 Notify(PSTR("\r\nAddress in use"), 0x80);
57 return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
60 // Get pointer to pseudo device with address 0 assigned
61 p = addrPool.GetUsbDevicePtr(0);
65 Notify(PSTR("\r\nAddress not found"), 0x80);
67 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
72 Notify(PSTR("\r\nepinfo is null"), 0x80);
74 return USB_ERROR_EPINFO_IS_NULL;
77 // Save old pointer to EP_RECORD of address 0
78 oldep_ptr = p->epinfo;
80 // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
83 p->lowspeed = lowspeed;
85 // Get device descriptor
86 rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
88 p->epinfo = oldep_ptr;
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) {
100 Notify(PSTR("\r\nYou have plugged in a wireless Xbox 360 controller - it doesn't support USB communication"), 0x80);
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);
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;
111 // Allocate new address according to device class
112 bAddress = addrPool.AllocAddress(parent, false, port);
115 return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
117 // Extract Max Packet Size from device descriptor
118 epInfo[0].maxPktSize = udd->bMaxPacketSize0;
120 // Assign new address to the device
121 rcode = pUsb->setAddr(0, 0, bAddress);
124 addrPool.FreeAddress(bAddress);
126 #ifdef DEBUG_USB_HOST
127 Notify(PSTR("\r\nsetAddr: "), 0x80);
128 D_PrintHex<uint8_t > (rcode, 0x80);
133 Notify(PSTR("\r\nAddr: "), 0x80);
134 D_PrintHex<uint8_t > (bAddress, 0x80);
136 //delay(300); // Spec says you should wait at least 200ms
140 //get pointer to assigned address record
141 p = addrPool.GetUsbDevicePtr(bAddress);
143 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
145 p->lowspeed = lowspeed;
147 // Assign epInfo to epinfo pointer - only EP0 is known
148 rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
150 goto FailSetDevTblEntry;
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 */
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;
170 rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
172 goto FailSetDevTblEntry;
174 delay(200); // Give time for address change
176 rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
178 goto FailSetConfDescr;
180 #ifdef DEBUG_USB_HOST
181 Notify(PSTR("\r\nXbox 360 Controller Connected\r\n"), 0x80);
184 Xbox360Connected = true;
186 return 0; // Successful configuration
188 /* Diagnostic messages */
190 #ifdef DEBUG_USB_HOST
191 NotifyFailGetDevDescr();
196 #ifdef DEBUG_USB_HOST
197 NotifyFailSetDevTblEntry();
202 #ifdef DEBUG_USB_HOST
203 NotifyFailSetConfDescr();
208 #ifdef DEBUG_USB_HOST
209 NotifyFailUnknownDevice(VID, PID);
211 rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
214 #ifdef DEBUG_USB_HOST
215 Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
222 /* Performs a cleanup after failed Init() attempt */
223 uint8_t XBOXUSB::Release() {
224 Xbox360Connected = false;
225 pUsb->GetAddressPool().FreeAddress(bAddress);
231 uint8_t XBOXUSB::Poll() {
234 uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
235 pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
238 printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
243 void XBOXUSB::readReport() {
246 if(readBuf[0] != 0x00 || readBuf[1] != 0x14) { // Check if it's the correct report - the controller also sends different status reports
250 ButtonState = (uint32_t)(readBuf[5] | ((uint16_t)readBuf[4] << 8) | ((uint32_t)readBuf[3] << 16) | ((uint32_t)readBuf[2] << 24));
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]);
257 //Notify(PSTR("\r\nButtonState"), 0x80);
258 //PrintHex<uint32_t>(ButtonState, 0x80);
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
264 if((uint8_t)(OldButtonState >> 8) == 0 && (uint8_t)(ButtonState >> 8) != 0)
266 OldButtonState = ButtonState;
270 void XBOXUSB::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
274 for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {
275 D_PrintHex<uint8_t > (readBuf[i], 0x80);
276 Notify(PSTR(" "), 0x80);
278 Notify(PSTR("\r\n"), 0x80);
282 uint8_t XBOXUSB::getButtonPress(ButtonEnum b) {
283 if(b == L2) // These are analog buttons
284 return (uint8_t)(ButtonState >> 8);
286 return (uint8_t)ButtonState;
287 return (bool)(ButtonState & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16));
290 bool XBOXUSB::getButtonClick(ButtonEnum b) {
304 uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
305 bool click = (ButtonClickState & button);
306 ButtonClickState &= ~button; // clear "click" event
310 int16_t XBOXUSB::getAnalogHat(AnalogHatEnum a) {
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);
320 void XBOXUSB::setLedRaw(uint8_t value) {
325 XboxCommand(writeBuf, 3);
328 void XBOXUSB::setLedOn(LEDEnum led) {
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);
335 void XBOXUSB::setLedBlink(LEDEnum led) {
336 setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]));
339 void XBOXUSB::setLedMode(LEDModeEnum ledMode) { // This function is used to do some special LED stuff the controller supports
340 setLedRaw((uint8_t)ledMode);
343 void XBOXUSB::setRumbleOn(uint8_t lValue, uint8_t rValue) {
347 writeBuf[3] = lValue; // big weight
348 writeBuf[4] = rValue; // small weight
353 XboxCommand(writeBuf, 8);
356 void XBOXUSB::onInit() {
358 pFuncOnInit(); // Call the user function