aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/comedi/drivers/serial2002.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/comedi/drivers/serial2002.c')
-rw-r--r--drivers/staging/comedi/drivers/serial2002.c895
1 files changed, 415 insertions, 480 deletions
diff --git a/drivers/staging/comedi/drivers/serial2002.c b/drivers/staging/comedi/drivers/serial2002.c
index e6177b48cca..8900086374d 100644
--- a/drivers/staging/comedi/drivers/serial2002.c
+++ b/drivers/staging/comedi/drivers/serial2002.c
@@ -31,8 +31,6 @@ Status: in development
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
#include "../comedidev.h"
#include <linux/delay.h>
@@ -72,7 +70,40 @@ struct serial_data {
unsigned long value;
};
-static long tty_ioctl(struct file *f, unsigned op, unsigned long param)
+/*
+ * The configuration serial_data.value read from the device is
+ * a bitmask that defines specific options of a channel:
+ *
+ * 4:0 - the channel to configure
+ * 7:5 - the kind of channel
+ * 9:8 - the command used to configure the channel
+ *
+ * The remaining bits vary in use depending on the command:
+ *
+ * BITS 15:10 - the channel bits (maxdata)
+ * MIN/MAX 12:10 - the units multiplier for the scale
+ * 13 - the sign of the scale
+ * 33:14 - the base value for the range
+ */
+#define S2002_CFG_CHAN(x) ((x) & 0x1f)
+#define S2002_CFG_KIND(x) (((x) >> 5) & 0x7)
+#define S2002_CFG_KIND_INVALID 0
+#define S2002_CFG_KIND_DIGITAL_IN 1
+#define S2002_CFG_KIND_DIGITAL_OUT 2
+#define S2002_CFG_KIND_ANALOG_IN 3
+#define S2002_CFG_KIND_ANALOG_OUT 4
+#define S2002_CFG_KIND_ENCODER_IN 5
+#define S2002_CFG_CMD(x) (((x) >> 8) & 0x3)
+#define S2002_CFG_CMD_BITS 0
+#define S2002_CFG_CMD_MIN 1
+#define S2002_CFG_CMD_MAX 2
+#define S2002_CFG_BITS(x) (((x) >> 10) & 0x3f)
+#define S2002_CFG_UNITS(x) (((x) >> 10) & 0x7)
+#define S2002_CFG_SIGN(x) (((x) >> 13) & 0x1)
+#define S2002_CFG_BASE(x) (((x) >> 14) & 0xfffff)
+
+static long serial2002_tty_ioctl(struct file *f, unsigned op,
+ unsigned long param)
{
if (f->f_op->unlocked_ioctl)
return f->f_op->unlocked_ioctl(f, op, param);
@@ -80,39 +111,58 @@ static long tty_ioctl(struct file *f, unsigned op, unsigned long param)
return -ENOSYS;
}
-static int tty_write(struct file *f, unsigned char *buf, int count)
+static int serial2002_tty_write(struct file *f, unsigned char *buf, int count)
{
+ const char __user *p = (__force const char __user *)buf;
int result;
mm_segment_t oldfs;
oldfs = get_fs();
set_fs(KERNEL_DS);
f->f_pos = 0;
- result = f->f_op->write(f, buf, count, &f->f_pos);
+ result = f->f_op->write(f, p, count, &f->f_pos);
set_fs(oldfs);
return result;
}
-#if 0
-/*
- * On 2.6.26.3 this occaisonally gave me page faults, worked around by
- * settings.c_cc[VMIN] = 0; settings.c_cc[VTIME] = 0
- */
-static int tty_available(struct file *f)
+static int serial2002_tty_readb(struct file *f, unsigned char *buf)
{
- long result = 0;
- mm_segment_t oldfs;
+ char __user *p = (__force char __user *)buf;
- oldfs = get_fs();
- set_fs(KERNEL_DS);
- tty_ioctl(f, FIONREAD, (unsigned long)&result);
- set_fs(oldfs);
- return result;
+ f->f_pos = 0;
+ return f->f_op->read(f, p, 1, &f->f_pos);
+}
+
+static void serial2002_tty_read_poll_wait(struct file *f, int timeout)
+{
+ struct poll_wqueues table;
+ struct timeval start, now;
+
+ do_gettimeofday(&start);
+ poll_initwait(&table);
+ while (1) {
+ long elapsed;
+ int mask;
+
+ mask = f->f_op->poll(f, &table.pt);
+ if (mask & (POLLRDNORM | POLLRDBAND | POLLIN |
+ POLLHUP | POLLERR)) {
+ break;
+ }
+ do_gettimeofday(&now);
+ elapsed = (1000000 * (now.tv_sec - start.tv_sec) +
+ now.tv_usec - start.tv_usec);
+ if (elapsed > timeout)
+ break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(((timeout - elapsed) * HZ) / 10000);
+ }
+ poll_freewait(&table);
}
-#endif
-static int tty_read(struct file *f, int timeout)
+static int serial2002_tty_read(struct file *f, int timeout)
{
+ unsigned char ch;
int result;
result = -1;
@@ -122,50 +172,19 @@ static int tty_read(struct file *f, int timeout)
oldfs = get_fs();
set_fs(KERNEL_DS);
if (f->f_op->poll) {
- struct poll_wqueues table;
- struct timeval start, now;
-
- do_gettimeofday(&start);
- poll_initwait(&table);
- while (1) {
- long elapsed;
- int mask;
-
- mask = f->f_op->poll(f, &table.pt);
- if (mask & (POLLRDNORM | POLLRDBAND | POLLIN |
- POLLHUP | POLLERR)) {
- break;
- }
- do_gettimeofday(&now);
- elapsed =
- (1000000 * (now.tv_sec - start.tv_sec) +
- now.tv_usec - start.tv_usec);
- if (elapsed > timeout)
- break;
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(((timeout -
- elapsed) * HZ) / 10000);
- }
- poll_freewait(&table);
- {
- unsigned char ch;
+ serial2002_tty_read_poll_wait(f, timeout);
- f->f_pos = 0;
- if (f->f_op->read(f, &ch, 1, &f->f_pos) == 1)
- result = ch;
- }
+ if (serial2002_tty_readb(f, &ch) == 1)
+ result = ch;
} else {
/* Device does not support poll, busy wait */
int retries = 0;
while (1) {
- unsigned char ch;
-
retries++;
if (retries >= timeout)
break;
- f->f_pos = 0;
- if (f->f_op->read(f, &ch, 1, &f->f_pos) == 1) {
+ if (serial2002_tty_readb(f, &ch) == 1) {
result = ch;
break;
}
@@ -177,90 +196,76 @@ static int tty_read(struct file *f, int timeout)
return result;
}
-static void tty_setspeed(struct file *f, int speed)
+static void serial2002_tty_setspeed(struct file *f, int speed)
{
+ struct termios termios;
+ struct serial_struct serial;
mm_segment_t oldfs;
oldfs = get_fs();
set_fs(KERNEL_DS);
- {
- /* Set speed */
- struct termios settings;
-
- tty_ioctl(f, TCGETS, (unsigned long)&settings);
-/* printk("Speed: %d\n", settings.c_cflag & (CBAUD | CBAUDEX)); */
- settings.c_iflag = 0;
- settings.c_oflag = 0;
- settings.c_lflag = 0;
- settings.c_cflag = CLOCAL | CS8 | CREAD;
- settings.c_cc[VMIN] = 0;
- settings.c_cc[VTIME] = 0;
- switch (speed) {
- case 2400:{
- settings.c_cflag |= B2400;
- }
- break;
- case 4800:{
- settings.c_cflag |= B4800;
- }
- break;
- case 9600:{
- settings.c_cflag |= B9600;
- }
- break;
- case 19200:{
- settings.c_cflag |= B19200;
- }
- break;
- case 38400:{
- settings.c_cflag |= B38400;
- }
- break;
- case 57600:{
- settings.c_cflag |= B57600;
- }
- break;
- case 115200:{
- settings.c_cflag |= B115200;
- }
- break;
- default:{
- settings.c_cflag |= B9600;
- }
- break;
- }
- tty_ioctl(f, TCSETS, (unsigned long)&settings);
-/* printk("Speed: %d\n", settings.c_cflag & (CBAUD | CBAUDEX)); */
- }
- {
- /* Set low latency */
- struct serial_struct settings;
- tty_ioctl(f, TIOCGSERIAL, (unsigned long)&settings);
- settings.flags |= ASYNC_LOW_LATENCY;
- tty_ioctl(f, TIOCSSERIAL, (unsigned long)&settings);
+ /* Set speed */
+ serial2002_tty_ioctl(f, TCGETS, (unsigned long)&termios);
+ termios.c_iflag = 0;
+ termios.c_oflag = 0;
+ termios.c_lflag = 0;
+ termios.c_cflag = CLOCAL | CS8 | CREAD;
+ termios.c_cc[VMIN] = 0;
+ termios.c_cc[VTIME] = 0;
+ switch (speed) {
+ case 2400:
+ termios.c_cflag |= B2400;
+ break;
+ case 4800:
+ termios.c_cflag |= B4800;
+ break;
+ case 9600:
+ termios.c_cflag |= B9600;
+ break;
+ case 19200:
+ termios.c_cflag |= B19200;
+ break;
+ case 38400:
+ termios.c_cflag |= B38400;
+ break;
+ case 57600:
+ termios.c_cflag |= B57600;
+ break;
+ case 115200:
+ termios.c_cflag |= B115200;
+ break;
+ default:
+ termios.c_cflag |= B9600;
+ break;
}
+ serial2002_tty_ioctl(f, TCSETS, (unsigned long)&termios);
+
+ /* Set low latency */
+ serial2002_tty_ioctl(f, TIOCGSERIAL, (unsigned long)&serial);
+ serial.flags |= ASYNC_LOW_LATENCY;
+ serial2002_tty_ioctl(f, TIOCSSERIAL, (unsigned long)&serial);
set_fs(oldfs);
}
-static void poll_digital(struct file *f, int channel)
+static void serial2002_poll_digital(struct file *f, int channel)
{
char cmd;
cmd = 0x40 | (channel & 0x1f);
- tty_write(f, &cmd, 1);
+ serial2002_tty_write(f, &cmd, 1);
}
-static void poll_channel(struct file *f, int channel)
+static void serial2002_poll_channel(struct file *f, int channel)
{
char cmd;
cmd = 0x60 | (channel & 0x1f);
- tty_write(f, &cmd, 1);
+ serial2002_tty_write(f, &cmd, 1);
}
-static struct serial_data serial_read(struct file *f, int timeout)
+static struct serial_data serial2002_read(struct file *f, int timeout)
{
struct serial_data result;
int length;
@@ -270,26 +275,23 @@ static struct serial_data serial_read(struct file *f, int timeout)
result.value = 0;
length = 0;
while (1) {
- int data = tty_read(f, timeout);
+ int data = serial2002_tty_read(f, timeout);
length++;
if (data < 0) {
- pr_err("Failed to read serial.\n");
break;
} else if (data & 0x80) {
result.value = (result.value << 7) | (data & 0x7f);
} else {
if (length == 1) {
switch ((data >> 5) & 0x03) {
- case 0:{
- result.value = 0;
- result.kind = is_digital;
- }
+ case 0:
+ result.value = 0;
+ result.kind = is_digital;
break;
- case 1:{
- result.value = 1;
- result.kind = is_digital;
- }
+ case 1:
+ result.value = 1;
+ result.kind = is_digital;
break;
}
} else {
@@ -305,12 +307,12 @@ static struct serial_data serial_read(struct file *f, int timeout)
}
-static void serial_write(struct file *f, struct serial_data data)
+static void serial2002_write(struct file *f, struct serial_data data)
{
if (data.kind == is_digital) {
unsigned char ch =
((data.value << 5) & 0x20) | (data.index & 0x1f);
- tty_write(f, &ch, 1);
+ serial2002_tty_write(f, &ch, 1);
} else {
unsigned char ch[6];
int i = 0;
@@ -334,319 +336,250 @@ static void serial_write(struct file *f, struct serial_data data)
i++;
ch[i] = ((data.value << 5) & 0x60) | (data.index & 0x1f);
i++;
- tty_write(f, ch, i);
+ serial2002_tty_write(f, ch, i);
}
}
-static int serial_2002_open(struct comedi_device *dev)
+struct config_t {
+ short int kind;
+ short int bits;
+ int min;
+ int max;
+};
+
+static int serial2002_setup_subdevice(struct comedi_subdevice *s,
+ struct config_t *cfg,
+ struct serial2002_range_table_t *range,
+ unsigned char *mapping,
+ int kind)
{
- struct serial2002_private *devpriv = dev->private;
- int result;
- char port[20];
+ const struct comedi_lrange **range_table_list = NULL;
+ unsigned int *maxdata_list;
+ int j, chan;
- sprintf(port, "/dev/ttyS%d", devpriv->port);
- devpriv->tty = filp_open(port, O_RDWR, 0);
- if (IS_ERR(devpriv->tty)) {
- result = (int)PTR_ERR(devpriv->tty);
- dev_err(dev->class_dev, "file open error = %d\n", result);
- } else {
- struct config_t {
-
- short int kind;
- short int bits;
- int min;
- int max;
- };
-
- struct config_t *dig_in_config;
- struct config_t *dig_out_config;
- struct config_t *chan_in_config;
- struct config_t *chan_out_config;
- int i;
-
- result = 0;
- dig_in_config = kcalloc(32, sizeof(struct config_t),
- GFP_KERNEL);
- dig_out_config = kcalloc(32, sizeof(struct config_t),
- GFP_KERNEL);
- chan_in_config = kcalloc(32, sizeof(struct config_t),
- GFP_KERNEL);
- chan_out_config = kcalloc(32, sizeof(struct config_t),
- GFP_KERNEL);
- if (!dig_in_config || !dig_out_config
- || !chan_in_config || !chan_out_config) {
- result = -ENOMEM;
- goto err_alloc_configs;
+ for (chan = 0, j = 0; j < 32; j++) {
+ if (cfg[j].kind == kind)
+ chan++;
+ }
+ s->n_chan = chan;
+ s->maxdata = 0;
+ kfree(s->maxdata_list);
+ maxdata_list = kmalloc(sizeof(unsigned int) * s->n_chan, GFP_KERNEL);
+ if (!maxdata_list)
+ return -ENOMEM;
+ s->maxdata_list = maxdata_list;
+ kfree(s->range_table_list);
+ s->range_table = NULL;
+ s->range_table_list = NULL;
+ if (kind == 1 || kind == 2) {
+ s->range_table = &range_digital;
+ } else if (range) {
+ range_table_list =
+ kmalloc(sizeof(struct serial2002_range_table_t) *
+ s->n_chan, GFP_KERNEL);
+ if (!range_table_list)
+ return -ENOMEM;
+ s->range_table_list = range_table_list;
+ }
+ for (chan = 0, j = 0; j < 32; j++) {
+ if (cfg[j].kind == kind) {
+ if (mapping)
+ mapping[chan] = j;
+ if (range) {
+ range[j].length = 1;
+ range[j].range.min = cfg[j].min;
+ range[j].range.max = cfg[j].max;
+ range_table_list[chan] =
+ (const struct comedi_lrange *)&range[j];
+ }
+ maxdata_list[chan] = ((long long)1 << cfg[j].bits) - 1;
+ chan++;
}
+ }
+ return 0;
+}
- tty_setspeed(devpriv->tty, devpriv->speed);
- poll_channel(devpriv->tty, 31); /* Start reading configuration */
- while (1) {
- struct serial_data data;
+static int serial2002_setup_subdevs(struct comedi_device *dev)
+{
+ struct serial2002_private *devpriv = dev->private;
+ struct config_t *di_cfg;
+ struct config_t *do_cfg;
+ struct config_t *ai_cfg;
+ struct config_t *ao_cfg;
+ struct config_t *cfg;
+ struct comedi_subdevice *s;
+ int result = 0;
+ int i;
- data = serial_read(devpriv->tty, 1000);
- if (data.kind != is_channel || data.index != 31
- || !(data.value & 0xe0)) {
- break;
- } else {
- int command, channel, kind;
- struct config_t *cur_config = NULL;
-
- 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;
- }
+ /* Allocate the temporary structs to hold the configuration data */
+ di_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
+ do_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
+ ai_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
+ ao_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
+ if (!di_cfg || !do_cfg || !ai_cfg || !ao_cfg) {
+ result = -ENOMEM;
+ goto err_alloc_configs;
+ }
- 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 */
- struct config_t *c;
- unsigned char *mapping = NULL;
- struct serial2002_range_table_t *range = NULL;
- int kind = 0;
-
- switch (i) {
- case 0:{
- c = dig_in_config;
- mapping = devpriv->digital_in_mapping;
- kind = 1;
- }
+ /* Read the configuration from the connected device */
+ serial2002_tty_setspeed(devpriv->tty, devpriv->speed);
+ serial2002_poll_channel(devpriv->tty, 31);
+ while (1) {
+ struct serial_data data;
+
+ data = serial2002_read(devpriv->tty, 1000);
+ if (data.kind != is_channel || data.index != 31 ||
+ S2002_CFG_KIND(data.value) == S2002_CFG_KIND_INVALID) {
+ break;
+ } else {
+ int channel = S2002_CFG_CHAN(data.value);
+ int range = S2002_CFG_BASE(data.value);
+
+ switch (S2002_CFG_KIND(data.value)) {
+ case S2002_CFG_KIND_DIGITAL_IN:
+ cfg = di_cfg;
break;
- case 1:{
- c = dig_out_config;
- mapping = devpriv->digital_out_mapping;
- kind = 2;
- }
+ case S2002_CFG_KIND_DIGITAL_OUT:
+ cfg = do_cfg;
break;
- case 2:{
- c = chan_in_config;
- mapping = devpriv->analog_in_mapping;
- range = devpriv->in_range;
- kind = 3;
- }
+ case S2002_CFG_KIND_ANALOG_IN:
+ cfg = ai_cfg;
break;
- case 3:{
- c = chan_out_config;
- mapping = devpriv->analog_out_mapping;
- range = devpriv->out_range;
- kind = 4;
- }
+ case S2002_CFG_KIND_ANALOG_OUT:
+ cfg = ao_cfg;
break;
- case 4:{
- c = chan_in_config;
- mapping = devpriv->encoder_in_mapping;
- range = devpriv->in_range;
- kind = 5;
- }
+ case S2002_CFG_KIND_ENCODER_IN:
+ cfg = ai_cfg;
break;
- default:{
- c = NULL;
- }
+ default:
+ cfg = NULL;
break;
}
- if (c) {
- struct comedi_subdevice *s;
- const struct 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(unsigned int) * s->n_chan,
- GFP_KERNEL);
- if (!s->maxdata_list)
- break; /* error handled below */
- kfree(s->range_table_list);
- s->range_table = NULL;
- s->range_table_list = NULL;
- if (kind == 1 || kind == 2) {
- s->range_table = &range_digital;
- } else if (range) {
- s->range_table_list = range_table_list =
- kmalloc(sizeof
- (struct
- serial2002_range_table_t) *
- s->n_chan, GFP_KERNEL);
- if (!s->range_table_list)
- break; /* err 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 struct
- comedi_lrange *)
- &range[j];
- }
- maxdata_list[chan] =
- ((long long)1 << c[j].bits)
- - 1;
- chan++;
- }
+ if (!cfg)
+ continue; /* unknown kind, skip it */
+
+ cfg[channel].kind = S2002_CFG_KIND(data.value);
+
+ switch (S2002_CFG_CMD(data.value)) {
+ case S2002_CFG_CMD_BITS:
+ cfg[channel].bits = S2002_CFG_BITS(data.value);
+ break;
+ case S2002_CFG_CMD_MIN:
+ case S2002_CFG_CMD_MAX:
+ switch (S2002_CFG_UNITS(data.value)) {
+ case 0:
+ range *= 1000000;
+ break;
+ case 1:
+ range *= 1000;
+ break;
+ case 2:
+ range *= 1;
+ break;
}
+ if (S2002_CFG_SIGN(data.value))
+ range = -range;
+ if (S2002_CFG_CMD(data.value) ==
+ S2002_CFG_CMD_MIN)
+ cfg[channel].min = range;
+ else
+ cfg[channel].max = range;
+ break;
}
}
- 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++) {
- struct comedi_subdevice *s;
-
- s = &dev->subdevices[i];
- kfree(s->maxdata_list);
- s->maxdata_list = NULL;
- kfree(s->range_table_list);
- s->range_table_list = NULL;
- }
+ }
+
+ /* Fill in subdevice data */
+ for (i = 0; i <= 4; i++) {
+ unsigned char *mapping = NULL;
+ struct serial2002_range_table_t *range = NULL;
+ int kind = 0;
+
+ s = &dev->subdevices[i];
+
+ switch (i) {
+ case 0:
+ cfg = di_cfg;
+ mapping = devpriv->digital_in_mapping;
+ kind = S2002_CFG_KIND_DIGITAL_IN;
+ break;
+ case 1:
+ cfg = do_cfg;
+ mapping = devpriv->digital_out_mapping;
+ kind = S2002_CFG_KIND_DIGITAL_OUT;
+ break;
+ case 2:
+ cfg = ai_cfg;
+ mapping = devpriv->analog_in_mapping;
+ range = devpriv->in_range;
+ kind = S2002_CFG_KIND_ANALOG_IN;
+ break;
+ case 3:
+ cfg = ao_cfg;
+ mapping = devpriv->analog_out_mapping;
+ range = devpriv->out_range;
+ kind = S2002_CFG_KIND_ANALOG_OUT;
+ break;
+ case 4:
+ cfg = ai_cfg;
+ mapping = devpriv->encoder_in_mapping;
+ range = devpriv->in_range;
+ kind = S2002_CFG_KIND_ENCODER_IN;
+ break;
}
+ if (serial2002_setup_subdevice(s, cfg, range, mapping, kind))
+ break; /* err handled below */
+ }
+ 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++) {
+ 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, NULL);
- devpriv->tty = NULL;
- }
+ kfree(di_cfg);
+ kfree(do_cfg);
+ kfree(ai_cfg);
+ kfree(ao_cfg);
+
+ if (result) {
+ if (devpriv->tty) {
+ filp_close(devpriv->tty, NULL);
+ devpriv->tty = NULL;
}
}
+
+ return result;
+}
+
+static int serial2002_open(struct comedi_device *dev)
+{
+ struct serial2002_private *devpriv = dev->private;
+ int result;
+ char port[20];
+
+ sprintf(port, "/dev/ttyS%d", devpriv->port);
+ devpriv->tty = filp_open(port, O_RDWR, 0);
+ if (IS_ERR(devpriv->tty)) {
+ result = (int)PTR_ERR(devpriv->tty);
+ dev_err(dev->class_dev, "file open error = %d\n", result);
+ } else {
+ result = serial2002_setup_subdevs(dev);
+ }
return result;
}
-static void serial_2002_close(struct comedi_device *dev)
+static void serial2002_close(struct comedi_device *dev)
{
struct serial2002_private *devpriv = dev->private;
@@ -654,9 +587,10 @@ static void serial_2002_close(struct comedi_device *dev)
filp_close(devpriv->tty, NULL);
}
-static int serial2002_di_rinsn(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+static int serial2002_di_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
struct serial2002_private *devpriv = dev->private;
int n;
@@ -666,9 +600,9 @@ static int serial2002_di_rinsn(struct comedi_device *dev,
for (n = 0; n < insn->n; n++) {
struct serial_data read;
- poll_digital(devpriv->tty, chan);
+ serial2002_poll_digital(devpriv->tty, chan);
while (1) {
- read = serial_read(devpriv->tty, 1000);
+ read = serial2002_read(devpriv->tty, 1000);
if (read.kind != is_digital || read.index == chan)
break;
}
@@ -677,9 +611,10 @@ static int serial2002_di_rinsn(struct comedi_device *dev,
return n;
}
-static int serial2002_do_winsn(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+static int serial2002_do_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
struct serial2002_private *devpriv = dev->private;
int n;
@@ -692,14 +627,15 @@ static int serial2002_do_winsn(struct comedi_device *dev,
write.kind = is_digital;
write.index = chan;
write.value = data[n];
- serial_write(devpriv->tty, write);
+ serial2002_write(devpriv->tty, write);
}
return n;
}
-static int serial2002_ai_rinsn(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+static int serial2002_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
struct serial2002_private *devpriv = dev->private;
int n;
@@ -709,9 +645,9 @@ static int serial2002_ai_rinsn(struct comedi_device *dev,
for (n = 0; n < insn->n; n++) {
struct serial_data read;
- poll_channel(devpriv->tty, chan);
+ serial2002_poll_channel(devpriv->tty, chan);
while (1) {
- read = serial_read(devpriv->tty, 1000);
+ read = serial2002_read(devpriv->tty, 1000);
if (read.kind != is_channel || read.index == chan)
break;
}
@@ -720,9 +656,10 @@ static int serial2002_ai_rinsn(struct comedi_device *dev,
return n;
}
-static int serial2002_ao_winsn(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+static int serial2002_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
struct serial2002_private *devpriv = dev->private;
int n;
@@ -735,15 +672,16 @@ static int serial2002_ao_winsn(struct comedi_device *dev,
write.kind = is_channel;
write.index = chan;
write.value = data[n];
- serial_write(devpriv->tty, write);
+ serial2002_write(devpriv->tty, write);
devpriv->ao_readback[chan] = data[n];
}
return n;
}
-static int serial2002_ao_rinsn(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+static int serial2002_ao_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
struct serial2002_private *devpriv = dev->private;
int n;
@@ -755,9 +693,10 @@ static int serial2002_ao_rinsn(struct comedi_device *dev,
return n;
}
-static int serial2002_ei_rinsn(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+static int serial2002_encoder_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
struct serial2002_private *devpriv = dev->private;
int n;
@@ -767,9 +706,9 @@ static int serial2002_ei_rinsn(struct comedi_device *dev,
for (n = 0; n < insn->n; n++) {
struct serial_data read;
- poll_channel(devpriv->tty, chan);
+ serial2002_poll_channel(devpriv->tty, chan);
while (1) {
- read = serial_read(devpriv->tty, 1000);
+ read = serial2002_read(devpriv->tty, 1000);
if (read.kind != is_channel || read.index == chan)
break;
}
@@ -785,20 +724,13 @@ static int serial2002_attach(struct comedi_device *dev,
struct comedi_subdevice *s;
int ret;
- dev_dbg(dev->class_dev, "serial2002: attach\n");
- dev->board_name = dev->driver->driver_name;
-
devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
if (!devpriv)
return -ENOMEM;
dev->private = devpriv;
- dev->open = serial_2002_open;
- dev->close = serial_2002_close;
devpriv->port = it->options[0];
devpriv->speed = it->options[1];
- dev_dbg(dev->class_dev, "/dev/ttyS%d @ %d\n", devpriv->port,
- devpriv->speed);
ret = comedi_alloc_subdevices(dev, 5);
if (ret)
@@ -806,51 +738,54 @@ static int serial2002_attach(struct comedi_device *dev,
/* digital input subdevice */
s = &dev->subdevices[0];
- s->type = COMEDI_SUBD_DI;
- s->subdev_flags = SDF_READABLE;
- s->n_chan = 0;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_read = &serial2002_di_rinsn;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_read = serial2002_di_insn_read;
/* digital output subdevice */
s = &dev->subdevices[1];
- s->type = COMEDI_SUBD_DO;
- s->subdev_flags = SDF_WRITEABLE;
- s->n_chan = 0;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_write = &serial2002_do_winsn;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITEABLE;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_write = serial2002_do_insn_write;
/* analog input subdevice */
s = &dev->subdevices[2];
- s->type = COMEDI_SUBD_AI;
- s->subdev_flags = SDF_READABLE | SDF_GROUND;
- s->n_chan = 0;
- s->maxdata = 1;
- s->range_table = NULL;
- s->insn_read = &serial2002_ai_rinsn;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = NULL;
+ s->insn_read = serial2002_ai_insn_read;
/* analog output subdevice */
s = &dev->subdevices[3];
- s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_WRITEABLE;
- s->n_chan = 0;
- s->maxdata = 1;
- s->range_table = NULL;
- s->insn_write = &serial2002_ao_winsn;
- s->insn_read = &serial2002_ao_rinsn;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITEABLE;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = NULL;
+ s->insn_write = serial2002_ao_insn_write;
+ s->insn_read = serial2002_ao_insn_read;
/* encoder input subdevice */
s = &dev->subdevices[4];
- s->type = COMEDI_SUBD_COUNTER;
- s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
- s->n_chan = 0;
- s->maxdata = 1;
- s->range_table = NULL;
- s->insn_read = &serial2002_ei_rinsn;
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = NULL;
+ s->insn_read = serial2002_encoder_insn_read;
+
+ dev->open = serial2002_open;
+ dev->close = serial2002_close;
- return 1;
+ return 0;
}
static void serial2002_detach(struct comedi_device *dev)
@@ -858,7 +793,7 @@ static void serial2002_detach(struct comedi_device *dev)
struct comedi_subdevice *s;
int i;
- for (i = 0; i < 5; i++) {
+ for (i = 0; i < dev->n_subdevices; i++) {
s = &dev->subdevices[i];
kfree(s->maxdata_list);
kfree(s->range_table_list);