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 | |
90 | static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg) |
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 | */ |
124 | dcb.BaudRate = cfg->serspeed; |
125 | msg = dupprintf("Configuring baud rate %d", cfg->serspeed); |
126 | logevent(serial->frontend, msg); |
127 | sfree(msg); |
128 | |
129 | dcb.ByteSize = cfg->serdatabits; |
130 | msg = dupprintf("Configuring %d data bits", cfg->serdatabits); |
131 | logevent(serial->frontend, msg); |
132 | sfree(msg); |
133 | |
134 | switch (cfg->serstopbits) { |
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 | |
144 | switch (cfg->serparity) { |
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 | |
155 | switch (cfg->serflow) { |
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, |
202 | Config *cfg, |
203 | char *host, int port, char **realhost, int nodelay, |
204 | int keepalive) |
205 | { |
206 | Serial serial; |
207 | HANDLE serport; |
208 | const char *err; |
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 | |
219 | { |
e5a76e12 |
220 | char *msg = dupprintf("Opening serial device %s", cfg->serline); |
d3b85d02 |
221 | logevent(serial->frontend, msg); |
222 | } |
223 | |
934dbf05 |
224 | { |
225 | /* |
226 | * Munge the string supplied by the user into a Windows filename. |
227 | * |
228 | * Windows supports opening a few "legacy" devices (including |
229 | * COM1-9) by specifying their names verbatim as a filename to |
230 | * open. (Thus, no files can ever have these names. See |
231 | * <http://msdn2.microsoft.com/en-us/library/aa365247.aspx> |
232 | * ("Naming a File") for the complete list of reserved names.) |
233 | * |
234 | * However, this doesn't let you get at devices COM10 and above. |
235 | * For that, you need to specify a filename like "\\.\COM10". |
236 | * This is also necessary for special serial and serial-like |
237 | * devices such as \\.\WCEUSBSH001. It also works for the "legacy" |
238 | * names, so you can do \\.\COM1 (verified as far back as Win95). |
239 | * See <http://msdn2.microsoft.com/en-us/library/aa363858.aspx> |
240 | * (CreateFile() docs). |
241 | * |
242 | * So, we believe that prepending "\\.\" should always be the |
243 | * Right Thing. However, just in case someone finds something to |
244 | * talk to that doesn't exist under there, if the serial line |
245 | * contains a backslash, we use it verbatim. (This also lets |
246 | * existing configurations using \\.\ continue working.) |
247 | */ |
248 | char *serfilename = |
249 | dupprintf("%s%s", |
250 | strchr(cfg->serline, '\\') ? "" : "\\\\.\\", |
251 | cfg->serline); |
252 | serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, |
253 | OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); |
254 | sfree(serfilename); |
255 | } |
256 | |
d3b85d02 |
257 | if (serport == INVALID_HANDLE_VALUE) |
258 | return "Unable to open serial port"; |
259 | |
260 | err = serial_configure(serial, serport, cfg); |
261 | if (err) |
262 | return err; |
263 | |
264 | serial->port = serport; |
265 | serial->out = handle_output_new(serport, serial_sentdata, serial, |
266 | HANDLE_FLAG_OVERLAPPED); |
267 | serial->in = handle_input_new(serport, serial_gotdata, serial, |
268 | HANDLE_FLAG_OVERLAPPED | |
c606abbc |
269 | HANDLE_FLAG_IGNOREEOF | |
270 | HANDLE_FLAG_UNITBUFFER); |
d3b85d02 |
271 | |
272 | *realhost = dupstr(cfg->serline); |
273 | |
45ef7ce3 |
274 | /* |
275 | * Specials are always available. |
276 | */ |
277 | update_specials_menu(serial->frontend); |
278 | |
d3b85d02 |
279 | return NULL; |
280 | } |
281 | |
282 | static void serial_free(void *handle) |
283 | { |
284 | Serial serial = (Serial) handle; |
285 | |
286 | serial_terminate(serial); |
45ef7ce3 |
287 | expire_timer_context(serial); |
d3b85d02 |
288 | sfree(serial); |
289 | } |
290 | |
291 | static void serial_reconfig(void *handle, Config *cfg) |
292 | { |
293 | Serial serial = (Serial) handle; |
294 | const char *err; |
295 | |
296 | err = serial_configure(serial, serial->port, cfg); |
297 | |
298 | /* |
299 | * FIXME: what should we do if err returns something? |
300 | */ |
301 | } |
302 | |
303 | /* |
304 | * Called to send data down the serial connection. |
305 | */ |
306 | static int serial_send(void *handle, char *buf, int len) |
307 | { |
308 | Serial serial = (Serial) handle; |
309 | |
310 | if (serial->out == NULL) |
311 | return 0; |
312 | |
313 | serial->bufsize = handle_write(serial->out, buf, len); |
314 | return serial->bufsize; |
315 | } |
316 | |
317 | /* |
318 | * Called to query the current sendability status. |
319 | */ |
320 | static int serial_sendbuffer(void *handle) |
321 | { |
322 | Serial serial = (Serial) handle; |
323 | return serial->bufsize; |
324 | } |
325 | |
326 | /* |
327 | * Called to set the size of the window |
328 | */ |
329 | static void serial_size(void *handle, int width, int height) |
330 | { |
331 | /* Do nothing! */ |
332 | return; |
333 | } |
334 | |
45ef7ce3 |
335 | static void serbreak_timer(void *ctx, long now) |
336 | { |
337 | Serial serial = (Serial)ctx; |
338 | |
339 | if (now >= serial->clearbreak_time && serial->port) { |
340 | ClearCommBreak(serial->port); |
341 | serial->break_in_progress = FALSE; |
342 | logevent(serial->frontend, "Finished serial break"); |
343 | } |
344 | } |
345 | |
d3b85d02 |
346 | /* |
347 | * Send serial special codes. |
348 | */ |
349 | static void serial_special(void *handle, Telnet_Special code) |
350 | { |
45ef7ce3 |
351 | Serial serial = (Serial) handle; |
352 | |
353 | if (serial->port && code == TS_BRK) { |
354 | logevent(serial->frontend, "Starting serial break at user request"); |
355 | SetCommBreak(serial->port); |
356 | /* |
357 | * To send a serial break on Windows, we call SetCommBreak |
358 | * to begin the break, then wait a bit, and then call |
359 | * ClearCommBreak to finish it. Hence, I must use timing.c |
360 | * to arrange a callback when it's time to do the latter. |
361 | * |
362 | * SUS says that a default break length must be between 1/4 |
363 | * and 1/2 second. FreeBSD apparently goes with 2/5 second, |
364 | * and so will I. |
365 | */ |
366 | serial->clearbreak_time = |
367 | schedule_timer(TICKSPERSEC * 2 / 5, serbreak_timer, serial); |
368 | serial->break_in_progress = TRUE; |
369 | } |
370 | |
d3b85d02 |
371 | return; |
372 | } |
373 | |
374 | /* |
375 | * Return a list of the special codes that make sense in this |
376 | * protocol. |
377 | */ |
378 | static const struct telnet_special *serial_get_specials(void *handle) |
379 | { |
45ef7ce3 |
380 | static const struct telnet_special specials[] = { |
381 | {"Break", TS_BRK}, |
382 | {NULL, TS_EXITMENU} |
383 | }; |
384 | return specials; |
d3b85d02 |
385 | } |
386 | |
387 | static int serial_connected(void *handle) |
388 | { |
389 | return 1; /* always connected */ |
390 | } |
391 | |
392 | static int serial_sendok(void *handle) |
393 | { |
394 | return 1; |
395 | } |
396 | |
397 | static void serial_unthrottle(void *handle, int backlog) |
398 | { |
399 | Serial serial = (Serial) handle; |
400 | if (serial->in) |
401 | handle_unthrottle(serial->in, backlog); |
402 | } |
403 | |
404 | static int serial_ldisc(void *handle, int option) |
405 | { |
406 | /* |
407 | * Local editing and local echo are off by default. |
408 | */ |
409 | return 0; |
410 | } |
411 | |
412 | static void serial_provide_ldisc(void *handle, void *ldisc) |
413 | { |
414 | /* This is a stub. */ |
415 | } |
416 | |
417 | static void serial_provide_logctx(void *handle, void *logctx) |
418 | { |
419 | /* This is a stub. */ |
420 | } |
421 | |
422 | static int serial_exitcode(void *handle) |
423 | { |
424 | Serial serial = (Serial) handle; |
4a0b6d61 |
425 | if (serial->port != INVALID_HANDLE_VALUE) |
d3b85d02 |
426 | return -1; /* still connected */ |
427 | else |
428 | /* Exit codes are a meaningless concept with serial ports */ |
429 | return INT_MAX; |
430 | } |
431 | |
432 | /* |
433 | * cfg_info for Serial does nothing at all. |
434 | */ |
435 | static int serial_cfg_info(void *handle) |
436 | { |
437 | return 0; |
438 | } |
439 | |
440 | Backend serial_backend = { |
441 | serial_init, |
442 | serial_free, |
443 | serial_reconfig, |
444 | serial_send, |
445 | serial_sendbuffer, |
446 | serial_size, |
447 | serial_special, |
448 | serial_get_specials, |
449 | serial_connected, |
450 | serial_exitcode, |
451 | serial_sendok, |
452 | serial_ldisc, |
453 | serial_provide_ldisc, |
454 | serial_provide_logctx, |
455 | serial_unthrottle, |
456 | serial_cfg_info, |
457 | 1 |
458 | }; |