#include #include "artnet.h" #include #include #include #include #define BACKEND_NAME "artnet" static uint8_t default_net = 0; static struct { char* host; char* port; } 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 = { .name = BACKEND_NAME, .conf = artnet_configure, .create = artnet_instance, .conf_instance = artnet_configure_instance, .channel = artnet_channel, .handle = artnet_set, .process = artnet_handle, .start = artnet_start, .shutdown = artnet_shutdown }; //register backend if(mm_backend_register(artnet)){ fprintf(stderr, "Failed to register ArtNet backend\n"); return 1; } return 0; } static int artnet_configure(char* option, char* value){ char* separator = value; if(!strcmp(option, "bind")){ for(; *separator && *separator != ' '; separator++){ } if(*separator){ *separator = 0; separator++; free(bind_info.port); bind_info.port = strdup(separator); } free(bind_info.host); bind_info.host = strdup(value); return 0; } else if(!strcmp(option, "net")){ //configure default net default_net = strtoul(value, NULL, 10); return 0; } fprintf(stderr, "Unknown ArtNet backend option %s\n", option); return 1; } static instance* artnet_instance(){ instance* inst = mm_instance(); if(!inst){ return NULL; } inst->impl = calloc(1, sizeof(artnet_instance_data)); if(!inst->impl){ fprintf(stderr, "Failed to allocate memory\n"); return NULL; } artnet_instance_data* data = (artnet_instance_data*) inst->impl; data->net = default_net; return inst; } static int artnet_configure_instance(instance* instance, char* option, char* value){ artnet_instance_data* data = (artnet_instance_data*) instance->impl; if(!strcmp(option, "net")){ data->net = strtoul(value, NULL, 10); return 0; } else if(!strcmp(option, "uni")){ data->uni = strtoul(value, NULL, 10); return 0; } else if(!strcmp(option, "output")){ if(!strcmp(value, "true")){ data->mode |= output; } else{ data->mode &= ~output; } return 0; } fprintf(stderr, "Unknown ArtNet instance option %s\n", option); return 1; } static channel* artnet_channel(instance* instance, char* spec){ unsigned channel = strtoul(spec, NULL, 10); if(channel > 512 || channel < 1){ fprintf(stderr, "Invalid ArtNet channel %s\n", spec); return NULL; } return mm_channel(instance, channel, 1); } static int artnet_set(instance* inst, size_t num, channel** c, channel_value* v){ //TODO return 1; } static int artnet_handle(size_t num, managed_fd* fds){ //TODO return 0; } static int artnet_start(){ 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_info.port){ bind_info.port = strdup("6454"); } if(!bind_info.host || !bind_info.port){ fprintf(stderr, "Failed to allocate memory\n"); return 1; } //fetch all defined instances if(mm_backend_instances(BACKEND_NAME, &n, &inst)){ fprintf(stderr, "Failed to fetch instance list\n"); return 1; } 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(){ size_t n, p; instance** inst = NULL; if(mm_backend_instances(BACKEND_NAME, &n, &inst)){ fprintf(stderr, "Failed to fetch instance list\n"); return 1; } for(p = 0; p < n; p++){ free(inst[p]->impl); } free(inst); free(bind_info.host); free(bind_info.port); fprintf(stderr, "ArtNet backend shut down\n"); return 0; }