1 /** \file rs232-win32-dev.c
2 * \brief RS232 Device emulation
3 *
4 * \author Spiro Trikaliotis <spiro.trikaliotis@gmx.de>
5 *
6 * The RS232 emulation captures the bytes sent to the RS232 interfaces
7 * available (currently, ACIA 6551, std. C64, and Daniel Dallmann's fast RS232
8 * with 9600 BPS).
9 *
10 * I/O is done to a physical COM port.
11 */
12
13 /*
14 * This file is part of VICE, the Versatile Commodore Emulator.
15 * See README for copyright notice.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
30 * 02111-1307 USA.
31 *
32 */
33
34 #undef DEBUG
35 /* #define DEBUG */
36
37 #define DEBUG_FAKE_INPUT_OUTPUT (5 * 80)
38 #undef DEBUG_FAKE_INPUT_OUTPUT
39
40 #include "vice.h"
41
42 #include <errno.h>
43 #include <stdint.h>
44 #include <string.h>
45 #include <winsock.h>
46
47 #ifdef HAVE_IO_H
48 #include <io.h>
49 #endif
50
51 #include "log.h"
52 #include "rs232.h"
53 #include "rs232dev.h"
54 #include "types.h"
55 #include "util.h"
56
57 #ifdef DEBUG
58 # define DEBUG_LOG_MESSAGE(_xxx) log_message _xxx
59 #else
60 # define DEBUG_LOG_MESSAGE(_xxx)
61 #endif
62
63 /* ------------------------------------------------------------------------- */
64
rs232dev_resources_init(void)65 int rs232dev_resources_init(void)
66 {
67 return 0;
68 }
69
rs232dev_resources_shutdown(void)70 void rs232dev_resources_shutdown(void)
71 {
72 }
73
rs232dev_cmdline_options_init(void)74 int rs232dev_cmdline_options_init(void)
75 {
76 return 0;
77 }
78
79 /* ------------------------------------------------------------------------- */
80
81 typedef struct rs232dev {
82 int inuse;
83 HANDLE fd;
84 char *file;
85 DCB restore_dcb;
86 int rts;
87 int dtr;
88 } rs232dev_t;
89
90 static rs232dev_t fds[RS232_NUM_DEVICES];
91
92 static log_t rs232dev_log = LOG_ERR;
93
94 /* ------------------------------------------------------------------------- */
95
96 void rs232dev_close(int fd);
97
98 /* initializes all RS232 stuff */
rs232dev_init(void)99 void rs232dev_init(void)
100 {
101 rs232dev_log = log_open("RS232DEV");
102 }
103
104 /* reset RS232 stuff */
rs232dev_reset(void)105 void rs232dev_reset(void)
106 {
107 int i;
108
109 for (i = 0; i < RS232_NUM_DEVICES; i++) {
110 if (fds[i].inuse) {
111 rs232dev_close(i);
112 }
113 }
114 }
115
116 /* opens a rs232 window, returns handle to give to functions below. */
rs232dev_open(int device)117 int rs232dev_open(int device)
118 {
119 HANDLE serial_port;
120 int ret = -1;
121 int i;
122
123 for (i = 0; i < RS232_NUM_DEVICES; i++) {
124 if (!fds[i].inuse) {
125 break;
126 }
127 }
128 if (i >= RS232_NUM_DEVICES) {
129 log_error(rs232dev_log, "No more devices available.");
130 return -1;
131 }
132
133 DEBUG_LOG_MESSAGE((rs232dev_log, "rs232dev_open(device=%d).", device));
134
135 do {
136 DCB dcb;
137 COMMTIMEOUTS comm_timeouts;
138 char *mode_string = strchr(rs232_devfile[device], ':');
139
140 if (mode_string != NULL) {
141 *mode_string = 0;
142 }
143
144 serial_port = CreateFile(rs232_devfile[device], GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
145
146 if (mode_string != NULL) {
147 *mode_string = ':';
148 }
149
150
151 if (serial_port == INVALID_HANDLE_VALUE) {
152 DEBUG_LOG_MESSAGE((rs232dev_log, "rs232dev: CreateFile '%s' failed: %d.\n", rs232_devfile[device], GetLastError()));
153 break;
154 }
155
156 memset(&dcb, 0, sizeof dcb);
157 dcb.DCBlength = sizeof dcb;
158
159 if (!GetCommState(serial_port, &dcb)) {
160 DEBUG_LOG_MESSAGE((rs232dev_log, "rs232dev: GetCommState '%s' failed: %d.\n", rs232_devfile[device], GetLastError()));
161 break;
162 }
163
164 fds[i].restore_dcb = dcb;
165
166 if (mode_string != NULL) {
167 ++mode_string;
168
169 while (*mode_string == ' ') {
170 ++mode_string;
171 }
172
173 if ( ! BuildCommDCB(mode_string, &dcb) ) {
174 DEBUG_LOG_MESSAGE((rs232dev_log, "rs232dev: BuildCommDCB '%s' for device '%s' failed: %d.\n", mode_string + 1, rs232_devfile[device], GetLastError()));
175 break;
176 }
177 }
178
179 if (! SetCommState(serial_port, &dcb)) {
180 DEBUG_LOG_MESSAGE((rs232dev_log, "rs232dev: SetCommState '%s' failed: %d.\n", rs232_devfile[device], GetLastError()));
181 break;
182 }
183
184 memset(&comm_timeouts, 0, sizeof comm_timeouts);
185
186 /*
187 * ensure that a read will always terminate and only return
188 * what is already in the buffers
189 */
190 comm_timeouts.ReadIntervalTimeout = UINT32_MAX;
191 comm_timeouts.ReadTotalTimeoutMultiplier = 0;
192 comm_timeouts.ReadTotalTimeoutConstant = 0;
193
194 /*
195 * Do not use total timeouts for write operations
196 */
197 comm_timeouts.WriteTotalTimeoutConstant = 0;
198 comm_timeouts.WriteTotalTimeoutMultiplier = 0;
199
200 if (!SetCommTimeouts(serial_port, &comm_timeouts)) {
201 DEBUG_LOG_MESSAGE((rs232dev_log, "rs232dev: SetCommTimeouts '%s' failed: %d.\n", rs232_devfile[device], GetLastError()));
202 break;
203 }
204
205 fds[i].inuse = 1;
206 fds[i].fd = serial_port;
207 fds[i].file = rs232_devfile[device];
208 ret = i;
209 serial_port = INVALID_HANDLE_VALUE;
210
211 } while (0);
212
213 if (serial_port != INVALID_HANDLE_VALUE) {
214 CloseHandle(serial_port);
215 }
216
217 return ret;
218 }
219
220 /* closes the rs232 window again */
rs232dev_close(int fd)221 void rs232dev_close(int fd)
222 {
223 DEBUG_LOG_MESSAGE((rs232dev_log, "rs232dev: close(fd=%d).", fd));
224
225 if (fd < 0 || fd >= RS232_NUM_DEVICES) {
226 log_error(rs232dev_log, "Attempt to close invalid fd %d.", fd);
227 return;
228 }
229 if (!fds[fd].inuse) {
230 log_error(rs232dev_log, "Attempt to close non-open fd %d.", fd);
231 return;
232 }
233
234 if (!SetCommState(fds[fd].fd, &fds[fd].restore_dcb)) {
235 DEBUG_LOG_MESSAGE((rs232dev_log, "rs232dev: SetCommState '%s' on close failed: %d.\n", rs232_devfile[fd], GetLastError()));
236 }
237
238 CloseHandle(fds[fd].fd);
239 fds[fd].inuse = 0;
240 }
241
242 #if DEBUG_FAKE_INPUT_OUTPUT
243 static char rs232_debug_fake_input = 0;
244 static unsigned rs232_debug_fake_input_available = 0;
245 #endif
246
247 /* sends a byte to the RS232 line */
rs232dev_putc(int fd,uint8_t b)248 int rs232dev_putc(int fd, uint8_t b)
249 {
250 uint32_t number_of_bytes = 1;
251
252 DEBUG_LOG_MESSAGE((rs232dev_log, "rs232dev: Output %u = `%c'.", (unsigned)b, b));
253
254 #if DEBUG_FAKE_INPUT_OUTPUT
255
256 rs232_debug_fake_input_available = DEBUG_FAKE_INPUT_OUTPUT;
257 rs232_debug_fake_input = b;
258
259 #else
260 if (WriteFile(fds[fd].fd, &b, (DWORD)1, (LPDWORD)&number_of_bytes, NULL) == 0) {
261 return -1;
262 }
263
264 if (number_of_bytes == 0) {
265 return -1;
266 }
267 #endif
268
269 return 0;
270 }
271
272 /* gets a byte from the RS232 line, returns !=0 if byte received, byte in *b. */
rs232dev_getc(int fd,uint8_t * b)273 int rs232dev_getc(int fd, uint8_t *b)
274 {
275 uint32_t number_of_bytes = 1;
276
277 #if DEBUG_FAKE_INPUT_OUTPUT
278
279 if (rs232_debug_fake_input_available != 0) {
280 --rs232_debug_fake_input_available;
281 *b = rs232_debug_fake_input;
282 } else {
283 number_of_bytes = 0;
284 }
285
286 #else
287 if (ReadFile(fds[fd].fd, b, (DWORD)1, (LPDWORD)&number_of_bytes, NULL) == 0) {
288 return -1;
289 }
290 #endif
291
292 if (number_of_bytes) {
293 DEBUG_LOG_MESSAGE((rs232dev_log, "rs232dev: Input %u = `%c'.", (unsigned)*b, *b));
294 return 1;
295 }
296
297 return 0;
298 }
299
300 /* set the status lines of the RS232 device */
rs232dev_set_status(int fd,enum rs232handshake_out status)301 int rs232dev_set_status(int fd, enum rs232handshake_out status)
302 {
303 int new_rts = (status & RS232_HSO_RTS) ? 1 : 0;
304 int new_dtr = (status & RS232_HSO_DTR) ? 1 : 0;
305
306 /* signal the RS232 device the current status, too */
307
308 if ( new_rts != fds[fd].rts ) {
309 EscapeCommFunction(fds[fd].fd, new_rts ? SETRTS : CLRRTS);
310 fds[fd].rts = new_rts;
311 }
312
313 if ( new_dtr != fds[fd].dtr ) {
314 EscapeCommFunction(fds[fd].fd, new_dtr ? SETDTR : CLRDTR);
315 fds[fd].dtr = new_dtr;
316 }
317
318 return 0;
319 }
320
321 /* get the status lines of the RS232 device */
rs232dev_get_status(int fd)322 enum rs232handshake_in rs232dev_get_status(int fd)
323 {
324 enum rs232handshake_in modem_status = 0;
325
326 do {
327 uint32_t modemstat = 0;
328 if (GetCommModemStatus(fds[fd].fd, (LPDWORD)&modemstat) == 0) {
329 DEBUG_LOG_MESSAGE((rs232dev_log, "Could not get modem status for device %d.", device));
330 break;
331 }
332
333 if (modemstat & MS_CTS_ON) {
334 modem_status |= RS232_HSI_CTS;
335 }
336
337 if (modemstat & MS_DSR_ON) {
338 modem_status |= RS232_HSI_DSR;
339 }
340 } while (0);
341
342 return modem_status;
343 }
344
345 /* set the bps rate of the physical device */
rs232dev_set_bps(int fd,unsigned int bps)346 void rs232dev_set_bps(int fd, unsigned int bps)
347 {
348 /*! \todo set the physical bps rate */
349 DEBUG_LOG_MESSAGE((rs232dev_log, "Setting bps to %u", bps));
350 }
351