1 /* Copyright (C) 2013 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 controller
23 /** Buttons on the controllers */
24 const uint8_t XBOXOLD_BUTTONS
[] PROGMEM
= {
35 // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
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
++) {
53 epInfo
[i
].maxPktSize
= (i
) ? 0 : 8;
54 epInfo
[i
].epAttribs
= 0;
55 epInfo
[i
].bmNakPower
= (i
) ? USB_NAK_NOWAIT
: USB_NAK_MAX_POWER
;
58 if(pUsb
) // register in USB subsystem
59 pUsb
->RegisterDeviceClass(this); //set devConfig[] entry
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
);
67 EpInfo
*oldep_ptr
= NULL
;
71 // get memory address of USB device address pool
72 AddressPool
&addrPool
= pUsb
->GetAddressPool();
74 Notify(PSTR("\r\nXBOXUSB Init"), 0x80);
76 // check if address has already been assigned to an instance
79 Notify(PSTR("\r\nAddress in use"), 0x80);
81 return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE
;
84 // Get pointer to pseudo device with address 0 assigned
85 p
= addrPool
.GetUsbDevicePtr(0);
89 Notify(PSTR("\r\nAddress not found"), 0x80);
91 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL
;
96 Notify(PSTR("\r\nepinfo is null"), 0x80);
98 return USB_ERROR_EPINFO_IS_NULL
;
101 // Save old pointer to EP_RECORD of address 0
102 oldep_ptr
= p
->epinfo
;
104 // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
107 p
->lowspeed
= lowspeed
;
109 // Get device descriptor
110 rcode
= pUsb
->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR
), (uint8_t*)buf
); // Get device descriptor - addr, ep, nbytes, data
112 p
->epinfo
= oldep_ptr
;
115 goto FailGetDevDescr
;
118 PID
= udd
->idProduct
;
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
;
123 // Allocate new address according to device class
124 bAddress
= addrPool
.AllocAddress(parent
, false, port
);
127 return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL
;
129 // Extract Max Packet Size from device descriptor
130 epInfo
[0].maxPktSize
= udd
->bMaxPacketSize0
;
132 // Assign new address to the device
133 rcode
= pUsb
->setAddr(0, 0, bAddress
);
136 addrPool
.FreeAddress(bAddress
);
138 #ifdef DEBUG_USB_HOST
139 Notify(PSTR("\r\nsetAddr: "), 0x80);
140 D_PrintHex
<uint8_t > (rcode
, 0x80);
145 Notify(PSTR("\r\nAddr: "), 0x80);
146 D_PrintHex
<uint8_t > (bAddress
, 0x80);
148 //delay(300); // Spec says you should wait at least 200ms
152 //get pointer to assigned address record
153 p
= addrPool
.GetUsbDevicePtr(bAddress
);
155 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL
;
157 p
->lowspeed
= lowspeed
;
159 // Assign epInfo to epinfo pointer - only EP0 is known
160 rcode
= pUsb
->setEpInfoEntry(bAddress
, 1, epInfo
);
162 goto FailSetDevTblEntry
;
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 */
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;
182 rcode
= pUsb
->setEpInfoEntry(bAddress
, 3, epInfo
);
184 goto FailSetDevTblEntry
;
186 delay(200); // Give time for address change
188 rcode
= pUsb
->setConf(bAddress
, epInfo
[ XBOX_CONTROL_PIPE
].epAddr
, 1);
190 goto FailSetConfDescr
;
192 #ifdef DEBUG_USB_HOST
193 Notify(PSTR("\r\nXbox Controller Connected\r\n"), 0x80);
196 pFuncOnInit(); // Call the user function
197 XboxConnected
= true;
199 return 0; // Successful configuration
201 /* Diagnostic messages */
203 #ifdef DEBUG_USB_HOST
204 NotifyFailGetDevDescr();
209 #ifdef DEBUG_USB_HOST
210 NotifyFailSetDevTblEntry();
215 #ifdef DEBUG_USB_HOST
216 NotifyFailSetConfDescr();
221 #ifdef DEBUG_USB_HOST
222 NotifyFailUnknownDevice(VID
, PID
);
224 rcode
= USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED
;
227 #ifdef DEBUG_USB_HOST
228 Notify(PSTR("\r\nXbox Init Failed, error code: "), 0x80);
235 /* Performs a cleanup after failed Init() attempt */
236 uint8_t XBOXOLD::Release() {
237 XboxConnected
= false;
238 pUsb
->GetAddressPool().FreeAddress(bAddress
);
244 uint8_t XBOXOLD::Poll() {
247 uint16_t BUFFER_SIZE
= EP_MAXPKTSIZE
;
248 pUsb
->inTransfer(bAddress
, epInfo
[ XBOX_INPUT_PIPE
].epAddr
, &BUFFER_SIZE
, readBuf
); // input on endpoint 1
251 printReport(BUFFER_SIZE
); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
256 void XBOXOLD::readReport() {
257 ButtonState
= readBuf
[2];
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
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]);
267 //Notify(PSTR("\r\nButtonState"), 0x80);
268 //PrintHex<uint8_t>(ButtonState, 0x80);
270 if(ButtonState
!= OldButtonState
|| memcmp(buttonValues
, oldButtonValues
, sizeof (buttonValues
)) != 0) {
271 ButtonClickState
= ButtonState
& ~OldButtonState
; // Update click state variable
272 OldButtonState
= ButtonState
;
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
];
282 void XBOXOLD::printReport(uint16_t length
) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
286 for(uint8_t i
= 0; i
< length
; i
++) {
287 D_PrintHex
<uint8_t > (readBuf
[i
], 0x80);
288 Notify(PSTR(" "), 0x80);
290 Notify(PSTR("\r\n"), 0x80);
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
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;
311 bool click
= (ButtonClickState
& button
);
312 ButtonClickState
&= ~button
; // clear "click" event
316 int16_t XBOXOLD::getAnalogHat(AnalogHatEnum a
) {
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
);
326 void XBOXOLD::setRumbleOn(uint8_t lValue
, uint8_t rValue
) {
332 writeBuf
[3] = rValue
; // small weight
334 writeBuf
[5] = lValue
; // big weight
336 XboxCommand(writeBuf
, 6);