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