Commit c4fbf910 authored by Anders Blomdell's avatar Anders Blomdell
Browse files

serial2002 work

parent d4d91bc5
......@@ -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 $<
......
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
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
......@@ -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;
}
......
/*
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++;
}