You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

XBOXUSB.cpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
  2. This software may be distributed and modified under the terms of the GNU
  3. General Public License version 2 (GPL2) as published by the Free Software
  4. Foundation and appearing in the file GPL2.TXT included in the packaging of
  5. this file. Please note that GPL2 Section 2[b] requires that all works based
  6. on this software must also be made publicly available under the terms of
  7. the GPL2 ("Copyleft").
  8. Contact information
  9. -------------------
  10. Kristian Lauszus, TKJ Electronics
  11. Web : http://www.tkjelectronics.com
  12. e-mail : [email protected]
  13. */
  14. #include "XBOXUSB.h"
  15. // To enable serial debugging see "settings.h"
  16. //#define EXTRADEBUG // Uncomment to get even more debugging data
  17. //#define PRINTREPORT // Uncomment to print the report send by the Xbox 360 Controller
  18. XBOXUSB::XBOXUSB(USB *p) :
  19. pUsb(p), // pointer to USB class instance - mandatory
  20. bAddress(0), // device address - mandatory
  21. bPollEnable(false) { // don't start polling before dongle is connected
  22. for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
  23. epInfo[i].epAddr = 0;
  24. epInfo[i].maxPktSize = (i) ? 0 : 8;
  25. epInfo[i].epAttribs = 0;
  26. epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
  27. }
  28. if(pUsb) // register in USB subsystem
  29. pUsb->RegisterDeviceClass(this); //set devConfig[] entry
  30. }
  31. uint8_t XBOXUSB::Init(uint8_t parent, uint8_t port, bool lowspeed) {
  32. uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
  33. USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
  34. uint8_t rcode;
  35. UsbDevice *p = NULL;
  36. EpInfo *oldep_ptr = NULL;
  37. uint16_t PID;
  38. uint16_t VID;
  39. // get memory address of USB device address pool
  40. AddressPool &addrPool = pUsb->GetAddressPool();
  41. #ifdef EXTRADEBUG
  42. Notify(PSTR("\r\nXBOXUSB Init"), 0x80);
  43. #endif
  44. // check if address has already been assigned to an instance
  45. if(bAddress) {
  46. #ifdef DEBUG_USB_HOST
  47. Notify(PSTR("\r\nAddress in use"), 0x80);
  48. #endif
  49. return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
  50. }
  51. // Get pointer to pseudo device with address 0 assigned
  52. p = addrPool.GetUsbDevicePtr(0);
  53. if(!p) {
  54. #ifdef DEBUG_USB_HOST
  55. Notify(PSTR("\r\nAddress not found"), 0x80);
  56. #endif
  57. return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
  58. }
  59. if(!p->epinfo) {
  60. #ifdef DEBUG_USB_HOST
  61. Notify(PSTR("\r\nepinfo is null"), 0x80);
  62. #endif
  63. return USB_ERROR_EPINFO_IS_NULL;
  64. }
  65. // Save old pointer to EP_RECORD of address 0
  66. oldep_ptr = p->epinfo;
  67. // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
  68. p->epinfo = epInfo;
  69. p->lowspeed = lowspeed;
  70. // Get device descriptor
  71. rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
  72. // Restore p->epinfo
  73. p->epinfo = oldep_ptr;
  74. if(rcode)
  75. goto FailGetDevDescr;
  76. VID = udd->idVendor;
  77. PID = udd->idProduct;
  78. if(VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID && VID != GAMESTOP_VID) // Check VID
  79. goto FailUnknownDevice;
  80. if(PID == XBOX_WIRELESS_PID) {
  81. #ifdef DEBUG_USB_HOST
  82. Notify(PSTR("\r\nYou have plugged in a wireless Xbox 360 controller - it doesn't support USB communication"), 0x80);
  83. #endif
  84. goto FailUnknownDevice;
  85. } else if(PID == XBOX_WIRELESS_RECEIVER_PID || PID == XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID) {
  86. #ifdef DEBUG_USB_HOST
  87. Notify(PSTR("\r\nThis library only supports Xbox 360 controllers via USB"), 0x80);
  88. #endif
  89. goto FailUnknownDevice;
  90. } else if(PID != XBOX_WIRED_PID && PID != MADCATZ_WIRED_PID && PID != GAMESTOP_WIRED_PID && PID != AFTERGLOW_WIRED_PID && PID != JOYTECH_WIRED_PID) // Check PID
  91. goto FailUnknownDevice;
  92. // Allocate new address according to device class
  93. bAddress = addrPool.AllocAddress(parent, false, port);
  94. if(!bAddress)
  95. return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
  96. // Extract Max Packet Size from device descriptor
  97. epInfo[0].maxPktSize = udd->bMaxPacketSize0;
  98. // Assign new address to the device
  99. rcode = pUsb->setAddr(0, 0, bAddress);
  100. if(rcode) {
  101. p->lowspeed = false;
  102. addrPool.FreeAddress(bAddress);
  103. bAddress = 0;
  104. #ifdef DEBUG_USB_HOST
  105. Notify(PSTR("\r\nsetAddr: "), 0x80);
  106. D_PrintHex<uint8_t > (rcode, 0x80);
  107. #endif
  108. return rcode;
  109. }
  110. #ifdef EXTRADEBUG
  111. Notify(PSTR("\r\nAddr: "), 0x80);
  112. D_PrintHex<uint8_t > (bAddress, 0x80);
  113. #endif
  114. //delay(300); // Spec says you should wait at least 200ms
  115. p->lowspeed = false;
  116. //get pointer to assigned address record
  117. p = addrPool.GetUsbDevicePtr(bAddress);
  118. if(!p)
  119. return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
  120. p->lowspeed = lowspeed;
  121. // Assign epInfo to epinfo pointer - only EP0 is known
  122. rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
  123. if(rcode)
  124. goto FailSetDevTblEntry;
  125. /* The application will work in reduced host mode, so we can save program and data
  126. memory space. After verifying the VID we will use known values for the
  127. configuration values for device, interface, endpoints and HID for the XBOX360 Controllers */
  128. /* Initialize data structures for endpoints of device */
  129. epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX 360 report endpoint
  130. epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
  131. epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
  132. epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
  133. epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0;
  134. epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0;
  135. epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX 360 output endpoint
  136. epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
  137. epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
  138. epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
  139. epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0;
  140. epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0;
  141. rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
  142. if(rcode)
  143. goto FailSetDevTblEntry;
  144. delay(200); // Give time for address change
  145. rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
  146. if(rcode)
  147. goto FailSetConfDescr;
  148. #ifdef DEBUG_USB_HOST
  149. Notify(PSTR("\r\nXbox 360 Controller Connected\r\n"), 0x80);
  150. #endif
  151. onInit();
  152. Xbox360Connected = true;
  153. bPollEnable = true;
  154. return 0; // Successful configuration
  155. /* Diagnostic messages */
  156. FailGetDevDescr:
  157. #ifdef DEBUG_USB_HOST
  158. NotifyFailGetDevDescr();
  159. goto Fail;
  160. #endif
  161. FailSetDevTblEntry:
  162. #ifdef DEBUG_USB_HOST
  163. NotifyFailSetDevTblEntry();
  164. goto Fail;
  165. #endif
  166. FailSetConfDescr:
  167. #ifdef DEBUG_USB_HOST
  168. NotifyFailSetConfDescr();
  169. #endif
  170. goto Fail;
  171. FailUnknownDevice:
  172. #ifdef DEBUG_USB_HOST
  173. NotifyFailUnknownDevice(VID, PID);
  174. #endif
  175. rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
  176. Fail:
  177. #ifdef DEBUG_USB_HOST
  178. Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
  179. NotifyFail(rcode);
  180. #endif
  181. Release();
  182. return rcode;
  183. }
  184. /* Performs a cleanup after failed Init() attempt */
  185. uint8_t XBOXUSB::Release() {
  186. Xbox360Connected = false;
  187. pUsb->GetAddressPool().FreeAddress(bAddress);
  188. bAddress = 0;
  189. bPollEnable = false;
  190. return 0;
  191. }
  192. uint8_t XBOXUSB::Poll() {
  193. if(!bPollEnable)
  194. return 0;
  195. uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
  196. pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
  197. readReport();
  198. #ifdef PRINTREPORT
  199. printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
  200. #endif
  201. return 0;
  202. }
  203. void XBOXUSB::readReport() {
  204. if(readBuf == NULL)
  205. return;
  206. if(readBuf[0] != 0x00 || readBuf[1] != 0x14) { // Check if it's the correct report - the controller also sends different status reports
  207. return;
  208. }
  209. ButtonState = (uint32_t)(readBuf[5] | ((uint16_t)readBuf[4] << 8) | ((uint32_t)readBuf[3] << 16) | ((uint32_t)readBuf[2] << 24));
  210. hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
  211. hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
  212. hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
  213. hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
  214. //Notify(PSTR("\r\nButtonState"), 0x80);
  215. //PrintHex<uint32_t>(ButtonState, 0x80);
  216. if(ButtonState != OldButtonState) {
  217. ButtonClickState = (ButtonState >> 16) & ((~OldButtonState) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
  218. if(((uint8_t)OldButtonState) == 0 && ((uint8_t)ButtonState) != 0) // The L2 and R2 buttons are special as they are analog buttons
  219. R2Clicked = true;
  220. if((uint8_t)(OldButtonState >> 8) == 0 && (uint8_t)(ButtonState >> 8) != 0)
  221. L2Clicked = true;
  222. OldButtonState = ButtonState;
  223. }
  224. }
  225. void XBOXUSB::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
  226. #ifdef PRINTREPORT
  227. if(readBuf == NULL)
  228. return;
  229. for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {
  230. D_PrintHex<uint8_t > (readBuf[i], 0x80);
  231. Notify(PSTR(" "), 0x80);
  232. }
  233. Notify(PSTR("\r\n"), 0x80);
  234. #endif
  235. }
  236. uint8_t XBOXUSB::getButtonPress(ButtonEnum b) {
  237. if(b == L2) // These are analog buttons
  238. return (uint8_t)(ButtonState >> 8);
  239. else if(b == R2)
  240. return (uint8_t)ButtonState;
  241. return (bool)(ButtonState & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16));
  242. }
  243. bool XBOXUSB::getButtonClick(ButtonEnum b) {
  244. if(b == L2) {
  245. if(L2Clicked) {
  246. L2Clicked = false;
  247. return true;
  248. }
  249. return false;
  250. } else if(b == R2) {
  251. if(R2Clicked) {
  252. R2Clicked = false;
  253. return true;
  254. }
  255. return false;
  256. }
  257. uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
  258. bool click = (ButtonClickState & button);
  259. ButtonClickState &= ~button; // clear "click" event
  260. return click;
  261. }
  262. int16_t XBOXUSB::getAnalogHat(AnalogHatEnum a) {
  263. return hatValue[a];
  264. }
  265. /* Xbox Controller commands */
  266. void XBOXUSB::XboxCommand(uint8_t* data, uint16_t nbytes) {
  267. //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)
  268. pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
  269. }
  270. void XBOXUSB::setLedRaw(uint8_t value) {
  271. writeBuf[0] = 0x01;
  272. writeBuf[1] = 0x03;
  273. writeBuf[2] = value;
  274. XboxCommand(writeBuf, 3);
  275. }
  276. void XBOXUSB::setLedOn(LEDEnum led) {
  277. if(led == OFF)
  278. setLedRaw(0);
  279. else if(led != ALL) // All LEDs can't be on a the same time
  280. setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]) + 4);
  281. }
  282. void XBOXUSB::setLedBlink(LEDEnum led) {
  283. setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]));
  284. }
  285. void XBOXUSB::setLedMode(LEDModeEnum ledMode) { // This function is used to do some special LED stuff the controller supports
  286. setLedRaw((uint8_t)ledMode);
  287. }
  288. void XBOXUSB::setRumbleOn(uint8_t lValue, uint8_t rValue) {
  289. writeBuf[0] = 0x00;
  290. writeBuf[1] = 0x08;
  291. writeBuf[2] = 0x00;
  292. writeBuf[3] = lValue; // big weight
  293. writeBuf[4] = rValue; // small weight
  294. writeBuf[5] = 0x00;
  295. writeBuf[6] = 0x00;
  296. writeBuf[7] = 0x00;
  297. XboxCommand(writeBuf, 8);
  298. }
  299. void XBOXUSB::onInit() {
  300. if(pFuncOnInit)
  301. pFuncOnInit(); // Call the user function
  302. else
  303. setLedOn(LED1);
  304. }