1 /*
2 * Serial back end (Windows-specific).
3 */
4
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 Serial;
14 struct Serial {
15 HANDLE port;
16 struct handle *out, *in;
17 Seat *seat;
18 LogContext *logctx;
19 int bufsize;
20 long clearbreak_time;
21 bool break_in_progress;
22 Backend backend;
23 };
24
serial_terminate(Serial * serial)25 static void serial_terminate(Serial *serial)
26 {
27 if (serial->out) {
28 handle_free(serial->out);
29 serial->out = NULL;
30 }
31 if (serial->in) {
32 handle_free(serial->in);
33 serial->in = NULL;
34 }
35 if (serial->port != INVALID_HANDLE_VALUE) {
36 if (serial->break_in_progress)
37 ClearCommBreak(serial->port);
38 CloseHandle(serial->port);
39 serial->port = INVALID_HANDLE_VALUE;
40 }
41 }
42
serial_gotdata(struct handle * h,const void * data,size_t len,int err)43 static size_t serial_gotdata(
44 struct handle *h, const void *data, size_t len, int err)
45 {
46 Serial *serial = (Serial *)handle_get_privdata(h);
47 if (err || len == 0) {
48 const char *error_msg;
49
50 /*
51 * Currently, len==0 should never happen because we're
52 * ignoring EOFs. However, it seems not totally impossible
53 * that this same back end might be usable to talk to named
54 * pipes or some other non-serial device, in which case EOF
55 * may become meaningful here.
56 */
57 if (!err)
58 error_msg = "End of file reading from serial device";
59 else
60 error_msg = "Error reading from serial device";
61
62 serial_terminate(serial);
63
64 seat_notify_remote_exit(serial->seat);
65
66 logevent(serial->logctx, error_msg);
67
68 seat_connection_fatal(serial->seat, "%s", error_msg);
69
70 return 0;
71 } else {
72 return seat_stdout(serial->seat, data, len);
73 }
74 }
75
serial_sentdata(struct handle * h,size_t new_backlog,int err)76 static void serial_sentdata(struct handle *h, size_t new_backlog, int err)
77 {
78 Serial *serial = (Serial *)handle_get_privdata(h);
79 if (err) {
80 const char *error_msg = "Error writing to serial device";
81
82 serial_terminate(serial);
83
84 seat_notify_remote_exit(serial->seat);
85
86 logevent(serial->logctx, error_msg);
87
88 seat_connection_fatal(serial->seat, "%s", error_msg);
89 } else {
90 serial->bufsize = new_backlog;
91 }
92 }
93
serial_configure(Serial * serial,HANDLE serport,Conf * conf)94 static char *serial_configure(Serial *serial, HANDLE serport, Conf *conf)
95 {
96 DCB dcb;
97 COMMTIMEOUTS timeouts;
98
99 /*
100 * Set up the serial port parameters. If we can't even
101 * GetCommState, we ignore the problem on the grounds that the
102 * user might have pointed us at some other type of two-way
103 * device instead of a serial port.
104 */
105 if (GetCommState(serport, &dcb)) {
106 const char *str;
107
108 /*
109 * Boilerplate.
110 */
111 dcb.fBinary = true;
112 dcb.fDtrControl = DTR_CONTROL_ENABLE;
113 dcb.fDsrSensitivity = false;
114 dcb.fTXContinueOnXoff = false;
115 dcb.fOutX = false;
116 dcb.fInX = false;
117 dcb.fErrorChar = false;
118 dcb.fNull = false;
119 dcb.fRtsControl = RTS_CONTROL_ENABLE;
120 dcb.fAbortOnError = false;
121 dcb.fOutxCtsFlow = false;
122 dcb.fOutxDsrFlow = false;
123
124 /*
125 * Configurable parameters.
126 */
127 dcb.BaudRate = conf_get_int(conf, CONF_serspeed);
128 logeventf(serial->logctx, "Configuring baud rate %lu",
129 (unsigned long)dcb.BaudRate);
130
131 dcb.ByteSize = conf_get_int(conf, CONF_serdatabits);
132 logeventf(serial->logctx, "Configuring %u data bits",
133 (unsigned)dcb.ByteSize);
134
135 switch (conf_get_int(conf, CONF_serstopbits)) {
136 case 2: dcb.StopBits = ONESTOPBIT; str = "1 stop bit"; break;
137 case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5 stop bits"; break;
138 case 4: dcb.StopBits = TWOSTOPBITS; str = "2 stop bits"; break;
139 default: return dupstr("Invalid number of stop bits "
140 "(need 1, 1.5 or 2)");
141 }
142 logeventf(serial->logctx, "Configuring %s", str);
143
144 switch (conf_get_int(conf, CONF_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 logeventf(serial->logctx, "Configuring %s parity", str);
152
153 switch (conf_get_int(conf, CONF_serflow)) {
154 case SER_FLOW_NONE:
155 str = "no";
156 break;
157 case SER_FLOW_XONXOFF:
158 dcb.fOutX = dcb.fInX = true;
159 str = "XON/XOFF";
160 break;
161 case SER_FLOW_RTSCTS:
162 dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
163 dcb.fOutxCtsFlow = true;
164 str = "RTS/CTS";
165 break;
166 case SER_FLOW_DSRDTR:
167 dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
168 dcb.fOutxDsrFlow = true;
169 str = "DSR/DTR";
170 break;
171 }
172 logeventf(serial->logctx, "Configuring %s flow control", str);
173
174 if (!SetCommState(serport, &dcb))
175 return dupprintf("Configuring serial port: %s",
176 win_strerror(GetLastError()));
177
178 timeouts.ReadIntervalTimeout = 1;
179 timeouts.ReadTotalTimeoutMultiplier = 0;
180 timeouts.ReadTotalTimeoutConstant = 0;
181 timeouts.WriteTotalTimeoutMultiplier = 0;
182 timeouts.WriteTotalTimeoutConstant = 0;
183 if (!SetCommTimeouts(serport, &timeouts))
184 return dupprintf("Configuring serial timeouts: %s",
185 win_strerror(GetLastError()));
186 }
187
188 return NULL;
189 }
190
191 /*
192 * Called to set up the serial connection.
193 *
194 * Returns an error message, or NULL on success.
195 *
196 * Also places the canonical host name into `realhost'. It must be
197 * freed by the caller.
198 */
serial_init(const BackendVtable * vt,Seat * seat,Backend ** backend_handle,LogContext * logctx,Conf * conf,const char * host,int port,char ** realhost,bool nodelay,bool keepalive)199 static char *serial_init(const BackendVtable *vt, Seat *seat,
200 Backend **backend_handle, LogContext *logctx,
201 Conf *conf, const char *host, int port,
202 char **realhost, bool nodelay, bool keepalive)
203 {
204 Serial *serial;
205 HANDLE serport;
206 char *err;
207 char *serline;
208
209 /* No local authentication phase in this protocol */
210 seat_set_trust_status(seat, false);
211
212 serial = snew(Serial);
213 serial->port = INVALID_HANDLE_VALUE;
214 serial->out = serial->in = NULL;
215 serial->bufsize = 0;
216 serial->break_in_progress = false;
217 serial->backend.vt = vt;
218 *backend_handle = &serial->backend;
219
220 serial->seat = seat;
221 serial->logctx = logctx;
222
223 serline = conf_get_str(conf, CONF_serline);
224 logeventf(serial->logctx, "Opening serial device %s", serline);
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 =
250 dupprintf("%s%s", strchr(serline, '\\') ? "" : "\\\\.\\", serline);
251 serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
252 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
253 if (serport == INVALID_HANDLE_VALUE) {
254 err = dupprintf("Opening '%s': %s",
255 serfilename, win_strerror(GetLastError()));
256 sfree(serfilename);
257 return err;
258 }
259
260 sfree(serfilename);
261
262 err = serial_configure(serial, serport, conf);
263 if (err)
264 return err;
265
266 serial->port = serport;
267 serial->out = handle_output_new(serport, serial_sentdata, serial,
268 HANDLE_FLAG_OVERLAPPED);
269 serial->in = handle_input_new(serport, serial_gotdata, serial,
270 HANDLE_FLAG_OVERLAPPED |
271 HANDLE_FLAG_IGNOREEOF |
272 HANDLE_FLAG_UNITBUFFER);
273
274 *realhost = dupstr(serline);
275
276 /*
277 * Specials are always available.
278 */
279 seat_update_specials_menu(serial->seat);
280
281 return NULL;
282 }
283
serial_free(Backend * be)284 static void serial_free(Backend *be)
285 {
286 Serial *serial = container_of(be, Serial, backend);
287
288 serial_terminate(serial);
289 expire_timer_context(serial);
290 sfree(serial);
291 }
292
serial_reconfig(Backend * be,Conf * conf)293 static void serial_reconfig(Backend *be, Conf *conf)
294 {
295 Serial *serial = container_of(be, Serial, backend);
296
297 serial_configure(serial, serial->port, conf);
298
299 /*
300 * FIXME: what should we do if that call returned a non-NULL error
301 * message?
302 */
303 }
304
305 /*
306 * Called to send data down the serial connection.
307 */
serial_send(Backend * be,const char * buf,size_t len)308 static size_t serial_send(Backend *be, const char *buf, size_t len)
309 {
310 Serial *serial = container_of(be, Serial, backend);
311
312 if (serial->out == NULL)
313 return 0;
314
315 serial->bufsize = handle_write(serial->out, buf, len);
316 return serial->bufsize;
317 }
318
319 /*
320 * Called to query the current sendability status.
321 */
serial_sendbuffer(Backend * be)322 static size_t serial_sendbuffer(Backend *be)
323 {
324 Serial *serial = container_of(be, Serial, backend);
325 return serial->bufsize;
326 }
327
328 /*
329 * Called to set the size of the window
330 */
serial_size(Backend * be,int width,int height)331 static void serial_size(Backend *be, int width, int height)
332 {
333 /* Do nothing! */
334 return;
335 }
336
serbreak_timer(void * ctx,unsigned long now)337 static void serbreak_timer(void *ctx, unsigned long now)
338 {
339 Serial *serial = (Serial *)ctx;
340
341 if (now == serial->clearbreak_time && serial->port) {
342 ClearCommBreak(serial->port);
343 serial->break_in_progress = false;
344 logevent(serial->logctx, "Finished serial break");
345 }
346 }
347
348 /*
349 * Send serial special codes.
350 */
serial_special(Backend * be,SessionSpecialCode code,int arg)351 static void serial_special(Backend *be, SessionSpecialCode code, int arg)
352 {
353 Serial *serial = container_of(be, Serial, backend);
354
355 if (serial->port && code == SS_BRK) {
356 logevent(serial->logctx, "Starting serial break at user request");
357 SetCommBreak(serial->port);
358 /*
359 * To send a serial break on Windows, we call SetCommBreak
360 * to begin the break, then wait a bit, and then call
361 * ClearCommBreak to finish it. Hence, I must use timing.c
362 * to arrange a callback when it's time to do the latter.
363 *
364 * SUS says that a default break length must be between 1/4
365 * and 1/2 second. FreeBSD apparently goes with 2/5 second,
366 * and so will I.
367 */
368 serial->clearbreak_time =
369 schedule_timer(TICKSPERSEC * 2 / 5, serbreak_timer, serial);
370 serial->break_in_progress = true;
371 }
372
373 return;
374 }
375
376 /*
377 * Return a list of the special codes that make sense in this
378 * protocol.
379 */
serial_get_specials(Backend * be)380 static const SessionSpecial *serial_get_specials(Backend *be)
381 {
382 static const SessionSpecial specials[] = {
383 {"Break", SS_BRK},
384 {NULL, SS_EXITMENU}
385 };
386 return specials;
387 }
388
serial_connected(Backend * be)389 static bool serial_connected(Backend *be)
390 {
391 return true; /* always connected */
392 }
393
serial_sendok(Backend * be)394 static bool serial_sendok(Backend *be)
395 {
396 return true;
397 }
398
serial_unthrottle(Backend * be,size_t backlog)399 static void serial_unthrottle(Backend *be, size_t backlog)
400 {
401 Serial *serial = container_of(be, Serial, backend);
402 if (serial->in)
403 handle_unthrottle(serial->in, backlog);
404 }
405
serial_ldisc(Backend * be,int option)406 static bool serial_ldisc(Backend *be, int option)
407 {
408 /*
409 * Local editing and local echo are off by default.
410 */
411 return false;
412 }
413
serial_provide_ldisc(Backend * be,Ldisc * ldisc)414 static void serial_provide_ldisc(Backend *be, Ldisc *ldisc)
415 {
416 /* This is a stub. */
417 }
418
serial_exitcode(Backend * be)419 static int serial_exitcode(Backend *be)
420 {
421 Serial *serial = container_of(be, Serial, backend);
422 if (serial->port != INVALID_HANDLE_VALUE)
423 return -1; /* still connected */
424 else
425 /* Exit codes are a meaningless concept with serial ports */
426 return INT_MAX;
427 }
428
429 /*
430 * cfg_info for Serial does nothing at all.
431 */
serial_cfg_info(Backend * be)432 static int serial_cfg_info(Backend *be)
433 {
434 return 0;
435 }
436
437 const BackendVtable serial_backend = {
438 .init = serial_init,
439 .free = serial_free,
440 .reconfig = serial_reconfig,
441 .send = serial_send,
442 .sendbuffer = serial_sendbuffer,
443 .size = serial_size,
444 .special = serial_special,
445 .get_specials = serial_get_specials,
446 .connected = serial_connected,
447 .exitcode = serial_exitcode,
448 .sendok = serial_sendok,
449 .ldisc_option_state = serial_ldisc,
450 .provide_ldisc = serial_provide_ldisc,
451 .unthrottle = serial_unthrottle,
452 .cfg_info = serial_cfg_info,
453 .id = "serial",
454 .displayname = "Serial",
455 .protocol = PROT_SERIAL,
456 .serial_parity_mask = ((1 << SER_PAR_NONE) |
457 (1 << SER_PAR_ODD) |
458 (1 << SER_PAR_EVEN) |
459 (1 << SER_PAR_MARK) |
460 (1 << SER_PAR_SPACE)),
461 .serial_flow_mask = ((1 << SER_FLOW_NONE) |
462 (1 << SER_FLOW_XONXOFF) |
463 (1 << SER_FLOW_RTSCTS) |
464 (1 << SER_FLOW_DSRDTR)),
465 };
466