1 /** \file rs232-unix-dev.c
2 * \brief RS232 Device emulation
3 *
4 * \author Andre Fachat <a.fachat@physik.tu-chemnitz.de>
5 *
6 * The RS232 emulation captures the bytes sent to the RS232 interfaces
7 * available (currently ACIA 6551, std C64 and Daniel Dallmanns fast RS232
8 * with 9600 Baud).
9 * The characters captured are sent to a file or an attached process.
10 * Characters sent from a process are sent back to the
11 * chip emulations.
12 */
13
14 /*
15 * This file is part of VICE, the Versatile Commodore Emulator.
16 * See README for copyright notice.
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
31 * 02111-1307 USA.
32 *
33 */
34
35
36 #undef DEBUG
37
38
39 #include <stdint.h>
40
41 #include "vice.h"
42
43
44 #ifdef HAVE_RS232DEV
45
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <stdio.h>
49 #include <string.h>
50
51 #ifdef HAVE_SYS_IOCTL_H
52 #include <sys/ioctl.h>
53 #endif
54
55 #ifdef HAVE_SYS_TIME_H
56 #include <sys/time.h>
57 #endif
58
59 #include <sys/types.h>
60 #include <sys/stat.h>
61
62 #include <termios.h>
63
64 #include <unistd.h>
65
66 /* <sys/select.h> is required for select(2) and fd_set */
67 #if defined(HAVE_SYS_SELECT_H) || \
68 defined(OPENSERVER6_COMPILE) || \
69 (defined(__QNX__) && !defined(__QNXNTO__))
70 #include <sys/select.h>
71 #endif
72
73 #ifndef FD_ISSET
74 #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1L << ((n) % NFDBITS)))
75 #endif
76
77 #endif
78
79 #if defined(OPENSTEP_COMPILE) || defined(NEXTSTEP_COMPILE)
80 #define ssize_t int
81 #endif
82
83 #include "cmdline.h"
84 #include "coproc.h"
85 #include "log.h"
86 #include "resources.h"
87 #include "rs232.h"
88 #include "rs232dev.h"
89 #include "types.h"
90
91
92 #if defined(NEXTSTEP_COMPILE) || defined(OPENSTEP_COMPILE)
cfsetispeed(struct termios * t,int speed)93 int cfsetispeed(struct termios *t, int speed)
94 {
95 t->c_ispeed = speed;
96 return 0;
97 }
98
cfsetospeed(struct termios * t,int speed)99 int cfsetospeed(struct termios *t, int speed)
100 {
101 t->c_ispeed = speed;
102 return 0;
103 }
104
tcgetattr(int fildes,struct termios * tp)105 int tcgetattr(int fildes, struct termios *tp)
106 {
107 return ioctl(fildes, TIOCGETA, tp);
108 }
109
tcsetattr(int fd,int opt,const struct termios * t)110 int tcsetattr(int fd, int opt, const struct termios *t)
111 {
112 int st;
113
114 switch(opt) {
115 case TCSANOW:
116 st = ioctl(fd, TIOCSETA, t);
117 break;
118 case TCSADRAIN:
119 st = ioctl(fd, TIOCSETAW, t);
120 break;
121 case TCSAFLUSH:
122 st = ioctl(fd, TIOCSETAF, t);
123 break;
124 default:
125 st = -1;
126 errno = EINVAL;
127 break;
128 }
129 return st;
130 }
131 #endif
132
133 /* ------------------------------------------------------------------------- */
134
135 /* resource handling */
136
137 static int devbaud[RS232_NUM_DEVICES];
138
set_devbaud(int val,void * param)139 static int set_devbaud(int val, void *param)
140 {
141 devbaud[vice_ptr_to_int(param)] = val;
142 return 0;
143 }
144
145 /* ------------------------------------------------------------------------- */
146
147 static const resource_int_t resources_int[] = {
148 { "RsDevice1Baud", 9600, RES_EVENT_NO, NULL,
149 &devbaud[0], set_devbaud, (void *)0 },
150 { "RsDevice2Baud", 9600, RES_EVENT_NO, NULL,
151 &devbaud[1], set_devbaud, (void *)1 },
152 { "RsDevice3Baud", 9600, RES_EVENT_NO, NULL,
153 &devbaud[2], set_devbaud, (void *)2 },
154 { "RsDevice4Baud", 9600, RES_EVENT_NO, NULL,
155 &devbaud[3], set_devbaud, (void *)3 },
156 RESOURCE_INT_LIST_END
157 };
158
rs232dev_resources_init(void)159 int rs232dev_resources_init(void)
160 {
161 return resources_register_int(resources_int);
162 }
163
rs232dev_resources_shutdown(void)164 void rs232dev_resources_shutdown(void)
165 {
166 }
167
168 static const cmdline_option_t cmdline_options[] =
169 {
170 { "-rsdev1baud", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS | CMDLINE_ATTRIB_NEED_BRACKETS,
171 NULL, NULL, "RsDevice1Baud", NULL,
172 "<baudrate>", "Specify baudrate of first RS232 device" },
173 { "-rsdev2baud", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS | CMDLINE_ATTRIB_NEED_BRACKETS,
174 NULL, NULL, "RsDevice2Baud", NULL,
175 "<baudrate>", "Specify baudrate of second RS232 device" },
176 { "-rsdev3baud", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS | CMDLINE_ATTRIB_NEED_BRACKETS,
177 NULL, NULL, "RsDevice3Baud", NULL,
178 "<baudrate>", "Specify baudrate of third RS232 device" },
179 { "-rsdev4baud", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS | CMDLINE_ATTRIB_NEED_BRACKETS,
180 NULL, NULL, "RsDevice4Baud", NULL,
181 "<baudrate>", "Specify baudrate of fourth RS232 device" },
182 CMDLINE_LIST_END
183 };
184
rs232dev_cmdline_options_init(void)185 int rs232dev_cmdline_options_init(void)
186 {
187 return cmdline_register_options(cmdline_options);
188 }
189
190 /* ------------------------------------------------------------------------- */
191
192 typedef struct rs232 {
193 int inuse;
194 int type;
195 int fd_r;
196 int fd_w;
197 char *file;
198 struct termios saved;
199 } rs232_t;
200
201 #define T_FILE 0
202 #define T_TTY 1
203 #define T_PROC 2
204
205 static rs232_t fds[RS232_NUM_DEVICES];
206
207 static log_t rs232dev_log = LOG_ERR;
208
209 /* ------------------------------------------------------------------------- */
210
211 void rs232dev_close(int fd);
212
213 /* initializes all RS232 stuff */
rs232dev_init(void)214 void rs232dev_init(void)
215 {
216 int i;
217
218 for (i = 0; i < RS232_NUM_DEVICES; i++) {
219 fds[i].inuse = 0;
220 }
221
222 rs232dev_log = log_open("RS232DEV");
223 }
224
225 /* resets terminal to old mode */
unset_tty(int i)226 static void unset_tty(int i)
227 {
228 tcsetattr(fds[i].fd_r, TCSAFLUSH, &fds[i].saved);
229 }
230
231 static struct {
232 int baud;
233 speed_t speed;
234 } speed_tab[] = {
235 { 300, B300 },
236 { 600, B600 },
237 { 1200, B1200 },
238 { 1800, B1800 },
239 { 2400, B2400 },
240 { 4800, B4800 },
241 { 9600, B9600 },
242 { 19200, B19200 },
243 { 38400, B38400 },
244 { 0, B9600 } /* fallback */
245 };
246
247 /* sets terminal to raw mode */
set_tty(int i,int baud)248 static void set_tty(int i, int baud)
249 {
250 /*
251 * set tty to raw mode as of
252 * "Advanced Programming in the Unix Environment"
253 * by W.R. Stevens, Addison-Wesley.
254 */
255 speed_t speed;
256 int fd = fds[i].fd_r;
257 struct termios buf;
258
259 if (tcgetattr(fd, &fds[i].saved) < 0) {
260 return /* -1 */ ;
261 }
262 buf = fds[i].saved;
263
264 buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
265
266 /* echho off, cononical mode off, extended input processing
267 * off, signal chars off */
268 buf.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
269
270 /* no SIGINT on Break, CR-to-NL off, input parity check off,
271 * don't strip 8th bit on input, output flow control off */
272 buf.c_cflag &= ~(CSIZE | PARENB);
273
274 /* clear size bits, parity checking off */
275 buf.c_cflag |= CS8;
276
277 /* set 8 bits/char */
278 buf.c_oflag &= ~(OPOST);
279
280 /* ouput processing off */
281 buf.c_cc[VMIN] = 1; /* 1 byte at a time, no timer */
282 buf.c_cc[VTIME] = 0;
283
284 for (i = 0; speed_tab[i].baud; i++) {
285 if (speed_tab[i].baud >= baud) {
286 break;
287 }
288 }
289 speed = speed_tab[i].speed;
290
291 cfsetispeed(&buf, speed);
292 cfsetospeed(&buf, speed);
293
294 tcsetattr(fd, TCSAFLUSH, &buf);
295 }
296
297 /* reset RS232 stuff */
rs232dev_reset(void)298 void rs232dev_reset(void)
299 {
300 int i;
301
302 for (i = 0; i < RS232_NUM_DEVICES; i++) {
303 if (fds[i].inuse) {
304 rs232dev_close(i);
305 }
306 }
307 }
308
309 /* opens a rs232 window, returns handle to give to functions below. */
rs232dev_open(int device)310 int rs232dev_open(int device)
311 {
312 int i, fd;
313
314 for (i = 0; i < RS232_NUM_DEVICES; i++) {
315 if (!fds[i].inuse) {
316 break;
317 }
318 }
319 if (i >= RS232_NUM_DEVICES) {
320 log_error(rs232dev_log, "No more devices available.");
321 return -1;
322 }
323
324 #ifdef DEBUG
325 log_message(rs232dev_log, "rs232dev_open(device=%d).", device);
326 #endif
327
328 if (rs232_devfile[device][0] == '|') {
329 #if defined(OPENSTEP_COMPILE) || defined(RHAPSODY_COMPILE) \
330 || defined(NEXTSTEP_COMPILE)
331 log_error(rs232dev_log, "Forking not supported on this platform.");
332 return -1;
333 #else
334 if (fork_coproc(&fds[i].fd_w, &fds[i].fd_r, rs232_devfile[device] + 1) < 0) {
335 log_error(rs232dev_log, "Cannot fork process.");
336 return -1;
337 }
338 #endif
339 fds[i].type = T_PROC;
340 fds[i].inuse = 1;
341 fds[i].file = rs232_devfile[device];
342 } else {
343 #if !defined(OPENSTEP_COMPILE) && !defined(NEXTSTEP_COMPILE)
344 fd = open(rs232_devfile[device], O_RDWR | O_NOCTTY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
345 if (fd < 0) {
346 log_error(rs232dev_log, "Cannot open file \"%s\": %s", rs232_devfile[device], strerror(errno));
347 return -1;
348 }
349 fds[i].fd_r = fds[i].fd_w = fd;
350 fds[i].file = rs232_devfile[device];
351
352 if (isatty(fd)) {
353 fds[i].type = T_TTY;
354 set_tty(i, devbaud[device]);
355 } else {
356 fds[i].type = T_FILE;
357 }
358 fds[i].inuse = 1;
359 #endif
360 }
361
362 return i;
363 }
364
365 /* closes the rs232 window again */
rs232dev_close(int fd)366 void rs232dev_close(int fd)
367 {
368 #ifdef DEBUG
369 log_debug(rs232dev_log, "close(fd=%d).", fd);
370 #endif
371
372 if (fd < 0 || fd >= RS232_NUM_DEVICES) {
373 log_error(rs232dev_log, "Attempt to close invalid fd %d.", fd);
374 return;
375 }
376 if (!fds[fd].inuse) {
377 log_error(rs232dev_log, "Attempt to close non-open fd %d.", fd);
378 return;
379 }
380
381 if (fds[fd].type == T_TTY) {
382 unset_tty(fd);
383 }
384 close(fds[fd].fd_r);
385 if ((fds[fd].type == T_PROC) && (fds[fd].fd_r != fds[fd].fd_w)) {
386 close(fds[fd].fd_w);
387 }
388 fds[fd].inuse = 0;
389 }
390
391 /* sends a byte to the RS232 line */
rs232dev_putc(int fd,uint8_t b)392 int rs232dev_putc(int fd, uint8_t b)
393 {
394 ssize_t n;
395
396 if (fd < 0 || fd >= RS232_NUM_DEVICES) {
397 log_error(rs232dev_log, "Attempt to write to invalid fd %d.", fd);
398 return -1;
399 }
400 if (!fds[fd].inuse) {
401 log_error(rs232dev_log, "Attempt to write to non-open fd %d.", fd);
402 return -1;
403 }
404
405 /* for the beginning... */
406 #ifdef DEBUG
407 log_message(rs232dev_log, "Output `%c'.", b);
408 #endif
409
410 do {
411 n = write(fds[fd].fd_w, &b, 1);
412 if (n < 0) {
413 log_error(rs232dev_log, "Error writing: %s.", strerror(errno));
414 return -1;
415 }
416 } while (n != 1);
417
418 return 0;
419 }
420
421 /* gets a byte to the RS232 line, returns !=0 if byte received, byte in *b. */
rs232dev_getc(int fd,uint8_t * b)422 int rs232dev_getc(int fd, uint8_t * b)
423 {
424 int ret;
425 size_t n;
426 fd_set rdset;
427 struct timeval ti;
428
429 if (fd < 0 || fd >= RS232_NUM_DEVICES) {
430 log_error(rs232dev_log, "Attempt to read from invalid fd %d.", fd);
431 return -1;
432 }
433 if (!fds[fd].inuse) {
434 log_error(rs232dev_log, "Attempt to read from non-open fd %d.", fd);
435 return -1;
436 }
437
438 if (fds[fd].type == T_FILE) {
439 return 0;
440 }
441
442 FD_ZERO(&rdset);
443 FD_SET(fds[fd].fd_r, &rdset);
444 ti.tv_sec = ti.tv_usec = 0;
445
446 ret = select(fds[fd].fd_r + 1, &rdset, NULL, NULL, &ti);
447
448 if (ret && (FD_ISSET(fds[fd].fd_r, &rdset))) {
449 n = read(fds[fd].fd_r, b, 1);
450 if (n) {
451 return 1;
452 }
453 }
454 return 0;
455 }
456
457 /* set the status lines of the RS232 device */
rs232dev_set_status(int fd,enum rs232handshake_out status)458 int rs232dev_set_status(int fd, enum rs232handshake_out status)
459 {
460 return -1;
461 }
462
463 /* get the status lines of the RS232 device */
rs232dev_get_status(int fd)464 enum rs232handshake_in rs232dev_get_status(int fd)
465 {
466 /*! \todo dummy */
467 return RS232_HSI_CTS | RS232_HSI_DSR;
468 }
469
470 /* set the bps rate of the physical device */
rs232dev_set_bps(int fd,unsigned int bps)471 void rs232dev_set_bps(int fd, unsigned int bps)
472 {
473 }
474