aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--artnet.c141
-rw-r--r--artnet.h29
-rw-r--r--midimonster.h1
3 files changed, 145 insertions, 26 deletions
diff --git a/artnet.c b/artnet.c
index 2147dd9..a15743b 100644
--- a/artnet.c
+++ b/artnet.c
@@ -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;
}
diff --git a/artnet.h b/artnet.h
index 806d125..eb60115 100644
--- a/artnet.h
+++ b/artnet.h
@@ -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.