diff options
author | Fernando S <fsahmkow27@gmail.com> | 2021-11-27 11:52:08 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-27 11:52:08 +0100 |
commit | 564f10527745f870621c08bbb5d16badee0ed861 (patch) | |
tree | e8ac8dee60086facf1837393882865f5df18c95e /src/input_common/helpers/udp_protocol.h | |
parent | Merge pull request #7431 from liushuyu/fix-linux-decoding (diff) | |
parent | config: Remove vibration configuration (diff) | |
download | yuzu-564f10527745f870621c08bbb5d16badee0ed861.tar yuzu-564f10527745f870621c08bbb5d16badee0ed861.tar.gz yuzu-564f10527745f870621c08bbb5d16badee0ed861.tar.bz2 yuzu-564f10527745f870621c08bbb5d16badee0ed861.tar.lz yuzu-564f10527745f870621c08bbb5d16badee0ed861.tar.xz yuzu-564f10527745f870621c08bbb5d16badee0ed861.tar.zst yuzu-564f10527745f870621c08bbb5d16badee0ed861.zip |
Diffstat (limited to 'src/input_common/helpers/udp_protocol.h')
-rw-r--r-- | src/input_common/helpers/udp_protocol.h | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/src/input_common/helpers/udp_protocol.h b/src/input_common/helpers/udp_protocol.h new file mode 100644 index 000000000..bcba12c58 --- /dev/null +++ b/src/input_common/helpers/udp_protocol.h @@ -0,0 +1,290 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <optional> +#include <type_traits> + +#include <boost/crc.hpp> + +#include "common/bit_field.h" +#include "common/swap.h" + +namespace InputCommon::CemuhookUDP { + +constexpr std::size_t MAX_PACKET_SIZE = 100; +constexpr u16 PROTOCOL_VERSION = 1001; +constexpr u32 CLIENT_MAGIC = 0x43555344; // DSUC (but flipped for LE) +constexpr u32 SERVER_MAGIC = 0x53555344; // DSUS (but flipped for LE) + +enum class Type : u32 { + Version = 0x00100000, + PortInfo = 0x00100001, + PadData = 0x00100002, +}; + +struct Header { + u32_le magic{}; + u16_le protocol_version{}; + u16_le payload_length{}; + u32_le crc{}; + u32_le id{}; + ///> In the protocol, the type of the packet is not part of the header, but its convenient to + ///> include in the header so the callee doesn't have to duplicate the type twice when building + ///> the data + Type type{}; +}; +static_assert(sizeof(Header) == 20, "UDP Message Header struct has wrong size"); +static_assert(std::is_trivially_copyable_v<Header>, "UDP Message Header is not trivially copyable"); + +using MacAddress = std::array<u8, 6>; +constexpr MacAddress EMPTY_MAC_ADDRESS = {0, 0, 0, 0, 0, 0}; + +#pragma pack(push, 1) +template <typename T> +struct Message { + Header header{}; + T data; +}; +#pragma pack(pop) + +template <typename T> +constexpr Type GetMessageType(); + +namespace Request { + +enum RegisterFlags : u8 { + AllPads, + PadID, + PadMACAdddress, +}; + +struct Version {}; +/** + * Requests the server to send information about what controllers are plugged into the ports + * In citra's case, we only have one controller, so for simplicity's sake, we can just send a + * request explicitly for the first controller port and leave it at that. In the future it would be + * nice to make this configurable + */ +constexpr u32 MAX_PORTS = 4; +struct PortInfo { + u32_le pad_count{}; ///> Number of ports to request data for + std::array<u8, MAX_PORTS> port; +}; +static_assert(std::is_trivially_copyable_v<PortInfo>, + "UDP Request PortInfo is not trivially copyable"); + +/** + * Request the latest pad information from the server. If the server hasn't received this message + * from the client in a reasonable time frame, the server will stop sending updates. The default + * timeout seems to be 5 seconds. + */ +struct PadData { + /// Determines which method will be used as a look up for the controller + RegisterFlags flags{}; + /// Index of the port of the controller to retrieve data about + u8 port_id{}; + /// Mac address of the controller to retrieve data about + MacAddress mac; +}; +static_assert(sizeof(PadData) == 8, "UDP Request PadData struct has wrong size"); +static_assert(std::is_trivially_copyable_v<PadData>, + "UDP Request PadData is not trivially copyable"); + +/** + * Creates a message with the proper header data that can be sent to the server. + * @param data Request body to send + * @param client_id ID of the udp client (usually not checked on the server) + */ +template <typename T> +Message<T> Create(const T data, const u32 client_id = 0) { + boost::crc_32_type crc; + Header header{ + CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType<T>(), + }; + Message<T> message{header, data}; + crc.process_bytes(&message, sizeof(Message<T>)); + message.header.crc = crc.checksum(); + return message; +} +} // namespace Request + +namespace Response { + +enum class ConnectionType : u8 { + None, + Usb, + Bluetooth, +}; + +enum class State : u8 { + Disconnected, + Reserved, + Connected, +}; + +enum class Model : u8 { + None, + PartialGyro, + FullGyro, + Generic, +}; + +enum class Battery : u8 { + None = 0x00, + Dying = 0x01, + Low = 0x02, + Medium = 0x03, + High = 0x04, + Full = 0x05, + Charging = 0xEE, + Charged = 0xEF, +}; + +struct Version { + u16_le version{}; +}; +static_assert(sizeof(Version) == 2, "UDP Response Version struct has wrong size"); +static_assert(std::is_trivially_copyable_v<Version>, + "UDP Response Version is not trivially copyable"); + +struct PortInfo { + u8 id{}; + State state{}; + Model model{}; + ConnectionType connection_type{}; + MacAddress mac; + Battery battery{}; + u8 is_pad_active{}; +}; +static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size"); +static_assert(std::is_trivially_copyable_v<PortInfo>, + "UDP Response PortInfo is not trivially copyable"); + +struct TouchPad { + u8 is_active{}; + u8 id{}; + u16_le x{}; + u16_le y{}; +}; +static_assert(sizeof(TouchPad) == 6, "UDP Response TouchPad struct has wrong size "); + +#pragma pack(push, 1) +struct PadData { + PortInfo info{}; + u32_le packet_counter{}; + + u16_le digital_button{}; + // The following union isn't trivially copyable but we don't use this input anyway. + // union DigitalButton { + // u16_le button; + // BitField<0, 1, u16> button_1; // Share + // BitField<1, 1, u16> button_2; // L3 + // BitField<2, 1, u16> button_3; // R3 + // BitField<3, 1, u16> button_4; // Options + // BitField<4, 1, u16> button_5; // Up + // BitField<5, 1, u16> button_6; // Right + // BitField<6, 1, u16> button_7; // Down + // BitField<7, 1, u16> button_8; // Left + // BitField<8, 1, u16> button_9; // L2 + // BitField<9, 1, u16> button_10; // R2 + // BitField<10, 1, u16> button_11; // L1 + // BitField<11, 1, u16> button_12; // R1 + // BitField<12, 1, u16> button_13; // Triangle + // BitField<13, 1, u16> button_14; // Circle + // BitField<14, 1, u16> button_15; // Cross + // BitField<15, 1, u16> button_16; // Square + // } digital_button; + + u8 home; + /// If the device supports a "click" on the touchpad, this will change to 1 when a click happens + u8 touch_hard_press{}; + u8 left_stick_x{}; + u8 left_stick_y{}; + u8 right_stick_x{}; + u8 right_stick_y{}; + + struct AnalogButton { + u8 button_dpad_left_analog{}; + u8 button_dpad_down_analog{}; + u8 button_dpad_right_analog{}; + u8 button_dpad_up_analog{}; + u8 button_square_analog{}; + u8 button_cross_analog{}; + u8 button_circle_analog{}; + u8 button_triangle_analog{}; + u8 button_r1_analog{}; + u8 button_l1_analog{}; + u8 trigger_r2{}; + u8 trigger_l2{}; + } analog_button; + + std::array<TouchPad, 2> touch; + + u64_le motion_timestamp; + + struct Accelerometer { + float x{}; + float y{}; + float z{}; + } accel; + + struct Gyroscope { + float pitch{}; + float yaw{}; + float roll{}; + } gyro; +}; +#pragma pack(pop) + +static_assert(sizeof(PadData) == 80, "UDP Response PadData struct has wrong size "); +static_assert(std::is_trivially_copyable_v<PadData>, + "UDP Response PadData is not trivially copyable"); + +static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE, + "UDP MAX_PACKET_SIZE is no longer larger than Message<PadData>"); + +static_assert(sizeof(PadData::AnalogButton) == 12, + "UDP Response AnalogButton struct has wrong size "); +static_assert(sizeof(PadData::Accelerometer) == 12, + "UDP Response Accelerometer struct has wrong size "); +static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size "); + +/** + * Create a Response Message from the data + * @param data array of bytes sent from the server + * @return boost::none if it failed to parse or Type if it succeeded. The client can then safely + * copy the data into the appropriate struct for that Type + */ +std::optional<Type> Validate(u8* data, std::size_t size); + +} // namespace Response + +template <> +constexpr Type GetMessageType<Request::Version>() { + return Type::Version; +} +template <> +constexpr Type GetMessageType<Request::PortInfo>() { + return Type::PortInfo; +} +template <> +constexpr Type GetMessageType<Request::PadData>() { + return Type::PadData; +} +template <> +constexpr Type GetMessageType<Response::Version>() { + return Type::Version; +} +template <> +constexpr Type GetMessageType<Response::PortInfo>() { + return Type::PortInfo; +} +template <> +constexpr Type GetMessageType<Response::PadData>() { + return Type::PadData; +} +} // namespace InputCommon::CemuhookUDP |