]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winser.c
Fix `puttygen-unix-perms': f_open(), PuTTY's wrapper on fopen, now
[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     serport = CreateFile(cfg->serline, GENERIC_READ | GENERIC_WRITE, 0, NULL,
225                          OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
226     if (serport == INVALID_HANDLE_VALUE)
227         return "Unable to open serial port";
228
229     err = serial_configure(serial, serport, cfg);
230     if (err)
231         return err;
232
233     serial->port = serport;
234     serial->out = handle_output_new(serport, serial_sentdata, serial,
235                                     HANDLE_FLAG_OVERLAPPED);
236     serial->in = handle_input_new(serport, serial_gotdata, serial,
237                                   HANDLE_FLAG_OVERLAPPED |
238                                   HANDLE_FLAG_IGNOREEOF |
239                                   HANDLE_FLAG_UNITBUFFER);
240
241     *realhost = dupstr(cfg->serline);
242
243     /*
244      * Specials are always available.
245      */
246     update_specials_menu(serial->frontend);
247
248     return NULL;
249 }
250
251 static void serial_free(void *handle)
252 {
253     Serial serial = (Serial) handle;
254
255     serial_terminate(serial);
256     expire_timer_context(serial);
257     sfree(serial);
258 }
259
260 static void serial_reconfig(void *handle, Config *cfg)
261 {
262     Serial serial = (Serial) handle;
263     const char *err;
264
265     err = serial_configure(serial, serial->port, cfg);
266
267     /*
268      * FIXME: what should we do if err returns something?
269      */
270 }
271
272 /*
273  * Called to send data down the serial connection.
274  */
275 static int serial_send(void *handle, char *buf, int len)
276 {
277     Serial serial = (Serial) handle;
278
279     if (serial->out == NULL)
280         return 0;
281
282     serial->bufsize = handle_write(serial->out, buf, len);
283     return serial->bufsize;
284 }
285
286 /*
287  * Called to query the current sendability status.
288  */
289 static int serial_sendbuffer(void *handle)
290 {
291     Serial serial = (Serial) handle;
292     return serial->bufsize;
293 }
294
295 /*
296  * Called to set the size of the window
297  */
298 static void serial_size(void *handle, int width, int height)
299 {
300     /* Do nothing! */
301     return;
302 }
303
304 static void serbreak_timer(void *ctx, long now)
305 {
306     Serial serial = (Serial)ctx;
307
308     if (now >= serial->clearbreak_time && serial->port) {
309         ClearCommBreak(serial->port);
310         serial->break_in_progress = FALSE;
311         logevent(serial->frontend, "Finished serial break");
312     }
313 }
314
315 /*
316  * Send serial special codes.
317  */
318 static void serial_special(void *handle, Telnet_Special code)
319 {
320     Serial serial = (Serial) handle;
321
322     if (serial->port && code == TS_BRK) {
323         logevent(serial->frontend, "Starting serial break at user request");
324         SetCommBreak(serial->port);
325         /*
326          * To send a serial break on Windows, we call SetCommBreak
327          * to begin the break, then wait a bit, and then call
328          * ClearCommBreak to finish it. Hence, I must use timing.c
329          * to arrange a callback when it's time to do the latter.
330          * 
331          * SUS says that a default break length must be between 1/4
332          * and 1/2 second. FreeBSD apparently goes with 2/5 second,
333          * and so will I. 
334          */
335         serial->clearbreak_time =
336             schedule_timer(TICKSPERSEC * 2 / 5, serbreak_timer, serial);
337         serial->break_in_progress = TRUE;
338     }
339
340     return;
341 }
342
343 /*
344  * Return a list of the special codes that make sense in this
345  * protocol.
346  */
347 static const struct telnet_special *serial_get_specials(void *handle)
348 {
349     static const struct telnet_special specials[] = {
350         {"Break", TS_BRK},
351         {NULL, TS_EXITMENU}
352     };
353     return specials;
354 }
355
356 static int serial_connected(void *handle)
357 {
358     return 1;                          /* always connected */
359 }
360
361 static int serial_sendok(void *handle)
362 {
363     return 1;
364 }
365
366 static void serial_unthrottle(void *handle, int backlog)
367 {
368     Serial serial = (Serial) handle;
369     if (serial->in)
370         handle_unthrottle(serial->in, backlog);
371 }
372
373 static int serial_ldisc(void *handle, int option)
374 {
375     /*
376      * Local editing and local echo are off by default.
377      */
378     return 0;
379 }
380
381 static void serial_provide_ldisc(void *handle, void *ldisc)
382 {
383     /* This is a stub. */
384 }
385
386 static void serial_provide_logctx(void *handle, void *logctx)
387 {
388     /* This is a stub. */
389 }
390
391 static int serial_exitcode(void *handle)
392 {
393     Serial serial = (Serial) handle;
394     if (serial->port != INVALID_HANDLE_VALUE)
395         return -1;                     /* still connected */
396     else
397         /* Exit codes are a meaningless concept with serial ports */
398         return INT_MAX;
399 }
400
401 /*
402  * cfg_info for Serial does nothing at all.
403  */
404 static int serial_cfg_info(void *handle)
405 {
406     return 0;
407 }
408
409 Backend serial_backend = {
410     serial_init,
411     serial_free,
412     serial_reconfig,
413     serial_send,
414     serial_sendbuffer,
415     serial_size,
416     serial_special,
417     serial_get_specials,
418     serial_connected,
419     serial_exitcode,
420     serial_sendok,
421     serial_ldisc,
422     serial_provide_ldisc,
423     serial_provide_logctx,
424     serial_unthrottle,
425     serial_cfg_info,
426     1
427 };