1 /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
2 Copyright (C) 2015 guruthree
4 This software may be distributed and modified under the terms of the GNU
5 General Public License version 2 (GPL2) as published by the Free Software
6 Foundation and appearing in the file GPL2.TXT included in the packaging of
7 this file. Please note that GPL2 Section 2[b] requires that all works based
8 on this software must also be made publicly available under the terms of
14 Kristian Lauszus, TKJ Electronics
15 Web : http://www.tkjelectronics.com
16 e-mail : kristianl@tkjelectronics.com
19 Web : https://github.com/guruthree/
23 // To enable serial debugging see "settings.h"
24 //#define EXTRADEBUG // Uncomment to get even more debugging data
25 //#define PRINTREPORT // Uncomment to print the report send by the Xbox ONE Controller
27 XBOXONE::XBOXONE(USB *p) :
28 pUsb(p), // pointer to USB class instance - mandatory
29 bAddress(0), // device address - mandatory
30 bPollEnable(false) { // don't start polling before dongle is connected
31 for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
33 epInfo[i].maxPktSize = (i) ? 0 : 8;
34 epInfo[i].epAttribs = 0;
35 epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
38 if(pUsb) // register in USB subsystem
39 pUsb->RegisterDeviceClass(this); //set devConfig[] entry
42 uint8_t XBOXONE::Init(uint8_t parent, uint8_t port, bool lowspeed) {
43 uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
44 USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
47 EpInfo *oldep_ptr = NULL;
51 // get memory address of USB device address pool
52 AddressPool &addrPool = pUsb->GetAddressPool();
54 Notify(PSTR("\r\nXBOXONE Init"), 0x80);
56 // check if address has already been assigned to an instance
59 Notify(PSTR("\r\nAddress in use"), 0x80);
61 return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
64 // Get pointer to pseudo device with address 0 assigned
65 p = addrPool.GetUsbDevicePtr(0);
69 Notify(PSTR("\r\nAddress not found"), 0x80);
71 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
76 Notify(PSTR("\r\nepinfo is null"), 0x80);
78 return USB_ERROR_EPINFO_IS_NULL;
81 // Save old pointer to EP_RECORD of address 0
82 oldep_ptr = p->epinfo;
84 // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
87 p->lowspeed = lowspeed;
89 // Get device descriptor
90 rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
92 p->epinfo = oldep_ptr;
100 if(!VIDPIDOK(VID, PID)) // Check VID
101 goto FailUnknownDevice;
103 // Allocate new address according to device class
104 bAddress = addrPool.AllocAddress(parent, false, port);
107 return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
109 // Extract Max Packet Size from device descriptor
110 epInfo[0].maxPktSize = udd->bMaxPacketSize0;
112 // Assign new address to the device
113 rcode = pUsb->setAddr(0, 0, bAddress);
116 addrPool.FreeAddress(bAddress);
118 #ifdef DEBUG_USB_HOST
119 Notify(PSTR("\r\nsetAddr: "), 0x80);
120 D_PrintHex<uint8_t > (rcode, 0x80);
125 Notify(PSTR("\r\nAddr: "), 0x80);
126 D_PrintHex<uint8_t > (bAddress, 0x80);
128 //delay(300); // Spec says you should wait at least 200ms
132 //get pointer to assigned address record
133 p = addrPool.GetUsbDevicePtr(bAddress);
135 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
137 p->lowspeed = lowspeed;
139 // Assign epInfo to epinfo pointer - only EP0 is known
140 rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
142 goto FailSetDevTblEntry;
144 /* The application will work in reduced host mode, so we can save program and data
145 memory space. After verifying the VID we will use known values for the
146 configuration values for device, interface, endpoints and HID for the XBOXONE Controllers */
148 /* Initialize data structures for endpoints of device */
149 epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x01; // XBOX one output endpoint
150 epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
151 epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
152 epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
153 epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0;
154 epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0;
155 epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX one input endpoint
156 epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
157 epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
158 epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
159 epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0;
160 epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0;
162 rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
164 goto FailSetDevTblEntry;
166 delay(200); // Give time for address change
168 rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
170 goto FailSetConfDescr;
172 #ifdef DEBUG_USB_HOST
173 Notify(PSTR("\r\nXbox One Controller Connected\r\n"), 0x80);
176 delay(200); // let things settle
178 // initialize the controller for input
181 rcode = XboxCommand(writeBuf, 2);
186 XboxOneConnected = true;
188 return 0; // Successful configuration
190 /* Diagnostic messages */
192 #ifdef DEBUG_USB_HOST
193 NotifyFailGetDevDescr();
198 #ifdef DEBUG_USB_HOST
199 NotifyFailSetDevTblEntry();
204 #ifdef DEBUG_USB_HOST
205 NotifyFailSetConfDescr();
210 #ifdef DEBUG_USB_HOST
211 NotifyFailUnknownDevice(VID, PID);
213 rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
216 #ifdef DEBUG_USB_HOST
217 Notify(PSTR("\r\nXbox One Init Failed, error code: "), 0x80);
224 /* Performs a cleanup after failed Init() attempt */
225 uint8_t XBOXONE::Release() {
226 XboxOneConnected = false;
227 pUsb->GetAddressPool().FreeAddress(bAddress);
230 #ifdef DEBUG_USB_HOST
231 Notify(PSTR("\r\nXbox One Controller Disconnected\r\n"), 0x80);
236 uint8_t XBOXONE::Poll() {
239 uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
240 uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf);
244 printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox ONE Controller
247 #ifdef DEBUG_USB_HOST
248 else if (rcode != 0x04) { // not a matter of no update to send
249 Notify(PSTR("\r\nXbox One Poll Failed, error code: "), 0x80);
256 void XBOXONE::readReport() {
259 if(readBuf[0] == 0x07) {
260 // The XBOX button has a separate message
262 ButtonState |= XBOX_BUTTONS[XBOX];
264 ButtonState &= ~XBOX_BUTTONS[XBOX];
266 if(readBuf[0] != 0x20) { // Check if it's the correct report, otherwise return - the controller also sends different status reports
268 Notify(PSTR("\r\nXbox Poll: "), 0x80);
269 D_PrintHex<uint8_t > (readBuf[0], 0x80); // 0x03 is a heart beat report!
274 uint16_t xbox = ButtonState & XBOX_BUTTONS[XBOX]; // Since the XBOX button is separate, save it and add it back in
275 // xbox button from before, dpad, abxy, start/back, sync, stick click, shoulder buttons
276 ButtonState = xbox | (((uint16_t)readBuf[5] & 0xF) << 8) | (readBuf[4] & 0xF0) | (((uint16_t)readBuf[4] & 0x0C) << 10) | ((readBuf[4] & 0x01) << 3) | (((uint16_t)readBuf[5] & 0xC0) << 8) | ((readBuf[5] & 0x30) >> 4);
278 triggerValue[0] = (uint16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
279 triggerValue[1] = (uint16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
281 hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
282 hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
283 hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);
284 hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);
286 //Notify(PSTR("\r\nButtonState"), 0x80);
287 //PrintHex<uint16_t>(ButtonState, 0x80);
289 if(ButtonState != OldButtonState) {
290 ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
291 OldButtonState = ButtonState;
294 // Handle click detection for triggers
295 if(triggerValue[0] != 0 && triggerValueOld[0] == 0)
297 triggerValueOld[0] = triggerValue[0];
298 if(triggerValue[1] != 0 && triggerValueOld[1] == 0)
300 triggerValueOld[1] = triggerValue[1];
303 void XBOXONE::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox ONE Controller
307 for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {
308 D_PrintHex<uint8_t > (readBuf[i], 0x80);
309 Notify(PSTR(" "), 0x80);
311 Notify(PSTR("\r\n"), 0x80);
315 uint16_t XBOXONE::getButtonPress(ButtonEnum b) {
316 if(b == L2) // These are analog buttons
317 return triggerValue[0];
319 return triggerValue[1];
320 return (bool)(ButtonState & ((uint16_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b])));
323 bool XBOXONE::getButtonClick(ButtonEnum b) {
337 uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
338 bool click = (ButtonClickState & button);
339 ButtonClickState &= ~button; // clear "click" event
343 int16_t XBOXONE::getAnalogHat(AnalogHatEnum a) {
347 /* Xbox Controller commands */
348 uint8_t XBOXONE::XboxCommand(uint8_t* data, uint16_t nbytes) {
349 uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_OUTPUT_PIPE ].epAddr, nbytes, data);
350 #ifdef DEBUG_USB_HOST
351 Notify(PSTR("\r\nXboxCommand, Return: "), 0x80);
352 D_PrintHex<uint8_t > (rcode, 0x80);
357 void XBOXONE::onInit() {
358 // a short buzz to show the controller is active
370 XboxCommand(writeBuf, 11);
373 pFuncOnInit(); // Call the user function