diff options
-rw-r--r-- | artnet.c | 141 | ||||
-rw-r--r-- | artnet.h | 29 | ||||
-rw-r--r-- | midimonster.h | 1 |
3 files changed, 145 insertions, 26 deletions
@@ -1,15 +1,71 @@ #include <string.h> #include "artnet.h" +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <unistd.h> #define BACKEND_NAME "artnet" static uint8_t default_net = 0; static struct { char* host; char* port; -} bind = { +} bind_info = { .host = NULL, .port = NULL }; +int artnet_fd = -1; + +static int artnet_listener(char* host, char* port){ + int fd = -1, status, yes = 1; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_DGRAM, + .ai_flags = AI_PASSIVE + }; + struct addrinfo* info; + struct addrinfo* addr_it; + + status = getaddrinfo(host, port, &hints, &info); + if(status){ + fprintf(stderr, "Failed to get socket info for %s port %s: %s\n", host, port, gai_strerror(status)); + return -1; + } + + for(addr_it = info; addr_it != NULL; addr_it = addr_it->ai_next){ + fd = socket(addr_it->ai_family, addr_it->ai_socktype, addr_it->ai_protocol); + if(fd < 0){ + continue; + } + + yes = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes)) < 0){ + fprintf(stderr, "Failed to set SO_REUSEADDR on socket\n"); + } + + yes = 1; + if(setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (void*)&yes, sizeof(yes)) < 0){ + fprintf(stderr, "Failed to set SO_BROADCAST on socket\n"); + } + + status = bind(fd, addr_it->ai_addr, addr_it->ai_addrlen); + if(status < 0){ + close(fd); + continue; + } + + break; + } + + freeaddrinfo(info); + + if(!addr_it){ + fprintf(stderr, "Failed to create listening socket for %s port %s\n", host, port); + return -1; + } + return fd; +} + int artnet_init(){ backend artnet = { @@ -41,12 +97,12 @@ static int artnet_configure(char* option, char* value){ if(*separator){ *separator = 0; separator++; - free(bind.port); - bind.port = strdup(separator); + free(bind_info.port); + bind_info.port = strdup(separator); } - free(bind.host); - bind.host = strdup(value); + free(bind_info.host); + bind_info.host = strdup(value); return 0; } else if(!strcmp(option, "net")){ @@ -70,6 +126,9 @@ static instance* artnet_instance(){ return NULL; } + artnet_instance_data* data = (artnet_instance_data*) inst->impl; + data->net = default_net; + return inst; } @@ -118,25 +177,73 @@ static int artnet_handle(size_t num, managed_fd* fds){ } static int artnet_start(){ - if(!bind.host){ - bind.host = strdup("127.0.0.1"); + size_t n, u, p; + int rv = 1; + instance** inst = NULL; + artnet_instance_data* data_a, *data_b; + artnet_instance_id id = { + .label = 0 + }; + + if(!bind_info.host){ + bind_info.host = strdup("127.0.0.1"); } - if(!bind.port){ - bind.port = strdup("6454"); + if(!bind_info.port){ + bind_info.port = strdup("6454"); } - if(!bind.host || !bind.port){ + if(!bind_info.host || !bind_info.port){ fprintf(stderr, "Failed to allocate memory\n"); return 1; } - //TODO allocate all active universes - //TODO open socket - fprintf(stderr, "Listening for ArtNet data on %s port %s\n", bind.host, bind.port); - //TODO parse all universe destinations + //fetch all defined instances + if(mm_backend_instances(BACKEND_NAME, &n, &inst)){ + fprintf(stderr, "Failed to fetch instance list\n"); + return 1; + } - return 0; + if(!n){ + return 0; + } + + for(u = 0; u < n; u++){ + data_a = (artnet_instance_data*) inst[u]->impl; + //set instance identifier + id.fields.net = data_a->net; + id.fields.uni = data_a->uni; + inst[u]->ident = id.label; + + //check for duplicate instances + for(p = u + 1; p < n; p++){ + data_b = (artnet_instance_data*) inst[p]->impl; + //FIXME might want to include destination in duplicate check + if(data_a->net == data_b->net + && data_a->uni == data_b->uni){ + fprintf(stderr, "Universe specified multiple times, use one instance: %s - %s\n", inst[u]->name, inst[p]->name); + goto bail; + } + } + } + + //open socket + artnet_fd = artnet_listener(bind_info.host, bind_info.port); + if(artnet_fd < 0){ + fprintf(stderr, "Failed to open ArtNet listener socket\n"); + goto bail; + } + fprintf(stderr, "Listening for ArtNet data on %s port %s\n", bind_info.host, bind_info.port); + + if(mm_manage_fd(artnet_fd, BACKEND_NAME, 1, NULL)){ + goto bail; + } + + //TODO parse all universe destinations + rv = 0; +bail: + free(inst); + return rv; } static int artnet_shutdown(){ @@ -152,8 +259,8 @@ static int artnet_shutdown(){ } free(inst); - free(bind.host); - free(bind.port); + free(bind_info.host); + free(bind_info.port); fprintf(stderr, "ArtNet backend shut down\n"); return 0; } @@ -1,5 +1,11 @@ #include "midimonster.h" +/* + * TODO + * bind per instance + * destination per instance + */ + int artnet_init(); static int artnet_configure(char* option, char* value); static int artnet_configure_instance(instance* instance, char* option, char* value); @@ -10,23 +16,28 @@ static int artnet_handle(size_t num, managed_fd* fds); static int artnet_start(); static int artnet_shutdown(); +typedef struct /*_artnet_universe_model*/ { + uint8_t last_frame; + uint8_t data[512]; +} artnet_universe; + typedef struct /*_artnet_instance_model*/ { uint8_t net; uint8_t uni; uint8_t mode; char* dest; + artnet_universe data; } artnet_instance_data; +typedef union /*_artnet_instance_id*/ { + struct { + uint8_t net; + uint8_t uni; + } fields; + uint64_t label; +} artnet_instance_id; + enum { output = 1, mark = 2 }; - -typedef struct /*_artnet_universe_model*/ { - uint8_t net; - uint8_t uni; - uint8_t flags; - uint8_t last_frame; - uint8_t data[512]; - uint8_t mask[512]; -} artnet_universe; diff --git a/midimonster.h b/midimonster.h index 27dcae4..8d439f5 100644 --- a/midimonster.h +++ b/midimonster.h @@ -111,6 +111,7 @@ instance* mm_instance_find(char* backend, uint64_t ident); * will receive a call to its channel_free function. */ channel* mm_channel(instance* i, uint64_t ident, uint8_t create); +//TODO channel* mm_channel_find() /* * Register a file descriptor to be selected on. The backend * will be notified via the mmbackend_process_fd call. |