Add an option to the TortoiseGitMerge settings dialog to specify the number of contex...
[TortoiseGit.git] / src / TortoisePlink / Windows / WINSER.C
blob2e3a9073ccf743f2005b651167197c5be01287d4
1 /*\r
2  * Serial back end (Windows-specific).\r
3  */\r
4 \r
5 #include <stdio.h>\r
6 #include <stdlib.h>\r
7 #include <limits.h>\r
8 \r
9 #include "putty.h"\r
11 #define SERIAL_MAX_BACKLOG 4096\r
13 typedef struct serial_backend_data {\r
14     HANDLE port;\r
15     struct handle *out, *in;\r
16     void *frontend;\r
17     int bufsize;\r
18     long clearbreak_time;\r
19     int break_in_progress;\r
20 } *Serial;\r
22 static void serial_terminate(Serial serial)\r
23 {\r
24     if (serial->out) {\r
25         handle_free(serial->out);\r
26         serial->out = NULL;\r
27     }\r
28     if (serial->in) {\r
29         handle_free(serial->in);\r
30         serial->in = NULL;\r
31     }\r
32     if (serial->port != INVALID_HANDLE_VALUE) {\r
33         if (serial->break_in_progress)\r
34             ClearCommBreak(serial->port);\r
35         CloseHandle(serial->port);\r
36         serial->port = INVALID_HANDLE_VALUE;\r
37     }\r
38 }\r
40 static int serial_gotdata(struct handle *h, void *data, int len)\r
41 {\r
42     Serial serial = (Serial)handle_get_privdata(h);\r
43     if (len <= 0) {\r
44         const char *error_msg;\r
46         /*\r
47          * Currently, len==0 should never happen because we're\r
48          * ignoring EOFs. However, it seems not totally impossible\r
49          * that this same back end might be usable to talk to named\r
50          * pipes or some other non-serial device, in which case EOF\r
51          * may become meaningful here.\r
52          */\r
53         if (len == 0)\r
54             error_msg = "End of file reading from serial device";\r
55         else\r
56             error_msg = "Error reading from serial device";\r
58         serial_terminate(serial);\r
60         notify_remote_exit(serial->frontend);\r
62         logevent(serial->frontend, error_msg);\r
64         connection_fatal(serial->frontend, "%s", error_msg);\r
66         return 0;                      /* placate optimiser */\r
67     } else {\r
68         return from_backend(serial->frontend, 0, data, len);\r
69     }\r
70 }\r
72 static void serial_sentdata(struct handle *h, int new_backlog)\r
73 {\r
74     Serial serial = (Serial)handle_get_privdata(h);\r
75     if (new_backlog < 0) {\r
76         const char *error_msg = "Error writing to serial device";\r
78         serial_terminate(serial);\r
80         notify_remote_exit(serial->frontend);\r
82         logevent(serial->frontend, error_msg);\r
84         connection_fatal(serial->frontend, "%s", error_msg);\r
85     } else {\r
86         serial->bufsize = new_backlog;\r
87     }\r
88 }\r
90 static const char *serial_configure(Serial serial, HANDLE serport, Conf *conf)\r
91 {\r
92     DCB dcb;\r
93     COMMTIMEOUTS timeouts;\r
95     /*\r
96      * Set up the serial port parameters. If we can't even\r
97      * GetCommState, we ignore the problem on the grounds that the\r
98      * user might have pointed us at some other type of two-way\r
99      * device instead of a serial port.\r
100      */\r
101     if (GetCommState(serport, &dcb)) {\r
102         char *msg;\r
103         const char *str;\r
105         /*\r
106          * Boilerplate.\r
107          */\r
108         dcb.fBinary = TRUE;\r
109         dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
110         dcb.fDsrSensitivity = FALSE;\r
111         dcb.fTXContinueOnXoff = FALSE;\r
112         dcb.fOutX = FALSE;\r
113         dcb.fInX = FALSE;\r
114         dcb.fErrorChar = FALSE;\r
115         dcb.fNull = FALSE;\r
116         dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
117         dcb.fAbortOnError = FALSE;\r
118         dcb.fOutxCtsFlow = FALSE;\r
119         dcb.fOutxDsrFlow = FALSE;\r
121         /*\r
122          * Configurable parameters.\r
123          */\r
124         dcb.BaudRate = conf_get_int(conf, CONF_serspeed);\r
125         msg = dupprintf("Configuring baud rate %d", dcb.BaudRate);\r
126         logevent(serial->frontend, msg);\r
127         sfree(msg);\r
129         dcb.ByteSize = conf_get_int(conf, CONF_serdatabits);\r
130         msg = dupprintf("Configuring %d data bits", dcb.ByteSize);\r
131         logevent(serial->frontend, msg);\r
132         sfree(msg);\r
134         switch (conf_get_int(conf, CONF_serstopbits)) {\r
135           case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break;\r
136           case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break;\r
137           case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break;\r
138           default: return "Invalid number of stop bits (need 1, 1.5 or 2)";\r
139         }\r
140         msg = dupprintf("Configuring %s data bits", str);\r
141         logevent(serial->frontend, msg);\r
142         sfree(msg);\r
144         switch (conf_get_int(conf, CONF_serparity)) {\r
145           case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break;\r
146           case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break;\r
147           case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break;\r
148           case SER_PAR_MARK: dcb.Parity = MARKPARITY; str = "mark"; break;\r
149           case SER_PAR_SPACE: dcb.Parity = SPACEPARITY; str = "space"; break;\r
150         }\r
151         msg = dupprintf("Configuring %s parity", str);\r
152         logevent(serial->frontend, msg);\r
153         sfree(msg);\r
155         switch (conf_get_int(conf, CONF_serflow)) {\r
156           case SER_FLOW_NONE:\r
157             str = "no";\r
158             break;\r
159           case SER_FLOW_XONXOFF:\r
160             dcb.fOutX = dcb.fInX = TRUE;\r
161             str = "XON/XOFF";\r
162             break;\r
163           case SER_FLOW_RTSCTS:\r
164             dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;\r
165             dcb.fOutxCtsFlow = TRUE;\r
166             str = "RTS/CTS";\r
167             break;\r
168           case SER_FLOW_DSRDTR:\r
169             dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;\r
170             dcb.fOutxDsrFlow = TRUE;\r
171             str = "DSR/DTR";\r
172             break;\r
173         }\r
174         msg = dupprintf("Configuring %s flow control", str);\r
175         logevent(serial->frontend, msg);\r
176         sfree(msg);\r
178         if (!SetCommState(serport, &dcb))\r
179             return "Unable to configure serial port";\r
181         timeouts.ReadIntervalTimeout = 1;\r
182         timeouts.ReadTotalTimeoutMultiplier = 0;\r
183         timeouts.ReadTotalTimeoutConstant = 0;\r
184         timeouts.WriteTotalTimeoutMultiplier = 0;\r
185         timeouts.WriteTotalTimeoutConstant = 0;\r
186         if (!SetCommTimeouts(serport, &timeouts))\r
187             return "Unable to configure serial timeouts";\r
188     }\r
190     return NULL;\r
193 /*\r
194  * Called to set up the serial connection.\r
195  * \r
196  * Returns an error message, or NULL on success.\r
197  *\r
198  * Also places the canonical host name into `realhost'. It must be\r
199  * freed by the caller.\r
200  */\r
201 static const char *serial_init(void *frontend_handle, void **backend_handle,\r
202                                Conf *conf, char *host, int port,\r
203                                char **realhost, int nodelay, int keepalive)\r
205     Serial serial;\r
206     HANDLE serport;\r
207     const char *err;\r
208     char *serline;\r
210     serial = snew(struct serial_backend_data);\r
211     serial->port = INVALID_HANDLE_VALUE;\r
212     serial->out = serial->in = NULL;\r
213     serial->bufsize = 0;\r
214     serial->break_in_progress = FALSE;\r
215     *backend_handle = serial;\r
217     serial->frontend = frontend_handle;\r
219     serline = conf_get_str(conf, CONF_serline);\r
220     {\r
221         char *msg = dupprintf("Opening serial device %s", serline);\r
222         logevent(serial->frontend, msg);\r
223     }\r
225     {\r
226         /*\r
227          * Munge the string supplied by the user into a Windows filename.\r
228          *\r
229          * Windows supports opening a few "legacy" devices (including\r
230          * COM1-9) by specifying their names verbatim as a filename to\r
231          * open. (Thus, no files can ever have these names. See\r
232          * <http://msdn2.microsoft.com/en-us/library/aa365247.aspx>\r
233          * ("Naming a File") for the complete list of reserved names.)\r
234          *\r
235          * However, this doesn't let you get at devices COM10 and above.\r
236          * For that, you need to specify a filename like "\\.\COM10".\r
237          * This is also necessary for special serial and serial-like\r
238          * devices such as \\.\WCEUSBSH001. It also works for the "legacy"\r
239          * names, so you can do \\.\COM1 (verified as far back as Win95).\r
240          * See <http://msdn2.microsoft.com/en-us/library/aa363858.aspx>\r
241          * (CreateFile() docs).\r
242          *\r
243          * So, we believe that prepending "\\.\" should always be the\r
244          * Right Thing. However, just in case someone finds something to\r
245          * talk to that doesn't exist under there, if the serial line\r
246          * contains a backslash, we use it verbatim. (This also lets\r
247          * existing configurations using \\.\ continue working.)\r
248          */\r
249         char *serfilename =\r
250             dupprintf("%s%s", strchr(serline, '\\') ? "" : "\\\\.\\", serline);\r
251         serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL,\r
252                              OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
253         sfree(serfilename);\r
254     }\r
256     if (serport == INVALID_HANDLE_VALUE)\r
257         return "Unable to open serial port";\r
259     err = serial_configure(serial, serport, conf);\r
260     if (err)\r
261         return err;\r
263     serial->port = serport;\r
264     serial->out = handle_output_new(serport, serial_sentdata, serial,\r
265                                     HANDLE_FLAG_OVERLAPPED);\r
266     serial->in = handle_input_new(serport, serial_gotdata, serial,\r
267                                   HANDLE_FLAG_OVERLAPPED |\r
268                                   HANDLE_FLAG_IGNOREEOF |\r
269                                   HANDLE_FLAG_UNITBUFFER);\r
271     *realhost = dupstr(serline);\r
273     /*\r
274      * Specials are always available.\r
275      */\r
276     update_specials_menu(serial->frontend);\r
278     return NULL;\r
281 static void serial_free(void *handle)\r
283     Serial serial = (Serial) handle;\r
285     serial_terminate(serial);\r
286     expire_timer_context(serial);\r
287     sfree(serial);\r
290 static void serial_reconfig(void *handle, Conf *conf)\r
292     Serial serial = (Serial) handle;\r
293     const char *err;\r
295     err = serial_configure(serial, serial->port, conf);\r
297     /*\r
298      * FIXME: what should we do if err returns something?\r
299      */\r
302 /*\r
303  * Called to send data down the serial connection.\r
304  */\r
305 static int serial_send(void *handle, char *buf, int len)\r
307     Serial serial = (Serial) handle;\r
309     if (serial->out == NULL)\r
310         return 0;\r
312     serial->bufsize = handle_write(serial->out, buf, len);\r
313     return serial->bufsize;\r
316 /*\r
317  * Called to query the current sendability status.\r
318  */\r
319 static int serial_sendbuffer(void *handle)\r
321     Serial serial = (Serial) handle;\r
322     return serial->bufsize;\r
325 /*\r
326  * Called to set the size of the window\r
327  */\r
328 static void serial_size(void *handle, int width, int height)\r
330     /* Do nothing! */\r
331     return;\r
334 static void serbreak_timer(void *ctx, unsigned long now)\r
336     Serial serial = (Serial)ctx;\r
338     if (now == serial->clearbreak_time && serial->port) {\r
339         ClearCommBreak(serial->port);\r
340         serial->break_in_progress = FALSE;\r
341         logevent(serial->frontend, "Finished serial break");\r
342     }\r
345 /*\r
346  * Send serial special codes.\r
347  */\r
348 static void serial_special(void *handle, Telnet_Special code)\r
350     Serial serial = (Serial) handle;\r
352     if (serial->port && code == TS_BRK) {\r
353         logevent(serial->frontend, "Starting serial break at user request");\r
354         SetCommBreak(serial->port);\r
355         /*\r
356          * To send a serial break on Windows, we call SetCommBreak\r
357          * to begin the break, then wait a bit, and then call\r
358          * ClearCommBreak to finish it. Hence, I must use timing.c\r
359          * to arrange a callback when it's time to do the latter.\r
360          * \r
361          * SUS says that a default break length must be between 1/4\r
362          * and 1/2 second. FreeBSD apparently goes with 2/5 second,\r
363          * and so will I. \r
364          */\r
365         serial->clearbreak_time =\r
366             schedule_timer(TICKSPERSEC * 2 / 5, serbreak_timer, serial);\r
367         serial->break_in_progress = TRUE;\r
368     }\r
370     return;\r
373 /*\r
374  * Return a list of the special codes that make sense in this\r
375  * protocol.\r
376  */\r
377 static const struct telnet_special *serial_get_specials(void *handle)\r
379     static const struct telnet_special specials[] = {\r
380         {"Break", TS_BRK},\r
381         {NULL, TS_EXITMENU}\r
382     };\r
383     return specials;\r
386 static int serial_connected(void *handle)\r
388     return 1;                          /* always connected */\r
391 static int serial_sendok(void *handle)\r
393     return 1;\r
396 static void serial_unthrottle(void *handle, int backlog)\r
398     Serial serial = (Serial) handle;\r
399     if (serial->in)\r
400         handle_unthrottle(serial->in, backlog);\r
403 static int serial_ldisc(void *handle, int option)\r
405     /*\r
406      * Local editing and local echo are off by default.\r
407      */\r
408     return 0;\r
411 static void serial_provide_ldisc(void *handle, void *ldisc)\r
413     /* This is a stub. */\r
416 static void serial_provide_logctx(void *handle, void *logctx)\r
418     /* This is a stub. */\r
421 static int serial_exitcode(void *handle)\r
423     Serial serial = (Serial) handle;\r
424     if (serial->port != INVALID_HANDLE_VALUE)\r
425         return -1;                     /* still connected */\r
426     else\r
427         /* Exit codes are a meaningless concept with serial ports */\r
428         return INT_MAX;\r
431 /*\r
432  * cfg_info for Serial does nothing at all.\r
433  */\r
434 static int serial_cfg_info(void *handle)\r
436     return 0;\r
439 Backend serial_backend = {\r
440     serial_init,\r
441     serial_free,\r
442     serial_reconfig,\r
443     serial_send,\r
444     serial_sendbuffer,\r
445     serial_size,\r
446     serial_special,\r
447     serial_get_specials,\r
448     serial_connected,\r
449     serial_exitcode,\r
450     serial_sendok,\r
451     serial_ldisc,\r
452     serial_provide_ldisc,\r
453     serial_provide_logctx,\r
454     serial_unthrottle,\r
455     serial_cfg_info,\r
456     "serial",\r
457     PROT_SERIAL,\r
458     0\r
459 };\r