diff --git a/Makefile b/Makefile index 5599e40..0ee8cb4 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ Q=@ CC=gcc GCCCFLAGS=-Wno-format-truncation -CFLAGS=-std=c11 -pedantic -g -Wall -pthread -D_GNU_SOURCE +CFLAGS=-std=c11 -pedantic -g -Wall -pthread -D_GNU_SOURCE -DVERBOSE LDFLAGS=-lm BUILD_DIR=./objects diff --git a/README.md b/README.md index 66faf09..6db19c5 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ and a client with this one: { "controllers": [ { - "tag": "Controller" + "tag": ["Controller"] } ] } @@ -107,8 +107,8 @@ The client configuration specifies what devices the client wants as well as unde { "controllers": [ { - // (required) Tag of the device to request - "tag": "Joystick", + // (required) Accepted tags of the device to request + "tag": ["Joystick"], // (default: 6969) Vendor code for the virtual device, expects a 4 long hex string "vendor": "dead", // (default: 0420) Product code for the virtual device, expects a 4 long hex string diff --git a/client.c b/client.c index 0b83afa..d053d85 100644 --- a/client.c +++ b/client.c @@ -72,7 +72,7 @@ static const JSONAdapter ControllerStateAdapter = { }; static const JSONPropertyAdapter ControllerAdapterProps[] = { - {".tag", &StringAdapter, offsetof(ClientController, tag), default_to_null, NULL }, + {".tag[]", &StringAdapter, offsetof(ClientController, tags), default_to_null, NULL }, {".vendor", &StringAdapter, offsetof(ClientController, device_vendor), default_vendor, tsf_hex_to_i32}, {".product", &StringAdapter, offsetof(ClientController, device_product), default_product, tsf_hex_to_i32}, {".name", &StringAdapter, offsetof(ClientController, device_name), default_name, NULL }, @@ -94,6 +94,26 @@ static const JSONAdapter ConfigAdapter = { .size = sizeof(ClientConfig), }; +// Print the current config, for debugging purposes +static void print_config() { + printf("CLIENT: Config\n"); + printf(" fifo_path: %s\n", config.fifo_path); + printf(" retry_delay: %fs\n", timespec_to_double(&config.retry_delay)); + printf(" controllers: \n"); + for (size_t i = 0; i < config.controller_count; i++) { + ClientController *ctr = &config.controllers[i]; + printf(" - tags: ['%s'", ctr->tags[0]); + for (size_t j = 1; j < ctr->tag_count; j++) { + printf(", '%s'", ctr->tags[j]); + } + printf("]\n"); + printf(" name: %s\n", ctr->device_name); + printf(" vendor: %04x\n", ctr->device_vendor); + printf(" product: %04x\n", ctr->device_product); + } + printf("\n"); +} + void destroy_devices(void) { for (int i = 0; i < config.controller_count; i++) { int fd = *(int *)vec_get(&devices_fd, i); @@ -351,14 +371,20 @@ void setup_server(char *address, uint16_t port) { } void build_device_request(void) { - char **tags = malloc(config.controller_count * sizeof(char *)); + TagList * reqs = malloc(config.controller_count * sizeof(TagList *)); for (int i = 0; i < config.controller_count; i++) { - tags[i] = config.controllers[i].tag; + TagList * req = &reqs[i]; + req->count = config.controllers[i].tag_count; + req->tags = malloc(req->count * sizeof(char *)); + + for (int j = 0; j < req->count; j++) { + req->tags[j] = config.controllers[i].tags[j]; + } } device_request.code = Request; device_request.request_count = config.controller_count; - device_request.requests = tags; + device_request.requests = reqs; } void client_run(char *address, uint16_t port, char *config_path) { @@ -381,6 +407,10 @@ void client_run(char *address, uint16_t port, char *config_path) { json_adapt(jbuf, &ConfigAdapter, &config); +#ifdef VERBOSE + print_config(); +#endif + free(cbuf); fclose(configfd); } diff --git a/client.h b/client.h index ec2d7f9..437795f 100644 --- a/client.h +++ b/client.h @@ -7,7 +7,8 @@ void client_run(char *address, uint16_t port, char *config_path); typedef struct { - char *tag; + char **tags; + size_t tag_count; int32_t device_vendor; int32_t device_product; char *device_name; diff --git a/hid.c b/hid.c index 5233451..d7d306b 100644 --- a/hid.c +++ b/hid.c @@ -172,10 +172,21 @@ void poll_devices_init(void) { available_devices = vec_of(Controller *); } +// Check if tag match any of the tags specified in the tags array (of length tag_count) +bool match_tags(char *tag, char **tags, size_t tag_count) { + for (int i = 0; i < tag_count; i++) { + if (strcmp(tag, tags[i]) == 0) { + return true; + } + } + + return false; +} + // Block to get a device, this is thread safe // stop: additional condition to check before doing anything, // if the condition is ever found to be true the function will return immediately with a NULL pointer. -Controller *get_device(char *tag, bool *stop) { +Controller *get_device(char **tags, size_t tag_count, bool *stop) { // Check if we can get one right away pthread_mutex_lock(&devices_mutex); @@ -187,7 +198,7 @@ Controller *get_device(char *tag, bool *stop) { for (int i = 0; i < available_devices.len; i++) { Controller *c = *(Controller **)vec_get(&available_devices, i); - if (strcmp(c->ctr.tag, tag) == 0) { + if (match_tags(c->ctr.tag, tags, tag_count)) { vec_remove(&available_devices, i, NULL); pthread_mutex_unlock(&devices_mutex); return c; @@ -196,7 +207,7 @@ Controller *get_device(char *tag, bool *stop) { for (int i = 0; i < cloneable_devices.len; i++) { Controller *c = *(Controller **)vec_get(&cloneable_devices, i); - if (strcmp(c->ctr.tag, tag) == 0) { + if (match_tags(c->ctr.tag, tags, tag_count)) { pthread_mutex_unlock(&devices_mutex); return c; } @@ -229,7 +240,7 @@ void forget_device(Controller *c) { pthread_mutex_lock(&devices_mutex); for (int i = 0; i < cloneable_devices.len; i++) { Controller *d = *(Controller **)vec_get(&cloneable_devices, i); - if (d->dev.id == c->dev.id) { + if (d->dev.uniq == c->dev.uniq) { vec_remove(&cloneable_devices, i, NULL); break; } @@ -239,10 +250,10 @@ void forget_device(Controller *c) { // Free the name if it was allocated if (c->dev.name != NULL && c->dev.name != DEVICE_DEFAULT_NAME) { - printf("HID: Forgetting device '%s' (%016lx)\n", c->dev.name, c->dev.id); + printf("HID: Forgetting device '%s' (%016lx)\n", c->dev.name, c->dev.uniq); free(c->dev.name); } else { - printf("HID: Forgetting device %016lx\n", c->dev.id); + printf("HID: Forgetting device %016lx\n", c->dev.uniq); } // try to close the file descriptor, they may be already closed if the device was unpugged. @@ -253,7 +264,7 @@ void forget_device(Controller *c) { pthread_mutex_lock(&known_devices_mutex); for (int i = 0; i < known_devices.len; i++) { Controller *d = vec_get(&known_devices, i); - if (d->dev.id == c->dev.id) { + if (d->dev.uniq == c->dev.uniq) { vec_remove(&known_devices, i, NULL); break; } @@ -343,7 +354,7 @@ void poll_devices(void) { pthread_mutex_lock(&known_devices_mutex); for (int i = 0; i < known_devices.len; i++) { Controller *c = vec_get(&known_devices, i); - if (c->dev.id == dev.id) { + if (c->dev.uniq == dev.uniq) { found = true; break; } @@ -412,7 +423,7 @@ void poll_devices(void) { // Pointer to the device in known_devices Controller *p = vec_get(&known_devices, index); - printf("HID: New device, %s [%s] (%s: %016lx)\n", name, ctr->tag, input->d_name, dev.id); + printf("HID: New device, %s [%s] (%s: %016lx)\n", name, ctr->tag, input->d_name, dev.uniq); if (ctr->duplicate) { pthread_mutex_lock(&devices_mutex); @@ -441,11 +452,11 @@ void poll_devices(void) { // "Execute" a MessageControllerState: set the led color, rumble and flash using the hidraw interface (Dualshock 4 only) void apply_controller_state(Controller *c, MessageControllerState *state) { if (c->ctr.ps4_hidraw && c->dev.hidraw < 0) { - printf("HID: Trying to apply controller state on incompatible device (%016lx)\n", c->dev.id); + printf("HID: Trying to apply controller state on incompatible device (%016lx)\n", c->dev.uniq); return; } - printf("HID: (%016lx) Controller state: #%02x%02x%02x flash: (%d, %d) rumble: (%d, %d)\n", c->dev.id, state->led[0], + printf("HID: (%016lx) Controller state: #%02x%02x%02x flash: (%d, %d) rumble: (%d, %d)\n", c->dev.uniq, state->led[0], state->led[1], state->led[2], state->flash_on, state->flash_off, state->small_rumble, state->big_rumble); uint8_t buf[32] = {0x05, 0xff, 0x00, 0x00}; diff --git a/hid.h b/hid.h index 89d5490..424caf2 100644 --- a/hid.h +++ b/hid.h @@ -6,6 +6,7 @@ #include #include +#include #include // Unique identifier for devices (provided by linux), May be the mac address @@ -40,7 +41,7 @@ typedef struct { void *hid_thread(void *arg); void return_device(Controller *c); void forget_device(Controller *c); -Controller *get_device(char *tag, bool *stop); +Controller *get_device(char **tags, size_t tag_count, bool *stop); void apply_controller_state(Controller *c, MessageControllerState *state); #endif diff --git a/json.c b/json.c index 4d54324..a42eb79 100644 --- a/json.c +++ b/json.c @@ -732,6 +732,12 @@ static void json_adapt_priv(uint8_t **buf, const JSONAdapter *adapter, void *ptr char *path) { JSONHeader *header = (JSONHeader *)*buf; + if (header->type == Array) { + path[0] = '['; + path[1] = ']'; + path[2] = '\0'; + } + if (is_primitive(adapter)) { // The type of a primitive adapter is stored in prop_count JSONType type = adapter->prop_count; @@ -747,17 +753,21 @@ static void json_adapt_priv(uint8_t **buf, const JSONAdapter *adapter, void *ptr if (type == Boolean) { *(bool *)ptr = *(uint64_t *)(*buf) == 1; + *buf += 8; } else if (type == Number) { *(double *)ptr = *(double *)(*buf); + *buf += 8; } else if (type == String) { - char *v = malloc(header->len + 1); + size_t len = header->len; + char *v = malloc(header->len + 1); strncpy(v, (char *)(*buf), header->len); v[header->len] = '\0'; *(char **)ptr = v; + *buf += align_8(len); } else { printf("JSON: Unknown or illegal primitive adapter of type %s\n", json_type_name(type)); @@ -771,12 +781,6 @@ static void json_adapt_priv(uint8_t **buf, const JSONAdapter *adapter, void *ptr json_adapt_set_defaults(adapter, ptr); } - if (header->type == Array) { - path[0] = '['; - path[1] = ']'; - path[2] = '\0'; - } - uint8_t buffer_small[64]; for (int i = 0; i < adapter->prop_count; i++) { @@ -787,15 +791,17 @@ static void json_adapt_priv(uint8_t **buf, const JSONAdapter *adapter, void *ptr if (header->type == Array) { uint8_t *array_buf = *buf + sizeof(JSONHeader); uint8_t *end = array_buf + header->len; - size_t len; + + // Count length of array + size_t len; for (len = 0; array_buf < end; len++) { array_buf += align_8(((JSONHeader *)array_buf)->len); array_buf += sizeof(JSONHeader); }; uint8_t *array_ptr = malloc(len * size); + array_buf = *buf + sizeof(JSONHeader); - array_buf = *buf + sizeof(JSONHeader); for (size_t index = 0; index < len; index++) { path[0] = '.'; path[1] = '\0'; diff --git a/net.c b/net.c index adf3e6d..ca78b92 100644 --- a/net.c +++ b/net.c @@ -140,8 +140,8 @@ int msg_deserialize(const uint8_t *buf, size_t len, Message *restrict dst) { dst->request.request_count = *(uint16_t *)(buf + 2); buf += 4; // 1 bytes for code, 1 byte for padding and 2 bytes for count - int count = dst->request.request_count; - char **tags = malloc(count * sizeof(char *)); + int count = dst->request.request_count; + TagList *reqs = malloc(count * sizeof(TagList)); // The length of the message, will be updated as we read more. int expected_len = 3; @@ -151,25 +151,37 @@ int msg_deserialize(const uint8_t *buf, size_t len, Message *restrict dst) { return -1; } - uint16_t str_len = *(uint16_t *)buf; + TagList *tags = &reqs[i]; + tags->count = *(uint16_t *)buf; + tags->tags = malloc(tags->count * sizeof(char *)); buf += 2; - expected_len += align_2(str_len); - if (len < expected_len) { - return -1; + for (int j = 0; j < tags->count; j++) { + expected_len += 2; + if (len < expected_len) { + return -1; + } + + uint16_t str_len = *(uint16_t *)buf; + buf += 2; + + expected_len += align_2(str_len); + if (len < expected_len) { + return -1; + } + + char *str = malloc(str_len + 1); + str[str_len] = '\0'; + + strncpy(str, (char *)buf, str_len); + + tags->tags[j] = str; + + buf += align_2(str_len); } - - char *str = malloc(str_len + 1); - str[str_len] = '\0'; - - strncpy(str, (char *)buf, str_len); - - tags[i] = str; - - buf += align_2(str_len); } - dst->request.requests = tags; + dst->request.requests = reqs; size = expected_len + 1; break; } @@ -313,31 +325,40 @@ int msg_serialize(uint8_t *restrict buf, size_t len, const Message *msg) { if (len < expected_len) return -1; - buf[0] = (uint8_t)msg->code; - buf16 = (uint16_t *)(buf + 2); - buf16[0] = msg->request.request_count; - - buf += 4; - buf16++; + buf[0] = (uint8_t)msg->code; + buf += 2; + *(uint16_t *)buf = msg->request.request_count; + buf += 2; for (int i = 0; i < msg->request.request_count; i++) { - int str_len = strlen(msg->request.requests[i]); - int byte_len = align_2(str_len); - *buf16++ = str_len; - buf = (uint8_t *)buf16; - expected_len += byte_len + 2; - if (len < expected_len) { - return -1; + uint16_t tag_count = msg->request.requests[i].count; + char **tags = msg->request.requests[i].tags; + + *(uint16_t *)buf = tag_count; + + buf += 2; + + for (int j = 0; j < tag_count; j++) { + printf("about to strlen\n"); + int str_len = strlen(tags[j]); + printf("len : %i\n", str_len); + int byte_len = align_2(str_len); + + expected_len += 2 + byte_len; + if (len < expected_len) { + return -1; + } + + *(uint16_t *)buf = str_len; + buf += 2; + + strncpy((char *)buf, tags[j], str_len); + buf += byte_len; } - - strncpy((char *)buf, msg->request.requests[i], str_len); - buf += byte_len; - // Buf has to be aligned here since byte_len is two aligned and we started off two aligned - buf16 = (uint16_t *)buf; } - size = expected_len; + size = expected_len + 1; break; } case DeviceDestroy: @@ -367,7 +388,10 @@ int msg_serialize(uint8_t *restrict buf, size_t len, const Message *msg) { void msg_free(Message *msg) { if (msg->code == Request) { for (int i = 0; i < msg->request.request_count; i++) { - free(msg->request.requests[i]); + for (int j = 0; j < msg->request.requests[i].count; j++) { + free(msg->request.requests[i].tags[j]); + } + free(msg->request.requests[i].tags); } free(msg->request.requests); } diff --git a/net.h b/net.h index 181fd86..3088d18 100644 --- a/net.h +++ b/net.h @@ -79,12 +79,18 @@ typedef struct { #define MSS_CONTROLLER_STATE 10 typedef struct { - MessageCode code; + char **tags; + uint16_t count; +} TagList; - char **requests; +typedef struct { + MessageCode code; + // + 1 byte of padding + + TagList *requests; uint16_t request_count; } MessageRequest; -#define MSS_REQUEST(count) (2 + 2 * count) +#define MSS_REQUEST(count) (2 + 2 * count + 1) typedef struct { MessageCode code; diff --git a/server.c b/server.c index 187069d..c3d4af1 100644 --- a/server.c +++ b/server.c @@ -7,6 +7,7 @@ #include "util.h" #include "vec.h" +#include #include #include #include @@ -31,7 +32,8 @@ struct Connection { struct DeviceThreadArgs { int index; - char *tag; + char **tags; + size_t tag_count; Controller **controller; struct Connection *conn; }; @@ -86,6 +88,34 @@ static sigset_t empty_sigset; if (sigaction(sig, &(struct sigaction){{SIG_IGN}}, NULL) != 0) \ printf("SERVER: can't ignore " #sig ".\n") +static void print_config() { + printf("SERVER: Config\n"); + printf(" retry_delay: %fs\n", (double)(config.request_timeout) / 1000.0); + printf(" poll_interval: %fs\n", timespec_to_double(&config.poll_interval)); + printf(" controllers:\n"); + for (size_t i = 0; i < config.controller_count; i++) { + ServerConfigController *ctr = &config.controllers[i]; + printf(" - tag: '%s'\n", ctr->tag); + printf(" duplicate: %s\n", ctr->duplicate ? "true" : "false"); + printf(" ps4_hidraw: %s\n", ctr->ps4_hidraw ? "true" : "false"); + if (ctr->filter.name != NULL || ctr->filter.js != false || ctr->filter.uniq != 0 || ctr->filter.vendor >= 0 || + ctr->filter.product >= 0) { + printf(" filter:\n"); + if (ctr->filter.name != NULL) + printf(" name: '%s'\n", ctr->filter.name); + if (ctr->filter.js) + printf(" has_js\n"); + if (ctr->filter.uniq != 0) + printf(" uniq: %016lx\n", ctr->filter.uniq); + if (ctr->filter.vendor >= 0) + printf(" vendor: %04x\n", ctr->filter.vendor); + if (ctr->filter.product >= 0) + printf(" product: %04x\n", ctr->filter.product); + } + } + printf("\n"); +} + void device_thread_exit(int _sig) { struct DeviceThreadArgs *args = pthread_getspecific(device_args_key); printf("CONN(%d): [%d] exiting\n", args->conn->id, args->index); @@ -95,7 +125,11 @@ void device_thread_exit(int _sig) { return_device(ctr); } - free(args->tag); + for (int i = 0; i < args->tag_count; i++) { + free(args->tags[i]); + } + + free(args->tags); free(args); pthread_exit(NULL); } @@ -113,7 +147,7 @@ void *device_thread(void *args_) { while (true) { *args->controller = NULL; - Controller *ctr = get_device(args->tag, &args->conn->closed); + Controller *ctr = get_device(args->tags, args->tag_count, &args->conn->closed); if (ctr == NULL) { break; } @@ -121,7 +155,7 @@ void *device_thread(void *args_) { dev_info = ctr->dev.device_info; dev_info.index = args->index; - printf("CONN(%d): [%d] Found suitable [%s] device: '%s' (%016lx)\n", args->conn->id, args->index, args->tag, + printf("CONN(%d): [%d] Found suitable [%s] device: '%s' (%016lx)\n", args->conn->id, args->index, ctr->ctr.tag, ctr->dev.name, ctr->dev.id); // Send over device info @@ -319,10 +353,16 @@ void *server_handle_conn(void *args_) { vec_push(&device_controllers, &ctr); struct DeviceThreadArgs *dev_args = malloc(sizeof(struct DeviceThreadArgs)); - dev_args->controller = vec_get(&device_controllers, index); - dev_args->tag = strdup(msg.request.requests[i]); - dev_args->conn = args; - dev_args->index = index; + + dev_args->controller = vec_get(&device_controllers, index); + dev_args->tag_count = msg.request.requests[i].count; + dev_args->tags = malloc(dev_args->tag_count * sizeof(char *)); + dev_args->conn = args; + dev_args->index = index; + + for (int j = 0; j < dev_args->tag_count; j++) { + dev_args->tags[j] = strdup(msg.request.requests[i].tags[j]); + } pthread_t thread; pthread_create(&thread, NULL, device_thread, dev_args); @@ -382,6 +422,10 @@ void server_run(uint16_t port, char *config_path) { json_adapt(jbuf, &ConfigAdapter, &config); +#ifdef VERBOSE + print_config(); +#endif + free(cbuf); fclose(configfd); } diff --git a/util.c b/util.c index 259b31d..66aac4f 100644 --- a/util.c +++ b/util.c @@ -27,6 +27,12 @@ uint16_t parse_port(const char *str) { return n; } +double timespec_to_double(struct timespec *ts) { + double secs = ts->tv_sec; + secs += ts->tv_nsec / 1000000000; + return secs; +} + uint8_t parse_hex_digit(char h) { if (h >= '0' && h <= '9') return h - '0'; @@ -149,7 +155,7 @@ void tsf_hex_to_color(void *arg, void *ptr) { return; } - uint8_t *color = ptr; + uint8_t *color = ptr; for (int i = 0; i < 3; i++) { uint8_t digits[2] = {hex_digit(s[1 + 2 * i]), hex_digit(s[2 + 2 * i])}; diff --git a/util.h b/util.h index 5be02f6..51aa57a 100644 --- a/util.h +++ b/util.h @@ -4,6 +4,7 @@ #include #include #include +#include // Print a formatted message and exit with code 1 void panicf(const char *fmt, ...); @@ -18,6 +19,8 @@ static inline size_t align_4(size_t n) { return (((n - 1) >> 2) + 1) << 2; } // Align n to the next 2 boundary static inline size_t align_2(size_t n) { return (((n - 1) >> 1) + 1) << 1; } uint8_t parse_hex_digit(char h); +// Convert timespec to double in seconds mostly for printing. +double timespec_to_double(struct timespec *ts); void default_to_null(void *ptr); void default_to_false(void *ptr);