diff --git a/XBOXChatpadEnums.h b/XBOXChatpadEnums.h new file mode 100644 index 000000000..8e91f02d0 --- /dev/null +++ b/XBOXChatpadEnums.h @@ -0,0 +1,90 @@ +/* Copyright (C) 2020 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"). + + Based on the project from here: https://github.com/Kytech/xbox360wirelesschatpad + */ + +#ifndef _xbox_chatpad_enums_h_ +#define _xbox_chatpad_enums_h_ + +enum ChatpadModiferEnum { + MODIFER_SHIFT = 0, + MODIFER_GREENBUTTON = 1, + MODIFER_ORANGEBUTTON = 2, + MODIFER_MESSENGER = 3 +}; + +enum ChatpadLEDEnum { + CHATPADLED_CAPSLOCK = 0, + CHATPADLED_GREEN = 1, + CHATPADLED_ORANGE = 2, + CHATPADLED_MESSENGER = 3, + + __NUM_CHATPAD_LED +}; + +/** Buttons on the chatpad + * This only accounts for the main keys and does not + * use the modifiers (orange/green) + */ +enum ChatpadButtonEnum { + XBOX_CHATPAD_D7 = 0, + XBOX_CHATPAD_D6 = 1, + XBOX_CHATPAD_D5 = 2, + XBOX_CHATPAD_D4 = 3, + XBOX_CHATPAD_D3 = 4, + XBOX_CHATPAD_D2 = 5, + XBOX_CHATPAD_D1 = 6, + + XBOX_CHATPAD_U = 8, + XBOX_CHATPAD_Y = 9, + XBOX_CHATPAD_T = 10, + XBOX_CHATPAD_R = 11, + XBOX_CHATPAD_E = 12, + XBOX_CHATPAD_W = 13, + XBOX_CHATPAD_Q = 14, + + XBOX_CHATPAD_J = 16, + XBOX_CHATPAD_H = 17, + XBOX_CHATPAD_G = 18, + XBOX_CHATPAD_F = 19, + XBOX_CHATPAD_D = 20, + XBOX_CHATPAD_S = 21, + XBOX_CHATPAD_A = 22, + + XBOX_CHATPAD_M = 33, + XBOX_CHATPAD_N = 24, + XBOX_CHATPAD_B = 25, + XBOX_CHATPAD_V = 26, + XBOX_CHATPAD_C = 27, + XBOX_CHATPAD_X = 28, + XBOX_CHATPAD_Z = 29, + + XBOX_CHATPAD_RIGHT = 32, + XBOX_CHATPAD_SPACE = 35, + XBOX_CHATPAD_LEFT = 36, + + XBOX_CHATPAD_COMMA = 41, + XBOX_CHATPAD_PERIOD = 34, + XBOX_CHATPAD_ENTER = 42, + XBOX_CHATPAD_P = 43, + XBOX_CHATPAD_D0 = 44, + XBOX_CHATPAD_D9 = 45, + XBOX_CHATPAD_D8 = 46, + + XBOX_CHATPAD_BACK = 48, + XBOX_CHATPAD_L = 49, + XBOX_CHATPAD_O = 52, + XBOX_CHATPAD_I = 53, + XBOX_CHATPAD_K = 54, + + __XBOX_CHATPAD_ENUM_MAX +}; + +#endif diff --git a/XBOXRECV.cpp b/XBOXRECV.cpp index 031713d43..dada2d141 100644 --- a/XBOXRECV.cpp +++ b/XBOXRECV.cpp @@ -298,6 +298,8 @@ uint8_t XBOXRECV::Poll() { checkStatus(); } + sendChatpadInitIfNeeded(); + uint8_t inputPipe; uint16_t bufferSize; for(uint8_t i = 0; i < 4; i++) { @@ -364,6 +366,11 @@ void XBOXRECV::readReport(uint8_t controller) { controllerStatus[controller] = ((uint16_t)readBuf[3] << 8) | readBuf[4]; return; } + if(readBuf[1] == 0x02) { + // Chatpad data + processChatpadData(controller, readBuf); + return; + } if(readBuf[1] != 0x01) // Check if it's the correct report - the receiver also sends different status reports return; @@ -407,6 +414,92 @@ void XBOXRECV::printReport(uint8_t controller __attribute__((unused)), uint8_t n #endif } + +void XBOXRECV::processChatpadData(uint8_t controller, uint8_t* dataPacket) { + // This function is called anytime received data is identified as chatpad data + // It will parse the data, and depending on the value, send a keyboard command, + // adjust a modifier for later use, flag initialization, or note the LED status. + if (dataPacket[24] == 0xF0) { + if (dataPacket[25] == 0x03) { + // This data represents handshake request, flag keep-alive to send + // chatpad initialization data. + chatpadInitNeeded[controller] = true; + } + else if (dataPacket[25] == 0x04) { + // This data represents the LED status. Not used because unsure of workings + //chatpadLED["Green"] = (dataPacket[26] & 0x08) > 0; + //chatpadLED["Orange"] = (dataPacket[26] & 0x10) > 0; + //chatpadLED["Messenger"] = (dataPacket[26] & 0x01) > 0; + //chatpadLED["Capslock"] = (dataPacket[26] & 0x20) > 0; + //Backlight = (dataPacket[26] & 0x80) > 0; + } + else { + // TODO: How to deal with invalid data? + return; + } + } + else if (dataPacket[24] == 0x00) { + // This data represents a key-press event + // Check if anything has changed since the last dataPacket + bool dataChanged = false; + if (!firstChatpadRun) { + if (chatpadDataPacketLast[controller][0] != dataPacket[25]) { + dataChanged = true; + } + else if (chatpadDataPacketLast[controller][1] != dataPacket[26]) { + dataChanged = true; + } + else if (chatpadDataPacketLast[controller][2] != dataPacket[27]) { + dataChanged = true; + } + } + else { + firstChatpadRun = false; + dataChanged = true; + } + + // Store bits 25-27 of the data packet for later comparison + chatpadDataPacketLast[controller][0] = dataPacket[25]; + chatpadDataPacketLast[controller][1] = dataPacket[26]; + chatpadDataPacketLast[controller][2] = dataPacket[27]; + + if (dataChanged) + { + // Record the Modifier Statuses + chatpadModState[controller] = dataPacket[25]; + + // Process the two different possible keys that could be held down + ProcessChatpadKeypress(controller, dataPacket[26]); + ProcessChatpadKeypress(controller, dataPacket[27]); + + if(chatpadClickState[controller] != chatpadClickStateOld[controller]) { + chatpadStateChanged[controller] = true; + chatpadClickStateOld[controller] = chatpadClickState[controller]; + } + + if(chatpadModState[controller] != chatpadModStateOld[controller]) { + // Update click state variable + chatpadModClickState[controller] = (chatpadModState[controller]) & ((~chatpadModStateOld[controller])); + chatpadModStateOld[controller] = chatpadModState[controller]; + } + } + } + else { + // TODO: Handle invalid data + } +} + +void XBOXRECV::ProcessChatpadKeypress(uint8_t controller, uint8_t value) { + value = (((value & 0xF0) - 0x10) >> 1) | ((value & 0x0F) - 1); + + if (value > __XBOX_CHATPAD_ENUM_MAX) { + // Invalid button + return; + } + + chatpadClickState[controller] |= (((uint64_t)1) << ((uint64_t)value)); +} + uint8_t XBOXRECV::getButtonPress(ButtonEnum b, uint8_t controller) { if(b == L2) // These are analog buttons return (uint8_t)(ButtonState[controller] >> 8); @@ -445,6 +538,30 @@ bool XBOXRECV::buttonChanged(uint8_t controller) { return state; } +bool XBOXRECV::getChatpadModifierPress(ChatpadModiferEnum b, uint8_t controller) { + return (bool)(chatpadModState[controller] & (1 << b)); +} + +bool XBOXRECV::getChatpadModifierClick(ChatpadModiferEnum b, uint8_t controller) { + uint8_t mask = (1 << b); + bool click = (chatpadModClickState[controller] & mask); + chatpadModClickState[controller] &= ~mask; // clear "click" event + return click; +} + +bool XBOXRECV::chatpadChanged(uint8_t controller) { + bool state = chatpadStateChanged[controller]; + chatpadStateChanged[controller] = false; + return state; +} + +bool XBOXRECV::getChatpadClick(ChatpadButtonEnum b, uint8_t controller) { + uint64_t mask = ((uint64_t)1) << ((uint64_t)b); + bool click = (chatpadClickState[controller] & mask); + chatpadClickState[controller] &= ~mask; // clear "click" event + return click; +} + /* ControllerStatus Breakdown ControllerStatus[controller] & 0x0001 // 0 @@ -524,6 +641,22 @@ void XBOXRECV::setLedBlink(LEDEnum led, uint8_t controller) { setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]), controller); } + +void XBOXRECV::setChatpadLed(ChatpadLEDEnum ledNumber, bool value, uint8_t controller) { + uint8_t ledOnByte[4] = {0x08, 0x09, 0x0A, 0x0B}; + uint8_t ledOffByte[4] = {0x00, 0x01, 0x02, 0x03}; + writeBuf[0] = 0x00; + writeBuf[1] = 0x00; + writeBuf[2] = 0x0C; + + if (value) { + writeBuf[3] = 0x08 + ledNumber; + } else { + writeBuf[3] = ledNumber; + } + XboxCommand(controller, writeBuf, 4); +} + void XBOXRECV::setLedMode(LEDModeEnum ledMode, uint8_t controller) { // This function is used to do some speciel LED stuff the controller supports setLedRaw((uint8_t)ledMode, controller); } @@ -552,6 +685,42 @@ void XBOXRECV::checkStatus() { if(Xbox360Connected[i]) XboxCommand(i, writeBuf, 4); } + + // Keep Alive 1 + writeBuf[0] = 0x00; + writeBuf[1] = 0x00; + writeBuf[2] = 0x0C; + writeBuf[3] = 0x1F; + for(uint8_t i = 0; i < 4; i++) { + if(Xbox360Connected[i]) + XboxCommand(i, writeBuf, 4); + } + // Keep Alive 2 + writeBuf[0] = 0x00; + writeBuf[1] = 0x00; + writeBuf[2] = 0x0C; + writeBuf[3] = 0x1F; + for(uint8_t i = 0; i < 4; i++) { + if(Xbox360Connected[i]) + XboxCommand(i, writeBuf, 4); + } +} + +void XBOXRECV::sendChatpadInitIfNeeded() { + // ChatpadInit + writeBuf[0] = 0x00; + writeBuf[1] = 0x00; + writeBuf[2] = 0x0C; + writeBuf[3] = 0x1B; + for(uint8_t i = 0; i < 4; i++) { + if(chatpadInitNeeded[i]) { + #ifdef EXTRADEBUG + Notify(PSTR("\r\nSending Chatpad Init to controller"), 0x80); + #endif + XboxCommand(i, writeBuf, 4); + chatpadInitNeeded[i] = false; + } + } } void XBOXRECV::setRumbleOn(uint8_t lValue, uint8_t rValue, uint8_t controller) { diff --git a/XBOXRECV.h b/XBOXRECV.h index 4f9214653..9dbd98b81 100644 --- a/XBOXRECV.h +++ b/XBOXRECV.h @@ -22,6 +22,7 @@ #include "Usb.h" #include "xboxEnums.h" +#include "XBOXChatpadEnums.h" /* Data Xbox 360 taken from descriptors */ #define EP_MAXPKTSIZE 32 // max size for data via USB @@ -223,6 +224,17 @@ class XBOXRECV : public USBDeviceConfig { void attachOnInit(void (*funcOnInit)(void)) { pFuncOnInit = funcOnInit; }; + + bool getChatpadModifier(ChatpadModiferEnum b, uint8_t controller = 0); + bool getChatpadClick(ChatpadButtonEnum b, uint8_t controller = 0); + bool chatpadChanged(uint8_t controller = 0); + + /** + * Set the chatpad LEDs on or off + * @param ledNumber Capslock = 0, Green = 1, Orange = 2, Messanger = 3 + * @param controller The controller to read from. Default to 0. + */ + void setChatpadLed(ChatpadLEDEnum led, bool value, uint8_t controller = 0); /**@}*/ /** True if a wireless receiver is connected. */ @@ -272,5 +284,26 @@ class XBOXRECV : public USBDeviceConfig { /* Private commands */ void XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes); void checkStatus(); + + /* Chatpad Commands */ + void sendChatpadInitIfNeeded(); + void processChatpadData(uint8_t controller, uint8_t* epBuffer); + void ProcessChatpadKeypress(uint8_t controller, uint8_t value); + + /* Chatpad State */ + bool firstChatpadRun = true; + bool chatpadInitNeeded[4] = { true }; + uint8_t chatpadModRaw[4] = {0}; + uint8_t chatpadModState[4] = {0}; + uint8_t chatpadModRawOld[4] = {0}; + uint8_t chatpadModClickState[4] = {0}; + uint8_t chatpadDataPacketLast[4][3]; + bool flagUpperCase = false; + + /* Variables to store chatpad buttons */ + uint64_t chatpadClickState[4]; + uint64_t chatpadClickStateOld[4]; + bool chatpadStateChanged[4]; // True if a button has changed }; #endif +