summaryrefslogtreecommitdiff
path: root/src/input_common/helpers/udp_protocol.h
diff options
context:
space:
mode:
authorGravatar german772021-09-20 16:57:55 -0500
committerGravatar Narr the Reg2021-11-24 20:30:22 -0600
commit4c6f2c2547e1d97f12ebe708fac693a6183bbc45 (patch)
tree0abcd35f56088bbf732a92838995a465bae0f0ee /src/input_common/helpers/udp_protocol.h
parentinput_common: Create input poller and mapping (diff)
downloadyuzu-4c6f2c2547e1d97f12ebe708fac693a6183bbc45.tar.gz
yuzu-4c6f2c2547e1d97f12ebe708fac693a6183bbc45.tar.xz
yuzu-4c6f2c2547e1d97f12ebe708fac693a6183bbc45.zip
input_common: Move touch and analog from button. Move udp protocol
Diffstat (limited to 'src/input_common/helpers/udp_protocol.h')
-rw-r--r--src/input_common/helpers/udp_protocol.h259
1 files changed, 259 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..1bdc9209e
--- /dev/null
+++ b/src/input_common/helpers/udp_protocol.h
@@ -0,0 +1,259 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <optional>
9#include <type_traits>
10
11#include <boost/crc.hpp>
12
13#include "common/bit_field.h"
14#include "common/swap.h"
15
16namespace InputCommon::CemuhookUDP {
17
18constexpr std::size_t MAX_PACKET_SIZE = 100;
19constexpr u16 PROTOCOL_VERSION = 1001;
20constexpr u32 CLIENT_MAGIC = 0x43555344; // DSUC (but flipped for LE)
21constexpr u32 SERVER_MAGIC = 0x53555344; // DSUS (but flipped for LE)
22
23enum class Type : u32 {
24 Version = 0x00100000,
25 PortInfo = 0x00100001,
26 PadData = 0x00100002,
27};
28
29struct Header {
30 u32_le magic{};
31 u16_le protocol_version{};
32 u16_le payload_length{};
33 u32_le crc{};
34 u32_le id{};
35 ///> In the protocol, the type of the packet is not part of the header, but its convenient to
36 ///> include in the header so the callee doesn't have to duplicate the type twice when building
37 ///> the data
38 Type type{};
39};
40static_assert(sizeof(Header) == 20, "UDP Message Header struct has wrong size");
41static_assert(std::is_trivially_copyable_v<Header>, "UDP Message Header is not trivially copyable");
42
43using MacAddress = std::array<u8, 6>;
44constexpr MacAddress EMPTY_MAC_ADDRESS = {0, 0, 0, 0, 0, 0};
45
46#pragma pack(push, 1)
47template <typename T>
48struct Message {
49 Header header{};
50 T data;
51};
52#pragma pack(pop)
53
54template <typename T>
55constexpr Type GetMessageType();
56
57namespace Request {
58
59struct Version {};
60/**
61 * Requests the server to send information about what controllers are plugged into the ports
62 * In citra's case, we only have one controller, so for simplicity's sake, we can just send a
63 * request explicitly for the first controller port and leave it at that. In the future it would be
64 * nice to make this configurable
65 */
66constexpr u32 MAX_PORTS = 4;
67struct PortInfo {
68 u32_le pad_count{}; ///> Number of ports to request data for
69 std::array<u8, MAX_PORTS> port;
70};
71static_assert(std::is_trivially_copyable_v<PortInfo>,
72 "UDP Request PortInfo is not trivially copyable");
73
74/**
75 * Request the latest pad information from the server. If the server hasn't received this message
76 * from the client in a reasonable time frame, the server will stop sending updates. The default
77 * timeout seems to be 5 seconds.
78 */
79struct PadData {
80 enum class Flags : u8 {
81 AllPorts,
82 Id,
83 Mac,
84 };
85 /// Determines which method will be used as a look up for the controller
86 Flags flags{};
87 /// Index of the port of the controller to retrieve data about
88 u8 port_id{};
89 /// Mac address of the controller to retrieve data about
90 MacAddress mac;
91};
92static_assert(sizeof(PadData) == 8, "UDP Request PadData struct has wrong size");
93static_assert(std::is_trivially_copyable_v<PadData>,
94 "UDP Request PadData is not trivially copyable");
95
96/**
97 * Creates a message with the proper header data that can be sent to the server.
98 * @param data Request body to send
99 * @param client_id ID of the udp client (usually not checked on the server)
100 */
101template <typename T>
102Message<T> Create(const T data, const u32 client_id = 0) {
103 boost::crc_32_type crc;
104 Header header{
105 CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType<T>(),
106 };
107 Message<T> message{header, data};
108 crc.process_bytes(&message, sizeof(Message<T>));
109 message.header.crc = crc.checksum();
110 return message;
111}
112} // namespace Request
113
114namespace Response {
115
116struct Version {
117 u16_le version{};
118};
119static_assert(sizeof(Version) == 2, "UDP Response Version struct has wrong size");
120static_assert(std::is_trivially_copyable_v<Version>,
121 "UDP Response Version is not trivially copyable");
122
123struct PortInfo {
124 u8 id{};
125 u8 state{};
126 u8 model{};
127 u8 connection_type{};
128 MacAddress mac;
129 u8 battery{};
130 u8 is_pad_active{};
131};
132static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size");
133static_assert(std::is_trivially_copyable_v<PortInfo>,
134 "UDP Response PortInfo is not trivially copyable");
135
136struct TouchPad {
137 u8 is_active{};
138 u8 id{};
139 u16_le x{};
140 u16_le y{};
141};
142static_assert(sizeof(TouchPad) == 6, "UDP Response TouchPad struct has wrong size ");
143
144#pragma pack(push, 1)
145struct PadData {
146 PortInfo info{};
147 u32_le packet_counter{};
148
149 u16_le digital_button{};
150 // The following union isn't trivially copyable but we don't use this input anyway.
151 // union DigitalButton {
152 // u16_le button;
153 // BitField<0, 1, u16> button_1; // Share
154 // BitField<1, 1, u16> button_2; // L3
155 // BitField<2, 1, u16> button_3; // R3
156 // BitField<3, 1, u16> button_4; // Options
157 // BitField<4, 1, u16> button_5; // Up
158 // BitField<5, 1, u16> button_6; // Right
159 // BitField<6, 1, u16> button_7; // Down
160 // BitField<7, 1, u16> button_8; // Left
161 // BitField<8, 1, u16> button_9; // L2
162 // BitField<9, 1, u16> button_10; // R2
163 // BitField<10, 1, u16> button_11; // L1
164 // BitField<11, 1, u16> button_12; // R1
165 // BitField<12, 1, u16> button_13; // Triangle
166 // BitField<13, 1, u16> button_14; // Circle
167 // BitField<14, 1, u16> button_15; // Cross
168 // BitField<15, 1, u16> button_16; // Square
169 // } digital_button;
170
171 u8 home;
172 /// If the device supports a "click" on the touchpad, this will change to 1 when a click happens
173 u8 touch_hard_press{};
174 u8 left_stick_x{};
175 u8 left_stick_y{};
176 u8 right_stick_x{};
177 u8 right_stick_y{};
178
179 struct AnalogButton {
180 u8 button_8{};
181 u8 button_7{};
182 u8 button_6{};
183 u8 button_5{};
184 u8 button_12{};
185 u8 button_11{};
186 u8 button_10{};
187 u8 button_9{};
188 u8 button_16{};
189 u8 button_15{};
190 u8 button_14{};
191 u8 button_13{};
192 } analog_button;
193
194 std::array<TouchPad, 2> touch;
195
196 u64_le motion_timestamp;
197
198 struct Accelerometer {
199 float x{};
200 float y{};
201 float z{};
202 } accel;
203
204 struct Gyroscope {
205 float pitch{};
206 float yaw{};
207 float roll{};
208 } gyro;
209};
210#pragma pack(pop)
211
212static_assert(sizeof(PadData) == 80, "UDP Response PadData struct has wrong size ");
213static_assert(std::is_trivially_copyable_v<PadData>,
214 "UDP Response PadData is not trivially copyable");
215
216static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE,
217 "UDP MAX_PACKET_SIZE is no longer larger than Message<PadData>");
218
219static_assert(sizeof(PadData::AnalogButton) == 12,
220 "UDP Response AnalogButton struct has wrong size ");
221static_assert(sizeof(PadData::Accelerometer) == 12,
222 "UDP Response Accelerometer struct has wrong size ");
223static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size ");
224
225/**
226 * Create a Response Message from the data
227 * @param data array of bytes sent from the server
228 * @return boost::none if it failed to parse or Type if it succeeded. The client can then safely
229 * copy the data into the appropriate struct for that Type
230 */
231std::optional<Type> Validate(u8* data, std::size_t size);
232
233} // namespace Response
234
235template <>
236constexpr Type GetMessageType<Request::Version>() {
237 return Type::Version;
238}
239template <>
240constexpr Type GetMessageType<Request::PortInfo>() {
241 return Type::PortInfo;
242}
243template <>
244constexpr Type GetMessageType<Request::PadData>() {
245 return Type::PadData;
246}
247template <>
248constexpr Type GetMessageType<Response::Version>() {
249 return Type::Version;
250}
251template <>
252constexpr Type GetMessageType<Response::PortInfo>() {
253 return Type::PortInfo;
254}
255template <>
256constexpr Type GetMessageType<Response::PadData>() {
257 return Type::PadData;
258}
259} // namespace InputCommon::CemuhookUDP