From dd91621ccee033550312683293b5bf40c3599053 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sun, 23 Feb 2020 18:20:12 +0100 Subject: Implement OpenPixelControl output --- backends/openpixelcontrol.c | 353 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 backends/openpixelcontrol.c (limited to 'backends/openpixelcontrol.c') diff --git a/backends/openpixelcontrol.c b/backends/openpixelcontrol.c new file mode 100644 index 0000000..062c015 --- /dev/null +++ b/backends/openpixelcontrol.c @@ -0,0 +1,353 @@ +#define BACKEND_NAME "openpixelcontrol" + +#include + +#include "libmmbackend.h" +#include "openpixelcontrol.h" + +/* + * TODO handle destination close/unregister/reopen + */ + +MM_PLUGIN_API int init(){ + backend openpixel = { + .name = BACKEND_NAME, + .conf = openpixel_configure, + .create = openpixel_instance, + .conf_instance = openpixel_configure_instance, + .channel = openpixel_channel, + .handle = openpixel_set, + .process = openpixel_handle, + .start = openpixel_start, + .shutdown = openpixel_shutdown + }; + + //register backend + if(mm_backend_register(openpixel)){ + LOG("Failed to register backend"); + return 1; + } + return 0; +} + +static int openpixel_configure(char* option, char* value){ + //no global configuration + LOG("No backend configuration possible"); + return 1; +} + +static int openpixel_configure_instance(instance* inst, char* option, char* value){ + char* host = NULL, *port = NULL; + openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; + + //FIXME this should store the destination/listen address and establish on _start + if(!strcmp(option, "destination")){ + mmbackend_parse_hostspec(value, &host, &port, NULL); + if(!host || !port){ + LOGPF("Invalid destination address specified for instance %s", inst->name); + return 1; + } + + data->dest_fd = mmbackend_socket(host, port, SOCK_STREAM, 0, 0); + if(data->dest_fd >= 0){ + return 0; + } + return 1; + } + if(!strcmp(option, "listen")){ + mmbackend_parse_hostspec(value, &host, &port, NULL); + if(!host || !port){ + LOGPF("Invalid listen address specified for instance %s", inst->name); + return 1; + } + + data->listen_fd = mmbackend_socket(host, port, SOCK_STREAM, 1, 0); + if(data->listen_fd >= 0 && listen(data->listen_fd, SOMAXCONN)){ + return 0; + } + return 1; + } + else if(!strcmp(option, "mode")){ + if(!strcmp(value, "16bit")){ + data->mode = rgb16; + return 0; + } + else if(!strcmp(value, "8bit")){ + data->mode = rgb8; + return 0; + } + LOGPF("Unknown instance mode %s\n", value); + return 1; + } + + LOGPF("Unknown instance option %s for instance %s", option, inst->name); + return 1; +} + +static int openpixel_instance(instance* inst){ + openpixel_instance_data* data = calloc(1, sizeof(openpixel_instance_data)); + inst->impl = data; + if(!inst->impl){ + LOG("Failed to allocate memory"); + return 1; + } + + data->dest_fd = -1; + data->listen_fd = -1; + return 0; +} + +static ssize_t openpixel_buffer_find(openpixel_instance_data* data, uint8_t strip, uint8_t input){ + ssize_t n = 0; + + for(n = 0; n < data->buffers; n++){ + if(data->buffer[n].strip == strip + && (data->buffer[n].flags & OPENPIXEL_INPUT) >= input){ + return n; + } + } + return -1; +} + +static int openpixel_buffer_extend(openpixel_instance_data* data, uint8_t strip, uint8_t input, uint8_t length){ + ssize_t buffer = openpixel_buffer_find(data, strip, input); + length = (length % 3) ? ((length / 3) + 1) * 3 : length; + size_t bytes_required = (data->mode == rgb8) ? length : length * 2; + if(buffer < 0){ + //allocate new buffer + data->buffer = realloc(data->buffer, (data->buffers + 1) * sizeof(openpixel_buffer)); + if(!data->buffer){ + data->buffers = 0; + LOG("Failed to allocate memory"); + return -1; + } + + buffer = data->buffers; + data->buffers++; + + data->buffer[buffer].strip = strip; + data->buffer[buffer].flags = input ? OPENPIXEL_INPUT : 0; + data->buffer[buffer].bytes = 0; + data->buffer[buffer].data.u8 = NULL; + } + + if(data->buffer[buffer].bytes < bytes_required){ + //resize buffer + data->buffer[buffer].data.u8 = realloc(data->buffer[buffer].data.u8, bytes_required); + if(!data->buffer[buffer].data.u8){ + data->buffer[buffer].bytes = 0; + LOG("Failed to allocate memory"); + return 1; + } + //FIXME might want to memset() only newly allocated channels + memset(data->buffer[buffer].data.u8, 0, bytes_required); + data->buffer[buffer].bytes = bytes_required; + } + return 0; +} + +static channel* openpixel_channel(instance* inst, char* spec, uint8_t flags){ + uint32_t strip = 0, channel = 0; + char* token = spec; + openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; + + //read strip index if supplied + if(!strncmp(spec, "strip", 5)){ + strip = strtoul(spec + 5, &token, 10); + //skip the dot + token++; + } + + //read (and calculate) channel index + if(!strncmp(token, "channel", 7)){ + channel = strtoul(token + 7, NULL, 10); + } + else if(!strncmp(token, "red", 3)){ + channel = strtoul(token + 3, NULL, 10) * 3 - 2; + } + else if(!strncmp(token, "green", 5)){ + channel = strtoul(token + 5, NULL, 10) * 3 - 1; + } + else if(!strncmp(token, "blue", 4)){ + channel = strtoul(token + 4, NULL, 10) * 3; + } + + if(!channel){ + LOGPF("Invalid channel specification %s", spec); + return NULL; + } + + //check channel direction + if(flags & mmchannel_input){ + //strip 0 (bcast) can not be mapped as input + if(!strip){ + LOGPF("Broadcast channel %s.%s can not be mapped as an input", inst->name, spec); + return NULL; + } + if(data->listen_fd < 0){ + LOGPF("Channel %s mapped as input, but instance %s is not accepting input", spec, inst->name); + return NULL; + } + + if(openpixel_buffer_extend(data, strip, 1, channel)){ + return NULL; + } + } + + if(flags & mmchannel_output){ + if(data->dest_fd < 0){ + LOGPF("Channel %s mapped as output, but instance %s is not sending output", spec, inst->name); + return NULL; + } + + if(openpixel_buffer_extend(data, strip, 0, channel)){ + return NULL; + } + } + + return mm_channel(inst, ((uint64_t) strip) << 32 | channel, 1); +} + +static int openpixel_set(instance* inst, size_t num, channel** c, channel_value* v){ + openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; + size_t u, p; + ssize_t buffer; + uint32_t strip, channel; + openpixel_header hdr; + + for(u = 0; u < num; u++){ + //read strip/channel + strip = c[u]->ident >> 32; + channel = c[u]->ident & 0xFFFFFFFF; + channel--; + + //find the buffer + buffer = openpixel_buffer_find(data, strip, 0); + if(buffer < 0){ + LOGPF("No buffer for channel %s.%d.%d\n", inst->name, strip, channel); + continue; + } + + //mark buffer for output + data->buffer[buffer].flags |= OPENPIXEL_MARK; + + //update data + switch(data->mode){ + case rgb8: + data->buffer[buffer].data.u8[channel] = ((uint8_t)(v[u].normalised * 255.0)); + break; + case rgb16: + data->buffer[buffer].data.u16[channel] = ((uint16_t)(v[u].normalised * 65535.0)); + break; + } + + if(strip == 0){ + //update values in all other output strips, dont mark + for(p = 0; p < data->buffers; p++){ + if(!(data->buffer[p].flags & OPENPIXEL_INPUT)){ + //check whether the buffer is large enough + if(data->mode == rgb8 && data->buffer[p].bytes >= channel){ + data->buffer[p].data.u8[channel] = ((uint8_t)(v[u].normalised * 255.0)); + } + else if(data->mode == rgb16 && data->buffer[p].bytes >= channel * 2){ + data->buffer[p].data.u16[channel] = ((uint16_t)(v[u].normalised * 65535.0)); + } + } + } + } + } + + //send updated strips + for(u = 0; u < data->buffers; u++){ + if(!(data->buffer[u].flags & OPENPIXEL_INPUT) && (data->buffer[u].flags & OPENPIXEL_MARK)){ + //remove mark + data->buffer[u].flags &= ~OPENPIXEL_MARK; + + //prepare header + hdr.strip = data->buffer[u].strip; + hdr.mode = data->mode; + hdr.length = htobe16(data->buffer[u].bytes); + + //output data + if(mmbackend_send(data->dest_fd, (uint8_t*) &hdr, sizeof(hdr)) + || mmbackend_send(data->dest_fd, data->buffer[u].data.u8, data->buffer[u].bytes)){ + return 1; + } + } + } + return 0; +} + +static int openpixel_handle(size_t num, managed_fd* fds){ + //TODO handle bcast + return 0; +} + +static int openpixel_start(size_t n, instance** inst){ + int rv = -1; + size_t u, nfds = 0; + openpixel_instance_data* data = NULL; + + for(u = 0; u < n; u++){ + data = (openpixel_instance_data*) inst[u]->impl; + + //register fds + if(data->dest_fd >= 0){ + if(mm_manage_fd(data->dest_fd, BACKEND_NAME, 1, inst[u])){ + LOGPF("Failed to register destination descriptor for instance %s with core", inst[u]->name); + goto bail; + } + nfds++; + } + if(data->listen_fd >= 0){ + if(mm_manage_fd(data->listen_fd, BACKEND_NAME, 1, inst[u])){ + LOGPF("Failed to register host descriptor for instance %s with core", inst[u]->name); + goto bail; + } + nfds++; + } + } + + LOGPF("Registered %" PRIsize_t " descriptors to core", nfds); + rv = 0; +bail: + return rv; +} + +static int openpixel_shutdown(size_t n, instance** inst){ + size_t u, p; + openpixel_instance_data* data = NULL; + + for(u = 0; u < n; u++){ + data = (openpixel_instance_data*) inst[u]->impl; + + //shutdown all clients + for(p = 0; p < data->clients; p++){ + if(data->client_fd[p] >= 0){ + close(data->client_fd[p]); + } + } + free(data->client_fd); + free(data->bytes_left); + + //close all configured fds + if(data->listen_fd >= 0){ + close(data->listen_fd); + } + if(data->dest_fd >= 0){ + close(data->dest_fd); + } + + //free all buffers + for(p = 0; p < data->buffers; p++){ + free(data->buffer[p].data.u8); + } + free(data->buffer); + + free(data); + inst[u]->impl = NULL; + } + + LOG("Backend shut down"); + return 0; +} -- cgit v1.2.3 From e1fcd4d11cfdbad54470b2cce98d8b749464ec00 Mon Sep 17 00:00:00 2001 From: cbdev Date: Thu, 27 Feb 2020 00:33:22 +0100 Subject: Implement OpenPixelControl server mode (8bit) --- backends/openpixelcontrol.c | 220 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 212 insertions(+), 8 deletions(-) (limited to 'backends/openpixelcontrol.c') diff --git a/backends/openpixelcontrol.c b/backends/openpixelcontrol.c index 062c015..3a94a12 100644 --- a/backends/openpixelcontrol.c +++ b/backends/openpixelcontrol.c @@ -52,6 +52,7 @@ static int openpixel_configure_instance(instance* inst, char* option, char* valu if(data->dest_fd >= 0){ return 0; } + LOGPF("Failed to connect to server for instance %s", inst->name); return 1; } if(!strcmp(option, "listen")){ @@ -62,9 +63,10 @@ static int openpixel_configure_instance(instance* inst, char* option, char* valu } data->listen_fd = mmbackend_socket(host, port, SOCK_STREAM, 1, 0); - if(data->listen_fd >= 0 && listen(data->listen_fd, SOMAXCONN)){ + if(data->listen_fd >= 0 && !listen(data->listen_fd, SOMAXCONN)){ return 0; } + LOGPF("Failed to bind server descriptor for instance %s", inst->name); return 1; } else if(!strcmp(option, "mode")){ @@ -103,15 +105,22 @@ static ssize_t openpixel_buffer_find(openpixel_instance_data* data, uint8_t stri for(n = 0; n < data->buffers; n++){ if(data->buffer[n].strip == strip && (data->buffer[n].flags & OPENPIXEL_INPUT) >= input){ + DBGPF("Using allocated %s buffer for requested strip %d, size %d", input ? "input" : "output", strip, data->buffer[n].bytes); return n; } } + DBGPF("Instance has no %s buffer for requested strip %d", input ? "input" : "output", strip); return -1; } -static int openpixel_buffer_extend(openpixel_instance_data* data, uint8_t strip, uint8_t input, uint8_t length){ +static int openpixel_buffer_extend(openpixel_instance_data* data, uint8_t strip, uint8_t input, uint16_t length){ ssize_t buffer = openpixel_buffer_find(data, strip, input); + + //length is in component-channels, round it to the nearest rgb-triplet + //this guarantees that any allocated buffer has at least three bytes, which is important to parts of the receive handler length = (length % 3) ? ((length / 3) + 1) * 3 : length; + + //calculate required buffer length size_t bytes_required = (data->mode == rgb8) ? length : length * 2; if(buffer < 0){ //allocate new buffer @@ -242,7 +251,7 @@ static int openpixel_set(instance* inst, size_t num, channel** c, channel_value* } if(strip == 0){ - //update values in all other output strips, dont mark + //update values in all other output strips, don't mark for(p = 0; p < data->buffers; p++){ if(!(data->buffer[p].flags & OPENPIXEL_INPUT)){ //check whether the buffer is large enough @@ -278,8 +287,204 @@ static int openpixel_set(instance* inst, size_t num, channel** c, channel_value* return 0; } +static int openpixel_client_new(instance* inst, int fd){ + if(fd < 0){ + return 1; + } + openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; + size_t u; + + //mark nonblocking + #ifdef _WIN32 + unsigned long flags = 1; + if(ioctlsocket(fd, FIONBIO, &flags)){ + #else + int flags = fcntl(fd, F_GETFL, 0); + if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0){ + #endif + LOGPF("Failed to set client descriptor on %s nonblocking", inst->name); + close(fd); + return 0; + } + + //find a client block + for(u = 0; u < data->clients; u++){ + if(data->client[u].fd <= 0){ + break; + } + } + + //if no free slot, make one + if(u == data->clients){ + data->client = realloc(data->client, (data->clients + 1) * sizeof(openpixel_client)); + if(!data->client){ + data->clients = 0; + LOG("Failed to allocate memory"); + return 1; + } + data->clients++; + } + + data->client[u].fd = fd; + data->client[u].buffer = -1; + data->client[u].offset = 0; + + return mm_manage_fd(fd, BACKEND_NAME, 1, inst); +} + +static int openpixel_client_handle(instance* inst, int fd){ + openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; + uint8_t buffer[8192]; + size_t c = 0, offset = 0, u; + ssize_t bytes_left = 0; + channel* chan = NULL; + channel_value val; + + for(c = 0; c < data->clients; c++){ + if(data->client[c].fd == fd){ + break; + } + } + + if(c == data->clients){ + LOGPF("Unknown client descriptor signaled on %s", inst->name); + return 1; + } + + //FIXME might want to read until EAGAIN + ssize_t bytes = recv(fd, buffer, sizeof(buffer), 0); + if(bytes <= 0){ + if(bytes < 0){ + LOGPF("Failed to receive from client: %s", strerror(errno)); + } + + //close the connection + close(fd); + data->client[c].fd = -1; + //unmanage the fd + mm_manage_fd(fd, BACKEND_NAME, 0, NULL); + return 0; + } + + for(bytes_left = bytes - offset; bytes_left > 0; bytes_left = bytes - offset){ + if(data->client[c].buffer == -1){ + //read a header + DBGPF("Reading %" PRIsize_t " bytes to header at offset %" PRIsize_t ", header size %" PRIsize_t ", %" PRIsize_t " bytes left", min(sizeof(openpixel_header) - data->client[c].offset, bytes_left), data->client[c].offset, sizeof(openpixel_header), bytes_left); + memcpy(((uint8_t*) (&data->client[c].hdr)) + data->client[c].offset, buffer + offset, min(sizeof(openpixel_header) - data->client[c].offset, bytes_left)); + + //if done, resolve buffer + if(sizeof(openpixel_header) - data->client[c].offset < bytes_left){ + data->client[c].buffer = openpixel_buffer_find(data, data->client[c].hdr.strip, 1); + //if no buffer or mode mismatch, ignore data + if(data->client[c].buffer < 0 + || data->mode != data->client[c].hdr.mode){ + data->client[c].buffer = -2; //mark for ignore + } + data->client[c].left = be16toh(data->client[c].hdr.length); + data->client[c].offset = 0; + } + //if not, update client offset + else{ + data->client[c].offset += bytes_left; + } + + //update scan offset + offset += min(sizeof(openpixel_header) - data->client[c].offset, bytes_left); + } + else{ + //read data + if(data->client[c].buffer == -2){ + //ignore data + offset += min(data->client[c].left, bytes_left); + data->client[c].offset += min(data->client[c].left, bytes_left); + data->client[c].left -= min(data->client[c].left, bytes_left); + } + else{ + if(data->mode == rgb8){ + for(u = 0; u < bytes_left; u++){ + //if over buffer length, ignore + if(u + data->client[c].offset >= data->buffer[data->client[c].buffer].bytes){ + data->client[c].buffer = -2; + break; + } + + //FIXME if at start of trailing non-multiple of 3, ignore + + //update changed channels + if(data->buffer[data->client[c].buffer].data.u8[u + data->client[c].offset] != buffer[offset + u]){ + data->buffer[data->client[c].buffer].data.u8[u + data->client[c].offset] = buffer[offset + u]; + chan = mm_channel(inst, ((uint64_t) data->client[c].hdr.strip << 32) | (u + data->client[c].offset + 1), 0); + if(chan){ + //push event + val.raw.u64 = buffer[offset + u]; + val.normalised = (double) buffer[offset + u] / 255.0; + if(mm_channel_event(chan, val)){ + LOG("Failed to push channel event to core"); + //FIXME err out here + } + } + } + } + + //update offsets + offset += u; + data->client[c].offset += u; + data->client[c].left -= u; + } + else{ + //TODO byte-order conversion may be on recv boundary + //if over buffer length, ignore + //skip non-multiple-of 6 trailing data + } + } + + //end of data, return to reading headers + if(data->client[c].left == 0){ + data->client[c].buffer = -1; + data->client[c].offset = 0; + data->client[c].left = 0; + } + } + } + + return 0; +} + static int openpixel_handle(size_t num, managed_fd* fds){ - //TODO handle bcast + size_t u; + instance* inst = NULL; + openpixel_instance_data* data = NULL; + uint8_t buffer[8192]; + ssize_t bytes; + + for(u = 0; u < num; u++){ + inst = (instance*) fds[u].impl; + data = (openpixel_instance_data*) inst->impl; + + if(fds[u].fd == data->dest_fd){ + //destination fd ready to read + //since the protocol does not define any responses, the connection was probably closed + bytes = recv(data->dest_fd, buffer, sizeof(buffer), 0); + if(bytes <= 0){ + LOGPF("Output descriptor closed on instance %s", inst->name); + //unmanage the fd to give the core some rest + mm_manage_fd(data->dest_fd, BACKEND_NAME, 0, NULL); + } + else{ + LOGPF("Unhandled response data on %s (%" PRIsize_t" bytes)", inst->name, bytes); + } + } + else if(fds[u].fd == data->listen_fd){ + //listen fd ready to read, accept a new client + if(openpixel_client_new(inst, accept(data->listen_fd, NULL, NULL))){ + return 1; + } + } + else{ + //handle client input + openpixel_client_handle(inst, fds[u].fd); + } + } return 0; } @@ -323,12 +528,11 @@ static int openpixel_shutdown(size_t n, instance** inst){ //shutdown all clients for(p = 0; p < data->clients; p++){ - if(data->client_fd[p] >= 0){ - close(data->client_fd[p]); + if(data->client[p].fd>= 0){ + close(data->client[p].fd); } } - free(data->client_fd); - free(data->bytes_left); + free(data->client); //close all configured fds if(data->listen_fd >= 0){ -- cgit v1.2.3 From cff53b6fb1996a24dbdef3657e4fac6558913c33 Mon Sep 17 00:00:00 2001 From: cbdev Date: Fri, 28 Feb 2020 18:54:35 +0100 Subject: Restructure openpixelcontrol --- backends/openpixelcontrol.c | 214 ++++++++++++++++++++++++++------------------ 1 file changed, 126 insertions(+), 88 deletions(-) (limited to 'backends/openpixelcontrol.c') diff --git a/backends/openpixelcontrol.c b/backends/openpixelcontrol.c index 3a94a12..d386c26 100644 --- a/backends/openpixelcontrol.c +++ b/backends/openpixelcontrol.c @@ -1,4 +1,5 @@ #define BACKEND_NAME "openpixelcontrol" +#define DEBUG #include @@ -217,12 +218,37 @@ static channel* openpixel_channel(instance* inst, char* spec, uint8_t flags){ return mm_channel(inst, ((uint64_t) strip) << 32 | channel, 1); } +static int openpixel_output_data(openpixel_instance_data* data){ + size_t u; + openpixel_header hdr; + + //send updated strips + for(u = 0; u < data->buffers; u++){ + if(!(data->buffer[u].flags & OPENPIXEL_INPUT) && (data->buffer[u].flags & OPENPIXEL_MARK)){ + //remove mark + data->buffer[u].flags &= ~OPENPIXEL_MARK; + + //prepare header + hdr.strip = data->buffer[u].strip; + hdr.mode = data->mode; + hdr.length = htobe16(data->buffer[u].bytes); + + //output data + if(mmbackend_send(data->dest_fd, (uint8_t*) &hdr, sizeof(hdr)) + || mmbackend_send(data->dest_fd, data->buffer[u].data.u8, data->buffer[u].bytes)){ + return 1; + } + } + } + + return 0; +} + static int openpixel_set(instance* inst, size_t num, channel** c, channel_value* v){ openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; size_t u, p; ssize_t buffer; uint32_t strip, channel; - openpixel_header hdr; for(u = 0; u < num; u++){ //read strip/channel @@ -266,25 +292,7 @@ static int openpixel_set(instance* inst, size_t num, channel** c, channel_value* } } - //send updated strips - for(u = 0; u < data->buffers; u++){ - if(!(data->buffer[u].flags & OPENPIXEL_INPUT) && (data->buffer[u].flags & OPENPIXEL_MARK)){ - //remove mark - data->buffer[u].flags &= ~OPENPIXEL_MARK; - - //prepare header - hdr.strip = data->buffer[u].strip; - hdr.mode = data->mode; - hdr.length = htobe16(data->buffer[u].bytes); - - //output data - if(mmbackend_send(data->dest_fd, (uint8_t*) &hdr, sizeof(hdr)) - || mmbackend_send(data->dest_fd, data->buffer[u].data.u8, data->buffer[u].bytes)){ - return 1; - } - } - } - return 0; + return openpixel_output_data(data); } static int openpixel_client_new(instance* inst, int fd){ @@ -329,17 +337,97 @@ static int openpixel_client_new(instance* inst, int fd){ data->client[u].buffer = -1; data->client[u].offset = 0; + LOGPF("New client on instance %s", inst->name); return mm_manage_fd(fd, BACKEND_NAME, 1, inst); } -static int openpixel_client_handle(instance* inst, int fd){ +static ssize_t openpixel_client_pixeldata(instance* inst, openpixel_client* client, uint8_t* buffer, size_t bytes_left){ openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; - uint8_t buffer[8192]; - size_t c = 0, offset = 0, u; - ssize_t bytes_left = 0; + size_t u; channel* chan = NULL; channel_value val; + if(client->buffer == -2){ + //ignore data + u = min(client->left, bytes_left); + client->offset += u; + client->left -= u; + return u; + } + else{ + if(data->mode == rgb8){ + for(u = 0; u < bytes_left; u++){ + //if over buffer length, ignore + if(u + client->offset >= data->buffer[client->buffer].bytes){ + client->buffer = -2; + break; + } + + //FIXME if at start of trailing non-multiple of 3, ignore + + //update changed channels + if(data->buffer[client->buffer].data.u8[u + client->offset] != buffer[u]){ + data->buffer[client->buffer].data.u8[u + client->offset] = buffer[u]; + chan = mm_channel(inst, ((uint64_t) client->hdr.strip << 32) | (u + client->offset + 1), 0); + if(chan){ + //push event + val.raw.u64 = buffer[u]; + val.normalised = (double) buffer[u] / 255.0; + if(mm_channel_event(chan, val)){ + LOG("Failed to push channel event to core"); + } + } + } + } + + //update offsets + client->offset += u; + client->left -= u; + return u; + } + else{ + //TODO byte-order conversion may be on recv boundary + //if over buffer length, ignore + //skip non-multiple-of 6 trailing data + } + } + return -1; +} + +static ssize_t openpixel_client_headerdata(instance* inst, openpixel_client* client, uint8_t* buffer, size_t bytes_left){ + openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; + size_t bytes_consumed = min(sizeof(openpixel_header) - client->offset, bytes_left); + + DBGPF("Reading %" PRIsize_t " bytes to header at offset %" PRIsize_t ", header size %" PRIsize_t ", %" PRIsize_t " bytes left", bytes_consumed, client->offset, sizeof(openpixel_header), bytes_left); + memcpy(((uint8_t*) (&client->hdr)) + client->offset, buffer, bytes_consumed); + + //if done, resolve buffer + if(sizeof(openpixel_header) - client->offset <= bytes_left){ + client->buffer = openpixel_buffer_find(data, client->hdr.strip, 1); + //TODO handle broadcast strip input + //if no buffer or mode mismatch, ignore data + if(client->buffer < 0 + || data->mode != client->hdr.mode){ + client->buffer = -2; //mark for ignore + } + client->left = be16toh(client->hdr.length); + client->offset = 0; + } + //if not, update client offset + else{ + client->offset += bytes_consumed; + } + + //update scan offset + return bytes_consumed; +} + +static int openpixel_client_handle(instance* inst, int fd){ + openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; + uint8_t buffer[8192]; + size_t c = 0, offset = 0; + ssize_t bytes_left = 0, bytes_handled; + for(c = 0; c < data->clients; c++){ if(data->client[c].fd == fd){ break; @@ -361,81 +449,27 @@ static int openpixel_client_handle(instance* inst, int fd){ //close the connection close(fd); data->client[c].fd = -1; + //unmanage the fd + LOGPF("Client disconnected on %s", inst->name); mm_manage_fd(fd, BACKEND_NAME, 0, NULL); return 0; } + DBGPF("Received %" PRIsize_t " bytes on %s", bytes, inst->name); for(bytes_left = bytes - offset; bytes_left > 0; bytes_left = bytes - offset){ if(data->client[c].buffer == -1){ //read a header - DBGPF("Reading %" PRIsize_t " bytes to header at offset %" PRIsize_t ", header size %" PRIsize_t ", %" PRIsize_t " bytes left", min(sizeof(openpixel_header) - data->client[c].offset, bytes_left), data->client[c].offset, sizeof(openpixel_header), bytes_left); - memcpy(((uint8_t*) (&data->client[c].hdr)) + data->client[c].offset, buffer + offset, min(sizeof(openpixel_header) - data->client[c].offset, bytes_left)); - - //if done, resolve buffer - if(sizeof(openpixel_header) - data->client[c].offset < bytes_left){ - data->client[c].buffer = openpixel_buffer_find(data, data->client[c].hdr.strip, 1); - //if no buffer or mode mismatch, ignore data - if(data->client[c].buffer < 0 - || data->mode != data->client[c].hdr.mode){ - data->client[c].buffer = -2; //mark for ignore - } - data->client[c].left = be16toh(data->client[c].hdr.length); - data->client[c].offset = 0; + bytes_handled = openpixel_client_headerdata(inst, data->client + c, buffer + offset, bytes_left); + if(bytes_handled < 0){ + //FIXME handle errors } - //if not, update client offset - else{ - data->client[c].offset += bytes_left; - } - - //update scan offset - offset += min(sizeof(openpixel_header) - data->client[c].offset, bytes_left); } else{ //read data - if(data->client[c].buffer == -2){ - //ignore data - offset += min(data->client[c].left, bytes_left); - data->client[c].offset += min(data->client[c].left, bytes_left); - data->client[c].left -= min(data->client[c].left, bytes_left); - } - else{ - if(data->mode == rgb8){ - for(u = 0; u < bytes_left; u++){ - //if over buffer length, ignore - if(u + data->client[c].offset >= data->buffer[data->client[c].buffer].bytes){ - data->client[c].buffer = -2; - break; - } - - //FIXME if at start of trailing non-multiple of 3, ignore - - //update changed channels - if(data->buffer[data->client[c].buffer].data.u8[u + data->client[c].offset] != buffer[offset + u]){ - data->buffer[data->client[c].buffer].data.u8[u + data->client[c].offset] = buffer[offset + u]; - chan = mm_channel(inst, ((uint64_t) data->client[c].hdr.strip << 32) | (u + data->client[c].offset + 1), 0); - if(chan){ - //push event - val.raw.u64 = buffer[offset + u]; - val.normalised = (double) buffer[offset + u] / 255.0; - if(mm_channel_event(chan, val)){ - LOG("Failed to push channel event to core"); - //FIXME err out here - } - } - } - } - - //update offsets - offset += u; - data->client[c].offset += u; - data->client[c].left -= u; - } - else{ - //TODO byte-order conversion may be on recv boundary - //if over buffer length, ignore - //skip non-multiple-of 6 trailing data - } + bytes_handled = openpixel_client_pixeldata(inst, data->client + c, buffer + offset, bytes_left); + if(bytes_handled < 0){ + //FIXME handle errors } //end of data, return to reading headers @@ -445,7 +479,9 @@ static int openpixel_client_handle(instance* inst, int fd){ data->client[c].left = 0; } } + offset += bytes_handled; } + DBGPF("Processing done on %s", inst->name); return 0; } @@ -482,7 +518,9 @@ static int openpixel_handle(size_t num, managed_fd* fds){ } else{ //handle client input - openpixel_client_handle(inst, fds[u].fd); + if(openpixel_client_handle(inst, fds[u].fd)){ + return 1; + } } } return 0; -- cgit v1.2.3 From a103c7a7dcabdb84399dce2a1ed517299d0f5414 Mon Sep 17 00:00:00 2001 From: cbdev Date: Fri, 28 Feb 2020 19:21:45 +0100 Subject: Do not build openpixelcontrol with debug output --- backends/openpixelcontrol.c | 1 - 1 file changed, 1 deletion(-) (limited to 'backends/openpixelcontrol.c') diff --git a/backends/openpixelcontrol.c b/backends/openpixelcontrol.c index d386c26..0f590c6 100644 --- a/backends/openpixelcontrol.c +++ b/backends/openpixelcontrol.c @@ -1,5 +1,4 @@ #define BACKEND_NAME "openpixelcontrol" -#define DEBUG #include -- cgit v1.2.3 From 454e757f740b74d714e9fa47bfa4954cb30e67ba Mon Sep 17 00:00:00 2001 From: cbdev Date: Fri, 28 Feb 2020 21:10:45 +0100 Subject: Implement openpixelcontrol 16bit server mode --- backends/openpixelcontrol.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) (limited to 'backends/openpixelcontrol.c') diff --git a/backends/openpixelcontrol.c b/backends/openpixelcontrol.c index 0f590c6..62cdb68 100644 --- a/backends/openpixelcontrol.c +++ b/backends/openpixelcontrol.c @@ -385,9 +385,38 @@ static ssize_t openpixel_client_pixeldata(instance* inst, openpixel_client* clie return u; } else{ - //TODO byte-order conversion may be on recv boundary - //if over buffer length, ignore - //skip non-multiple-of 6 trailing data + for(u = 0; u < bytes_left; u++){ + //if over buffer length, ignore + if(u + client->offset >= data->buffer[client->buffer].bytes){ + client->buffer = -2; + break; + } + + //if at start of trailing non-multiple of 6, ignore + if((client->offset + u) >= (client->offset + client->left) - ((client->offset + client->left) % 6)){ + client->buffer = -2; + break; + } + + //byte-order conversion may be on message boundary, do it via a buffer + client->boundary.u8[(client->offset + u) % 2] = buffer[u]; + + //detect and update changed channels + if((client->offset + u) % 2 + && data->buffer[client->buffer].data.u16[(u + client->offset) / 2] != be16toh(client->boundary.u16)){ + data->buffer[client->buffer].data.u16[(u + client->offset) / 2] = be16toh(client->boundary.u16); + chan = mm_channel(inst, ((uint64_t) client->hdr.strip << 32) | ((u + client->offset) / 2 + 1), 0); + if(chan){ + //push event + val.raw.u64 = be16toh(client->boundary.u16);; + val.normalised = (double) val.raw.u64 / 65535.0; + if(mm_channel_event(chan, val)){ + LOG("Failed to push channel event to core"); + } + } + + } + } } } return -1; -- cgit v1.2.3 From 1b3f7610d47ba5464e7aa2e16b5b1a27b7d0f5a3 Mon Sep 17 00:00:00 2001 From: cbdev Date: Fri, 6 Mar 2020 23:46:43 +0100 Subject: Implement openpixelcontrol broadcast (strip 0) input --- backends/openpixelcontrol.c | 191 +++++++++++++++++++++++++++++--------------- 1 file changed, 128 insertions(+), 63 deletions(-) (limited to 'backends/openpixelcontrol.c') diff --git a/backends/openpixelcontrol.c b/backends/openpixelcontrol.c index 62cdb68..ca395d3 100644 --- a/backends/openpixelcontrol.c +++ b/backends/openpixelcontrol.c @@ -1,4 +1,5 @@ #define BACKEND_NAME "openpixelcontrol" +#define DEBUG #include @@ -115,7 +116,7 @@ static ssize_t openpixel_buffer_find(openpixel_instance_data* data, uint8_t stri static int openpixel_buffer_extend(openpixel_instance_data* data, uint8_t strip, uint8_t input, uint16_t length){ ssize_t buffer = openpixel_buffer_find(data, strip, input); - + //length is in component-channels, round it to the nearest rgb-triplet //this guarantees that any allocated buffer has at least three bytes, which is important to parts of the receive handler length = (length % 3) ? ((length / 3) + 1) * 3 : length; @@ -340,12 +341,86 @@ static int openpixel_client_new(instance* inst, int fd){ return mm_manage_fd(fd, BACKEND_NAME, 1, inst); } -static ssize_t openpixel_client_pixeldata(instance* inst, openpixel_client* client, uint8_t* buffer, size_t bytes_left){ - openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; +static size_t openpixel_strip_pixeldata8(instance* inst, openpixel_client* client, uint8_t* data, openpixel_buffer* buffer, size_t bytes_left){ + channel* chan = NULL; + channel_value val; size_t u; + + for(u = 0; u < bytes_left; u++){ + //if over buffer length, ignore + if(u + client->offset >= buffer->bytes){ + client->buffer = -2; + break; + } + + //FIXME if at start of trailing non-multiple of 3, ignore + + //update changed channels + if(buffer->data.u8[u + client->offset] != data[u]){ + buffer->data.u8[u + client->offset] = data[u]; + chan = mm_channel(inst, ((uint64_t) buffer->strip << 32) | (u + client->offset + 1), 0); + if(chan){ + //push event + val.raw.u64 = data[u]; + val.normalised = (double) data[u] / 255.0; + if(mm_channel_event(chan, val)){ + LOG("Failed to push channel event to core"); + } + } + } + } + return u; +} + +static size_t openpixel_strip_pixeldata16(instance* inst, openpixel_client* client, uint8_t* data, openpixel_buffer* buffer, size_t bytes_left){ channel* chan = NULL; channel_value val; + size_t u; + for(u = 0; u < bytes_left; u++){ + //if over buffer length, ignore + if(u + client->offset >= buffer->bytes){ + client->buffer = -2; + break; + } + + //if at start of trailing non-multiple of 6, ignore + if((client->offset + u) >= (client->offset + client->left) - ((client->offset + client->left) % 6)){ + client->buffer = -2; + break; + } + + //byte-order conversion may be on message boundary, do it via a buffer + client->boundary.u8[(client->offset + u) % 2] = data[u]; + + //detect and update changed channels + if((client->offset + u) % 2 + && buffer->data.u16[(u + client->offset) / 2] != be16toh(client->boundary.u16)){ + buffer->data.u16[(u + client->offset) / 2] = be16toh(client->boundary.u16); + chan = mm_channel(inst, ((uint64_t) buffer->strip << 32) | ((u + client->offset) / 2 + 1), 0); + if(chan){ + //push event + val.raw.u64 = be16toh(client->boundary.u16);; + val.normalised = (double) val.raw.u64 / 65535.0; + if(mm_channel_event(chan, val)){ + LOG("Failed to push channel event to core"); + } + } + + } + } + return u; +} + +static ssize_t openpixel_client_pixeldata(instance* inst, openpixel_client* client, uint8_t* buffer, size_t bytes_left){ + openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; + openpixel_client temp_client = { + .fd = -1 + }; + ssize_t u, p; + uint8_t processing_done = 1; + + //ignore data if(client->buffer == -2){ //ignore data u = min(client->left, bytes_left); @@ -353,71 +428,55 @@ static ssize_t openpixel_client_pixeldata(instance* inst, openpixel_client* clie client->left -= u; return u; } - else{ - if(data->mode == rgb8){ - for(u = 0; u < bytes_left; u++){ - //if over buffer length, ignore - if(u + client->offset >= data->buffer[client->buffer].bytes){ - client->buffer = -2; - break; + //handle broadcast data + else if(client->buffer == -3){ + //iterate all input strips + for(p = 0; p < data->buffers; p++){ + if(data->buffer[p].flags & OPENPIXEL_INPUT){ + //prepare temporary client + temp_client.buffer = p; + temp_client.hdr = client->hdr; + temp_client.hdr.strip = data->buffer[p].strip; + temp_client.offset = client->offset; + temp_client.left = client->left; + + //run processing on strip + if(data->mode == rgb8){ + openpixel_strip_pixeldata8(inst, &temp_client, buffer, data->buffer + p, bytes_left); } - - //FIXME if at start of trailing non-multiple of 3, ignore - - //update changed channels - if(data->buffer[client->buffer].data.u8[u + client->offset] != buffer[u]){ - data->buffer[client->buffer].data.u8[u + client->offset] = buffer[u]; - chan = mm_channel(inst, ((uint64_t) client->hdr.strip << 32) | (u + client->offset + 1), 0); - if(chan){ - //push event - val.raw.u64 = buffer[u]; - val.normalised = (double) buffer[u] / 255.0; - if(mm_channel_event(chan, val)){ - LOG("Failed to push channel event to core"); - } - } + else{ + openpixel_strip_pixeldata16(inst, &temp_client, buffer, data->buffer + p, bytes_left); + } + if(temp_client.buffer != -2){ + processing_done = 0; } } - - //update offsets - client->offset += u; - client->left -= u; - return u; } - else{ - for(u = 0; u < bytes_left; u++){ - //if over buffer length, ignore - if(u + client->offset >= data->buffer[client->buffer].bytes){ - client->buffer = -2; - break; - } - - //if at start of trailing non-multiple of 6, ignore - if((client->offset + u) >= (client->offset + client->left) - ((client->offset + client->left) % 6)){ - client->buffer = -2; - break; - } - //byte-order conversion may be on message boundary, do it via a buffer - client->boundary.u8[(client->offset + u) % 2] = buffer[u]; - - //detect and update changed channels - if((client->offset + u) % 2 - && data->buffer[client->buffer].data.u16[(u + client->offset) / 2] != be16toh(client->boundary.u16)){ - data->buffer[client->buffer].data.u16[(u + client->offset) / 2] = be16toh(client->boundary.u16); - chan = mm_channel(inst, ((uint64_t) client->hdr.strip << 32) | ((u + client->offset) / 2 + 1), 0); - if(chan){ - //push event - val.raw.u64 = be16toh(client->boundary.u16);; - val.normalised = (double) val.raw.u64 / 65535.0; - if(mm_channel_event(chan, val)){ - LOG("Failed to push channel event to core"); - } - } + //if all strips report being done, ignore the rest of the data + if(processing_done){ + client->buffer = -2; + } - } - } + //remove data + u = min(client->left, bytes_left); + client->offset += u; + client->left -= u; + return u; + } + //process data + else{ + if(data->mode == rgb8){ + u = openpixel_strip_pixeldata8(inst, client, buffer, data->buffer + client->buffer, bytes_left); + } + else{ + u = openpixel_strip_pixeldata16(inst, client, buffer, data->buffer + client->buffer, bytes_left); } + + //update offsets + client->offset += u; + client->left -= u; + return u; } return -1; } @@ -431,8 +490,14 @@ static ssize_t openpixel_client_headerdata(instance* inst, openpixel_client* cli //if done, resolve buffer if(sizeof(openpixel_header) - client->offset <= bytes_left){ - client->buffer = openpixel_buffer_find(data, client->hdr.strip, 1); - //TODO handle broadcast strip input + //if broadcast strip, mark broadcast + if(client->hdr.strip == 0 + && data->mode == client->hdr.mode){ + client->buffer = -3; + } + else{ + client->buffer = openpixel_buffer_find(data, client->hdr.strip, 1); + } //if no buffer or mode mismatch, ignore data if(client->buffer < 0 || data->mode != client->hdr.mode){ -- cgit v1.2.3 From d72d7cc6366f901eb9503bac613e3bc280ac8fc5 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 7 Mar 2020 00:01:35 +0100 Subject: Whitespace fixes --- backends/openpixelcontrol.c | 1 - 1 file changed, 1 deletion(-) (limited to 'backends/openpixelcontrol.c') diff --git a/backends/openpixelcontrol.c b/backends/openpixelcontrol.c index ca395d3..d9d7d4b 100644 --- a/backends/openpixelcontrol.c +++ b/backends/openpixelcontrol.c @@ -1,5 +1,4 @@ #define BACKEND_NAME "openpixelcontrol" -#define DEBUG #include -- cgit v1.2.3 From 413ff0c6eb83fc603f96f870769e50c6234bd171 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 7 Mar 2020 11:31:12 +0100 Subject: Fix openpixelcontrol edge case --- backends/openpixelcontrol.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'backends/openpixelcontrol.c') diff --git a/backends/openpixelcontrol.c b/backends/openpixelcontrol.c index d9d7d4b..b5885b0 100644 --- a/backends/openpixelcontrol.c +++ b/backends/openpixelcontrol.c @@ -1,4 +1,5 @@ #define BACKEND_NAME "openpixelcontrol" +#define DEBUG #include @@ -422,10 +423,9 @@ static ssize_t openpixel_client_pixeldata(instance* inst, openpixel_client* clie //ignore data if(client->buffer == -2){ //ignore data - u = min(client->left, bytes_left); - client->offset += u; - client->left -= u; - return u; + client->offset += bytes_left; + client->left -= bytes_left; + return bytes_left; } //handle broadcast data else if(client->buffer == -3){ @@ -559,7 +559,7 @@ static int openpixel_client_handle(instance* inst, int fd){ } else{ //read data - bytes_handled = openpixel_client_pixeldata(inst, data->client + c, buffer + offset, bytes_left); + bytes_handled = openpixel_client_pixeldata(inst, data->client + c, buffer + offset, min(bytes_left, data->client[c].left)); if(bytes_handled < 0){ //FIXME handle errors } -- cgit v1.2.3 From dde2cca435b2c314a08024825b1daffa6437b947 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 7 Mar 2020 14:11:26 +0100 Subject: Update interval() documentation --- backends/openpixelcontrol.c | 1 - 1 file changed, 1 deletion(-) (limited to 'backends/openpixelcontrol.c') diff --git a/backends/openpixelcontrol.c b/backends/openpixelcontrol.c index b5885b0..168e077 100644 --- a/backends/openpixelcontrol.c +++ b/backends/openpixelcontrol.c @@ -1,5 +1,4 @@ #define BACKEND_NAME "openpixelcontrol" -#define DEBUG #include -- cgit v1.2.3 From 636a9592eab1963c2b1a77bd59eaff317fa55b75 Mon Sep 17 00:00:00 2001 From: cbdev Date: Tue, 17 Mar 2020 01:09:52 +0100 Subject: Fix registry apply bug, fix openpixelcontrol broadcast --- backends/openpixelcontrol.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'backends/openpixelcontrol.c') diff --git a/backends/openpixelcontrol.c b/backends/openpixelcontrol.c index 168e077..2a5e01f 100644 --- a/backends/openpixelcontrol.c +++ b/backends/openpixelcontrol.c @@ -495,11 +495,11 @@ static ssize_t openpixel_client_headerdata(instance* inst, openpixel_client* cli } else{ client->buffer = openpixel_buffer_find(data, client->hdr.strip, 1); - } - //if no buffer or mode mismatch, ignore data - if(client->buffer < 0 - || data->mode != client->hdr.mode){ - client->buffer = -2; //mark for ignore + //if no buffer or mode mismatch, ignore data + if(client->buffer < 0 + || data->mode != client->hdr.mode){ + client->buffer = -2; //mark for ignore + } } client->left = be16toh(client->hdr.length); client->offset = 0; -- cgit v1.2.3