diff --git a/inc/hal/Usb/DreamcastControllerObserver.hpp b/inc/hal/Usb/DreamcastControllerObserver.hpp index 7e09249..7344611 100644 --- a/inc/hal/Usb/DreamcastControllerObserver.hpp +++ b/inc/hal/Usb/DreamcastControllerObserver.hpp @@ -44,10 +44,30 @@ class DreamcastControllerObserver uint8_t lAnalogLR; //!< 0: left; 128: neutral; 255: right } __attribute__ ((packed)); + //! The secondary controller condition bits + struct SecondaryControllerCondition + { + // Digital bits: + // 0: pressed + // 1: released + unsigned up:1; + unsigned down:1; + unsigned left:1; + unsigned right:1; + unsigned a:1; + unsigned b:1; + } __attribute__ ((packed)); + //! Sets the current Dreamcast controller condition //! @param[in] controllerCondition The current condition of the Dreamcast controller virtual void setControllerCondition(const ControllerCondition& controllerCondition) = 0; + //! Sets the current Dreamcast secondary controller condition + //! @param[in] secondaryControllerCondition The current secondary condition of the + //! Dreamcast controller + virtual void setSecondaryControllerCondition( + const SecondaryControllerCondition& secondaryControllerCondition) = 0; + //! Called when controller connected virtual void controllerConnected() = 0; diff --git a/src/hal/Usb/Hid/UsbGamepad.cpp b/src/hal/Usb/Hid/UsbGamepad.cpp index b796f11..3d0f01b 100644 --- a/src/hal/Usb/Hid/UsbGamepad.cpp +++ b/src/hal/Usb/Hid/UsbGamepad.cpp @@ -8,6 +8,8 @@ #include "class/hid/hid.h" #include "class/hid/hid_device.h" +#include "utils.h" + UsbGamepad::UsbGamepad(uint8_t interfaceId, uint8_t reportId) : interfaceId(interfaceId), reportId(reportId), @@ -20,10 +22,18 @@ UsbGamepad::UsbGamepad(uint8_t interfaceId, uint8_t reportId) : bool UsbGamepad::isButtonPressed() { - return (currentDpad[DPAD_UP] || currentDpad[DPAD_DOWN] || currentDpad[DPAD_LEFT] - || currentDpad[DPAD_RIGHT] || currentButtons != 0 - || currentLeftAnalog[0] != 0 || currentLeftAnalog[1] != 0 || currentLeftAnalog[2] != -128 - || currentRightAnalog[0] != 0 || currentRightAnalog[1] != 0 || currentRightAnalog[2] != -128); + return ( + currentDpad[DPAD_UP] + || currentDpad[DPAD_DOWN] + || currentDpad[DPAD_LEFT] + || currentDpad[DPAD_RIGHT] + || currentButtons != 0 + || isAnalogPressed(currentLeftAnalog[0]) + || isAnalogPressed(currentLeftAnalog[1]) + || currentLeftAnalog[2] > (MIN_ANALOG_VALUE + ANALOG_PRESSED_TOL) + || isAnalogPressed(currentRightAnalog[0]) + || isAnalogPressed(currentRightAnalog[1]) + || currentRightAnalog[2] > (MIN_ANALOG_VALUE + ANALOG_PRESSED_TOL)); } //--------------------------------------------------------------------+ @@ -31,6 +41,7 @@ bool UsbGamepad::isButtonPressed() //--------------------------------------------------------------------+ void UsbGamepad::setAnalogThumbX(bool isLeft, int8_t x) { + x = limit_value(x, MIN_ANALOG_VALUE, MAX_ANALOG_VALUE); int8_t lastX = 0; if (isLeft) { @@ -47,6 +58,7 @@ void UsbGamepad::setAnalogThumbX(bool isLeft, int8_t x) void UsbGamepad::setAnalogThumbY(bool isLeft, int8_t y) { + y = limit_value(y, MIN_ANALOG_VALUE, MAX_ANALOG_VALUE); int8_t lastY = 0; if (isLeft) { @@ -63,6 +75,7 @@ void UsbGamepad::setAnalogThumbY(bool isLeft, int8_t y) void UsbGamepad::setAnalogTrigger(bool isLeft, int8_t z) { + z = limit_value(z, MIN_ANALOG_VALUE, MAX_ANALOG_VALUE); int8_t lastZ = 0; if (isLeft) { @@ -120,9 +133,9 @@ void UsbGamepad::setDigitalPad(UsbGamepad::DpadButtons button, bool isPressed) buttonsUpdated = buttonsUpdated || (oldValue != currentDpad[button]); } -void UsbGamepad::setButtonMask(uint16_t mask, bool isPressed) +void UsbGamepad::setButtonMask(uint32_t mask, bool isPressed) { - uint16_t lastButtons = currentButtons; + uint32_t lastButtons = currentButtons; if (isPressed) { currentButtons |= mask; @@ -145,10 +158,10 @@ void UsbGamepad::updateAllReleased() { currentLeftAnalog[0] = 0; currentLeftAnalog[1] = 0; - currentLeftAnalog[2] = -128; + currentLeftAnalog[2] = MIN_ANALOG_VALUE; currentRightAnalog[0] = 0; currentRightAnalog[1] = 0; - currentRightAnalog[2] = -128; + currentRightAnalog[2] = MIN_ANALOG_VALUE; currentDpad[DPAD_UP] = false; currentDpad[DPAD_DOWN] = false; currentDpad[DPAD_LEFT] = false; diff --git a/src/hal/Usb/Hid/UsbGamepad.h b/src/hal/Usb/Hid/UsbGamepad.h index 3e6b463..e5fc017 100644 --- a/src/hal/Usb/Hid/UsbGamepad.h +++ b/src/hal/Usb/Hid/UsbGamepad.h @@ -34,6 +34,23 @@ class UsbGamepad : public UsbControllerDevice GAMEPAD_BUTTON_MODE = 12, GAMEPAD_BUTTON_THUMBL = 13, GAMEPAD_BUTTON_THUMBR = 14, + BUTTON15 = 15, + BUTTON16 = 16, + BUTTON17 = 17, + BUTTON18 = 18, + BUTTON19 = 19, + BUTTON20 = 20, + BUTTON21 = 21, + BUTTON22 = 22, + BUTTON23 = 23, + BUTTON24 = 24, + BUTTON25 = 25, + BUTTON26 = 26, + BUTTON27 = 27, + BUTTON28 = 28, + BUTTON29 = 29, + BUTTON30 = 30, + BUTTON31 = 31 }; public: @@ -70,7 +87,7 @@ class UsbGamepad : public UsbControllerDevice //! Sets or releases one or more of the 16 buttons as a mask value //! @param[in] mask The mask value to set or release //! @param[in] isPressed True to set the mask or false to release - void setButtonMask(uint16_t mask, bool isPressed); + void setButtonMask(uint32_t mask, bool isPressed); //! Sets the state of a single button //! @param[in] button Button value [0,15] //! @param[in] isPressed The state of @p button @@ -93,6 +110,23 @@ class UsbGamepad : public UsbControllerDevice //! @returns the hat value based on current dpad state uint8_t getHatValue(); + private: + //! @param[in] analog The analog value to check + //! @returns true if the given analog is considered "pressed" + inline bool isAnalogPressed(int8_t analog) + { + return (analog > ANALOG_PRESSED_TOL || analog < -ANALOG_PRESSED_TOL); + } + + public: + // TODO: Move these constants to descriptors that this header pulls from + //! Minumum analog value defined in USB HID descriptors + static const int8_t MIN_ANALOG_VALUE = -127; + //! Maximum analog value defined in USB HID descriptors + static const int8_t MAX_ANALOG_VALUE = 127; + //! Tolerance for when analog is considered "pressed" for status LED + static const int8_t ANALOG_PRESSED_TOL = 5; + private: const uint8_t interfaceId; //! The report ID to use when sending keys to host @@ -104,7 +138,7 @@ class UsbGamepad : public UsbControllerDevice //! Current d-pad buttons bool currentDpad[DPAD_COUNT]; //! Current button states - uint16_t currentButtons; + uint32_t currentButtons; //! True when something has been updated since the last successful send bool buttonsUpdated; }; diff --git a/src/hal/Usb/Hid/UsbGamepadDreamcastControllerObserver.cpp b/src/hal/Usb/Hid/UsbGamepadDreamcastControllerObserver.cpp index a455b40..0063cdc 100644 --- a/src/hal/Usb/Hid/UsbGamepadDreamcastControllerObserver.cpp +++ b/src/hal/Usb/Hid/UsbGamepadDreamcastControllerObserver.cpp @@ -37,6 +37,19 @@ void UsbGamepadDreamcastControllerObserver::setControllerCondition(const Control mUsbController.send(); } +void UsbGamepadDreamcastControllerObserver::setSecondaryControllerCondition( + const SecondaryControllerCondition& secondaryControllerCondition) +{ + mUsbController.setButton(UsbGamepad::GAMEPAD_BUTTON_MODE, 0 == secondaryControllerCondition.a); + mUsbController.setButton(UsbGamepad::BUTTON15, 0 == secondaryControllerCondition.b); + mUsbController.setButton(UsbGamepad::BUTTON16, 0 == secondaryControllerCondition.up); + mUsbController.setButton(UsbGamepad::BUTTON17, 0 == secondaryControllerCondition.down); + mUsbController.setButton(UsbGamepad::BUTTON18, 0 == secondaryControllerCondition.left); + mUsbController.setButton(UsbGamepad::BUTTON19, 0 == secondaryControllerCondition.right); + + mUsbController.send(); +} + void UsbGamepadDreamcastControllerObserver::controllerConnected() { mUsbController.updateControllerConnected(true); diff --git a/src/hal/Usb/Hid/UsbGamepadDreamcastControllerObserver.hpp b/src/hal/Usb/Hid/UsbGamepadDreamcastControllerObserver.hpp index 80ad3dd..3a7f8eb 100644 --- a/src/hal/Usb/Hid/UsbGamepadDreamcastControllerObserver.hpp +++ b/src/hal/Usb/Hid/UsbGamepadDreamcastControllerObserver.hpp @@ -17,6 +17,12 @@ class UsbGamepadDreamcastControllerObserver : public DreamcastControllerObserver //! @param[in] controllerCondition The current condition of the Dreamcast controller virtual void setControllerCondition(const ControllerCondition& controllerCondition) final; + //! Sets the current Dreamcast secondary controller condition + //! @param[in] secondaryControllerCondition The current secondary condition of the + //! Dreamcast controller + virtual void setSecondaryControllerCondition( + const SecondaryControllerCondition& secondaryControllerCondition) final; + //! Called when controller connected virtual void controllerConnected() final; diff --git a/src/hostLib/peripherals/DreamcastTimer.cpp b/src/hostLib/peripherals/DreamcastTimer.cpp index fce52cb..dba1351 100644 --- a/src/hostLib/peripherals/DreamcastTimer.cpp +++ b/src/hostLib/peripherals/DreamcastTimer.cpp @@ -1,11 +1,29 @@ #include "DreamcastTimer.hpp" +#include DreamcastTimer::DreamcastTimer(uint8_t addr, uint32_t fd, std::shared_ptr scheduler, PlayerData playerData) : - DreamcastPeripheral("timer", addr, fd, scheduler, playerData.playerIndex) -{} + DreamcastPeripheral("timer", addr, fd, scheduler, playerData.playerIndex), + mGamepad(playerData.gamepad), + mButtonStatusId(0) +{ + // Poll only the upper VMU button states + if (addr & SUB_PERIPHERAL_ADDR_START_MASK) + { + uint32_t payload = FUNCTION_CODE; + mButtonStatusId = mEndpointTxScheduler->add( + PrioritizedTxScheduler::TX_TIME_ASAP, + this, + COMMAND_GET_CONDITION, + &payload, + 1, + true, + 2, + BUTTON_POLL_PERIOD_US); + } +} DreamcastTimer::~DreamcastTimer() {} @@ -23,4 +41,15 @@ void DreamcastTimer::txFailed(bool writeFailed, void DreamcastTimer::txComplete(std::shared_ptr packet, std::shared_ptr tx) -{} +{ + if (tx->transmissionId == mButtonStatusId + && packet->getFrameCommand() == COMMAND_RESPONSE_DATA_XFER + && packet->payload.size() >= 2) + { + // Set controller! + const uint8_t cond = packet->payload[1] >> COND_RIGHT_SHIFT; + DreamcastControllerObserver::SecondaryControllerCondition secondaryCondition; + memcpy(&secondaryCondition, &cond, 1); + mGamepad.setSecondaryControllerCondition(secondaryCondition); + } +} diff --git a/src/hostLib/peripherals/DreamcastTimer.hpp b/src/hostLib/peripherals/DreamcastTimer.hpp index 2acd2c9..609b05e 100644 --- a/src/hostLib/peripherals/DreamcastTimer.hpp +++ b/src/hostLib/peripherals/DreamcastTimer.hpp @@ -39,6 +39,14 @@ class DreamcastTimer : public DreamcastPeripheral public: //! Function code for timer static const uint32_t FUNCTION_CODE = DEVICE_FN_TIMER; + //! Polling period for the upper VMU button states + static const uint32_t BUTTON_POLL_PERIOD_US = 50000; + //! Number of bits to shift the condition word to the right + static const uint8_t COND_RIGHT_SHIFT = 24; private: + //! Gamepad to send secondary status to + DreamcastControllerObserver& mGamepad; + //! Transmit ID of button status message + uint32_t mButtonStatusId; };