summaryrefslogtreecommitdiff
path: root/src/input_common/helpers/udp_protocol.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/helpers/udp_protocol.h')
-rw-r--r--src/input_common/helpers/udp_protocol.h290
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 @@
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
59enum RegisterFlags : u8 {
60 AllPads,
61 PadID,
62 PadMACAdddress,
63};
64
65struct Version {};
66/**
67 * Requests the server to send information about what controllers are plugged into the ports
68 * In citra's case, we only have one controller, so for simplicity's sake, we can just send a
69 * request explicitly for the first controller port and leave it at that. In the future it would be
70 * nice to make this configurable
71 */
72constexpr u32 MAX_PORTS = 4;
73struct PortInfo {
74 u32_le pad_count{}; ///> Number of ports to request data for
75 std::array<u8, MAX_PORTS> port;
76};
77static_assert(std::is_trivially_copyable_v<PortInfo>,
78 "UDP Request PortInfo is not trivially copyable");
79
80/**
81 * Request the latest pad information from the server. If the server hasn't received this message
82 * from the client in a reasonable time frame, the server will stop sending updates. The default
83 * timeout seems to be 5 seconds.
84 */
85struct PadData {
86 /// Determines which method will be used as a look up for the controller
87 RegisterFlags flags{};
88 /// Index of the port of the controller to retrieve data about
89 u8 port_id{};
90 /// Mac address of the controller to retrieve data about
91 MacAddress mac;
92};
93static_assert(sizeof(PadData) == 8, "UDP Request PadData struct has wrong size");
94static_assert(std::is_trivially_copyable_v<PadData>,
95 "UDP Request PadData is not trivially copyable");
96
97/**
98 * Creates a message with the proper header data that can be sent to the server.
99 * @param data Request body to send
100 * @param client_id ID of the udp client (usually not checked on the server)
101 */
102template <typename T>
103Message<T> Create(const T data, const u32 client_id = 0) {
104 boost::crc_32_type crc;
105 Header header{
106 CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType<T>(),
107 };
108 Message<T> message{header, data};
109 crc.process_bytes(&message, sizeof(Message<T>));
110 message.header.crc = crc.checksum();
111 return message;
112}
113} // namespace Request
114
115namespace Response {
116
117enum class ConnectionType : u8 {
118 None,
119 Usb,
120 Bluetooth,
121};
122
123enum class State : u8 {
124 Disconnected,
125 Reserved,
126 Connected,
127};
128
129enum class Model : u8 {
130 None,
131 PartialGyro,
132 FullGyro,
133 Generic,
134};
135
136enum class Battery : u8 {
137 None = 0x00,
138 Dying = 0x01,
139 Low = 0x02,
140 Medium = 0x03,
141 High = 0x04,
142 Full = 0x05,
143 Charging = 0xEE,
144 Charged = 0xEF,
145};
146
147struct Version {
148 u16_le version{};
149};
150static_assert(sizeof(Version) == 2, "UDP Response Version struct has wrong size");
151static_assert(std::is_trivially_copyable_v<Version>,
152 "UDP Response Version is not trivially copyable");
153
154struct PortInfo {
155 u8 id{};
156 State state{};
157 Model model{};
158 ConnectionType connection_type{};
159 MacAddress mac;
160 Battery battery{};
161 u8 is_pad_active{};
162};
163static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size");
164static_assert(std::is_trivially_copyable_v<PortInfo>,
165 "UDP Response PortInfo is not trivially copyable");
166
167struct TouchPad {
168 u8 is_active{};
169 u8 id{};
170 u16_le x{};
171 u16_le y{};
172};
173static_assert(sizeof(TouchPad) == 6, "UDP Response TouchPad struct has wrong size ");
174
175#pragma pack(push, 1)
176struct PadData {
177 PortInfo info{};
178 u32_le packet_counter{};
179
180 u16_le digital_button{};
181 // The following union isn't trivially copyable but we don't use this input anyway.
182 // union DigitalButton {
183 // u16_le button;
184 // BitField<0, 1, u16> button_1; // Share
185 // BitField<1, 1, u16> button_2; // L3
186 // BitField<2, 1, u16> button_3; // R3
187 // BitField<3, 1, u16> button_4; // Options
188 // BitField<4, 1, u16> button_5; // Up
189 // BitField<5, 1, u16> button_6; // Right
190 // BitField<6, 1, u16> button_7; // Down
191 // BitField<7, 1, u16> button_8; // Left
192 // BitField<8, 1, u16> button_9; // L2
193 // BitField<9, 1, u16> button_10; // R2
194 // BitField<10, 1, u16> button_11; // L1
195 // BitField<11, 1, u16> button_12; // R1
196 // BitField<12, 1, u16> button_13; // Triangle
197 // BitField<13, 1, u16> button_14; // Circle
198 // BitField<14, 1, u16> button_15; // Cross
199 // BitField<15, 1, u16> button_16; // Square
200 // } digital_button;
201
202 u8 home;
203 /// If the device supports a "click" on the touchpad, this will change to 1 when a click happens
204 u8 touch_hard_press{};
205 u8 left_stick_x{};
206 u8 left_stick_y{};
207 u8 right_stick_x{};
208 u8 right_stick_y{};
209
210 struct AnalogButton {
211 u8 button_dpad_left_analog{};
212 u8 button_dpad_down_analog{};
213 u8 button_dpad_right_analog{};
214 u8 button_dpad_up_analog{};
215 u8 button_square_analog{};
216 u8 button_cross_analog{};
217 u8 button_circle_analog{};
218 u8 button_triangle_analog{};
219 u8 button_r1_analog{};
220 u8 button_l1_analog{};
221 u8 trigger_r2{};
222 u8 trigger_l2{};
223 } analog_button;
224
225 std::array<TouchPad, 2> touch;
226
227 u64_le motion_timestamp;
228
229 struct Accelerometer {
230 float x{};
231 float y{};
232 float z{};
233 } accel;
234
235 struct Gyroscope {
236 float pitch{};
237 float yaw{};
238 float roll{};
239 } gyro;
240};
241#pragma pack(pop)
242
243static_assert(sizeof(PadData) == 80, "UDP Response PadData struct has wrong size ");
244static_assert(std::is_trivially_copyable_v<PadData>,
245 "UDP Response PadData is not trivially copyable");
246
247static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE,
248 "UDP MAX_PACKET_SIZE is no longer larger than Message<PadData>");
249
250static_assert(sizeof(PadData::AnalogButton) == 12,
251 "UDP Response AnalogButton struct has wrong size ");
252static_assert(sizeof(PadData::Accelerometer) == 12,
253 "UDP Response Accelerometer struct has wrong size ");
254static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size ");
255
256/**
257 * Create a Response Message from the data
258 * @param data array of bytes sent from the server
259 * @return boost::none if it failed to parse or Type if it succeeded. The client can then safely
260 * copy the data into the appropriate struct for that Type
261 */
262std::optional<Type> Validate(u8* data, std::size_t size);
263
264} // namespace Response
265
266template <>
267constexpr Type GetMessageType<Request::Version>() {
268 return Type::Version;
269}
270template <>
271constexpr Type GetMessageType<Request::PortInfo>() {
272 return Type::PortInfo;
273}
274template <>
275constexpr Type GetMessageType<Request::PadData>() {
276 return Type::PadData;
277}
278template <>
279constexpr Type GetMessageType<Response::Version>() {
280 return Type::Version;
281}
282template <>
283constexpr Type GetMessageType<Response::PortInfo>() {
284 return Type::PortInfo;
285}
286template <>
287constexpr Type GetMessageType<Response::PadData>() {
288 return Type::PadData;
289}
290} // namespace InputCommon::CemuhookUDP