]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winser.c
ab88406d7202157297eb7a45f7bc08ed9fd2175c
[PuTTY.git] / windows / winser.c
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_backend_data {
14     HANDLE port;
15     struct handle *out, *in;
16     void *frontend;
17     int bufsize;
18     long clearbreak_time;
19     int break_in_progress;
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     }
32     if (serial->port != INVALID_HANDLE_VALUE) {
33         if (serial->break_in_progress)
34             ClearCommBreak(serial->port);
35         CloseHandle(serial->port);
36         serial->port = INVALID_HANDLE_VALUE;
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);
211     serial->port = INVALID_HANDLE_VALUE;
212     serial->out = serial->in = NULL;
213     serial->bufsize = 0;
214     serial->break_in_progress = FALSE;
215     *backend_handle = serial;
216
217     serial->frontend = frontend_handle;
218
219     {
220         char *msg = dupprintf("Opening serial device %s", cfg->serline);
221         logevent(serial->frontend, msg);
222     }
223
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
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 |
269                                   HANDLE_FLAG_IGNOREEOF |
270                                   HANDLE_FLAG_UNITBUFFER);
271
272     *realhost = dupstr(cfg->serline);
273
274     /*
275      * Specials are always available.
276      */
277     update_specials_menu(serial->frontend);
278
279     return NULL;
280 }
281
282 static void serial_free(void *handle)
283 {
284     Serial serial = (Serial) handle;
285
286     serial_terminate(serial);
287     expire_timer_context(serial);
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
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
346 /*
347  * Send serial special codes.
348  */
349 static void serial_special(void *handle, Telnet_Special code)
350 {
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
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 {
380     static const struct telnet_special specials[] = {
381         {"Break", TS_BRK},
382         {NULL, TS_EXITMENU}
383     };
384     return specials;
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;
425     if (serial->port != INVALID_HANDLE_VALUE)
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     "serial",
458     PROT_SERIAL,
459     0
460 };