From 2dfc564edc0c89c4a8de7e384806aae5d593426d Mon Sep 17 00:00:00 2001 From: cbdev Date: Fri, 2 Mar 2018 03:20:11 +0100 Subject: Move backend implementations to subdirectory --- sacn.c | 736 ----------------------------------------------------------------- 1 file changed, 736 deletions(-) delete mode 100644 sacn.c (limited to 'sacn.c') diff --git a/sacn.c b/sacn.c deleted file mode 100644 index 9a3202d..0000000 --- a/sacn.c +++ /dev/null @@ -1,736 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sacn.h" -//upper limit imposed by using the fd index as 16-bit part of the instance id -#define MAX_FDS 4096 -#define BACKEND_NAME "sacn" - -static struct /*_sacn_global_config*/ { - uint8_t source_name[64]; - uint8_t cid[16]; - size_t fds; - sacn_fd* fd; - uint64_t last_announce; -} global_cfg = { - .source_name = "MIDIMonster", - .cid = {'M', 'I', 'D', 'I', 'M', 'o', 'n', 's', 't', 'e', 'r'}, - .fds = 0, - .fd = NULL, - .last_announce = 0 -}; - -int init(){ - backend sacn = { - .name = BACKEND_NAME, - .conf = sacn_configure, - .create = sacn_instance, - .conf_instance = sacn_configure_instance, - .channel = sacn_channel, - .handle = sacn_set, - .process = sacn_handle, - .start = sacn_start, - .shutdown = sacn_shutdown - }; - - //register the backend - if(mm_backend_register(sacn)){ - fprintf(stderr, "Failed to register sACN backend\n"); - return 1; - } - - return 0; -} - -static int sacn_listener(char* host, char* port, uint8_t fd_flags){ - int fd = -1, status, yes = 1, flags; - struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_DGRAM, - .ai_flags = AI_PASSIVE - }; - struct addrinfo* info; - struct addrinfo* addr_it; - - if(global_cfg.fds >= MAX_FDS){ - fprintf(stderr, "sACN backend descriptor limit reached\n"); - return -1; - } - - 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"); - } - - yes = 0; - if(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (void*)&yes, sizeof(yes)) < 0){ - fprintf(stderr, "Failed to unset IP_MULTICAST_LOOP option: %s\n", strerror(errno)); - } - - 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; - } - - //set nonblocking - flags = fcntl(fd, F_GETFL, 0); - if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0){ - fprintf(stderr, "Failed to set sACN descriptor nonblocking\n"); - return -1; - } - - //store fd - global_cfg.fd = realloc(global_cfg.fd, (global_cfg.fds + 1) * sizeof(sacn_fd)); - if(!global_cfg.fd){ - fprintf(stderr, "Failed to allocate memory\n"); - return -1; - } - - fprintf(stderr, "sACN backend interface %zu bound to %s port %s\n", global_cfg.fds, host, port); - global_cfg.fd[global_cfg.fds].fd = fd; - global_cfg.fd[global_cfg.fds].flags = fd_flags; - global_cfg.fd[global_cfg.fds].universes = 0; - global_cfg.fd[global_cfg.fds].universe = NULL; - global_cfg.fd[global_cfg.fds].last_frame = NULL; - global_cfg.fds++; - return 0; -} - -static int sacn_parse_addr(char* host, char* port, struct sockaddr_storage* addr, socklen_t* len){ - struct addrinfo* head; - struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_DGRAM - }; - - int error = getaddrinfo(host, port, &hints, &head); - if(error || !head){ - fprintf(stderr, "Failed to parse address %s port %s: %s\n", host, port, gai_strerror(error)); - return 1; - } - - memcpy(addr, head->ai_addr, head->ai_addrlen); - *len = head->ai_addrlen; - - freeaddrinfo(head); - return 0; -} - -static int sacn_parse_hostspec(char* in, char** host, char** port, uint8_t* flags){ - size_t u; - - if(!in || !host || !port){ - return 1; - } - - for(u = 0; in[u] && !isspace(in[u]); u++){ - } - - //guess - *host = in; - - if(in[u]){ - in[u] = 0; - *port = in + u + 1; - } - else{ - //no port given - *port = SACN_PORT; - } - - if(flags){ - //TODO parse hostspec trailing data for options - *flags = 0; - } - return 0; -} - -static int sacn_configure(char* option, char* value){ - char* host = NULL, *port = NULL, *next = NULL; - uint8_t flags = 0; - size_t u; - - if(!strcmp(option, "name")){ - if(strlen(value) > 63){ - fprintf(stderr, "Invalid sACN source name %s, limit is 63 characters\n", value); - return 1; - } - - memset(global_cfg.source_name, 0, sizeof(global_cfg.source_name)); - memcpy(global_cfg.source_name, value, strlen(value)); - return 0; - } - else if(!strcmp(option, "cid")){ - next = value; - for(u = 0; u < sizeof(global_cfg.cid); u++){ - global_cfg.cid[u] = (strtoul(next, &next, 0) & 0xFF); - } - } - else if(!strcmp(option, "bind")){ - if(sacn_parse_hostspec(value, &host, &port, &flags)){ - fprintf(stderr, "Not a valid sACN bind address: %s\n", value); - return 1; - } - - if(sacn_listener(host, port, flags)){ - fprintf(stderr, "Failed to bind sACN descriptor: %s\n", value); - return 1; - } - return 0; - } - - fprintf(stderr, "Unknown sACN backend option %s\n", option); - return 1; -} - -static int sacn_configure_instance(instance* inst, char* option, char* value){ - sacn_instance_data* data = (sacn_instance_data*) inst->impl; - char* host = NULL, *port = NULL, *next = NULL; - size_t u; - - if(!strcmp(option, "universe")){ - data->uni = strtoul(value, NULL, 10); - return 0; - } - else if(!strcmp(option, "interface")){ - data->fd_index = strtoul(value, NULL, 10); - - if(data->fd_index >= global_cfg.fds){ - fprintf(stderr, "Configured sACN interface index is out of range on instance %s\n", inst->name); - return 1; - } - return 0; - } - else if(!strcmp(option, "priority")){ - data->xmit_prio = strtoul(value, NULL, 10); - return 0; - } - else if(!strcmp(option, "destination")){ - if(sacn_parse_hostspec(value, &host, &port, NULL)){ - fprintf(stderr, "Not a valid sACN destination for instance %s: %s\n", inst->name, value); - return 1; - } - - return sacn_parse_addr(host, port, &data->dest_addr, &data->dest_len); - } - else if(!strcmp(option, "from")){ - next = value; - data->filter_enabled = 1; - for(u = 0; u < sizeof(data->cid_filter); u++){ - data->cid_filter[u] = (strtoul(next, &next, 0) & 0xFF); - } - fprintf(stderr, "Enabled source CID filter for instance %s\n", inst->name); - return 0; - } - else if(!strcmp(option, "unicast")){ - data->unicast_input = strtoul(value, NULL, 10); - } - - fprintf(stderr, "Unknown configuration option %s for sACN backend\n", option); - return 1; -} - -static instance* sacn_instance(){ - instance* inst = mm_instance(); - if(!inst){ - return NULL; - } - - inst->impl = calloc(1, sizeof(sacn_instance_data)); - if(!inst->impl){ - fprintf(stderr, "Failed to allocate memory"); - return NULL; - } - - return inst; -} - -static channel* sacn_channel(instance* inst, char* spec){ - sacn_instance_data* data = (sacn_instance_data*) inst->impl; - char* spec_next = spec; - - unsigned chan_a = strtoul(spec, &spec_next, 10), chan_b = 0; - - //range check - if(!chan_a || chan_a > 512){ - fprintf(stderr, "sACN channel out of range on instance %s: %s\n", inst->name, spec); - return NULL; - } - chan_a--; - - //if wide channel, mark fine - if(*spec_next == '+'){ - chan_b = strtoul(spec_next + 1, NULL, 10); - if(!chan_b || chan_b > 512){ - fprintf(stderr, "Invalid wide-channel spec on instance %s: %s\n", inst->name, spec); - return NULL; - } - chan_b--; - - //if already mapped, bail - if(IS_ACTIVE(data->data.map[chan_b]) && data->data.map[chan_b] != (MAP_FINE | chan_a)){ - fprintf(stderr, "Fine channel %u already mapped on instance %s\n", chan_b, inst->name); - return NULL; - } - - data->data.map[chan_b] = MAP_FINE | chan_a; - } - - //if already active, assert that nothing changes - if(IS_ACTIVE(data->data.map[chan_a])){ - if((*spec_next == '+' && data->data.map[chan_a] != (MAP_COARSE | chan_b)) - || (*spec_next != '+' && data->data.map[chan_a] != (MAP_SINGLE | chan_a))){ - fprintf(stderr, "Primary sACN channel %u already mapped in another mode on instance %s\n", chan_a, inst->name); - return NULL; - } - } - - data->data.map[chan_a] = (*spec_next == '+') ? (MAP_COARSE | chan_b) : (MAP_SINGLE | chan_a); - return mm_channel(inst, chan_a, 1); -} - -static int sacn_transmit(instance* inst){ - size_t u; - sacn_instance_data* data = (sacn_instance_data*) inst->impl; - sacn_data_pdu pdu = { - .root = { - .preamble_size = htobe16(0x10), - .postamble_size = 0, - .magic = { 0 }, //memcpy'd - .flags = htobe16(0x7000 | 0x026e), - .vector = htobe32(ROOT_E131_DATA), - .sender_cid = { 0 }, //memcpy'd - .frame_flags = htobe16(0x7000 | 0x0258), - .frame_vector = htobe32(FRAME_E131_DATA) - }, - .data = { - .source_name = "", //memcpy'd - .priority = data->xmit_prio, - .sync_addr = 0, - .sequence = data->data.last_seq++, - .options = 0, - .universe = htobe16(data->uni), - .flags = htobe16(0x7000 | 0x0205), - .vector = DMP_SET_PROPERTY, - .format = 0xA1, - .startcode_offset = 0, - .address_increment = htobe16(1), - .channels = htobe16(513), - .data = { 0 } //memcpy'd - } - }; - - memcpy(pdu.root.magic, SACN_PDU_MAGIC, sizeof(pdu.root.magic)); - memcpy(pdu.root.sender_cid, global_cfg.cid, sizeof(pdu.root.sender_cid)); - memcpy(pdu.data.source_name, global_cfg.source_name, sizeof(pdu.data.source_name)); - memcpy((((uint8_t*)pdu.data.data) + 1), data->data.out, 512); - - if(sendto(global_cfg.fd[data->fd_index].fd, &pdu, sizeof(pdu), 0, (struct sockaddr*) &data->dest_addr, data->dest_len) < 0){ - fprintf(stderr, "Failed to output sACN frame for instance %s: %s\n", inst->name, strerror(errno)); - } - - //update last transmit timestamp - for(u = 0; u < global_cfg.fd[data->fd_index].universes; u++){ - if(global_cfg.fd[data->fd_index].universe[u] == data->uni){ - global_cfg.fd[data->fd_index].last_frame[u] = mm_timestamp(); - } - } - return 0; -} - -static int sacn_set(instance* inst, size_t num, channel** c, channel_value* v){ - size_t u, mark = 0; - sacn_instance_data* data = (sacn_instance_data*) inst->impl; - - if(!num){ - return 0; - } - - if(!data->xmit_prio){ - fprintf(stderr, "sACN instance %s not enabled for output (%zu channel events)\n", inst->name, num); - return 0; - } - - for(u = 0; u < num; u++){ - if(IS_WIDE(data->data.map[c[u]->ident])){ - uint32_t val = v[u].normalised * ((double) 0xFFFF); - - if(data->data.out[c[u]->ident] != ((val >> 8) & 0xFF)){ - mark = 1; - data->data.out[c[u]->ident] = (val >> 8) & 0xFF; - } - - if(data->data.out[MAPPED_CHANNEL(data->data.map[c[u]->ident])] != (val & 0xFF)){ - mark = 1; - data->data.out[MAPPED_CHANNEL(data->data.map[c[u]->ident])] = val & 0xFF; - } - } - else if(data->data.out[c[u]->ident] != (v[u].normalised * 255.0)){ - mark = 1; - data->data.out[c[u]->ident] = v[u].normalised * 255.0; - } - } - - //send packet if required - if(mark){ - sacn_transmit(inst); - } - - return 0; -} - -static int sacn_process_frame(instance* inst, sacn_frame_root* frame, sacn_frame_data* data){ - size_t u, max_mark = 0; - channel* chan = NULL; - channel_value val; - sacn_instance_data* inst_data = (sacn_instance_data*) inst->impl; - - //source filtering - if(inst_data->filter_enabled && memcmp(inst_data->cid_filter, frame->sender_cid, 16)){ - return 0; - } - - if(data->format != 0xa1 - || data->startcode_offset - || be16toh(data->address_increment) != 1){ - fprintf(stderr, "sACN framing not supported\n"); - return 1; - } - - if(be16toh(data->channels) > 513){ - fprintf(stderr, "Invalid sACN frame channel count\n"); - return 1; - } - - //handle source priority (currently a 1-bit counter) - if(inst_data->data.last_priority > data->priority){ - inst_data->data.last_priority = data->priority; - return 0; - } - inst_data->data.last_priority = data->priority; - - //read data (except start code), mark changed channels - for(u = 1; u < be16toh(data->channels); u++){ - if(IS_ACTIVE(inst_data->data.map[u - 1]) - && data->data[u] != inst_data->data.in[u - 1]){ - inst_data->data.in[u - 1] = data->data[u]; - inst_data->data.map[u - 1] |= MAP_MARK; - max_mark = u - 1; - } - } - - //generate events - for(u = 0; u <= max_mark; u++){ - if(inst_data->data.map[u] & MAP_MARK){ - //unmark and get channel - inst_data->data.map[u] &= ~MAP_MARK; - if(inst_data->data.map[u] & MAP_FINE){ - chan = mm_channel(inst, MAPPED_CHANNEL(inst_data->data.map[u]), 0); - } - else{ - chan = mm_channel(inst, u, 0); - } - - if(!chan){ - fprintf(stderr, "Active channel %zu on %s not known to core", u, inst->name); - return 1; - } - - //generate value - if(IS_WIDE(inst_data->data.map[u])){ - inst_data->data.map[MAPPED_CHANNEL(inst_data->data.map[u])] &= ~MAP_MARK; - val.raw.u64 = inst_data->data.in[u] << ((inst_data->data.map[u] & MAP_COARSE) ? 8 : 0); - val.raw.u64 |= inst_data->data.in[MAPPED_CHANNEL(inst_data->data.map[u])] << ((inst_data->data.map[u] & MAP_COARSE) ? 0 : 8); - val.normalised = (double) val.raw.u64 / (double) 0xFFFF; - } - else{ - val.raw.u64 = inst_data->data.in[u]; - val.normalised = (double) val.raw.u64 / 255.0; - } - - if(mm_channel_event(chan, val)){ - fprintf(stderr, "Failed to push sACN channel event to core\n"); - return 1; - } - } - } - return 0; -} - -static void sacn_discovery(size_t fd){ - size_t page = 0, pages = (global_cfg.fd[fd].universes / 512) + 1, universes; - struct sockaddr_in discovery_dest = { - .sin_family = AF_INET, - .sin_port = htobe16(SACN_PORT), - .sin_addr.s_addr = htobe32(((uint32_t) 0xefff0000) | 64214) - }; - - sacn_discovery_pdu pdu = { - .root = { - .preamble_size = htobe16(0x10), - .postamble_size = 0, - .magic = { 0 }, //memcpy'd - .flags = 0, //filled later - .vector = htobe32(ROOT_E131_EXTENDED), - .sender_cid = { 0 }, //memcpy'd - .frame_flags = 0, //filled later - .frame_vector = htobe32(FRAME_E131_DISCOVERY) - }, - .data = { - .source_name = "", //memcpy'd - .flags = 0, //filled later - .vector = htobe32(DISCOVERY_UNIVERSE_LIST), - .page = 0, //filled later - .max_page = pages - 1, - .data = { 0 } //memcpy'd - } - }; - - memcpy(pdu.root.magic, SACN_PDU_MAGIC, sizeof(pdu.root.magic)); - memcpy(pdu.root.sender_cid, global_cfg.cid, sizeof(pdu.root.sender_cid)); - memcpy(pdu.data.source_name, global_cfg.source_name, sizeof(pdu.data.source_name)); - - for(; page < pages; page++){ - universes = (global_cfg.fd[fd].universes - page * 512 >= 512) ? 512 : (global_cfg.fd[fd].universes % 512); - pdu.root.flags = htobe16(0x7000 | (104 + universes * sizeof(uint16_t))); - pdu.root.frame_flags = htobe16(0x7000 | (82 + universes * sizeof(uint16_t))); - pdu.data.flags = htobe16(0x7000 | (8 + universes * sizeof(uint16_t))); - - pdu.data.page = page; - memcpy(pdu.data.data, global_cfg.fd[fd].universe + page * 512, universes * sizeof(uint16_t)); - - if(sendto(global_cfg.fd[fd].fd, &pdu, sizeof(pdu) - (512 - universes) * sizeof(uint16_t), 0, (struct sockaddr*) &discovery_dest, sizeof(discovery_dest)) < 0){ - fprintf(stderr, "Failed to output sACN universe discovery frame for interface %zu: %s\n", fd, strerror(errno)); - } - } -} - -static int sacn_handle(size_t num, managed_fd* fds){ - size_t u, c; - uint64_t timestamp = mm_timestamp(); - ssize_t bytes_read; - char recv_buf[SACN_RECV_BUF]; - instance* inst = NULL; - sacn_instance_id instance_id = { - .label = 0 - }; - sacn_frame_root* frame = (sacn_frame_root*) recv_buf; - sacn_frame_data* data = (sacn_frame_data*) (recv_buf + sizeof(sacn_frame_root)); - - if(mm_timestamp() - global_cfg.last_announce > SACN_DISCOVERY_TIMEOUT){ - //send universe discovery pdu - for(u = 0; u < global_cfg.fds; u++){ - if(global_cfg.fd[u].universes){ - sacn_discovery(u); - } - } - global_cfg.last_announce = timestamp; - } - - //check for keepalive frames - for(u = 0; u < global_cfg.fds; u++){ - for(c = 0; c < global_cfg.fd[u].universes; c++){ - if(timestamp - global_cfg.fd[u].last_frame[c] >= SACN_KEEPALIVE_INTERVAL){ - instance_id.fields.fd_index = u; - instance_id.fields.uni = global_cfg.fd[u].universe[c]; - inst = mm_instance_find(BACKEND_NAME, instance_id.label); - if(inst){ - sacn_transmit(inst); - } - } - } - } - - //early exit - if(!num){ - return 0; - } - - for(u = 0; u < num; u++){ - do{ - bytes_read = recv(fds[u].fd, recv_buf, sizeof(recv_buf), 0); - if(bytes_read > 0 && bytes_read > sizeof(sacn_frame_root)){ - if(!memcmp(frame->magic, SACN_PDU_MAGIC, 12) - && be16toh(frame->preamble_size) == 0x10 - && frame->postamble_size == 0 - && be32toh(frame->vector) == ROOT_E131_DATA - && be32toh(frame->frame_vector) == FRAME_E131_DATA - && data->vector == DMP_SET_PROPERTY){ - instance_id.fields.fd_index = ((uint64_t) fds[u].impl) & 0xFFFF; - instance_id.fields.uni = be16toh(data->universe); - inst = mm_instance_find(BACKEND_NAME, instance_id.label); - if(inst && sacn_process_frame(inst, frame, data)){ - fprintf(stderr, "Failed to process sACN frame\n"); - } - } - } - } while(bytes_read > 0); - - if(bytes_read < 0 && errno != EAGAIN){ - fprintf(stderr, "sACN failed to receive data: %s\n", strerror(errno)); - } - - if(bytes_read == 0){ - fprintf(stderr, "sACN listener closed\n"); - return 1; - } - } - - return 0; -} - -static int sacn_start(){ - size_t n, u, p; - int rv = 1; - instance** inst = NULL; - sacn_instance_data* data = NULL; - sacn_instance_id id = { - .label = 0 - }; - struct ip_mreq mcast_req = { - .imr_interface = { INADDR_ANY } - }; - struct sockaddr_in* dest_v4 = NULL; - - //fetch all instances - if(mm_backend_instances(BACKEND_NAME, &n, &inst)){ - fprintf(stderr, "Failed to fetch instance list\n"); - return 1; - } - - if(!n){ - free(inst); - return 0; - } - - if(!global_cfg.fds){ - fprintf(stderr, "Failed to start sACN backend: no descriptors bound\n"); - return 1; - } - - //update instance identifiers, join multicast groups - for(u = 0; u < n; u++){ - data = (sacn_instance_data*) inst[u]->impl; - id.fields.fd_index = data->fd_index; - id.fields.uni = data->uni; - inst[u]->ident = id.label; - - if(!data->uni){ - fprintf(stderr, "Please specify a universe on instance %s\n", inst[u]->name); - goto bail; - } - - //find duplicates - for(p = 0; p < u; p++){ - if(inst[u]->ident == inst[p]->ident){ - fprintf(stderr, "Colliding sACN instances, use one: %s - %s\n", inst[u]->name, inst[p]->name); - goto bail; - } - } - - if(!data->unicast_input){ - mcast_req.imr_multiaddr.s_addr = htobe32(((uint32_t) 0xefff0000) | ((uint32_t) data->uni)); - if(setsockopt(global_cfg.fd[data->fd_index].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mcast_req, sizeof(mcast_req))){ - fprintf(stderr, "Failed to join Multicast group for sACN universe %u on instance %s: %s\n", data->uni, inst[u]->name, strerror(errno)); - } - } - - if(data->xmit_prio){ - //add to list of advertised universes for this fd - global_cfg.fd[data->fd_index].universe = realloc(global_cfg.fd[data->fd_index].universe, (global_cfg.fd[data->fd_index].universes + 1) * sizeof(uint16_t)); - if(!global_cfg.fd[data->fd_index].universe){ - fprintf(stderr, "Failed to allocate memory\n"); - goto bail; - } - - global_cfg.fd[data->fd_index].universe[global_cfg.fd[data->fd_index].universes] = data->uni; - global_cfg.fd[data->fd_index].universes++; - - //generate multicast destination address if none set - if(!data->dest_len){ - data->dest_len = sizeof(struct sockaddr_in); - dest_v4 = (struct sockaddr_in*) (&data->dest_addr); - dest_v4->sin_family = AF_INET; - dest_v4->sin_port = htobe16(strtoul(SACN_PORT, NULL, 10)); - dest_v4->sin_addr = mcast_req.imr_multiaddr; - } - } - } - - fprintf(stderr, "sACN backend registering %zu descriptors to core\n", global_cfg.fds); - for(u = 0; u < global_cfg.fds; u++){ - //allocate memory for storing last frame transmission timestamp - global_cfg.fd[u].last_frame = calloc(global_cfg.fd[u].universes, sizeof(uint64_t)); - if(!global_cfg.fd[u].last_frame){ - fprintf(stderr, "Failed to allocate memory\n"); - goto bail; - } - if(mm_manage_fd(global_cfg.fd[u].fd, BACKEND_NAME, 1, (void*) u)){ - goto bail; - } - } - - rv = 0; -bail: - free(inst); - return rv; -} - -static int sacn_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); - - for(p = 0; p < global_cfg.fds; p++){ - close(global_cfg.fd[p].fd); - free(global_cfg.fd[p].universe); - free(global_cfg.fd[p].last_frame); - } - free(global_cfg.fd); - fprintf(stderr, "sACN backend shut down\n"); - return 0; -} -- cgit v1.2.3