diff --git a/Makefile b/Makefile index 9690024b5deac070debc5c16a60261f1d837a48b..f57f57a08430b9699992d658d71fb682359f1cb5 100644 --- a/Makefile +++ b/Makefile @@ -31,13 +31,15 @@ build/%.o: %.c Makefile build/lib/%.o: %.c Makefile | build/lib $(CC) $(CCFLAGS) -c -fPIC -o $@ $< -.PHONY: $(PLUGINS) $(ADAPTORS) -$(ADAPTORS) $(PLUGINS): +.PHONY: $(ADAPTORS) +$(ADAPTORS): Makefile $(MAKE) -C $@ -.PHONY: $(PLUGINS) $(ADAPTORS) -$(ADAPTORS) $(PLUGINS): +.PHONY: $(PLUGINS) +$(PLUGINS): Makefile $(MAKE) -C $@ + cp $@/build/libmoberg_*.so build + .PHONY: TAR TAR: @@ -51,8 +53,7 @@ moberg-$(MOBERG_VERSION).spec: moberg.spec.template Makefile .PHONY: SRPM SRPM: moberg-$(MOBERG_VERSION).spec TAR - rpmbuild --define "_sourcedir $$(pwd)" \ - -bs $< + rpmbuild --define "_sourcedir $$(pwd)" -bs $< diff --git a/plugins/comedi/Makefile b/plugins/comedi/Makefile index 7a574aeb4833e5806e01665c8fcc7a0e8935ec84..346e57658e1ce12e114a43c655630b64729ff87d 100644 --- a/plugins/comedi/Makefile +++ b/plugins/comedi/Makefile @@ -1,14 +1,23 @@ LIBRARIES=libmoberg_comedi.so -CCFLAGS+=-Wall -Werror -I../.. -g +CCFLAGS+=-Wall -Werror -I../.. -I. -O3 -g -fPIC +LDFLAGS+=-Lbuild/ -lmoberg +LDFLAGS_comedi=-shared -fPIC -L../../build -lmoberg -all: $(LIBRARIES:%=../../build/%) +all: $(LIBRARIES:%=build/%) -../../build/libmoberg_comedi.so: comedi.c Makefile - $(CC) -o $@ $(CCFLAGS) -shared -fPIC -lcomedi -L../../build -lmoberg $< +build/libmoberg_%.so: build/%.o Makefile | build + $(CC) $(LDFLAGS) $(LDFLAGS_$(*)) -o $@ $(filter %.o,$^) -../../build/libmoberg_comedi.so: ../../moberg.h -../../build/libmoberg_comedi.so: ../../moberg_config.h -../../build/libmoberg_comedi.so: ../../moberg_device.h -../../build/libmoberg_comedi.so: ../../moberg_inline.h -../../build/libmoberg_comedi.so: ../../moberg_module.h -../../build/libmoberg_comedi.so: ../../moberg_parser.h +.PRECIOUS: build/%.o +build/%.o: %.c Makefile | build + $(CC) $(CCFLAGS) -c -o $@ $< + +build: + mkdir -p $@ + +build/comedi.c: ../../moberg.h +build/comedi.o: ../../moberg_config.h +build/comedi.o: ../../moberg_device.h +build/comedi.o: ../../moberg_inline.h +build/comedi.o: ../../moberg_module.h +build/comedi.o: ../../moberg_parser.h diff --git a/plugins/serial2002/Makefile b/plugins/serial2002/Makefile index 9d13537c2362bf126e44833a792ceb233bf005ad..f5a9a0a068787f033301a726a51e9bd208aed013 100644 --- a/plugins/serial2002/Makefile +++ b/plugins/serial2002/Makefile @@ -1,11 +1,21 @@ LIBRARIES=libmoberg_serial2002.so -CCFLAGS+=-Wall -Werror -I../.. -g +CCFLAGS+=-Wall -Werror -I../.. -I. -O3 -g -fPIC LDFLAGS+=-Lbuild/ -lmoberg -MODULES=$(wildcard modules/*) +LDFLAGS_serial2002=-shared -fPIC -L../../build -lmoberg -all: $(LIBRARIES:%=../../build/%) +all: $(LIBRARIES:%=build/%) -../../build/libmoberg_serial2002.so: serial2002.c Makefile - $(CC) -o $@ $(CCFLAGS) -shared -fPIC -L../../build -lmoberg $< +build/libmoberg_%.so: build/%.o Makefile | build + $(CC) $(LDFLAGS) $(LDFLAGS_$(*)) -o $@ $(filter %.o,$^) -../../build/libmoberg_serial2002.so: ../../moberg_module.h +.PRECIOUS: build/%.o +build/%.o: %.c Makefile | build + $(CC) $(CCFLAGS) -c -o $@ $< + +build: + mkdir -p $@ + +build/serial2002.o: ../../moberg_module.h +build/serial2002.o: serial2002_lib.h +build/libmoberg_serial2002.so: build/serial2002.o +build/libmoberg_serial2002.so: build/serial2002_lib.o diff --git a/plugins/serial2002/serial2002.c b/plugins/serial2002/serial2002.c index 06c8176cfba7a342a71c30c3df835cf61130062b..fc90d523260880646882f8a4be77869a2c1903f1 100644 --- a/plugins/serial2002/serial2002.c +++ b/plugins/serial2002/serial2002.c @@ -19,6 +19,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <asm/termbits.h> +#include <linux/serial.h> +#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -29,20 +36,43 @@ #include <moberg_inline.h> #include <moberg_module.h> #include <moberg_parser.h> +#include <serial2002_lib.h> struct moberg_device_context { struct moberg *moberg; int (*dlclose)(void *dlhandle); void *dlhandle; int use_count; - char *name; - int baud; + struct { + int count; + char *name; + int baud; + int fd; + } port; + struct remap_analog { + int count; + struct { + unsigned char index; + unsigned long maxdata; + double min; + double max; + double delta; + } map[31]; + } analog_in, analog_out; + struct remap_digital { + int count; + struct { + unsigned char index; + } map[32]; + } digital_in, digital_out, + encoder_in; }; struct moberg_channel_context { void *to_free; /* To be free'd when use_count goes to zero */ struct moberg_device_context *device; int use_count; + int index; }; struct moberg_channel_analog_in { @@ -70,6 +100,59 @@ struct moberg_channel_encoder_in { struct moberg_channel_context channel_context; }; +static struct moberg_status analog_in_read( + struct moberg_channel_analog_in *analog_in, + double *value) +{ + if (! value) { goto err_einval; } + + struct moberg_channel_context *channel = &analog_in->channel_context; + struct moberg_device_context *device = channel->device; + struct serial2002_data data = { 0, 0 }; + serial2002_poll_channel(device->port.fd, + device->analog_in.map[channel->index].index); + struct moberg_status result = serial2002_read(device->port.fd, 1000, &data); + if (OK(result)) { + *value = (data.value * device->analog_in.map[channel->index].delta + + device->analog_in.map[channel->index].min); + } + return result; +err_einval: + return MOBERG_ERRNO(EINVAL); +} + +static struct moberg_status analog_out_write( + struct moberg_channel_analog_out *analog_out, + double value) +{ + fprintf(stderr, "%s\n", __FUNCTION__); + return MOBERG_OK; +} + +static struct moberg_status digital_in_read( + struct moberg_channel_digital_in *digital_in, + int *value) +{ + fprintf(stderr, "%s\n", __FUNCTION__); + *value = 1; + return MOBERG_OK; +} + +static struct moberg_status digital_out_write( + struct moberg_channel_digital_out *digital_out, + int value) +{ + fprintf(stderr, "%s\n", __FUNCTION__); + return MOBERG_OK; +} + +static struct moberg_status encoder_in_read(struct moberg_channel_encoder_in *encoder_in, + long *value) +{ + fprintf(stderr, "%s\n", __FUNCTION__); + return MOBERG_OK; +} + static struct moberg_device_context *new_context(struct moberg *moberg, int (*dlclose)(void *dlhandle), void *dlhandle) @@ -96,13 +179,123 @@ static int device_down(struct moberg_device_context *context) if (context->use_count <= 0) { moberg_deferred_action(context->moberg, context->dlclose, context->dlhandle); - free(context->name); + free(context->port.name); free(context); return 0; } return context->use_count; } +static void remap_analog( + struct remap_analog *remap, + enum serial2002_kind kind, + struct serial2002_channel *channel, + int count) +{ + remap->count = 0; + for (int i = 0 ; i < count ; i++) { + if (channel[i].kind == kind) { + remap->map[remap->count].index = i; + remap->map[remap->count].maxdata = (1 << channel[i].bits) - 1; + remap->map[remap->count].min = channel[i].min; + remap->map[remap->count].max = channel[i].max; + if (remap->map[remap->count].maxdata) { + remap->map[remap->count].delta = + (remap->map[remap->count].max - remap->map[remap->count].min) / + remap->map[remap->count].maxdata; + } + + fprintf(stderr, "%d -> %d\n", remap->count, i); + remap->count++; + } + } +} + +static void remap_digital( + struct remap_digital *remap, + enum serial2002_kind kind, + struct serial2002_channel *channel, + int count) +{ + remap->count = 0; + for (int i = 0 ; i < count ; i++) { + if (channel[i].kind == kind) { + remap->map[remap->count].index = i; + fprintf(stderr, "%d -> %d\n", remap->count, i); + remap->count++; + } + } +} + +static struct moberg_status device_open(struct moberg_device_context *device) +{ + struct moberg_status result; + int fd = -1; + + fprintf(stderr, "%s\n", __FUNCTION__); + if (device->port.count == 0) { + fd = open(device->port.name, O_RDWR); + if (fd < 0) { goto err_errno; } + struct termios2 termios2; + if (ioctl(fd, TCGETS2, &termios2) < 0) { goto err_errno; } + termios2.c_iflag = 0; + termios2.c_oflag = 0; + termios2.c_lflag = 0; + termios2.c_cflag = CLOCAL | CS8 | CREAD | BOTHER; + termios2.c_cc[VMIN] = 0; + termios2.c_cc[VTIME] = 0; + termios2.c_ispeed = device->port.baud; + termios2.c_ospeed = device->port.baud; + if (ioctl(fd, TCSETS2, &termios2) < 0) { goto err_errno; } + struct serial_struct settings; + if (ioctl(fd, TIOCGSERIAL, &settings) >= 0) { + /* It's expected for this to fail for at least some USB serial adapters */ + ioctl(fd, TIOCSSERIAL, &settings); + } + struct serial2002_config config; + result = serial2002_read_config(fd, &config); + if (! OK(result)) { goto err_result; } + remap_analog(&device->analog_in, SERIAL2002_ANALOG_IN, + config.channel_in, 31); + remap_analog(&device->analog_out, SERIAL2002_ANALOG_OUT, + config.channel_out, 31); + remap_digital(&device->digital_in, SERIAL2002_DIGITAL_IN, + config.digital_in, 32); + remap_digital(&device->digital_out, SERIAL2002_DIGITAL_OUT, + config.digital_out, 32); + remap_digital(&device->encoder_in, SERIAL2002_COUNTER_IN, + config.channel_in, 31); + device->port.fd = fd; + } + device->port.count++; + fprintf(stderr, "OPENED %d\n", device->port.count); + return MOBERG_OK; +err_errno: + fprintf(stderr, "ERRNO %d\n", errno); + result = MOBERG_ERRNO(errno); +err_result: + if (fd >= 0) { + close(fd); + } + return result; +} + +static struct moberg_status device_close(struct moberg_device_context *device) +{ + fprintf(stderr, "%s\n", __FUNCTION__); + if (device->port.count < 0) { errno = ENODEV; goto err_errno; } + device->port.count--; + if (device->port.count == 0) { + fprintf(stderr, "CLOSE\n"); + if (close(device->port.fd) < 0) { goto err_errno; } + } + return MOBERG_OK; +err_errno: + fprintf(stderr, "CLOSE %d\n", errno); + return MOBERG_ERRNO(errno); + +} + static int channel_up(struct moberg_channel *channel) { device_up(channel->context->device); @@ -121,23 +314,39 @@ static int channel_down(struct moberg_channel *channel) return channel->context->use_count; } +static struct moberg_status channel_open(struct moberg_channel *channel) +{ + fprintf(stderr, "%s\n", __FUNCTION__); + struct moberg_status result = device_open(channel->context->device); + return result; +} + +static struct moberg_status channel_close(struct moberg_channel *channel) +{ + fprintf(stderr, "%s\n", __FUNCTION__); + struct moberg_status result = device_close(channel->context->device); + return result; +} + static void init_channel( struct moberg_channel *channel, void *to_free, struct moberg_channel_context *context, - struct moberg_device_context *device, + struct moberg_device_context *device, + int index, enum moberg_channel_kind kind, union moberg_channel_action action) { context->to_free = to_free; context->device = device; context->use_count = 0; - + context->index = index; + channel->context = context; channel->up = channel_up; channel->down = channel_down; - channel->open = NULL; - channel->close = NULL; + channel->open = channel_open; + channel->close = channel_close; channel->kind = kind; channel->action = action; }; @@ -155,14 +364,14 @@ static struct moberg_status parse_config( if (! acceptsym(c, tok_EQUAL, NULL)) { goto syntax_err; } if (! acceptsym(c, tok_STRING, &name)) { goto syntax_err; } if (! acceptsym(c, tok_SEMICOLON, NULL)) { goto syntax_err; } - device->name = strndup(name.u.idstr.value, name.u.idstr.length); - if (! device->name) { goto err_enomem; } + device->port.name = strndup(name.u.idstr.value, name.u.idstr.length); + if (! device->port.name) { goto err_enomem; } } else if (acceptkeyword(c, "baud")) { token_t baud; if (! acceptsym(c, tok_EQUAL, NULL)) { goto syntax_err; } if (! acceptsym(c, tok_INTEGER, &baud)) { goto syntax_err; } if (! acceptsym(c, tok_SEMICOLON, NULL)) { goto syntax_err; } - device->baud = baud.u.integer.value; + device->port.baud = baud.u.integer.value; } else { goto syntax_err; } @@ -207,10 +416,11 @@ static struct moberg_status parse_map( channel, &channel->channel_context, device, + i, kind, (union moberg_channel_action) { .analog_in.context=channel, - .analog_in.read=NULL }); + .analog_in.read=analog_in_read }); map->map(map->device, &channel->channel); } break; case chan_ANALOGOUT: { @@ -221,10 +431,11 @@ static struct moberg_status parse_map( channel, &channel->channel_context, device, + i, kind, (union moberg_channel_action) { .analog_out.context=channel, - .analog_out.write=NULL }); + .analog_out.write=analog_out_write }); map->map(map->device, &channel->channel); } break; case chan_DIGITALIN: { @@ -235,10 +446,11 @@ static struct moberg_status parse_map( channel, &channel->channel_context, device, + i, kind, (union moberg_channel_action) { .digital_in.context=channel, - .digital_in.read=NULL }); + .digital_in.read=digital_in_read }); map->map(map->device, &channel->channel); } break; case chan_DIGITALOUT: { @@ -249,10 +461,11 @@ static struct moberg_status parse_map( channel, &channel->channel_context, device, + i, kind, (union moberg_channel_action) { .digital_out.context=channel, - .digital_out.write=NULL }); + .digital_out.write=digital_out_write }); map->map(map->device, &channel->channel); } break; case chan_ENCODERIN: { @@ -263,10 +476,11 @@ static struct moberg_status parse_map( channel, &channel->channel_context, device, + i, kind, (union moberg_channel_action) { .encoder_in.context=channel, - .encoder_in.read=NULL }); + .encoder_in.read=encoder_in_read }); map->map(map->device, &channel->channel); } break; } diff --git a/plugins/serial2002/serial2002_lib.c b/plugins/serial2002/serial2002_lib.c new file mode 100644 index 0000000000000000000000000000000000000000..295b43e3003cd8a46ee1b2db3026a24ff632ae34 --- /dev/null +++ b/plugins/serial2002/serial2002_lib.c @@ -0,0 +1,693 @@ +/* + serial2002_lib.c -- serial2002 protocol + + Copyright (C) 2002,2019 Anders Blomdell <anders.blomdell@gmail.com> + + This file is part of Moberg. + + Moberg is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +#include <unistd.h> +#include <stdio.h> +#include <poll.h> +#include <errno.h> +#include <string.h> +#include <moberg_inline.h> +#include <serial2002_lib.h> + +static struct moberg_status tty_write(int fd, unsigned char *buf, int count) +{ + int n = 0; + while (n < count) { + int written = write(fd, &buf[n], count - n); + if (written == 0) { + fprintf(stderr, "Failed to write\n"); + return MOBERG_ERRNO(ENODATA); + } else if (written < 0) { + return MOBERG_ERRNO(errno); + } + n += written; + } + return MOBERG_OK; +} + +static struct moberg_status tty_read(int fd, int timeout, unsigned char *value) +{ + struct pollfd pollfd; + + while (1) { + pollfd.fd = fd; + pollfd.events = POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR; + int err = poll(&pollfd, 1, timeout); + if (err >= 1) { + break; + } else if (err == 0) { + return MOBERG_ERRNO(ETIMEDOUT); + } else if (err < 0) { + return MOBERG_ERRNO(errno); + } + } + int err = read(fd, value, 1); + if (err == 1) { + return MOBERG_OK; + } else { + return MOBERG_ERRNO(errno); + } +} + +struct moberg_status serial2002_poll_digital(int fd, int channel) +{ + unsigned char cmd; + + cmd = 0x40 | (channel & 0x1f); + return tty_write(fd, &cmd, 1); +} + +struct moberg_status serial2002_poll_channel(int fd, int channel) +{ + unsigned char cmd; + + cmd = 0x60 | (channel & 0x1f); + return tty_write(fd, &cmd, 1); +} + +struct moberg_status serial2002_read(int f, int timeout, + struct serial2002_data *value) +{ + int length; + + value->kind = is_invalid; + value->index = 0; + value->value = 0; + length = 0; + while (1) { + unsigned char data; + struct moberg_status result = tty_read(f, timeout, &data); + if (! OK(result)) { + return result; + } + + length++; + if (data < 0) { + fprintf(stderr, "serial2002 error\n"); + break; + } else if (length < 6 && data & 0x80) { + value->value = (value->value << 7) | (data & 0x7f); + } else if (length == 6 && data & 0x80) { + fprintf(stderr, "Too long\n"); + value->kind = is_invalid; + break; + } else { + if (length == 1) { + switch ((data >> 5) & 0x03) { + case 0:{ + value->value = 0; + value->kind = is_digital; + } break; + case 1:{ + value->value = 1; + value->kind = is_digital; + } break; + } + } else { + value->value = (value->value << 2) | ((data & 0x60) >> 5); + value->kind = is_channel; + } + value->index = data & 0x1f; + break; + } + } + return MOBERG_OK; +} + +struct moberg_status serial2002_write(int f, struct serial2002_data data) +{ + if (data.kind == is_digital) { + unsigned char ch = ((data.value << 5) & 0x20) | (data.index & 0x1f); + return tty_write(f, &ch, 1); + } else { + unsigned char ch[6]; + int i = 0; + if (data.value >= (1L << 30)) { + ch[i] = 0x80 | ((data.value >> 30) & 0x03); + i++; + } + if (data.value >= (1L << 23)) { + ch[i] = 0x80 | ((data.value >> 23) & 0x7f); + i++; + } + if (data.value >= (1L << 16)) { + ch[i] = 0x80 | ((data.value >> 16) & 0x7f); + i++; + } + if (data.value >= (1L << 9)) { + ch[i] = 0x80 | ((data.value >> 9) & 0x7f); + i++; + } + ch[i] = 0x80 | ((data.value >> 2) & 0x7f); + i++; + ch[i] = ((data.value << 5) & 0x60) | (data.index & 0x1f); + i++; + return tty_write(f, ch, i); + } +} + +static double interpret_value(int value) +{ + unsigned int unit = (value & 0x7); + unsigned int sign = (value & 0x8) >> 3; + double result = (value & ~0xf) >> 4; + if (sign == 1) { + result = -result; + } + switch (unit) { + case 1: + result *= 1e-3; + break; + case 2: + result *= 1e-6; + break; + } + fprintf(stderr, "%f\n", result); + return result; +} + +static struct moberg_status update_channel(struct serial2002_channel *channel, + unsigned int kind, + unsigned int cmd, + unsigned int value) +{ + if (channel->kind == 0) { + channel->kind = kind; + } else if (channel->kind != kind) { + return MOBERG_ERRNO(EINVAL); + } + channel->valid |= 1 << cmd; + switch (cmd) { + case 0: + channel->bits = value; + break; + case 1: + channel->min = interpret_value(value); + break; + case 2: + channel->max = interpret_value(value); + break; + } + return MOBERG_OK; +} + +static struct moberg_status do_read_config( + int fd, + struct serial2002_config *config) +{ + struct serial2002_data data = { 0, 0 }; + + memset(config, 0, sizeof(*config)); + serial2002_poll_channel(fd, 31); + while (1) { + struct moberg_status result = serial2002_read(fd, 1000, &data); + if (! OK(result)) { return result; } + unsigned int channel = (data.value & 0x0001f); + unsigned int kind = (data.value & 0x00e0) >> 5; + unsigned int cmd = (data.value & 0x0300) >> 8; + unsigned int value = (data.value & ~0x3ff) >> 10; + switch (kind) { + case 0: return MOBERG_OK; + case 1: /* digital in */ + config->digital_in[channel].valid = 1; + config->digital_in[channel].kind = kind; + break; + case 2: /* digital out */ + config->digital_out[channel].valid = 1; + config->digital_out[channel].kind = kind; + break; + case 3: /* analog in */ + update_channel(&config->channel_in[channel], kind, cmd, value); + break; + case 4: /* analog out */ + update_channel(&config->channel_out[channel], kind, cmd, value); + break; + case 5: /* counter in */ + update_channel(&config->channel_in[channel], kind, cmd, value); + break; + } + } +} + +static struct moberg_status check_config(struct serial2002_config *c) +{ + struct moberg_status result = MOBERG_OK; + + for (int i = 0 ; i < 31 ; i++) { + switch (c->channel_in[i].kind) { + case 3: /* analog in */ + if (c->channel_in[i].valid && c->channel_in[i].valid != 0x7) { + fprintf(stderr, "Analog input channel[%d] is missing ", i); + if (! (c->channel_in[i].valid & 0x1)) { fprintf(stderr, "#bits "); } + if (! (c->channel_in[i].valid & 0x2)) { fprintf(stderr, "min "); } + if (! (c->channel_in[i].valid & 0x4)) { fprintf(stderr, "max "); } + fprintf(stderr, "\n"); + result = MOBERG_ERRNO(EINVAL); + } + break; + case 4: /* analog out */ + if (c->channel_out[i].valid && c->channel_out[i].valid != 0x7) { + fprintf(stderr, "Analog output channel[%d] is missing ", i); + if (! (c->channel_out[i].valid & 0x1)) { fprintf(stderr, "#bits "); } + if (! (c->channel_out[i].valid & 0x2)) { fprintf(stderr, "min "); } + if (! (c->channel_out[i].valid & 0x4)) { fprintf(stderr, "max "); } + fprintf(stderr, "\n"); + result = MOBERG_ERRNO(EINVAL); + } + case 5: /* counter in */ + if (c->channel_in[i].valid && c->channel_in[i].valid != 0x1) { + fprintf(stderr, "Counter input channel[%d] is missing #bits\n", i); + result = MOBERG_ERRNO(EINVAL); + } + break; + } + } + return result; +} + +struct moberg_status serial2002_read_config( + int fd, + struct serial2002_config *config) +{ + struct moberg_status result = do_read_config(fd, config); + if (OK(result)) { + result = check_config(config); + } + return result; +} + + +#if 0 + +static void x() { + typedef struct { + short int kind; + short int bits; + int min; + int max; + } config_t; + config_t *dig_in_config; + config_t *dig_out_config; + config_t *chan_in_config; + config_t *chan_out_config; + int i; + + result = 0; + dig_in_config = kcalloc(32, sizeof(config_t), GFP_KERNEL); + dig_out_config = kcalloc(32, sizeof(config_t), GFP_KERNEL); + chan_in_config = kcalloc(32, sizeof(config_t), GFP_KERNEL); + chan_out_config = kcalloc(32, sizeof(config_t), GFP_KERNEL); + if (!dig_in_config || !dig_out_config + || !chan_in_config || !chan_out_config) { + result = -ENOMEM; + goto err_alloc_configs; + } + + tty_setspeed(devpriv->tty, devpriv->speed); + poll_channel(devpriv->tty, 31); // Start reading configuration + while (1) { + struct serial2002_data data; + + data = serial_read(devpriv->tty, 1000); + if (data.kind != is_channel || data.index != 31 + || !(data.value & 0xe0)) { + break; + } else { + int command, channel, kind; + config_t *cur_config = 0; + + channel = data.value & 0x1f; + kind = (data.value >> 5) & 0x7; + command = (data.value >> 8) & 0x3; + switch (kind) { + case 1:{ + cur_config = dig_in_config; + } + break; + case 2:{ + cur_config = dig_out_config; + } + break; + case 3:{ + cur_config = chan_in_config; + } + break; + case 4:{ + cur_config = chan_out_config; + } + break; + case 5:{ + cur_config = chan_in_config; + } + break; + } + + if (cur_config) { + cur_config[channel].kind = kind; + switch (command) { + case 0:{ + cur_config[channel]. + bits = + (data. + value >> 10) & + 0x3f; + } + break; + case 1:{ + int unit, sign, min; + unit = (data. + value >> 10) & + 0x7; + sign = (data. + value >> 13) & + 0x1; + min = (data. + value >> 14) & + 0xfffff; + + switch (unit) { + case 0:{ + min = min * 1000000; + } + break; + case 1:{ + min = min * 1000; + } + break; + case 2:{ + min = min * 1; + } + break; + } + if (sign) { + min = -min; + } + cur_config[channel]. + min = min; + } + break; + case 2:{ + int unit, sign, max; + unit = (data. + value >> 10) & + 0x7; + sign = (data. + value >> 13) & + 0x1; + max = (data. + value >> 14) & + 0xfffff; + + switch (unit) { + case 0:{ + max = max * 1000000; + } + break; + case 1:{ + max = max * 1000; + } + break; + case 2:{ + max = max * 1; + } + break; + } + if (sign) { + max = -max; + } + cur_config[channel]. + max = max; + } + break; + } + } + } + } + for (i = 0; i <= 4; i++) { + // Fill in subdev data + config_t *c; + unsigned char *mapping = 0; + serial2002_range_table_t *range = 0; + int kind = 0; + + switch (i) { + case 0:{ + c = dig_in_config; + mapping = devpriv->digital_in_mapping; + kind = 1; + } + break; + case 1:{ + c = dig_out_config; + mapping = devpriv->digital_out_mapping; + kind = 2; + } + break; + case 2:{ + c = chan_in_config; + mapping = devpriv->analog_in_mapping; + range = devpriv->in_range; + kind = 3; + } + break; + case 3:{ + c = chan_out_config; + mapping = devpriv->analog_out_mapping; + range = devpriv->out_range; + kind = 4; + } + break; + case 4:{ + c = chan_in_config; + mapping = devpriv->encoder_in_mapping; + range = devpriv->in_range; + kind = 5; + } + break; + default:{ + c = 0; + } + break; + } + if (c) { + comedi_subdevice *s; + const comedi_lrange **range_table_list = NULL; + unsigned int *maxdata_list; + int j, chan; + + for (chan = 0, j = 0; j < 32; j++) { + if (c[j].kind == kind) { + chan++; + } + } + s = &dev->subdevices[i]; + s->n_chan = chan; + s->maxdata = 0; + kfree(s->maxdata_list); + s->maxdata_list = maxdata_list = + kmalloc(sizeof(lsampl_t) * s->n_chan, + GFP_KERNEL); + if (!s->maxdata_list) { + break; /* error handled below */ + } + kfree(s->range_table_list); + s->range_table = 0; + s->range_table_list = 0; + if (kind == 1 || kind == 2) { + s->range_table = &range_digital; + } else if (range) { + s->range_table_list = range_table_list = + kmalloc(sizeof + (serial2002_range_table_t) * + s->n_chan, GFP_KERNEL); + if (!s->range_table_list) { + break; /* error handled below */ + } + } + for (chan = 0, j = 0; j < 32; j++) { + if (c[j].kind == kind) { + if (mapping) { + mapping[chan] = j; + } + if (range) { + range[j].length = 1; + range[j].range.min = + c[j].min; + range[j].range.max = + c[j].max; + range_table_list[chan] = + (const + comedi_lrange *) + &range[j]; + } + maxdata_list[chan] = + ((long long)1 << c[j]. + bits) - 1; + chan++; + } + } + } + } + if (i <= 4) { + /* Failed to allocate maxdata_list or range_table_list + * for a subdevice that needed it. */ + result = -ENOMEM; + for (i = 0; i <= 4; i++) { + comedi_subdevice *s = &dev->subdevices[i]; + + kfree(s->maxdata_list); + s->maxdata_list = NULL; + kfree(s->range_table_list); + s->range_table_list = NULL; + } + } +err_alloc_configs: + kfree(dig_in_config); + kfree(dig_out_config); + kfree(chan_in_config); + kfree(chan_out_config); + if (result) { + if (devpriv->tty) { + filp_close(devpriv->tty, 0); + devpriv->tty = NULL; + } + } +} +return result; +} + +static int serial2002_di_rinsn(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + int n; + int chan; + + chan = devpriv->digital_in_mapping[CR_CHAN(insn->chanspec)]; + for (n = 0; n < insn->n; n++) { + struct serial2002_data read; + + poll_digital(devpriv->tty, chan); + while (1) { + read = serial_read(devpriv->tty, 1000); + if (read.kind != is_digital || read.index == chan) { + break; + } + } + data[n] = read.value; + } + return n; +} + +static int serial2002_do_winsn(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + int n; + int chan; + + chan = devpriv->digital_out_mapping[CR_CHAN(insn->chanspec)]; + for (n = 0; n < insn->n; n++) { + struct serial2002_data write; + + write.kind = is_digital; + write.index = chan; + write.value = data[n]; + serial_write(devpriv->tty, write); + } + return n; +} + +static int serial2002_ai_rinsn(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + int n; + int chan; + + chan = devpriv->analog_in_mapping[CR_CHAN(insn->chanspec)]; + for (n = 0; n < insn->n; n++) { + struct serial2002_data read; + + poll_channel(devpriv->tty, chan); + while (1) { + read = serial_read(devpriv->tty, 1000); + if (read.kind != is_channel || read.index == chan) { + break; + } + } + data[n] = read.value; + } + return n; +} + +static int serial2002_ao_winsn(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + int n; + int chan; + + chan = devpriv->analog_out_mapping[CR_CHAN(insn->chanspec)]; + for (n = 0; n < insn->n; n++) { + struct serial2002_data write; + + write.kind = is_channel; + write.index = chan; + write.value = data[n]; + serial_write(devpriv->tty, write); + devpriv->ao_readback[chan] = data[n]; + } + return n; +} + +static int serial2002_ao_rinsn(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + int n; + int chan = CR_CHAN(insn->chanspec); + + for (n = 0; n < insn->n; n++) { + data[n] = devpriv->ao_readback[chan]; + } + + return n; +} + +static int serial2002_ei_rinsn(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + int n; + int chan; + + chan = devpriv->encoder_in_mapping[CR_CHAN(insn->chanspec)]; + for (n = 0; n < insn->n; n++) { + struct serial2002_data read; + + poll_channel(devpriv->tty, chan); + while (1) { + read = serial_read(devpriv->tty, 1000); + if (read.kind != is_channel || read.index == chan) { + break; + } + } + data[n] = read.value; + } + return n; +} + +#endif diff --git a/plugins/serial2002/serial2002_lib.h b/plugins/serial2002/serial2002_lib.h new file mode 100644 index 0000000000000000000000000000000000000000..f53133a0a942f16325806bc4a87e55d8dd2758a8 --- /dev/null +++ b/plugins/serial2002/serial2002_lib.h @@ -0,0 +1,64 @@ +/* + serial2002_lib.h -- serial2002 protocol + + Copyright (C) 2002,2019 Anders Blomdell <anders.blomdell@gmail.com> + + This file is part of Moberg. + + Moberg is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +#ifndef __SERIAL2002_LIB_H__ +#define __SERIAL2002_LIB_H__ + +#include <moberg.h> + +struct serial2002_data { + enum { is_invalid, is_digital, is_channel } kind; + int index; + unsigned long value; +}; + +enum serial2002_kind { + SERIAL2002_DIGITAL_IN = 1, + SERIAL2002_DIGITAL_OUT = 2, + SERIAL2002_ANALOG_IN = 3, + SERIAL2002_ANALOG_OUT = 4, + SERIAL2002_COUNTER_IN = 5 +}; + +struct serial2002_config { + struct serial2002_channel { + int valid; + int kind; + int bits; + double min; + double max; + } channel_in[31], channel_out[31], + digital_in[32], digital_out[32]; +}; + +struct moberg_status serial2002_poll_digital(int fd, int channel); + +struct moberg_status serial2002_poll_channel(int fd, int channel); + +struct moberg_status serial2002_read(int fd, int timeout, + struct serial2002_data *data); + +struct moberg_status serial2002_write(int fd, struct serial2002_data data); + +struct moberg_status serial2002_read_config(int fd, + struct serial2002_config *config); + +#endif