|
| 1 | +#include <arpa/inet.h> |
| 2 | +#include <errno.h> |
| 3 | +#include <pthread.h> |
| 4 | +#include <stdarg.h> |
| 5 | +#include <stdio.h> |
| 6 | +#include <stdlib.h> |
| 7 | +#include <string.h> |
| 8 | +#include <sys/time.h> |
| 9 | +#include <unistd.h> |
| 10 | + |
| 11 | +#include "../lib/gamepad/command.h" |
| 12 | +#include "../lib/util.h" |
| 13 | +#include "../pipe/ports.h" |
| 14 | + |
| 15 | +static const int PORTS[] = {PORT_VID, PORT_AUD, PORT_HID, PORT_MSG, PORT_CMD}; |
| 16 | +#define PORT_COUNT 5 |
| 17 | + |
| 18 | +typedef struct listener_data_t listener_data_t; |
| 19 | +typedef void (*pkt_handler_t)(listener_data_t *ctx, const void *data, size_t size); |
| 20 | + |
| 21 | +static int RUNNING = 1; |
| 22 | + |
| 23 | +typedef struct listener_data_t { |
| 24 | + int socket; |
| 25 | + uint16_t port; |
| 26 | + pthread_t thread; |
| 27 | + int thread_started; |
| 28 | + struct timeval time; |
| 29 | + pkt_handler_t handler; |
| 30 | +} listener_data_t; |
| 31 | + |
| 32 | +void nlprint(const char *fmt, ...) |
| 33 | +{ |
| 34 | + va_list va; |
| 35 | + va_start(va, fmt); |
| 36 | + vfprintf(stderr, fmt, va); |
| 37 | + fprintf(stderr, "\n"); |
| 38 | + va_end(va); |
| 39 | +} |
| 40 | + |
| 41 | +ssize_t send_to_gamepad(int skt, const void *data, size_t size) |
| 42 | +{ |
| 43 | + struct sockaddr_in sa = {0}; |
| 44 | + sa.sin_family = AF_INET; |
| 45 | + sa.sin_addr.s_addr = inet_addr("192.168.1.11"); |
| 46 | + sa.sin_port = htons(PORT_CMD); |
| 47 | + |
| 48 | + sendto(skt, data, size, 0, (const struct sockaddr *) &sa, sizeof(sa)); |
| 49 | +} |
| 50 | + |
| 51 | +int create_socket(uint16_t port) |
| 52 | +{ |
| 53 | + struct sockaddr_in sa = {0}; |
| 54 | + sa.sin_family = AF_INET; |
| 55 | + sa.sin_addr.s_addr = INADDR_ANY; |
| 56 | + sa.sin_port = htons(port - 100); |
| 57 | + |
| 58 | + int skt = socket(AF_INET, SOCK_DGRAM, 0); |
| 59 | + if (skt == -1) { |
| 60 | + nlprint("FAILED TO CREATE SOCKET FOR PORT %u: %i", port, errno); |
| 61 | + return -1; |
| 62 | + } |
| 63 | + |
| 64 | + // Timeout after 250ms |
| 65 | + struct timeval tv = {0}; |
| 66 | + tv.tv_usec = 250000; |
| 67 | + setsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); |
| 68 | + |
| 69 | + int ret = bind(skt, (const struct sockaddr *) &sa, sizeof(sa)); |
| 70 | + if (ret == -1) { |
| 71 | + nlprint("FAILED TO BIND SOCKET FOR PORT %u: %i", port, errno); |
| 72 | + close(skt); |
| 73 | + return -1; |
| 74 | + } |
| 75 | + |
| 76 | + return skt; |
| 77 | +} |
| 78 | + |
| 79 | +void printstart(listener_data_t *ctx) |
| 80 | +{ |
| 81 | + struct timeval tv; |
| 82 | + gettimeofday(&tv, 0); |
| 83 | + |
| 84 | + long time = (tv.tv_sec - ctx->time.tv_sec) * 1000000 + (tv.tv_usec - ctx->time.tv_usec); |
| 85 | + |
| 86 | + fprintf(stderr, "[%li.%06li] [%u] ", time / 1000000, time % 1000000, ctx->port); |
| 87 | +} |
| 88 | + |
| 89 | +void default_handler(listener_data_t *ctx, const void *data, size_t size) |
| 90 | +{ |
| 91 | + printstart(ctx); |
| 92 | + nlprint("Received packet of size %zi", size); |
| 93 | +} |
| 94 | + |
| 95 | +void hid_handler(listener_data_t *ctx, const void *data, size_t size) |
| 96 | +{ |
| 97 | + static int count = 0; |
| 98 | + const int threshold = 180; |
| 99 | + count++; |
| 100 | + if (count % threshold == 0) { |
| 101 | + // printstart(ctx); |
| 102 | + // nlprint("%i input events", threshold); |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +static pthread_mutex_t cmd_mutex = PTHREAD_MUTEX_INITIALIZER; |
| 107 | +static UvcUacPacket uvc_uac_pkt; |
| 108 | +static int cmd_seq_id = 0; |
| 109 | +void *send_uvc_uac_pkt_loop(void *arg) |
| 110 | +{ |
| 111 | + listener_data_t *data = (listener_data_t *) arg; |
| 112 | + |
| 113 | + uvc_uac_pkt.cmd_header.packet_type = PACKET_TYPE_REQUEST; |
| 114 | + uvc_uac_pkt.cmd_header.query_type = CMD_UVC_UAC; |
| 115 | + uvc_uac_pkt.cmd_header.payload_size = sizeof(uvc_uac_pkt.uac_uvc); |
| 116 | + |
| 117 | + uvc_uac_pkt.uac_uvc.f1 = 1; |
| 118 | + uvc_uac_pkt.uac_uvc.unknown_0 = 0; |
| 119 | + uvc_uac_pkt.uac_uvc.f3 = 0; |
| 120 | + uvc_uac_pkt.uac_uvc.mic_enable = 0; |
| 121 | + uvc_uac_pkt.uac_uvc.mic_mute = 0; |
| 122 | + uvc_uac_pkt.uac_uvc.mic_volume = 0x1600; |
| 123 | + uvc_uac_pkt.uac_uvc.mic_volume_2 = 0x1900; |
| 124 | + uvc_uac_pkt.uac_uvc.unknown_A = 0; |
| 125 | + uvc_uac_pkt.uac_uvc.unknown_B = 0; |
| 126 | + uvc_uac_pkt.uac_uvc.mic_freq = 16000; |
| 127 | + uvc_uac_pkt.uac_uvc.cam_enable = 0; |
| 128 | + uvc_uac_pkt.uac_uvc.cam_power = 0; |
| 129 | + uvc_uac_pkt.uac_uvc.cam_power_freq = 0; |
| 130 | + uvc_uac_pkt.uac_uvc.cam_auto_expo = 1; |
| 131 | + uvc_uac_pkt.uac_uvc.cam_expo_absolute = 0x009E0200; |
| 132 | + uvc_uac_pkt.uac_uvc.cam_brightness = 0; |
| 133 | + uvc_uac_pkt.uac_uvc.cam_contrast = 0; |
| 134 | + uvc_uac_pkt.uac_uvc.cam_gain = 0; |
| 135 | + uvc_uac_pkt.uac_uvc.cam_hue = 0x0070; |
| 136 | + uvc_uac_pkt.uac_uvc.cam_saturation = 0; |
| 137 | + uvc_uac_pkt.uac_uvc.cam_sharpness = 0x4040; |
| 138 | + uvc_uac_pkt.uac_uvc.cam_gamma = 3; |
| 139 | + uvc_uac_pkt.uac_uvc.cam_key_frame = 0x2D; |
| 140 | + uvc_uac_pkt.uac_uvc.cam_white_balance_auto = 0; |
| 141 | + uvc_uac_pkt.uac_uvc.cam_white_balance = 0x00800100; |
| 142 | + uvc_uac_pkt.uac_uvc.cam_multiplier = 0x40; |
| 143 | + uvc_uac_pkt.uac_uvc.cam_multiplier_limit = 0; |
| 144 | + |
| 145 | + while (RUNNING) { |
| 146 | + pthread_mutex_lock(&cmd_mutex); |
| 147 | + uvc_uac_pkt.cmd_header.seq_id = cmd_seq_id; |
| 148 | + cmd_seq_id++; |
| 149 | + pthread_mutex_unlock(&cmd_mutex); |
| 150 | + |
| 151 | + nlprint("Sending UAC/UVC packet (seq ID: %X)...", uvc_uac_pkt.cmd_header.seq_id); |
| 152 | + send_to_gamepad(data->socket, &uvc_uac_pkt, sizeof(uvc_uac_pkt)); |
| 153 | + |
| 154 | + sleep(1); |
| 155 | + } |
| 156 | +} |
| 157 | + |
| 158 | +void cmd_handler(listener_data_t *ctx, const void *data, size_t size) |
| 159 | +{ |
| 160 | + static long last_cmd = 0; |
| 161 | + |
| 162 | + struct timeval tv; |
| 163 | + gettimeofday(&tv, 0); |
| 164 | + |
| 165 | + long now = tv.tv_sec * 1000000 + tv.tv_usec; |
| 166 | + long delta = now - last_cmd; |
| 167 | + last_cmd = now; |
| 168 | + |
| 169 | + CmdHeader *hdr = (CmdHeader *) data; |
| 170 | + const char *packet_type_str = ""; |
| 171 | + switch (hdr->packet_type) { |
| 172 | + case PACKET_TYPE_REQUEST: packet_type_str = "PACKET_TYPE_REQUEST"; break; |
| 173 | + case PACKET_TYPE_REQUEST_ACK: packet_type_str = "PACKET_TYPE_REQUEST_ACK"; break; |
| 174 | + case PACKET_TYPE_RESPONSE: packet_type_str = "PACKET_TYPE_RESPONSE"; break; |
| 175 | + case PACKET_TYPE_RESPONSE_ACK: packet_type_str = "PACKET_TYPE_RESPONSE_ACK"; break; |
| 176 | + } |
| 177 | + nlprint("[CMD] packet_type = %s, query_type = %X, payload_size = %X, seq_id = %x, delta = %li us", packet_type_str, hdr->query_type, hdr->payload_size, hdr->seq_id, delta); |
| 178 | + if (hdr->payload_size > 0) { |
| 179 | + switch (hdr->query_type) { |
| 180 | + case CMD_GENERIC: |
| 181 | + { |
| 182 | + GenericPacket *gen = (GenericPacket *) data; |
| 183 | + nlprint("[GEN] version: %x, flags: %x, service ID: %u, method ID: %u, error code: %x, ids: %x %x %x, payload_size: %x", |
| 184 | + gen->generic_cmd_header.version, |
| 185 | + gen->generic_cmd_header.flags, |
| 186 | + gen->generic_cmd_header.service_id, |
| 187 | + gen->generic_cmd_header.method_id, |
| 188 | + gen->generic_cmd_header.error_code, |
| 189 | + gen->generic_cmd_header.ids[0], |
| 190 | + gen->generic_cmd_header.ids[1], |
| 191 | + gen->generic_cmd_header.ids[2], |
| 192 | + gen->generic_cmd_header.payload_size); |
| 193 | + break; |
| 194 | + } |
| 195 | + case CMD_UVC_UAC: |
| 196 | + { |
| 197 | + // UvcUacPacket *uvc = (UvcUacPacket *) data; |
| 198 | + // nlprint("[UVC] f1: %x, unknown_0: %x, f3: %x, mic_enable: %x, mic_mute: %x, mic_volume: %x, mic_volume_2: %x, unknown_A: %x, unknown_B: %x, mic_freq: %u", |
| 199 | + // uvc->uac_uvc.f1, |
| 200 | + // uvc->uac_uvc.unknown_0, |
| 201 | + // uvc->uac_uvc.f3, |
| 202 | + // uvc->uac_uvc.mic_enable, |
| 203 | + // uvc->uac_uvc.mic_mute, |
| 204 | + // uvc->uac_uvc.mic_volume, |
| 205 | + // uvc->uac_uvc.mic_volume_2, |
| 206 | + // uvc->uac_uvc.unknown_A, |
| 207 | + // uvc->uac_uvc.unknown_B, |
| 208 | + // uvc->uac_uvc.mic_freq); |
| 209 | + print_hex(((const char *) data) + sizeof(CmdHeader), hdr->payload_size); |
| 210 | + printf("\n"); |
| 211 | + break; |
| 212 | + } |
| 213 | + default: |
| 214 | + { |
| 215 | + print_hex(((const char *) data) + sizeof(CmdHeader), hdr->payload_size); |
| 216 | + printf("\n"); |
| 217 | + break; |
| 218 | + } |
| 219 | + } |
| 220 | + } |
| 221 | + |
| 222 | + if (hdr->packet_type == PACKET_TYPE_REQUEST || hdr->packet_type == PACKET_TYPE_RESPONSE) { |
| 223 | + CmdHeader ack = create_ack_packet(hdr); |
| 224 | + send_to_gamepad(ctx->socket, &ack, sizeof(ack)); |
| 225 | + } |
| 226 | +} |
| 227 | + |
| 228 | +void *listen_socket(void *arg) |
| 229 | +{ |
| 230 | + listener_data_t *data = (listener_data_t *) arg; |
| 231 | + char buf[4096]; |
| 232 | + |
| 233 | + while (RUNNING) { |
| 234 | + ssize_t r = recv(data->socket, buf, sizeof(buf), 0); |
| 235 | + if (r <= 0) { |
| 236 | + continue; |
| 237 | + } else { |
| 238 | + if (data->handler) { |
| 239 | + data->handler(data, buf, r); |
| 240 | + } |
| 241 | + } |
| 242 | + } |
| 243 | +} |
| 244 | + |
| 245 | +int main(int argc, const char **argv) |
| 246 | +{ |
| 247 | + listener_data_t data[PORT_COUNT]; |
| 248 | + |
| 249 | + pthread_t uvc_uac_loop_thread; |
| 250 | + int uvc_uac_loop_thread_created = 0; |
| 251 | + |
| 252 | + struct timeval start; |
| 253 | + gettimeofday(&start, 0); |
| 254 | + |
| 255 | + // Set up defaults |
| 256 | + for (int i = 0; i < PORT_COUNT; i++) { |
| 257 | + listener_data_t *d = &data[i]; |
| 258 | + d->port = PORTS[i]; |
| 259 | + d->socket = -1; |
| 260 | + d->thread_started = 0; |
| 261 | + d->time = start; |
| 262 | + d->handler = default_handler; |
| 263 | + } |
| 264 | + |
| 265 | + data[2].handler = hid_handler; |
| 266 | + data[4].handler = cmd_handler; |
| 267 | + |
| 268 | + int ready = 1; |
| 269 | + |
| 270 | + // Try to set up sockets |
| 271 | + for (int i = 0; i < PORT_COUNT; i++) { |
| 272 | + listener_data_t *d = &data[i]; |
| 273 | + int port = d->port; |
| 274 | + int skt = create_socket(port); |
| 275 | + if (skt == -1) { |
| 276 | + ready = 0; |
| 277 | + break; |
| 278 | + } |
| 279 | + |
| 280 | + d->socket = skt; |
| 281 | + } |
| 282 | + |
| 283 | + // If all sockets set up, start listening threads |
| 284 | + if (ready) { |
| 285 | + for (int i = 0; i < PORT_COUNT; i++) { |
| 286 | + listener_data_t *d = &data[i]; |
| 287 | + if (pthread_create(&d->thread, 0, listen_socket, d) == 0) { |
| 288 | + d->thread_started = 1; |
| 289 | + } else { |
| 290 | + ready = 0; |
| 291 | + break; |
| 292 | + } |
| 293 | + } |
| 294 | + } |
| 295 | + |
| 296 | + if (ready) { |
| 297 | + // Start ancillary threads |
| 298 | + if (pthread_create(&uvc_uac_loop_thread, 0, send_uvc_uac_pkt_loop, &data[4]) == 0) { |
| 299 | + uvc_uac_loop_thread_created = 1; |
| 300 | + } |
| 301 | + |
| 302 | + // Wait for close |
| 303 | + nlprint("Running, type 'exit' or 'quit' or 'bye' to leave"); |
| 304 | + while (RUNNING) { |
| 305 | + char *d = 0; |
| 306 | + size_t n; |
| 307 | + if (getline(&d, &n, stdin) >= 0) { |
| 308 | + for (size_t i = 0; i < n; i++) { |
| 309 | + if (d[i] == '\n') { |
| 310 | + d[i] = '\0'; |
| 311 | + break; |
| 312 | + } |
| 313 | + } |
| 314 | + static uint16_t seq_id = 0; |
| 315 | + if (!strcmp("exit", d) || !strcmp("quit", d) || !strcmp("bye", d)) { |
| 316 | + RUNNING = 0; |
| 317 | + } else if (!strcmp("cmd", d)) { |
| 318 | + GenericPacket pkt; |
| 319 | + pkt.cmd_header.packet_type = PACKET_TYPE_REQUEST; |
| 320 | + pkt.cmd_header.query_type = CMD_GENERIC; |
| 321 | + pkt.cmd_header.payload_size = sizeof(GenericCmdHeader); |
| 322 | + pkt.cmd_header.seq_id = seq_id; |
| 323 | + seq_id++; |
| 324 | + |
| 325 | + memset(&pkt.generic_cmd_header, 0, sizeof(pkt.generic_cmd_header)); |
| 326 | + pkt.generic_cmd_header.magic_0x7E = 0x7E; |
| 327 | + pkt.generic_cmd_header.version = 1; |
| 328 | + pkt.generic_cmd_header.flags = 0x40; |
| 329 | + pkt.generic_cmd_header.service_id = SERVICE_ID_PERIPHERAL; |
| 330 | + pkt.generic_cmd_header.method_id = METHOD_ID_PERIPHERAL_EEPROM; |
| 331 | + |
| 332 | + nlprint("Sending generic packet (seq ID: %X)...", pkt.cmd_header.seq_id); |
| 333 | + |
| 334 | + send_to_gamepad(data[4].socket, &pkt, sizeof(CmdHeader) + sizeof(GenericCmdHeader)); |
| 335 | + } |
| 336 | + } |
| 337 | + free(d); |
| 338 | + } |
| 339 | + } |
| 340 | + |
| 341 | + // Join all threads and close all sockets |
| 342 | + RUNNING = 0; |
| 343 | + for (int i = 0; i < PORT_COUNT; i++) { |
| 344 | + listener_data_t *d = &data[i]; |
| 345 | + if (d->thread_started) { |
| 346 | + pthread_join(d->thread, 0); |
| 347 | + } |
| 348 | + |
| 349 | + if (d->socket != -1) { |
| 350 | + close(d->socket); |
| 351 | + } |
| 352 | + } |
| 353 | + |
| 354 | + if (uvc_uac_loop_thread_created) { |
| 355 | + pthread_join(uvc_uac_loop_thread, 0); |
| 356 | + } |
| 357 | + |
| 358 | + return 0; |
| 359 | +} |
0 commit comments