Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 1 | /* |
H Hartley Sweeten | 28a2993 | 2015-08-17 13:18:41 -0700 | [diff] [blame] | 2 | * serial2002.c |
| 3 | * Comedi driver for serial connected hardware |
| 4 | * |
| 5 | * COMEDI - Linux Control and Measurement Device Interface |
| 6 | * Copyright (C) 2002 Anders Blomdell <anders.blomdell@control.lth.se> |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License as published by |
| 10 | * the Free Software Foundation; either version 2 of the License, or |
| 11 | * (at your option) any later version. |
| 12 | * |
| 13 | * This program is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | * GNU General Public License for more details. |
| 17 | */ |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 18 | |
| 19 | /* |
H Hartley Sweeten | 28a2993 | 2015-08-17 13:18:41 -0700 | [diff] [blame] | 20 | * Driver: serial2002 |
| 21 | * Description: Driver for serial connected hardware |
| 22 | * Devices: |
| 23 | * Author: Anders Blomdell |
| 24 | * Updated: Fri, 7 Jun 2002 12:56:45 -0700 |
| 25 | * Status: in development |
| 26 | */ |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 27 | |
H Hartley Sweeten | ce157f8 | 2013-06-24 17:04:43 -0700 | [diff] [blame] | 28 | #include <linux/module.h> |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 29 | #include "../comedidev.h" |
| 30 | |
| 31 | #include <linux/delay.h> |
Greg Kroah-Hartman | 0292148 | 2009-10-05 13:23:31 -0700 | [diff] [blame] | 32 | #include <linux/sched.h> |
Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 33 | #include <linux/slab.h> |
Abhilash Jindal | d38c59e | 2015-08-11 10:31:53 -0400 | [diff] [blame] | 34 | #include <linux/ktime.h> |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 35 | |
Ravishankar | b041267 | 2011-07-07 17:35:27 +0530 | [diff] [blame] | 36 | #include <linux/termios.h> |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 37 | #include <asm/ioctls.h> |
| 38 | #include <linux/serial.h> |
| 39 | #include <linux/poll.h> |
| 40 | |
Bill Pemberton | b3fb588 | 2009-03-16 22:17:44 -0400 | [diff] [blame] | 41 | struct serial2002_range_table_t { |
Bill Pemberton | 2696fb5 | 2009-03-27 11:29:34 -0400 | [diff] [blame] | 42 | /* HACK... */ |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 43 | int length; |
Bill Pemberton | 1f6325d | 2009-03-16 22:06:31 -0400 | [diff] [blame] | 44 | struct comedi_krange range; |
Bill Pemberton | b3fb588 | 2009-03-16 22:17:44 -0400 | [diff] [blame] | 45 | }; |
| 46 | |
Bill Pemberton | e21ffa4 | 2009-03-16 22:18:54 -0400 | [diff] [blame] | 47 | struct serial2002_private { |
Bill Pemberton | 2696fb5 | 2009-03-27 11:29:34 -0400 | [diff] [blame] | 48 | int port; /* /dev/ttyS<port> */ |
| 49 | int speed; /* baudrate */ |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 50 | struct file *tty; |
Bill Pemberton | 790c554 | 2009-03-16 22:05:02 -0400 | [diff] [blame] | 51 | unsigned int ao_readback[32]; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 52 | unsigned char digital_in_mapping[32]; |
| 53 | unsigned char digital_out_mapping[32]; |
| 54 | unsigned char analog_in_mapping[32]; |
| 55 | unsigned char analog_out_mapping[32]; |
| 56 | unsigned char encoder_in_mapping[32]; |
Bill Pemberton | b3fb588 | 2009-03-16 22:17:44 -0400 | [diff] [blame] | 57 | struct serial2002_range_table_t in_range[32], out_range[32]; |
Bill Pemberton | e21ffa4 | 2009-03-16 22:18:54 -0400 | [diff] [blame] | 58 | }; |
| 59 | |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 60 | struct serial_data { |
| 61 | enum { is_invalid, is_digital, is_channel } kind; |
| 62 | int index; |
| 63 | unsigned long value; |
| 64 | }; |
| 65 | |
H Hartley Sweeten | ee422bb | 2013-04-05 16:09:40 -0700 | [diff] [blame] | 66 | /* |
| 67 | * The configuration serial_data.value read from the device is |
| 68 | * a bitmask that defines specific options of a channel: |
| 69 | * |
| 70 | * 4:0 - the channel to configure |
| 71 | * 7:5 - the kind of channel |
| 72 | * 9:8 - the command used to configure the channel |
| 73 | * |
| 74 | * The remaining bits vary in use depending on the command: |
| 75 | * |
| 76 | * BITS 15:10 - the channel bits (maxdata) |
| 77 | * MIN/MAX 12:10 - the units multiplier for the scale |
| 78 | * 13 - the sign of the scale |
| 79 | * 33:14 - the base value for the range |
| 80 | */ |
| 81 | #define S2002_CFG_CHAN(x) ((x) & 0x1f) |
| 82 | #define S2002_CFG_KIND(x) (((x) >> 5) & 0x7) |
| 83 | #define S2002_CFG_KIND_INVALID 0 |
| 84 | #define S2002_CFG_KIND_DIGITAL_IN 1 |
| 85 | #define S2002_CFG_KIND_DIGITAL_OUT 2 |
| 86 | #define S2002_CFG_KIND_ANALOG_IN 3 |
| 87 | #define S2002_CFG_KIND_ANALOG_OUT 4 |
| 88 | #define S2002_CFG_KIND_ENCODER_IN 5 |
| 89 | #define S2002_CFG_CMD(x) (((x) >> 8) & 0x3) |
| 90 | #define S2002_CFG_CMD_BITS 0 |
| 91 | #define S2002_CFG_CMD_MIN 1 |
| 92 | #define S2002_CFG_CMD_MAX 2 |
| 93 | #define S2002_CFG_BITS(x) (((x) >> 10) & 0x3f) |
| 94 | #define S2002_CFG_UNITS(x) (((x) >> 10) & 0x7) |
| 95 | #define S2002_CFG_SIGN(x) (((x) >> 13) & 0x1) |
| 96 | #define S2002_CFG_BASE(x) (((x) >> 14) & 0xfffff) |
| 97 | |
Chris Cesare | 2e3d417 | 2016-06-09 15:39:02 -0400 | [diff] [blame] | 98 | static long serial2002_tty_ioctl(struct file *f, unsigned int op, |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 99 | unsigned long param) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 100 | { |
Shawn Bohrer | 00a1855 | 2009-10-18 15:32:15 -0500 | [diff] [blame] | 101 | if (f->f_op->unlocked_ioctl) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 102 | return f->f_op->unlocked_ioctl(f, op, param); |
Shawn Bohrer | 00a1855 | 2009-10-18 15:32:15 -0500 | [diff] [blame] | 103 | |
H Hartley Sweeten | 8d63f5b | 2015-08-17 13:18:43 -0700 | [diff] [blame] | 104 | return -ENOTTY; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 105 | } |
| 106 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 107 | static int serial2002_tty_write(struct file *f, unsigned char *buf, int count) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 108 | { |
H Hartley Sweeten | 32ef0e3 | 2013-04-05 16:07:49 -0700 | [diff] [blame] | 109 | const char __user *p = (__force const char __user *)buf; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 110 | int result; |
Al Viro | 79ff50c | 2015-04-03 15:14:42 -0400 | [diff] [blame] | 111 | loff_t offset = 0; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 112 | mm_segment_t oldfs; |
| 113 | |
| 114 | oldfs = get_fs(); |
| 115 | set_fs(KERNEL_DS); |
Al Viro | 79ff50c | 2015-04-03 15:14:42 -0400 | [diff] [blame] | 116 | result = __vfs_write(f, p, count, &offset); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 117 | set_fs(oldfs); |
| 118 | return result; |
| 119 | } |
| 120 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 121 | static void serial2002_tty_read_poll_wait(struct file *f, int timeout) |
H Hartley Sweeten | 0a24598 | 2013-04-05 16:08:13 -0700 | [diff] [blame] | 122 | { |
| 123 | struct poll_wqueues table; |
Abhilash Jindal | d38c59e | 2015-08-11 10:31:53 -0400 | [diff] [blame] | 124 | ktime_t start, now; |
H Hartley Sweeten | 0a24598 | 2013-04-05 16:08:13 -0700 | [diff] [blame] | 125 | |
Abhilash Jindal | d38c59e | 2015-08-11 10:31:53 -0400 | [diff] [blame] | 126 | start = ktime_get(); |
H Hartley Sweeten | 0a24598 | 2013-04-05 16:08:13 -0700 | [diff] [blame] | 127 | poll_initwait(&table); |
| 128 | while (1) { |
| 129 | long elapsed; |
| 130 | int mask; |
| 131 | |
| 132 | mask = f->f_op->poll(f, &table.pt); |
| 133 | if (mask & (POLLRDNORM | POLLRDBAND | POLLIN | |
| 134 | POLLHUP | POLLERR)) { |
| 135 | break; |
| 136 | } |
Abhilash Jindal | d38c59e | 2015-08-11 10:31:53 -0400 | [diff] [blame] | 137 | now = ktime_get(); |
| 138 | elapsed = ktime_us_delta(now, start); |
H Hartley Sweeten | 0a24598 | 2013-04-05 16:08:13 -0700 | [diff] [blame] | 139 | if (elapsed > timeout) |
| 140 | break; |
| 141 | set_current_state(TASK_INTERRUPTIBLE); |
| 142 | schedule_timeout(((timeout - elapsed) * HZ) / 10000); |
| 143 | } |
| 144 | poll_freewait(&table); |
| 145 | } |
| 146 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 147 | static int serial2002_tty_read(struct file *f, int timeout) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 148 | { |
H Hartley Sweeten | 32ef0e3 | 2013-04-05 16:07:49 -0700 | [diff] [blame] | 149 | unsigned char ch; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 150 | int result; |
| 151 | |
| 152 | result = -1; |
| 153 | if (!IS_ERR(f)) { |
| 154 | mm_segment_t oldfs; |
Al Viro | 79ff50c | 2015-04-03 15:14:42 -0400 | [diff] [blame] | 155 | char __user *p = (__force char __user *)&ch; |
| 156 | loff_t offset = 0; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 157 | |
| 158 | oldfs = get_fs(); |
| 159 | set_fs(KERNEL_DS); |
| 160 | if (f->f_op->poll) { |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 161 | serial2002_tty_read_poll_wait(f, timeout); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 162 | |
Al Viro | 79ff50c | 2015-04-03 15:14:42 -0400 | [diff] [blame] | 163 | if (__vfs_read(f, p, 1, &offset) == 1) |
H Hartley Sweeten | 32ef0e3 | 2013-04-05 16:07:49 -0700 | [diff] [blame] | 164 | result = ch; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 165 | } else { |
| 166 | /* Device does not support poll, busy wait */ |
| 167 | int retries = 0; |
H Hartley Sweeten | 96afd18 | 2014-07-16 11:02:01 -0700 | [diff] [blame] | 168 | |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 169 | while (1) { |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 170 | retries++; |
Ravishankar | b041267 | 2011-07-07 17:35:27 +0530 | [diff] [blame] | 171 | if (retries >= timeout) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 172 | break; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 173 | |
Al Viro | 79ff50c | 2015-04-03 15:14:42 -0400 | [diff] [blame] | 174 | if (__vfs_read(f, p, 1, &offset) == 1) { |
Bill Pemberton | 356cdbc | 2009-04-09 16:07:27 -0400 | [diff] [blame] | 175 | result = ch; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 176 | break; |
| 177 | } |
H Hartley Sweeten | 3335dfd | 2015-08-17 13:18:42 -0700 | [diff] [blame] | 178 | usleep_range(100, 1000); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 179 | } |
| 180 | } |
| 181 | set_fs(oldfs); |
| 182 | } |
| 183 | return result; |
| 184 | } |
| 185 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 186 | static void serial2002_tty_setspeed(struct file *f, int speed) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 187 | { |
H Hartley Sweeten | 071e086 | 2013-04-05 16:08:32 -0700 | [diff] [blame] | 188 | struct termios termios; |
| 189 | struct serial_struct serial; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 190 | mm_segment_t oldfs; |
| 191 | |
| 192 | oldfs = get_fs(); |
| 193 | set_fs(KERNEL_DS); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 194 | |
H Hartley Sweeten | 071e086 | 2013-04-05 16:08:32 -0700 | [diff] [blame] | 195 | /* Set speed */ |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 196 | serial2002_tty_ioctl(f, TCGETS, (unsigned long)&termios); |
H Hartley Sweeten | 071e086 | 2013-04-05 16:08:32 -0700 | [diff] [blame] | 197 | termios.c_iflag = 0; |
| 198 | termios.c_oflag = 0; |
| 199 | termios.c_lflag = 0; |
| 200 | termios.c_cflag = CLOCAL | CS8 | CREAD; |
| 201 | termios.c_cc[VMIN] = 0; |
| 202 | termios.c_cc[VTIME] = 0; |
| 203 | switch (speed) { |
| 204 | case 2400: |
| 205 | termios.c_cflag |= B2400; |
| 206 | break; |
| 207 | case 4800: |
| 208 | termios.c_cflag |= B4800; |
| 209 | break; |
| 210 | case 9600: |
| 211 | termios.c_cflag |= B9600; |
| 212 | break; |
| 213 | case 19200: |
| 214 | termios.c_cflag |= B19200; |
| 215 | break; |
| 216 | case 38400: |
| 217 | termios.c_cflag |= B38400; |
| 218 | break; |
| 219 | case 57600: |
| 220 | termios.c_cflag |= B57600; |
| 221 | break; |
| 222 | case 115200: |
| 223 | termios.c_cflag |= B115200; |
| 224 | break; |
| 225 | default: |
| 226 | termios.c_cflag |= B9600; |
| 227 | break; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 228 | } |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 229 | serial2002_tty_ioctl(f, TCSETS, (unsigned long)&termios); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 230 | |
H Hartley Sweeten | 071e086 | 2013-04-05 16:08:32 -0700 | [diff] [blame] | 231 | /* Set low latency */ |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 232 | serial2002_tty_ioctl(f, TIOCGSERIAL, (unsigned long)&serial); |
H Hartley Sweeten | 071e086 | 2013-04-05 16:08:32 -0700 | [diff] [blame] | 233 | serial.flags |= ASYNC_LOW_LATENCY; |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 234 | serial2002_tty_ioctl(f, TIOCSSERIAL, (unsigned long)&serial); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 235 | |
| 236 | set_fs(oldfs); |
| 237 | } |
| 238 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 239 | static void serial2002_poll_digital(struct file *f, int channel) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 240 | { |
| 241 | char cmd; |
| 242 | |
| 243 | cmd = 0x40 | (channel & 0x1f); |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 244 | serial2002_tty_write(f, &cmd, 1); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 245 | } |
| 246 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 247 | static void serial2002_poll_channel(struct file *f, int channel) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 248 | { |
| 249 | char cmd; |
| 250 | |
| 251 | cmd = 0x60 | (channel & 0x1f); |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 252 | serial2002_tty_write(f, &cmd, 1); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 253 | } |
| 254 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 255 | static struct serial_data serial2002_read(struct file *f, int timeout) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 256 | { |
| 257 | struct serial_data result; |
| 258 | int length; |
| 259 | |
| 260 | result.kind = is_invalid; |
| 261 | result.index = 0; |
| 262 | result.value = 0; |
| 263 | length = 0; |
| 264 | while (1) { |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 265 | int data = serial2002_tty_read(f, timeout); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 266 | |
| 267 | length++; |
| 268 | if (data < 0) { |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 269 | break; |
| 270 | } else if (data & 0x80) { |
| 271 | result.value = (result.value << 7) | (data & 0x7f); |
| 272 | } else { |
| 273 | if (length == 1) { |
| 274 | switch ((data >> 5) & 0x03) { |
H Hartley Sweeten | 761a38a | 2013-04-05 16:08:51 -0700 | [diff] [blame] | 275 | case 0: |
| 276 | result.value = 0; |
| 277 | result.kind = is_digital; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 278 | break; |
H Hartley Sweeten | 761a38a | 2013-04-05 16:08:51 -0700 | [diff] [blame] | 279 | case 1: |
| 280 | result.value = 1; |
| 281 | result.kind = is_digital; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 282 | break; |
| 283 | } |
| 284 | } else { |
| 285 | result.value = |
Mithlesh Thukral | 0a85b6f | 2009-06-08 21:04:41 +0530 | [diff] [blame] | 286 | (result.value << 2) | ((data & 0x60) >> 5); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 287 | result.kind = is_channel; |
| 288 | } |
| 289 | result.index = data & 0x1f; |
| 290 | break; |
| 291 | } |
| 292 | } |
| 293 | return result; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 294 | } |
| 295 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 296 | static void serial2002_write(struct file *f, struct serial_data data) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 297 | { |
| 298 | if (data.kind == is_digital) { |
| 299 | unsigned char ch = |
Mithlesh Thukral | 0a85b6f | 2009-06-08 21:04:41 +0530 | [diff] [blame] | 300 | ((data.value << 5) & 0x20) | (data.index & 0x1f); |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 301 | serial2002_tty_write(f, &ch, 1); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 302 | } else { |
| 303 | unsigned char ch[6]; |
| 304 | int i = 0; |
H Hartley Sweeten | 96afd18 | 2014-07-16 11:02:01 -0700 | [diff] [blame] | 305 | |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 306 | if (data.value >= (1L << 30)) { |
| 307 | ch[i] = 0x80 | ((data.value >> 30) & 0x03); |
| 308 | i++; |
| 309 | } |
| 310 | if (data.value >= (1L << 23)) { |
| 311 | ch[i] = 0x80 | ((data.value >> 23) & 0x7f); |
| 312 | i++; |
| 313 | } |
| 314 | if (data.value >= (1L << 16)) { |
| 315 | ch[i] = 0x80 | ((data.value >> 16) & 0x7f); |
| 316 | i++; |
| 317 | } |
| 318 | if (data.value >= (1L << 9)) { |
| 319 | ch[i] = 0x80 | ((data.value >> 9) & 0x7f); |
| 320 | i++; |
| 321 | } |
| 322 | ch[i] = 0x80 | ((data.value >> 2) & 0x7f); |
| 323 | i++; |
| 324 | ch[i] = ((data.value << 5) & 0x60) | (data.index & 0x1f); |
| 325 | i++; |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 326 | serial2002_tty_write(f, ch, i); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 327 | } |
| 328 | } |
| 329 | |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 330 | struct config_t { |
| 331 | short int kind; |
| 332 | short int bits; |
| 333 | int min; |
| 334 | int max; |
| 335 | }; |
| 336 | |
| 337 | static int serial2002_setup_subdevice(struct comedi_subdevice *s, |
| 338 | struct config_t *cfg, |
| 339 | struct serial2002_range_table_t *range, |
| 340 | unsigned char *mapping, |
| 341 | int kind) |
| 342 | { |
| 343 | const struct comedi_lrange **range_table_list = NULL; |
| 344 | unsigned int *maxdata_list; |
| 345 | int j, chan; |
| 346 | |
| 347 | for (chan = 0, j = 0; j < 32; j++) { |
| 348 | if (cfg[j].kind == kind) |
| 349 | chan++; |
| 350 | } |
| 351 | s->n_chan = chan; |
| 352 | s->maxdata = 0; |
| 353 | kfree(s->maxdata_list); |
H Hartley Sweeten | 205781b | 2014-07-16 11:22:52 -0700 | [diff] [blame] | 354 | maxdata_list = kmalloc_array(s->n_chan, sizeof(unsigned int), |
| 355 | GFP_KERNEL); |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 356 | if (!maxdata_list) |
| 357 | return -ENOMEM; |
| 358 | s->maxdata_list = maxdata_list; |
| 359 | kfree(s->range_table_list); |
| 360 | s->range_table = NULL; |
| 361 | s->range_table_list = NULL; |
| 362 | if (kind == 1 || kind == 2) { |
| 363 | s->range_table = &range_digital; |
| 364 | } else if (range) { |
H Hartley Sweeten | 205781b | 2014-07-16 11:22:52 -0700 | [diff] [blame] | 365 | range_table_list = kmalloc_array(s->n_chan, sizeof(*range), |
| 366 | GFP_KERNEL); |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 367 | if (!range_table_list) |
| 368 | return -ENOMEM; |
| 369 | s->range_table_list = range_table_list; |
| 370 | } |
| 371 | for (chan = 0, j = 0; j < 32; j++) { |
| 372 | if (cfg[j].kind == kind) { |
| 373 | if (mapping) |
| 374 | mapping[chan] = j; |
H Hartley Sweeten | a437dee | 2015-04-20 11:49:06 -0700 | [diff] [blame] | 375 | if (range && range_table_list) { |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 376 | range[j].length = 1; |
| 377 | range[j].range.min = cfg[j].min; |
| 378 | range[j].range.max = cfg[j].max; |
| 379 | range_table_list[chan] = |
| 380 | (const struct comedi_lrange *)&range[j]; |
| 381 | } |
Chris Cesare | c9b0f2b | 2016-06-09 15:39:03 -0400 | [diff] [blame] | 382 | if (cfg[j].bits < 32) |
| 383 | maxdata_list[chan] = (1u << cfg[j].bits) - 1; |
| 384 | else |
| 385 | maxdata_list[chan] = 0xffffffff; |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 386 | chan++; |
| 387 | } |
| 388 | } |
| 389 | return 0; |
| 390 | } |
| 391 | |
| 392 | static int serial2002_setup_subdevs(struct comedi_device *dev) |
| 393 | { |
| 394 | struct serial2002_private *devpriv = dev->private; |
| 395 | struct config_t *di_cfg; |
| 396 | struct config_t *do_cfg; |
| 397 | struct config_t *ai_cfg; |
| 398 | struct config_t *ao_cfg; |
| 399 | struct config_t *cfg; |
| 400 | struct comedi_subdevice *s; |
H Hartley Sweeten | ee422bb | 2013-04-05 16:09:40 -0700 | [diff] [blame] | 401 | int result = 0; |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 402 | int i; |
| 403 | |
H Hartley Sweeten | ee422bb | 2013-04-05 16:09:40 -0700 | [diff] [blame] | 404 | /* Allocate the temporary structs to hold the configuration data */ |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 405 | di_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL); |
| 406 | do_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL); |
| 407 | ai_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL); |
| 408 | ao_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL); |
| 409 | if (!di_cfg || !do_cfg || !ai_cfg || !ao_cfg) { |
| 410 | result = -ENOMEM; |
| 411 | goto err_alloc_configs; |
| 412 | } |
| 413 | |
H Hartley Sweeten | ee422bb | 2013-04-05 16:09:40 -0700 | [diff] [blame] | 414 | /* Read the configuration from the connected device */ |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 415 | serial2002_tty_setspeed(devpriv->tty, devpriv->speed); |
| 416 | serial2002_poll_channel(devpriv->tty, 31); |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 417 | while (1) { |
H Hartley Sweeten | 624e644 | 2014-07-16 11:02:02 -0700 | [diff] [blame] | 418 | struct serial_data data = serial2002_read(devpriv->tty, 1000); |
| 419 | int kind = S2002_CFG_KIND(data.value); |
| 420 | int channel = S2002_CFG_CHAN(data.value); |
| 421 | int range = S2002_CFG_BASE(data.value); |
| 422 | int cmd = S2002_CFG_CMD(data.value); |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 423 | |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 424 | if (data.kind != is_channel || data.index != 31 || |
H Hartley Sweeten | 624e644 | 2014-07-16 11:02:02 -0700 | [diff] [blame] | 425 | kind == S2002_CFG_KIND_INVALID) |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 426 | break; |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 427 | |
H Hartley Sweeten | 624e644 | 2014-07-16 11:02:02 -0700 | [diff] [blame] | 428 | switch (kind) { |
| 429 | case S2002_CFG_KIND_DIGITAL_IN: |
| 430 | cfg = di_cfg; |
| 431 | break; |
| 432 | case S2002_CFG_KIND_DIGITAL_OUT: |
| 433 | cfg = do_cfg; |
| 434 | break; |
| 435 | case S2002_CFG_KIND_ANALOG_IN: |
| 436 | cfg = ai_cfg; |
| 437 | break; |
| 438 | case S2002_CFG_KIND_ANALOG_OUT: |
| 439 | cfg = ao_cfg; |
| 440 | break; |
| 441 | case S2002_CFG_KIND_ENCODER_IN: |
| 442 | cfg = ai_cfg; |
| 443 | break; |
| 444 | default: |
| 445 | cfg = NULL; |
| 446 | break; |
| 447 | } |
| 448 | if (!cfg) |
| 449 | continue; /* unknown kind, skip it */ |
| 450 | |
| 451 | cfg[channel].kind = kind; |
| 452 | |
| 453 | switch (cmd) { |
| 454 | case S2002_CFG_CMD_BITS: |
| 455 | cfg[channel].bits = S2002_CFG_BITS(data.value); |
| 456 | break; |
| 457 | case S2002_CFG_CMD_MIN: |
| 458 | case S2002_CFG_CMD_MAX: |
| 459 | switch (S2002_CFG_UNITS(data.value)) { |
| 460 | case 0: |
| 461 | range *= 1000000; |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 462 | break; |
H Hartley Sweeten | 624e644 | 2014-07-16 11:02:02 -0700 | [diff] [blame] | 463 | case 1: |
| 464 | range *= 1000; |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 465 | break; |
H Hartley Sweeten | 624e644 | 2014-07-16 11:02:02 -0700 | [diff] [blame] | 466 | case 2: |
| 467 | range *= 1; |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 468 | break; |
| 469 | } |
H Hartley Sweeten | 624e644 | 2014-07-16 11:02:02 -0700 | [diff] [blame] | 470 | if (S2002_CFG_SIGN(data.value)) |
| 471 | range = -range; |
| 472 | if (cmd == S2002_CFG_CMD_MIN) |
| 473 | cfg[channel].min = range; |
| 474 | else |
| 475 | cfg[channel].max = range; |
| 476 | break; |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 477 | } |
| 478 | } |
| 479 | |
| 480 | /* Fill in subdevice data */ |
| 481 | for (i = 0; i <= 4; i++) { |
| 482 | unsigned char *mapping = NULL; |
| 483 | struct serial2002_range_table_t *range = NULL; |
| 484 | int kind = 0; |
| 485 | |
H Hartley Sweeten | ee422bb | 2013-04-05 16:09:40 -0700 | [diff] [blame] | 486 | s = &dev->subdevices[i]; |
| 487 | |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 488 | switch (i) { |
| 489 | case 0: |
| 490 | cfg = di_cfg; |
| 491 | mapping = devpriv->digital_in_mapping; |
H Hartley Sweeten | ee422bb | 2013-04-05 16:09:40 -0700 | [diff] [blame] | 492 | kind = S2002_CFG_KIND_DIGITAL_IN; |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 493 | break; |
| 494 | case 1: |
| 495 | cfg = do_cfg; |
| 496 | mapping = devpriv->digital_out_mapping; |
H Hartley Sweeten | ee422bb | 2013-04-05 16:09:40 -0700 | [diff] [blame] | 497 | kind = S2002_CFG_KIND_DIGITAL_OUT; |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 498 | break; |
| 499 | case 2: |
| 500 | cfg = ai_cfg; |
| 501 | mapping = devpriv->analog_in_mapping; |
| 502 | range = devpriv->in_range; |
H Hartley Sweeten | ee422bb | 2013-04-05 16:09:40 -0700 | [diff] [blame] | 503 | kind = S2002_CFG_KIND_ANALOG_IN; |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 504 | break; |
| 505 | case 3: |
| 506 | cfg = ao_cfg; |
| 507 | mapping = devpriv->analog_out_mapping; |
| 508 | range = devpriv->out_range; |
H Hartley Sweeten | ee422bb | 2013-04-05 16:09:40 -0700 | [diff] [blame] | 509 | kind = S2002_CFG_KIND_ANALOG_OUT; |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 510 | break; |
| 511 | case 4: |
| 512 | cfg = ai_cfg; |
| 513 | mapping = devpriv->encoder_in_mapping; |
| 514 | range = devpriv->in_range; |
H Hartley Sweeten | ee422bb | 2013-04-05 16:09:40 -0700 | [diff] [blame] | 515 | kind = S2002_CFG_KIND_ENCODER_IN; |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 516 | break; |
| 517 | } |
| 518 | |
H Hartley Sweeten | ee422bb | 2013-04-05 16:09:40 -0700 | [diff] [blame] | 519 | if (serial2002_setup_subdevice(s, cfg, range, mapping, kind)) |
| 520 | break; /* err handled below */ |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 521 | } |
| 522 | if (i <= 4) { |
| 523 | /* |
| 524 | * Failed to allocate maxdata_list or range_table_list |
| 525 | * for a subdevice that needed it. |
| 526 | */ |
| 527 | result = -ENOMEM; |
| 528 | for (i = 0; i <= 4; i++) { |
| 529 | s = &dev->subdevices[i]; |
| 530 | kfree(s->maxdata_list); |
| 531 | s->maxdata_list = NULL; |
| 532 | kfree(s->range_table_list); |
| 533 | s->range_table_list = NULL; |
| 534 | } |
| 535 | } |
| 536 | |
| 537 | err_alloc_configs: |
| 538 | kfree(di_cfg); |
| 539 | kfree(do_cfg); |
| 540 | kfree(ai_cfg); |
| 541 | kfree(ao_cfg); |
| 542 | |
| 543 | if (result) { |
| 544 | if (devpriv->tty) { |
| 545 | filp_close(devpriv->tty, NULL); |
| 546 | devpriv->tty = NULL; |
| 547 | } |
| 548 | } |
| 549 | |
| 550 | return result; |
| 551 | } |
| 552 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 553 | static int serial2002_open(struct comedi_device *dev) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 554 | { |
H Hartley Sweeten | 9a1a6cf | 2012-10-15 10:15:52 -0700 | [diff] [blame] | 555 | struct serial2002_private *devpriv = dev->private; |
Ian Abbott | 3c17ba07 | 2010-05-19 14:10:00 +0100 | [diff] [blame] | 556 | int result; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 557 | char port[20]; |
| 558 | |
| 559 | sprintf(port, "/dev/ttyS%d", devpriv->port); |
OGAWA Hirofumi | 2021937 | 2010-02-23 03:04:42 +0900 | [diff] [blame] | 560 | devpriv->tty = filp_open(port, O_RDWR, 0); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 561 | if (IS_ERR(devpriv->tty)) { |
Ian Abbott | 3c17ba07 | 2010-05-19 14:10:00 +0100 | [diff] [blame] | 562 | result = (int)PTR_ERR(devpriv->tty); |
YAMANE Toshiaki | 4f72b73 | 2012-10-25 09:16:49 +0900 | [diff] [blame] | 563 | dev_err(dev->class_dev, "file open error = %d\n", result); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 564 | } else { |
H Hartley Sweeten | 623a739 | 2013-04-05 16:09:15 -0700 | [diff] [blame] | 565 | result = serial2002_setup_subdevs(dev); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 566 | } |
Ian Abbott | 3c17ba07 | 2010-05-19 14:10:00 +0100 | [diff] [blame] | 567 | return result; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 568 | } |
| 569 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 570 | static void serial2002_close(struct comedi_device *dev) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 571 | { |
H Hartley Sweeten | 9a1a6cf | 2012-10-15 10:15:52 -0700 | [diff] [blame] | 572 | struct serial2002_private *devpriv = dev->private; |
| 573 | |
H Hartley Sweeten | 27494c0 | 2012-06-07 17:14:25 -0700 | [diff] [blame] | 574 | if (!IS_ERR(devpriv->tty) && devpriv->tty) |
| 575 | filp_close(devpriv->tty, NULL); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 576 | } |
| 577 | |
H Hartley Sweeten | adbd174 | 2013-04-05 16:10:22 -0700 | [diff] [blame] | 578 | static int serial2002_di_insn_read(struct comedi_device *dev, |
| 579 | struct comedi_subdevice *s, |
| 580 | struct comedi_insn *insn, |
| 581 | unsigned int *data) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 582 | { |
H Hartley Sweeten | 9a1a6cf | 2012-10-15 10:15:52 -0700 | [diff] [blame] | 583 | struct serial2002_private *devpriv = dev->private; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 584 | int n; |
| 585 | int chan; |
| 586 | |
| 587 | chan = devpriv->digital_in_mapping[CR_CHAN(insn->chanspec)]; |
| 588 | for (n = 0; n < insn->n; n++) { |
| 589 | struct serial_data read; |
| 590 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 591 | serial2002_poll_digital(devpriv->tty, chan); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 592 | while (1) { |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 593 | read = serial2002_read(devpriv->tty, 1000); |
Ravishankar | e9d1cf8 | 2011-07-07 19:53:04 +0530 | [diff] [blame] | 594 | if (read.kind != is_digital || read.index == chan) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 595 | break; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 596 | } |
| 597 | data[n] = read.value; |
| 598 | } |
| 599 | return n; |
| 600 | } |
| 601 | |
H Hartley Sweeten | adbd174 | 2013-04-05 16:10:22 -0700 | [diff] [blame] | 602 | static int serial2002_do_insn_write(struct comedi_device *dev, |
| 603 | struct comedi_subdevice *s, |
| 604 | struct comedi_insn *insn, |
| 605 | unsigned int *data) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 606 | { |
H Hartley Sweeten | 9a1a6cf | 2012-10-15 10:15:52 -0700 | [diff] [blame] | 607 | struct serial2002_private *devpriv = dev->private; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 608 | int n; |
| 609 | int chan; |
| 610 | |
| 611 | chan = devpriv->digital_out_mapping[CR_CHAN(insn->chanspec)]; |
| 612 | for (n = 0; n < insn->n; n++) { |
| 613 | struct serial_data write; |
| 614 | |
| 615 | write.kind = is_digital; |
| 616 | write.index = chan; |
| 617 | write.value = data[n]; |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 618 | serial2002_write(devpriv->tty, write); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 619 | } |
| 620 | return n; |
| 621 | } |
| 622 | |
H Hartley Sweeten | adbd174 | 2013-04-05 16:10:22 -0700 | [diff] [blame] | 623 | static int serial2002_ai_insn_read(struct comedi_device *dev, |
| 624 | struct comedi_subdevice *s, |
| 625 | struct comedi_insn *insn, |
| 626 | unsigned int *data) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 627 | { |
H Hartley Sweeten | 9a1a6cf | 2012-10-15 10:15:52 -0700 | [diff] [blame] | 628 | struct serial2002_private *devpriv = dev->private; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 629 | int n; |
| 630 | int chan; |
| 631 | |
| 632 | chan = devpriv->analog_in_mapping[CR_CHAN(insn->chanspec)]; |
| 633 | for (n = 0; n < insn->n; n++) { |
| 634 | struct serial_data read; |
| 635 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 636 | serial2002_poll_channel(devpriv->tty, chan); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 637 | while (1) { |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 638 | read = serial2002_read(devpriv->tty, 1000); |
Ravishankar | e9d1cf8 | 2011-07-07 19:53:04 +0530 | [diff] [blame] | 639 | if (read.kind != is_channel || read.index == chan) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 640 | break; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 641 | } |
| 642 | data[n] = read.value; |
| 643 | } |
| 644 | return n; |
| 645 | } |
| 646 | |
H Hartley Sweeten | adbd174 | 2013-04-05 16:10:22 -0700 | [diff] [blame] | 647 | static int serial2002_ao_insn_write(struct comedi_device *dev, |
| 648 | struct comedi_subdevice *s, |
| 649 | struct comedi_insn *insn, |
| 650 | unsigned int *data) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 651 | { |
H Hartley Sweeten | 9a1a6cf | 2012-10-15 10:15:52 -0700 | [diff] [blame] | 652 | struct serial2002_private *devpriv = dev->private; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 653 | int n; |
| 654 | int chan; |
| 655 | |
| 656 | chan = devpriv->analog_out_mapping[CR_CHAN(insn->chanspec)]; |
| 657 | for (n = 0; n < insn->n; n++) { |
| 658 | struct serial_data write; |
| 659 | |
| 660 | write.kind = is_channel; |
| 661 | write.index = chan; |
| 662 | write.value = data[n]; |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 663 | serial2002_write(devpriv->tty, write); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 664 | devpriv->ao_readback[chan] = data[n]; |
| 665 | } |
| 666 | return n; |
| 667 | } |
| 668 | |
H Hartley Sweeten | adbd174 | 2013-04-05 16:10:22 -0700 | [diff] [blame] | 669 | static int serial2002_ao_insn_read(struct comedi_device *dev, |
| 670 | struct comedi_subdevice *s, |
| 671 | struct comedi_insn *insn, |
| 672 | unsigned int *data) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 673 | { |
H Hartley Sweeten | 9a1a6cf | 2012-10-15 10:15:52 -0700 | [diff] [blame] | 674 | struct serial2002_private *devpriv = dev->private; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 675 | int n; |
| 676 | int chan = CR_CHAN(insn->chanspec); |
| 677 | |
Ravishankar | e9d1cf8 | 2011-07-07 19:53:04 +0530 | [diff] [blame] | 678 | for (n = 0; n < insn->n; n++) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 679 | data[n] = devpriv->ao_readback[chan]; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 680 | |
| 681 | return n; |
| 682 | } |
| 683 | |
H Hartley Sweeten | adbd174 | 2013-04-05 16:10:22 -0700 | [diff] [blame] | 684 | static int serial2002_encoder_insn_read(struct comedi_device *dev, |
| 685 | struct comedi_subdevice *s, |
| 686 | struct comedi_insn *insn, |
| 687 | unsigned int *data) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 688 | { |
H Hartley Sweeten | 9a1a6cf | 2012-10-15 10:15:52 -0700 | [diff] [blame] | 689 | struct serial2002_private *devpriv = dev->private; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 690 | int n; |
| 691 | int chan; |
| 692 | |
| 693 | chan = devpriv->encoder_in_mapping[CR_CHAN(insn->chanspec)]; |
| 694 | for (n = 0; n < insn->n; n++) { |
| 695 | struct serial_data read; |
| 696 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 697 | serial2002_poll_channel(devpriv->tty, chan); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 698 | while (1) { |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 699 | read = serial2002_read(devpriv->tty, 1000); |
Ravishankar | e9d1cf8 | 2011-07-07 19:53:04 +0530 | [diff] [blame] | 700 | if (read.kind != is_channel || read.index == chan) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 701 | break; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 702 | } |
| 703 | data[n] = read.value; |
| 704 | } |
| 705 | return n; |
| 706 | } |
| 707 | |
Mithlesh Thukral | 0a85b6f | 2009-06-08 21:04:41 +0530 | [diff] [blame] | 708 | static int serial2002_attach(struct comedi_device *dev, |
| 709 | struct comedi_devconfig *it) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 710 | { |
H Hartley Sweeten | 9a1a6cf | 2012-10-15 10:15:52 -0700 | [diff] [blame] | 711 | struct serial2002_private *devpriv; |
Bill Pemberton | 34c4392 | 2009-03-16 22:05:14 -0400 | [diff] [blame] | 712 | struct comedi_subdevice *s; |
H Hartley Sweeten | 8b6c569 | 2012-06-12 11:59:33 -0700 | [diff] [blame] | 713 | int ret; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 714 | |
H Hartley Sweeten | 0bdab50 | 2013-06-24 16:55:44 -0700 | [diff] [blame] | 715 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
H Hartley Sweeten | c34fa26 | 2012-10-23 13:22:37 -0700 | [diff] [blame] | 716 | if (!devpriv) |
| 717 | return -ENOMEM; |
H Hartley Sweeten | 9a1a6cf | 2012-10-15 10:15:52 -0700 | [diff] [blame] | 718 | |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 719 | devpriv->port = it->options[0]; |
| 720 | devpriv->speed = it->options[1]; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 721 | |
H Hartley Sweeten | 8b6c569 | 2012-06-12 11:59:33 -0700 | [diff] [blame] | 722 | ret = comedi_alloc_subdevices(dev, 5); |
| 723 | if (ret) |
| 724 | return ret; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 725 | |
| 726 | /* digital input subdevice */ |
H Hartley Sweeten | 8e27a73 | 2012-09-05 18:57:21 -0700 | [diff] [blame] | 727 | s = &dev->subdevices[0]; |
H Hartley Sweeten | adbd174 | 2013-04-05 16:10:22 -0700 | [diff] [blame] | 728 | s->type = COMEDI_SUBD_DI; |
| 729 | s->subdev_flags = SDF_READABLE; |
| 730 | s->n_chan = 0; |
| 731 | s->maxdata = 1; |
| 732 | s->range_table = &range_digital; |
| 733 | s->insn_read = serial2002_di_insn_read; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 734 | |
| 735 | /* digital output subdevice */ |
H Hartley Sweeten | 8e27a73 | 2012-09-05 18:57:21 -0700 | [diff] [blame] | 736 | s = &dev->subdevices[1]; |
H Hartley Sweeten | adbd174 | 2013-04-05 16:10:22 -0700 | [diff] [blame] | 737 | s->type = COMEDI_SUBD_DO; |
H Hartley Sweeten | ef49d83 | 2014-10-30 11:19:34 -0700 | [diff] [blame] | 738 | s->subdev_flags = SDF_WRITABLE; |
H Hartley Sweeten | adbd174 | 2013-04-05 16:10:22 -0700 | [diff] [blame] | 739 | s->n_chan = 0; |
| 740 | s->maxdata = 1; |
| 741 | s->range_table = &range_digital; |
| 742 | s->insn_write = serial2002_do_insn_write; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 743 | |
| 744 | /* analog input subdevice */ |
H Hartley Sweeten | 8e27a73 | 2012-09-05 18:57:21 -0700 | [diff] [blame] | 745 | s = &dev->subdevices[2]; |
H Hartley Sweeten | adbd174 | 2013-04-05 16:10:22 -0700 | [diff] [blame] | 746 | s->type = COMEDI_SUBD_AI; |
| 747 | s->subdev_flags = SDF_READABLE | SDF_GROUND; |
| 748 | s->n_chan = 0; |
| 749 | s->maxdata = 1; |
| 750 | s->range_table = NULL; |
| 751 | s->insn_read = serial2002_ai_insn_read; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 752 | |
| 753 | /* analog output subdevice */ |
H Hartley Sweeten | 8e27a73 | 2012-09-05 18:57:21 -0700 | [diff] [blame] | 754 | s = &dev->subdevices[3]; |
H Hartley Sweeten | adbd174 | 2013-04-05 16:10:22 -0700 | [diff] [blame] | 755 | s->type = COMEDI_SUBD_AO; |
H Hartley Sweeten | ef49d83 | 2014-10-30 11:19:34 -0700 | [diff] [blame] | 756 | s->subdev_flags = SDF_WRITABLE; |
H Hartley Sweeten | adbd174 | 2013-04-05 16:10:22 -0700 | [diff] [blame] | 757 | s->n_chan = 0; |
| 758 | s->maxdata = 1; |
| 759 | s->range_table = NULL; |
| 760 | s->insn_write = serial2002_ao_insn_write; |
| 761 | s->insn_read = serial2002_ao_insn_read; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 762 | |
| 763 | /* encoder input subdevice */ |
H Hartley Sweeten | 8e27a73 | 2012-09-05 18:57:21 -0700 | [diff] [blame] | 764 | s = &dev->subdevices[4]; |
H Hartley Sweeten | adbd174 | 2013-04-05 16:10:22 -0700 | [diff] [blame] | 765 | s->type = COMEDI_SUBD_COUNTER; |
| 766 | s->subdev_flags = SDF_READABLE | SDF_LSAMPL; |
| 767 | s->n_chan = 0; |
| 768 | s->maxdata = 1; |
| 769 | s->range_table = NULL; |
| 770 | s->insn_read = serial2002_encoder_insn_read; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 771 | |
H Hartley Sweeten | a058be6 | 2013-04-05 16:12:10 -0700 | [diff] [blame] | 772 | dev->open = serial2002_open; |
| 773 | dev->close = serial2002_close; |
H Hartley Sweeten | e7ccc45 | 2013-04-05 16:11:50 -0700 | [diff] [blame] | 774 | |
H Hartley Sweeten | 81c01c3 | 2013-04-05 16:11:13 -0700 | [diff] [blame] | 775 | return 0; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 776 | } |
| 777 | |
H Hartley Sweeten | 484ecc9 | 2012-05-17 17:11:14 -0700 | [diff] [blame] | 778 | static void serial2002_detach(struct comedi_device *dev) |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 779 | { |
Bill Pemberton | 34c4392 | 2009-03-16 22:05:14 -0400 | [diff] [blame] | 780 | struct comedi_subdevice *s; |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 781 | int i; |
| 782 | |
H Hartley Sweeten | fd47579 | 2013-04-05 16:10:01 -0700 | [diff] [blame] | 783 | for (i = 0; i < dev->n_subdevices; i++) { |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 784 | s = &dev->subdevices[i]; |
Ilia Mirkin | e4e1f28 | 2011-03-13 00:28:57 -0500 | [diff] [blame] | 785 | kfree(s->maxdata_list); |
| 786 | kfree(s->range_table_list); |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 787 | } |
Anders Blomdell | 578c018 | 2009-02-12 16:27:11 -0800 | [diff] [blame] | 788 | } |
| 789 | |
H Hartley Sweeten | 294f930 | 2012-05-03 15:09:40 -0700 | [diff] [blame] | 790 | static struct comedi_driver serial2002_driver = { |
H Hartley Sweeten | cb1648b | 2012-04-27 14:36:26 -0700 | [diff] [blame] | 791 | .driver_name = "serial2002", |
| 792 | .module = THIS_MODULE, |
| 793 | .attach = serial2002_attach, |
| 794 | .detach = serial2002_detach, |
H Hartley Sweeten | cb1648b | 2012-04-27 14:36:26 -0700 | [diff] [blame] | 795 | }; |
H Hartley Sweeten | 294f930 | 2012-05-03 15:09:40 -0700 | [diff] [blame] | 796 | module_comedi_driver(serial2002_driver); |
Arun Thomas | 90f703d | 2010-06-06 22:23:29 +0200 | [diff] [blame] | 797 | |
| 798 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
| 799 | MODULE_DESCRIPTION("Comedi low-level driver"); |
| 800 | MODULE_LICENSE("GPL"); |