]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winser.c
Post-release destabilisation! Completely remove the struct type
[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, Conf *conf)
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 = conf_get_int(conf, CONF_serspeed);
125         msg = dupprintf("Configuring baud rate %d", dcb.BaudRate);
126         logevent(serial->frontend, msg);
127         sfree(msg);
128
129         dcb.ByteSize = conf_get_int(conf, CONF_serdatabits);
130         msg = dupprintf("Configuring %d data bits", dcb.ByteSize);
131         logevent(serial->frontend, msg);
132         sfree(msg);
133
134         switch (conf_get_int(conf, CONF_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 (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         msg = dupprintf("Configuring %s parity", str);
152         logevent(serial->frontend, msg);
153         sfree(msg);
154
155         switch (conf_get_int(conf, CONF_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                                Conf *conf, char *host, int port,
203                                char **realhost, int nodelay, int keepalive)
204 {
205     Serial serial;
206     HANDLE serport;
207     const char *err;
208     char *serline;
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     serline = conf_get_str(conf, CONF_serline);
220     {
221         char *msg = dupprintf("Opening serial device %s", serline);
222         logevent(serial->frontend, msg);
223     }
224
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         sfree(serfilename);
254     }
255
256     if (serport == INVALID_HANDLE_VALUE)
257         return "Unable to open serial port";
258
259     err = serial_configure(serial, serport, conf);
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 |
268                                   HANDLE_FLAG_IGNOREEOF |
269                                   HANDLE_FLAG_UNITBUFFER);
270
271     *realhost = dupstr(serline);
272
273     /*
274      * Specials are always available.
275      */
276     update_specials_menu(serial->frontend);
277
278     return NULL;
279 }
280
281 static void serial_free(void *handle)
282 {
283     Serial serial = (Serial) handle;
284
285     serial_terminate(serial);
286     expire_timer_context(serial);
287     sfree(serial);
288 }
289
290 static void serial_reconfig(void *handle, Conf *conf)
291 {
292     Serial serial = (Serial) handle;
293     const char *err;
294
295     err = serial_configure(serial, serial->port, conf);
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
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
345 /*
346  * Send serial special codes.
347  */
348 static void serial_special(void *handle, Telnet_Special code)
349 {
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
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 {
379     static const struct telnet_special specials[] = {
380         {"Break", TS_BRK},
381         {NULL, TS_EXITMENU}
382     };
383     return specials;
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;
424     if (serial->port != INVALID_HANDLE_VALUE)
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,
456     "serial",
457     PROT_SERIAL,
458     0
459 };