2013-07-18 17:52:23 +02:00
/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 ( GPL2 ) as published by the Free Software
Foundation and appearing in the file GPL2 . TXT included in the packaging of
this file . Please note that GPL2 Section 2 [ b ] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ( " Copyleft " ) .
Contact information
- - - - - - - - - - - - - - - - - - -
Kristian Lauszus , TKJ Electronics
Web : http : //www.tkjelectronics.com
e - mail : kristianl @ tkjelectronics . com
*/
2013-07-18 20:35:23 +02:00
# include "XBOXOLD.h"
2013-11-11 00:43:50 +01:00
// To enable serial debugging see "settings.h"
2013-07-18 17:52:23 +02:00
//#define EXTRADEBUG // Uncomment to get even more debugging data
2013-07-18 20:35:23 +02:00
//#define PRINTREPORT // Uncomment to print the report send by the Xbox controller
2013-07-18 17:52:23 +02:00
/** Buttons on the controllers */
2014-01-04 13:43:49 +01:00
const uint8_t XBOXOLD_BUTTONS [ ] PROGMEM = {
2013-07-18 17:52:23 +02:00
0x01 , // UP
0x08 , // RIGHT
0x02 , // DOWN
0x04 , // LEFT
0x20 , // BACK
0x10 , // START
0x40 , // L3
0x80 , // R3
// A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
4 , // BLACK
5 , // WHTIE
6 , // L1
7 , // R1
1 , // B
0 , // A
2 , // X
3 , // Y
2013-12-05 00:40:23 +01:00
} ;
2013-07-18 17:52:23 +02:00
2013-07-18 20:35:23 +02:00
XBOXOLD : : XBOXOLD ( USB * p ) :
2013-07-18 17:52:23 +02:00
pUsb ( p ) , // pointer to USB class instance - mandatory
bAddress ( 0 ) , // device address - mandatory
bPollEnable ( false ) { // don't start polling before dongle is connected
2013-12-25 11:09:57 +01:00
for ( uint8_t i = 0 ; i < XBOX_MAX_ENDPOINTS ; i + + ) {
2013-07-18 17:52:23 +02:00
epInfo [ i ] . epAddr = 0 ;
epInfo [ i ] . maxPktSize = ( i ) ? 0 : 8 ;
epInfo [ i ] . epAttribs = 0 ;
epInfo [ i ] . bmNakPower = ( i ) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER ;
}
2013-12-25 11:09:57 +01:00
if ( pUsb ) // register in USB subsystem
2013-07-18 17:52:23 +02:00
pUsb - > RegisterDeviceClass ( this ) ; //set devConfig[] entry
}
2013-07-18 20:35:23 +02:00
uint8_t XBOXOLD : : Init ( uint8_t parent , uint8_t port , bool lowspeed ) {
2013-07-18 17:52:23 +02:00
uint8_t buf [ sizeof ( USB_DEVICE_DESCRIPTOR ) ] ;
2013-12-05 00:40:23 +01:00
USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast < USB_DEVICE_DESCRIPTOR * > ( buf ) ;
2013-07-18 17:52:23 +02:00
uint8_t rcode ;
UsbDevice * p = NULL ;
EpInfo * oldep_ptr = NULL ;
uint16_t PID ;
uint16_t VID ;
// get memory address of USB device address pool
AddressPool & addrPool = pUsb - > GetAddressPool ( ) ;
# ifdef EXTRADEBUG
Notify ( PSTR ( " \r \n XBOXUSB Init " ) , 0x80 ) ;
# endif
// check if address has already been assigned to an instance
2013-12-25 11:09:57 +01:00
if ( bAddress ) {
2013-07-18 17:52:23 +02:00
# ifdef DEBUG_USB_HOST
Notify ( PSTR ( " \r \n Address in use " ) , 0x80 ) ;
# endif
return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE ;
}
// Get pointer to pseudo device with address 0 assigned
p = addrPool . GetUsbDevicePtr ( 0 ) ;
2013-12-25 11:09:57 +01:00
if ( ! p ) {
2013-07-18 17:52:23 +02:00
# ifdef DEBUG_USB_HOST
Notify ( PSTR ( " \r \n Address not found " ) , 0x80 ) ;
# endif
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL ;
}
2013-12-25 11:09:57 +01:00
if ( ! p - > epinfo ) {
2013-07-18 17:52:23 +02:00
# ifdef DEBUG_USB_HOST
Notify ( PSTR ( " \r \n epinfo is null " ) , 0x80 ) ;
# endif
return USB_ERROR_EPINFO_IS_NULL ;
}
// Save old pointer to EP_RECORD of address 0
oldep_ptr = p - > epinfo ;
// Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
p - > epinfo = epInfo ;
p - > lowspeed = lowspeed ;
// Get device descriptor
rcode = pUsb - > getDevDescr ( 0 , 0 , sizeof ( USB_DEVICE_DESCRIPTOR ) , ( uint8_t * ) buf ) ; // Get device descriptor - addr, ep, nbytes, data
// Restore p->epinfo
p - > epinfo = oldep_ptr ;
2013-12-25 11:09:57 +01:00
if ( rcode )
2013-07-18 17:52:23 +02:00
goto FailGetDevDescr ;
2013-12-05 00:40:23 +01:00
VID = udd - > idVendor ;
PID = udd - > idProduct ;
2013-07-18 17:52:23 +02:00
2013-12-25 11:09:57 +01:00
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
2013-07-18 17:52:23 +02:00
goto FailUnknownDevice ;
2013-12-05 00:40:23 +01:00
2013-07-18 17:52:23 +02:00
// Allocate new address according to device class
bAddress = addrPool . AllocAddress ( parent , false , port ) ;
2013-12-25 11:09:57 +01:00
if ( ! bAddress )
2013-07-18 17:52:23 +02:00
return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL ;
// Extract Max Packet Size from device descriptor
2013-12-05 00:40:23 +01:00
epInfo [ 0 ] . maxPktSize = udd - > bMaxPacketSize0 ;
2013-07-18 17:52:23 +02:00
// Assign new address to the device
rcode = pUsb - > setAddr ( 0 , 0 , bAddress ) ;
2013-12-25 11:09:57 +01:00
if ( rcode ) {
2013-07-18 17:52:23 +02:00
p - > lowspeed = false ;
addrPool . FreeAddress ( bAddress ) ;
bAddress = 0 ;
# ifdef DEBUG_USB_HOST
Notify ( PSTR ( " \r \n setAddr: " ) , 0x80 ) ;
D_PrintHex < uint8_t > ( rcode , 0x80 ) ;
2013-07-20 12:57:23 +02:00
# endif
2013-07-18 17:52:23 +02:00
return rcode ;
}
# ifdef EXTRADEBUG
Notify ( PSTR ( " \r \n Addr: " ) , 0x80 ) ;
D_PrintHex < uint8_t > ( bAddress , 0x80 ) ;
# endif
2013-12-19 03:58:24 +01:00
//delay(300); // Spec says you should wait at least 200ms
2013-12-05 00:40:23 +01:00
2013-07-18 17:52:23 +02:00
p - > lowspeed = false ;
//get pointer to assigned address record
p = addrPool . GetUsbDevicePtr ( bAddress ) ;
2013-12-25 11:09:57 +01:00
if ( ! p )
2013-07-18 17:52:23 +02:00
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL ;
p - > lowspeed = lowspeed ;
// Assign epInfo to epinfo pointer - only EP0 is known
rcode = pUsb - > setEpInfoEntry ( bAddress , 1 , epInfo ) ;
2013-12-25 11:09:57 +01:00
if ( rcode )
2013-07-18 17:52:23 +02:00
goto FailSetDevTblEntry ;
/* The application will work in reduced host mode, so we can save program and data
memory space . After verifying the VID we will use known values for the
configuration values for device , interface , endpoints and HID for the XBOX controllers */
/* Initialize data structures for endpoints of device */
epInfo [ XBOX_INPUT_PIPE ] . epAddr = 0x01 ; // XBOX report endpoint
2014-09-02 09:16:48 +02:00
epInfo [ XBOX_INPUT_PIPE ] . epAttribs = USB_TRANSFER_TYPE_INTERRUPT ;
2013-07-18 17:52:23 +02:00
epInfo [ XBOX_INPUT_PIPE ] . bmNakPower = USB_NAK_NOWAIT ; // Only poll once for interrupt endpoints
epInfo [ XBOX_INPUT_PIPE ] . maxPktSize = EP_MAXPKTSIZE ;
2013-12-05 00:40:23 +01:00
epInfo [ XBOX_INPUT_PIPE ] . bmSndToggle = 0 ;
epInfo [ XBOX_INPUT_PIPE ] . bmRcvToggle = 0 ;
2013-07-18 17:52:23 +02:00
epInfo [ XBOX_OUTPUT_PIPE ] . epAddr = 0x02 ; // XBOX output endpoint
2014-09-02 09:16:48 +02:00
epInfo [ XBOX_OUTPUT_PIPE ] . epAttribs = USB_TRANSFER_TYPE_INTERRUPT ;
2013-07-18 17:52:23 +02:00
epInfo [ XBOX_OUTPUT_PIPE ] . bmNakPower = USB_NAK_NOWAIT ; // Only poll once for interrupt endpoints
epInfo [ XBOX_OUTPUT_PIPE ] . maxPktSize = EP_MAXPKTSIZE ;
2013-12-05 00:40:23 +01:00
epInfo [ XBOX_OUTPUT_PIPE ] . bmSndToggle = 0 ;
epInfo [ XBOX_OUTPUT_PIPE ] . bmRcvToggle = 0 ;
2013-07-18 17:52:23 +02:00
rcode = pUsb - > setEpInfoEntry ( bAddress , 3 , epInfo ) ;
2013-12-25 11:09:57 +01:00
if ( rcode )
2013-07-18 17:52:23 +02:00
goto FailSetDevTblEntry ;
delay ( 200 ) ; // Give time for address change
rcode = pUsb - > setConf ( bAddress , epInfo [ XBOX_CONTROL_PIPE ] . epAddr , 1 ) ;
2013-12-25 11:09:57 +01:00
if ( rcode )
2013-07-18 17:52:23 +02:00
goto FailSetConfDescr ;
# ifdef DEBUG_USB_HOST
Notify ( PSTR ( " \r \n Xbox Controller Connected \r \n " ) , 0x80 ) ;
# endif
2013-12-25 11:09:57 +01:00
if ( pFuncOnInit )
2013-07-18 17:52:23 +02:00
pFuncOnInit ( ) ; // Call the user function
XboxConnected = true ;
bPollEnable = true ;
return 0 ; // Successful configuration
/* Diagnostic messages */
FailGetDevDescr :
# ifdef DEBUG_USB_HOST
NotifyFailGetDevDescr ( ) ;
goto Fail ;
# endif
FailSetDevTblEntry :
# ifdef DEBUG_USB_HOST
NotifyFailSetDevTblEntry ( ) ;
goto Fail ;
# endif
FailSetConfDescr :
# ifdef DEBUG_USB_HOST
NotifyFailSetConfDescr ( ) ;
# endif
2013-12-06 22:58:16 +01:00
goto Fail ;
2013-07-18 17:52:23 +02:00
FailUnknownDevice :
# ifdef DEBUG_USB_HOST
NotifyFailUnknownDevice ( VID , PID ) ;
# endif
rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED ;
2013-12-05 00:40:23 +01:00
Fail :
2013-12-06 22:58:16 +01:00
# ifdef DEBUG_USB_HOST
2013-07-18 17:52:23 +02:00
Notify ( PSTR ( " \r \n Xbox Init Failed, error code: " ) , 0x80 ) ;
NotifyFail ( rcode ) ;
# endif
Release ( ) ;
return rcode ;
}
/* Performs a cleanup after failed Init() attempt */
2013-07-18 20:35:23 +02:00
uint8_t XBOXOLD : : Release ( ) {
2013-07-18 17:52:23 +02:00
XboxConnected = false ;
pUsb - > GetAddressPool ( ) . FreeAddress ( bAddress ) ;
bAddress = 0 ;
bPollEnable = false ;
return 0 ;
}
2013-07-18 20:35:23 +02:00
uint8_t XBOXOLD : : Poll ( ) {
2013-12-25 11:09:57 +01:00
if ( ! bPollEnable )
2013-07-18 17:52:23 +02:00
return 0 ;
uint16_t BUFFER_SIZE = EP_MAXPKTSIZE ;
pUsb - > inTransfer ( bAddress , epInfo [ XBOX_INPUT_PIPE ] . epAddr , & BUFFER_SIZE , readBuf ) ; // input on endpoint 1
readReport ( ) ;
# ifdef PRINTREPORT
printReport ( BUFFER_SIZE ) ; // Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
# endif
return 0 ;
}
2013-07-18 20:35:23 +02:00
void XBOXOLD : : readReport ( ) {
2013-07-18 17:52:23 +02:00
ButtonState = readBuf [ 2 ] ;
2013-12-25 11:09:57 +01:00
for ( uint8_t i = 0 ; i < sizeof ( buttonValues ) ; i + + )
buttonValues [ i ] = readBuf [ i + 4 ] ; // A, B, X, Y, BLACK, WHITE, L1, and R1
2013-07-18 17:52:23 +02:00
hatValue [ LeftHatX ] = ( int16_t ) ( ( ( uint16_t ) readBuf [ 12 ] < < 8 ) | readBuf [ 13 ] ) ;
hatValue [ LeftHatY ] = ( int16_t ) ( ( ( uint16_t ) readBuf [ 14 ] < < 8 ) | readBuf [ 15 ] ) ;
hatValue [ RightHatX ] = ( int16_t ) ( ( ( uint16_t ) readBuf [ 16 ] < < 8 ) | readBuf [ 17 ] ) ;
hatValue [ RightHatY ] = ( int16_t ) ( ( ( uint16_t ) readBuf [ 18 ] < < 8 ) | readBuf [ 19 ] ) ;
//Notify(PSTR("\r\nButtonState"), 0x80);
//PrintHex<uint8_t>(ButtonState, 0x80);
2013-12-25 11:09:57 +01:00
if ( ButtonState ! = OldButtonState | | memcmp ( buttonValues , oldButtonValues , sizeof ( buttonValues ) ) ! = 0 ) {
2013-07-18 17:52:23 +02:00
ButtonClickState = ButtonState & ~ OldButtonState ; // Update click state variable
OldButtonState = ButtonState ;
2013-12-25 11:09:57 +01:00
for ( uint8_t i = 0 ; i < sizeof ( buttonValues ) ; i + + ) {
if ( oldButtonValues [ i ] = = 0 & & buttonValues [ i ] ! = 0 )
buttonClicked [ i ] = true ; // Update A, B, X, Y, BLACK, WHITE, L1, and R1 click state
oldButtonValues [ i ] = buttonValues [ i ] ;
2013-07-18 17:52:23 +02:00
}
}
}
2013-07-18 20:35:23 +02:00
void XBOXOLD : : printReport ( uint16_t length ) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
2013-07-18 17:52:23 +02:00
# ifdef PRINTREPORT
2013-12-25 11:09:57 +01:00
if ( readBuf = = NULL )
2013-07-18 17:52:23 +02:00
return ;
2013-12-25 11:09:57 +01:00
for ( uint8_t i = 0 ; i < length ; i + + ) {
2013-07-18 17:52:23 +02:00
D_PrintHex < uint8_t > ( readBuf [ i ] , 0x80 ) ;
Notify ( PSTR ( " " ) , 0x80 ) ;
}
Notify ( PSTR ( " \r \n " ) , 0x80 ) ;
# endif
}
2014-01-04 13:43:49 +01:00
uint8_t XBOXOLD : : getButtonPress ( ButtonEnum b ) {
uint8_t button = pgm_read_byte ( & XBOXOLD_BUTTONS [ ( uint8_t ) b ] ) ;
2013-12-25 11:09:57 +01:00
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
return buttonValues [ button ] ; // Analog buttons
2013-11-11 00:43:50 +01:00
return ( ButtonState & button ) ; // Digital buttons
2013-07-18 17:52:23 +02:00
}
2014-01-04 13:43:49 +01:00
bool XBOXOLD : : getButtonClick ( ButtonEnum b ) {
uint8_t button = pgm_read_byte ( & XBOXOLD_BUTTONS [ ( uint8_t ) b ] ) ;
2013-12-25 11:09:57 +01:00
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
if ( buttonClicked [ button ] ) {
buttonClicked [ button ] = false ;
return true ;
}
return false ;
2013-07-18 17:52:23 +02:00
}
bool click = ( ButtonClickState & button ) ;
ButtonClickState & = ~ button ; // clear "click" event
return click ;
}
2014-01-04 13:43:49 +01:00
int16_t XBOXOLD : : getAnalogHat ( AnalogHatEnum a ) {
2013-07-18 17:52:23 +02:00
return hatValue [ a ] ;
}
/* Xbox Controller commands */
2013-07-18 20:35:23 +02:00
void XBOXOLD : : XboxCommand ( uint8_t * data , uint16_t nbytes ) {
2013-07-18 17:52:23 +02:00
//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)
pUsb - > ctrlReq ( bAddress , epInfo [ XBOX_CONTROL_PIPE ] . epAddr , bmREQ_HID_OUT , HID_REQUEST_SET_REPORT , 0x00 , 0x02 , 0x00 , nbytes , nbytes , data , NULL ) ;
}
2013-07-18 20:35:23 +02:00
void XBOXOLD : : setRumbleOn ( uint8_t lValue , uint8_t rValue ) {
2013-07-19 12:49:13 +02:00
uint8_t writeBuf [ 6 ] ;
2013-07-18 17:52:23 +02:00
writeBuf [ 0 ] = 0x00 ;
writeBuf [ 1 ] = 0x06 ;
writeBuf [ 2 ] = 0x00 ;
writeBuf [ 3 ] = rValue ; // small weight
writeBuf [ 4 ] = 0x00 ;
writeBuf [ 5 ] = lValue ; // big weight
XboxCommand ( writeBuf , 6 ) ;
2013-12-25 11:09:57 +01:00
}