d3b85d02 |
1 | /* |
2 | * Serial back end (Windows-specific). |
3 | */ |
4 | |
d3b85d02 |
5 | #include <stdio.h> |
6 | #include <stdlib.h> |
7 | #include <limits.h> |
8 | |
9 | #include "putty.h" |
10 | |
11 | #define SERIAL_MAX_BACKLOG 4096 |
12 | |
13 | typedef struct serial_backend_data { |
14 | HANDLE port; |
15 | struct handle *out, *in; |
16 | void *frontend; |
17 | int bufsize; |
45ef7ce3 |
18 | long clearbreak_time; |
19 | int break_in_progress; |
d3b85d02 |
20 | } *Serial; |
21 | |
22 | static void serial_terminate(Serial serial) |
23 | { |
24 | if (serial->out) { |
25 | handle_free(serial->out); |
26 | serial->out = NULL; |
27 | } |
28 | if (serial->in) { |
29 | handle_free(serial->in); |
30 | serial->in = NULL; |
31 | } |
4a0b6d61 |
32 | if (serial->port != INVALID_HANDLE_VALUE) { |
45ef7ce3 |
33 | if (serial->break_in_progress) |
34 | ClearCommBreak(serial->port); |
d3b85d02 |
35 | CloseHandle(serial->port); |
4a0b6d61 |
36 | serial->port = INVALID_HANDLE_VALUE; |
d3b85d02 |
37 | } |
38 | } |
39 | |
40 | static int serial_gotdata(struct handle *h, void *data, int len) |
41 | { |
42 | Serial serial = (Serial)handle_get_privdata(h); |
43 | if (len <= 0) { |
44 | const char *error_msg; |
45 | |
46 | /* |
47 | * Currently, len==0 should never happen because we're |
48 | * ignoring EOFs. However, it seems not totally impossible |
49 | * that this same back end might be usable to talk to named |
50 | * pipes or some other non-serial device, in which case EOF |
51 | * may become meaningful here. |
52 | */ |
53 | if (len == 0) |
54 | error_msg = "End of file reading from serial device"; |
55 | else |
56 | error_msg = "Error reading from serial device"; |
57 | |
58 | serial_terminate(serial); |
59 | |
60 | notify_remote_exit(serial->frontend); |
61 | |
62 | logevent(serial->frontend, error_msg); |
63 | |
64 | connection_fatal(serial->frontend, "%s", error_msg); |
65 | |
66 | return 0; /* placate optimiser */ |
67 | } else { |
68 | return from_backend(serial->frontend, 0, data, len); |
69 | } |
70 | } |
71 | |
72 | static void serial_sentdata(struct handle *h, int new_backlog) |
73 | { |
74 | Serial serial = (Serial)handle_get_privdata(h); |
75 | if (new_backlog < 0) { |
76 | const char *error_msg = "Error writing to serial device"; |
77 | |
78 | serial_terminate(serial); |
79 | |
80 | notify_remote_exit(serial->frontend); |
81 | |
82 | logevent(serial->frontend, error_msg); |
83 | |
84 | connection_fatal(serial->frontend, "%s", error_msg); |
85 | } else { |
86 | serial->bufsize = new_backlog; |
87 | } |
88 | } |
89 | |
4a693cfc |
90 | static const char *serial_configure(Serial serial, HANDLE serport, Conf *conf) |
d3b85d02 |
91 | { |
92 | DCB dcb; |
93 | COMMTIMEOUTS timeouts; |
94 | |
95 | /* |
96 | * Set up the serial port parameters. If we can't even |
97 | * GetCommState, we ignore the problem on the grounds that the |
98 | * user might have pointed us at some other type of two-way |
99 | * device instead of a serial port. |
100 | */ |
101 | if (GetCommState(serport, &dcb)) { |
102 | char *msg; |
103 | const char *str; |
104 | |
105 | /* |
106 | * Boilerplate. |
107 | */ |
108 | dcb.fBinary = TRUE; |
109 | dcb.fDtrControl = DTR_CONTROL_ENABLE; |
110 | dcb.fDsrSensitivity = FALSE; |
111 | dcb.fTXContinueOnXoff = FALSE; |
112 | dcb.fOutX = FALSE; |
113 | dcb.fInX = FALSE; |
114 | dcb.fErrorChar = FALSE; |
115 | dcb.fNull = FALSE; |
116 | dcb.fRtsControl = RTS_CONTROL_ENABLE; |
117 | dcb.fAbortOnError = FALSE; |
118 | dcb.fOutxCtsFlow = FALSE; |
119 | dcb.fOutxDsrFlow = FALSE; |
120 | |
121 | /* |
122 | * Configurable parameters. |
123 | */ |
4a693cfc |
124 | dcb.BaudRate = conf_get_int(conf, CONF_serspeed); |
125 | msg = dupprintf("Configuring baud rate %d", dcb.BaudRate); |
d3b85d02 |
126 | logevent(serial->frontend, msg); |
127 | sfree(msg); |
128 | |
4a693cfc |
129 | dcb.ByteSize = conf_get_int(conf, CONF_serdatabits); |
130 | msg = dupprintf("Configuring %d data bits", dcb.ByteSize); |
d3b85d02 |
131 | logevent(serial->frontend, msg); |
132 | sfree(msg); |
133 | |
4a693cfc |
134 | switch (conf_get_int(conf, CONF_serstopbits)) { |
d3b85d02 |
135 | case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break; |
136 | case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break; |
137 | case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break; |
138 | default: return "Invalid number of stop bits (need 1, 1.5 or 2)"; |
139 | } |
140 | msg = dupprintf("Configuring %s data bits", str); |
141 | logevent(serial->frontend, msg); |
142 | sfree(msg); |
143 | |
4a693cfc |
144 | switch (conf_get_int(conf, CONF_serparity)) { |
d3b85d02 |
145 | case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break; |
146 | case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break; |
147 | case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break; |
148 | case SER_PAR_MARK: dcb.Parity = MARKPARITY; str = "mark"; break; |
149 | case SER_PAR_SPACE: dcb.Parity = SPACEPARITY; str = "space"; break; |
150 | } |
151 | msg = dupprintf("Configuring %s parity", str); |
152 | logevent(serial->frontend, msg); |
153 | sfree(msg); |
154 | |
4a693cfc |
155 | switch (conf_get_int(conf, CONF_serflow)) { |
d3b85d02 |
156 | case SER_FLOW_NONE: |
157 | str = "no"; |
158 | break; |
159 | case SER_FLOW_XONXOFF: |
160 | dcb.fOutX = dcb.fInX = TRUE; |
161 | str = "XON/XOFF"; |
162 | break; |
163 | case SER_FLOW_RTSCTS: |
164 | dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; |
165 | dcb.fOutxCtsFlow = TRUE; |
166 | str = "RTS/CTS"; |
167 | break; |
168 | case SER_FLOW_DSRDTR: |
169 | dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; |
170 | dcb.fOutxDsrFlow = TRUE; |
171 | str = "DSR/DTR"; |
172 | break; |
173 | } |
174 | msg = dupprintf("Configuring %s flow control", str); |
175 | logevent(serial->frontend, msg); |
176 | sfree(msg); |
177 | |
178 | if (!SetCommState(serport, &dcb)) |
179 | return "Unable to configure serial port"; |
180 | |
181 | timeouts.ReadIntervalTimeout = 1; |
182 | timeouts.ReadTotalTimeoutMultiplier = 0; |
183 | timeouts.ReadTotalTimeoutConstant = 0; |
184 | timeouts.WriteTotalTimeoutMultiplier = 0; |
185 | timeouts.WriteTotalTimeoutConstant = 0; |
186 | if (!SetCommTimeouts(serport, &timeouts)) |
187 | return "Unable to configure serial timeouts"; |
188 | } |
189 | |
190 | return NULL; |
191 | } |
192 | |
193 | /* |
194 | * Called to set up the serial connection. |
195 | * |
196 | * Returns an error message, or NULL on success. |
197 | * |
198 | * Also places the canonical host name into `realhost'. It must be |
199 | * freed by the caller. |
200 | */ |
201 | static const char *serial_init(void *frontend_handle, void **backend_handle, |
4a693cfc |
202 | Conf *conf, char *host, int port, |
203 | char **realhost, int nodelay, int keepalive) |
d3b85d02 |
204 | { |
205 | Serial serial; |
206 | HANDLE serport; |
207 | const char *err; |
4a693cfc |
208 | char *serline; |
d3b85d02 |
209 | |
210 | serial = snew(struct serial_backend_data); |
4a0b6d61 |
211 | serial->port = INVALID_HANDLE_VALUE; |
d3b85d02 |
212 | serial->out = serial->in = NULL; |
dcf640d6 |
213 | serial->bufsize = 0; |
45ef7ce3 |
214 | serial->break_in_progress = FALSE; |
d3b85d02 |
215 | *backend_handle = serial; |
216 | |
217 | serial->frontend = frontend_handle; |
218 | |
4a693cfc |
219 | serline = conf_get_str(conf, CONF_serline); |
d3b85d02 |
220 | { |
4a693cfc |
221 | char *msg = dupprintf("Opening serial device %s", serline); |
d3b85d02 |
222 | logevent(serial->frontend, msg); |
223 | } |
224 | |
934dbf05 |
225 | { |
226 | /* |
227 | * Munge the string supplied by the user into a Windows filename. |
228 | * |
229 | * Windows supports opening a few "legacy" devices (including |
230 | * COM1-9) by specifying their names verbatim as a filename to |
231 | * open. (Thus, no files can ever have these names. See |
232 | * <http://msdn2.microsoft.com/en-us/library/aa365247.aspx> |
233 | * ("Naming a File") for the complete list of reserved names.) |
234 | * |
235 | * However, this doesn't let you get at devices COM10 and above. |
236 | * For that, you need to specify a filename like "\\.\COM10". |
237 | * This is also necessary for special serial and serial-like |
238 | * devices such as \\.\WCEUSBSH001. It also works for the "legacy" |
239 | * names, so you can do \\.\COM1 (verified as far back as Win95). |
240 | * See <http://msdn2.microsoft.com/en-us/library/aa363858.aspx> |
241 | * (CreateFile() docs). |
242 | * |
243 | * So, we believe that prepending "\\.\" should always be the |
244 | * Right Thing. However, just in case someone finds something to |
245 | * talk to that doesn't exist under there, if the serial line |
246 | * contains a backslash, we use it verbatim. (This also lets |
247 | * existing configurations using \\.\ continue working.) |
248 | */ |
249 | char *serfilename = |
4a693cfc |
250 | dupprintf("%s%s", strchr(serline, '\\') ? "" : "\\\\.\\", serline); |
934dbf05 |
251 | serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, |
252 | OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); |
253 | sfree(serfilename); |
254 | } |
255 | |
d3b85d02 |
256 | if (serport == INVALID_HANDLE_VALUE) |
257 | return "Unable to open serial port"; |
258 | |
4a693cfc |
259 | err = serial_configure(serial, serport, conf); |
d3b85d02 |
260 | if (err) |
261 | return err; |
262 | |
263 | serial->port = serport; |
264 | serial->out = handle_output_new(serport, serial_sentdata, serial, |
265 | HANDLE_FLAG_OVERLAPPED); |
266 | serial->in = handle_input_new(serport, serial_gotdata, serial, |
267 | HANDLE_FLAG_OVERLAPPED | |
c606abbc |
268 | HANDLE_FLAG_IGNOREEOF | |
269 | HANDLE_FLAG_UNITBUFFER); |
d3b85d02 |
270 | |
4a693cfc |
271 | *realhost = dupstr(serline); |
d3b85d02 |
272 | |
45ef7ce3 |
273 | /* |
274 | * Specials are always available. |
275 | */ |
276 | update_specials_menu(serial->frontend); |
277 | |
d3b85d02 |
278 | return NULL; |
279 | } |
280 | |
281 | static void serial_free(void *handle) |
282 | { |
283 | Serial serial = (Serial) handle; |
284 | |
285 | serial_terminate(serial); |
45ef7ce3 |
286 | expire_timer_context(serial); |
d3b85d02 |
287 | sfree(serial); |
288 | } |
289 | |
4a693cfc |
290 | static void serial_reconfig(void *handle, Conf *conf) |
d3b85d02 |
291 | { |
292 | Serial serial = (Serial) handle; |
293 | const char *err; |
294 | |
4a693cfc |
295 | err = serial_configure(serial, serial->port, conf); |
d3b85d02 |
296 | |
297 | /* |
298 | * FIXME: what should we do if err returns something? |
299 | */ |
300 | } |
301 | |
302 | /* |
303 | * Called to send data down the serial connection. |
304 | */ |
305 | static int serial_send(void *handle, char *buf, int len) |
306 | { |
307 | Serial serial = (Serial) handle; |
308 | |
309 | if (serial->out == NULL) |
310 | return 0; |
311 | |
312 | serial->bufsize = handle_write(serial->out, buf, len); |
313 | return serial->bufsize; |
314 | } |
315 | |
316 | /* |
317 | * Called to query the current sendability status. |
318 | */ |
319 | static int serial_sendbuffer(void *handle) |
320 | { |
321 | Serial serial = (Serial) handle; |
322 | return serial->bufsize; |
323 | } |
324 | |
325 | /* |
326 | * Called to set the size of the window |
327 | */ |
328 | static void serial_size(void *handle, int width, int height) |
329 | { |
330 | /* Do nothing! */ |
331 | return; |
332 | } |
333 | |
45ef7ce3 |
334 | static void serbreak_timer(void *ctx, long now) |
335 | { |
336 | Serial serial = (Serial)ctx; |
337 | |
338 | if (now >= serial->clearbreak_time && serial->port) { |
339 | ClearCommBreak(serial->port); |
340 | serial->break_in_progress = FALSE; |
341 | logevent(serial->frontend, "Finished serial break"); |
342 | } |
343 | } |
344 | |
d3b85d02 |
345 | /* |
346 | * Send serial special codes. |
347 | */ |
348 | static void serial_special(void *handle, Telnet_Special code) |
349 | { |
45ef7ce3 |
350 | Serial serial = (Serial) handle; |
351 | |
352 | if (serial->port && code == TS_BRK) { |
353 | logevent(serial->frontend, "Starting serial break at user request"); |
354 | SetCommBreak(serial->port); |
355 | /* |
356 | * To send a serial break on Windows, we call SetCommBreak |
357 | * to begin the break, then wait a bit, and then call |
358 | * ClearCommBreak to finish it. Hence, I must use timing.c |
359 | * to arrange a callback when it's time to do the latter. |
360 | * |
361 | * SUS says that a default break length must be between 1/4 |
362 | * and 1/2 second. FreeBSD apparently goes with 2/5 second, |
363 | * and so will I. |
364 | */ |
365 | serial->clearbreak_time = |
366 | schedule_timer(TICKSPERSEC * 2 / 5, serbreak_timer, serial); |
367 | serial->break_in_progress = TRUE; |
368 | } |
369 | |
d3b85d02 |
370 | return; |
371 | } |
372 | |
373 | /* |
374 | * Return a list of the special codes that make sense in this |
375 | * protocol. |
376 | */ |
377 | static const struct telnet_special *serial_get_specials(void *handle) |
378 | { |
45ef7ce3 |
379 | static const struct telnet_special specials[] = { |
380 | {"Break", TS_BRK}, |
381 | {NULL, TS_EXITMENU} |
382 | }; |
383 | return specials; |
d3b85d02 |
384 | } |
385 | |
386 | static int serial_connected(void *handle) |
387 | { |
388 | return 1; /* always connected */ |
389 | } |
390 | |
391 | static int serial_sendok(void *handle) |
392 | { |
393 | return 1; |
394 | } |
395 | |
396 | static void serial_unthrottle(void *handle, int backlog) |
397 | { |
398 | Serial serial = (Serial) handle; |
399 | if (serial->in) |
400 | handle_unthrottle(serial->in, backlog); |
401 | } |
402 | |
403 | static int serial_ldisc(void *handle, int option) |
404 | { |
405 | /* |
406 | * Local editing and local echo are off by default. |
407 | */ |
408 | return 0; |
409 | } |
410 | |
411 | static void serial_provide_ldisc(void *handle, void *ldisc) |
412 | { |
413 | /* This is a stub. */ |
414 | } |
415 | |
416 | static void serial_provide_logctx(void *handle, void *logctx) |
417 | { |
418 | /* This is a stub. */ |
419 | } |
420 | |
421 | static int serial_exitcode(void *handle) |
422 | { |
423 | Serial serial = (Serial) handle; |
4a0b6d61 |
424 | if (serial->port != INVALID_HANDLE_VALUE) |
d3b85d02 |
425 | return -1; /* still connected */ |
426 | else |
427 | /* Exit codes are a meaningless concept with serial ports */ |
428 | return INT_MAX; |
429 | } |
430 | |
431 | /* |
432 | * cfg_info for Serial does nothing at all. |
433 | */ |
434 | static int serial_cfg_info(void *handle) |
435 | { |
436 | return 0; |
437 | } |
438 | |
439 | Backend serial_backend = { |
440 | serial_init, |
441 | serial_free, |
442 | serial_reconfig, |
443 | serial_send, |
444 | serial_sendbuffer, |
445 | serial_size, |
446 | serial_special, |
447 | serial_get_specials, |
448 | serial_connected, |
449 | serial_exitcode, |
450 | serial_sendok, |
451 | serial_ldisc, |
452 | serial_provide_ldisc, |
453 | serial_provide_logctx, |
454 | serial_unthrottle, |
455 | serial_cfg_info, |
9e164d82 |
456 | "serial", |
457 | PROT_SERIAL, |
a4451dd1 |
458 | 0 |
d3b85d02 |
459 | }; |