7374c779 |
1 | /*\r |
2 | * Serial back end (Windows-specific).\r |
3 | */\r |
4 | \r |
5 | /*\r |
6 | * TODO:\r |
7 | * \r |
8 | * - sending breaks?\r |
9 | * + looks as if you do this by calling SetCommBreak(handle),\r |
10 | * then waiting a bit, then doing ClearCommBreak(handle). A\r |
11 | * small job for timing.c, methinks.\r |
12 | *\r |
13 | * - why are we dropping data when talking to judicator?\r |
14 | */\r |
15 | \r |
16 | #include <stdio.h>\r |
17 | #include <stdlib.h>\r |
18 | #include <limits.h>\r |
19 | \r |
20 | #include "putty.h"\r |
21 | \r |
22 | #define SERIAL_MAX_BACKLOG 4096\r |
23 | \r |
24 | typedef struct serial_backend_data {\r |
25 | HANDLE port;\r |
26 | struct handle *out, *in;\r |
27 | void *frontend;\r |
28 | int bufsize;\r |
29 | } *Serial;\r |
30 | \r |
31 | static void serial_terminate(Serial serial)\r |
32 | {\r |
33 | if (serial->out) {\r |
34 | handle_free(serial->out);\r |
35 | serial->out = NULL;\r |
36 | }\r |
37 | if (serial->in) {\r |
38 | handle_free(serial->in);\r |
39 | serial->in = NULL;\r |
40 | }\r |
41 | if (serial->port) {\r |
42 | CloseHandle(serial->port);\r |
43 | serial->port = NULL;\r |
44 | }\r |
45 | }\r |
46 | \r |
47 | static int serial_gotdata(struct handle *h, void *data, int len)\r |
48 | {\r |
49 | Serial serial = (Serial)handle_get_privdata(h);\r |
50 | if (len <= 0) {\r |
51 | const char *error_msg;\r |
52 | \r |
53 | /*\r |
54 | * Currently, len==0 should never happen because we're\r |
55 | * ignoring EOFs. However, it seems not totally impossible\r |
56 | * that this same back end might be usable to talk to named\r |
57 | * pipes or some other non-serial device, in which case EOF\r |
58 | * may become meaningful here.\r |
59 | */\r |
60 | if (len == 0)\r |
61 | error_msg = "End of file reading from serial device";\r |
62 | else\r |
63 | error_msg = "Error reading from serial device";\r |
64 | \r |
65 | serial_terminate(serial);\r |
66 | \r |
67 | notify_remote_exit(serial->frontend);\r |
68 | \r |
69 | logevent(serial->frontend, error_msg);\r |
70 | \r |
71 | connection_fatal(serial->frontend, "%s", error_msg);\r |
72 | \r |
73 | return 0; /* placate optimiser */\r |
74 | } else {\r |
75 | return from_backend(serial->frontend, 0, data, len);\r |
76 | }\r |
77 | }\r |
78 | \r |
79 | static void serial_sentdata(struct handle *h, int new_backlog)\r |
80 | {\r |
81 | Serial serial = (Serial)handle_get_privdata(h);\r |
82 | if (new_backlog < 0) {\r |
83 | const char *error_msg = "Error writing to serial device";\r |
84 | \r |
85 | serial_terminate(serial);\r |
86 | \r |
87 | notify_remote_exit(serial->frontend);\r |
88 | \r |
89 | logevent(serial->frontend, error_msg);\r |
90 | \r |
91 | connection_fatal(serial->frontend, "%s", error_msg);\r |
92 | } else {\r |
93 | serial->bufsize = new_backlog;\r |
94 | }\r |
95 | }\r |
96 | \r |
97 | static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg)\r |
98 | {\r |
99 | DCB dcb;\r |
100 | COMMTIMEOUTS timeouts;\r |
101 | \r |
102 | /*\r |
103 | * Set up the serial port parameters. If we can't even\r |
104 | * GetCommState, we ignore the problem on the grounds that the\r |
105 | * user might have pointed us at some other type of two-way\r |
106 | * device instead of a serial port.\r |
107 | */\r |
108 | if (GetCommState(serport, &dcb)) {\r |
109 | char *msg;\r |
110 | const char *str;\r |
111 | \r |
112 | /*\r |
113 | * Boilerplate.\r |
114 | */\r |
115 | dcb.fBinary = TRUE;\r |
116 | dcb.fDtrControl = DTR_CONTROL_ENABLE;\r |
117 | dcb.fDsrSensitivity = FALSE;\r |
118 | dcb.fTXContinueOnXoff = FALSE;\r |
119 | dcb.fOutX = FALSE;\r |
120 | dcb.fInX = FALSE;\r |
121 | dcb.fErrorChar = FALSE;\r |
122 | dcb.fNull = FALSE;\r |
123 | dcb.fRtsControl = RTS_CONTROL_ENABLE;\r |
124 | dcb.fAbortOnError = FALSE;\r |
125 | dcb.fOutxCtsFlow = FALSE;\r |
126 | dcb.fOutxDsrFlow = FALSE;\r |
127 | \r |
128 | /*\r |
129 | * Configurable parameters.\r |
130 | */\r |
131 | dcb.BaudRate = cfg->serspeed;\r |
132 | msg = dupprintf("Configuring baud rate %d", cfg->serspeed);\r |
133 | logevent(serial->frontend, msg);\r |
134 | sfree(msg);\r |
135 | \r |
136 | dcb.ByteSize = cfg->serdatabits;\r |
137 | msg = dupprintf("Configuring %d data bits", cfg->serdatabits);\r |
138 | logevent(serial->frontend, msg);\r |
139 | sfree(msg);\r |
140 | \r |
141 | switch (cfg->serstopbits) {\r |
142 | case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break;\r |
143 | case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break;\r |
144 | case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break;\r |
145 | default: return "Invalid number of stop bits (need 1, 1.5 or 2)";\r |
146 | }\r |
147 | msg = dupprintf("Configuring %s data bits", str);\r |
148 | logevent(serial->frontend, msg);\r |
149 | sfree(msg);\r |
150 | \r |
151 | switch (cfg->serparity) {\r |
152 | case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break;\r |
153 | case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break;\r |
154 | case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break;\r |
155 | case SER_PAR_MARK: dcb.Parity = MARKPARITY; str = "mark"; break;\r |
156 | case SER_PAR_SPACE: dcb.Parity = SPACEPARITY; str = "space"; break;\r |
157 | }\r |
158 | msg = dupprintf("Configuring %s parity", str);\r |
159 | logevent(serial->frontend, msg);\r |
160 | sfree(msg);\r |
161 | \r |
162 | switch (cfg->serflow) {\r |
163 | case SER_FLOW_NONE:\r |
164 | str = "no";\r |
165 | break;\r |
166 | case SER_FLOW_XONXOFF:\r |
167 | dcb.fOutX = dcb.fInX = TRUE;\r |
168 | str = "XON/XOFF";\r |
169 | break;\r |
170 | case SER_FLOW_RTSCTS:\r |
171 | dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;\r |
172 | dcb.fOutxCtsFlow = TRUE;\r |
173 | str = "RTS/CTS";\r |
174 | break;\r |
175 | case SER_FLOW_DSRDTR:\r |
176 | dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;\r |
177 | dcb.fOutxDsrFlow = TRUE;\r |
178 | str = "DSR/DTR";\r |
179 | break;\r |
180 | }\r |
181 | msg = dupprintf("Configuring %s flow control", str);\r |
182 | logevent(serial->frontend, msg);\r |
183 | sfree(msg);\r |
184 | \r |
185 | if (!SetCommState(serport, &dcb))\r |
186 | return "Unable to configure serial port";\r |
187 | \r |
188 | timeouts.ReadIntervalTimeout = 1;\r |
189 | timeouts.ReadTotalTimeoutMultiplier = 0;\r |
190 | timeouts.ReadTotalTimeoutConstant = 0;\r |
191 | timeouts.WriteTotalTimeoutMultiplier = 0;\r |
192 | timeouts.WriteTotalTimeoutConstant = 0;\r |
193 | if (!SetCommTimeouts(serport, &timeouts))\r |
194 | return "Unable to configure serial timeouts";\r |
195 | }\r |
196 | \r |
197 | return NULL;\r |
198 | }\r |
199 | \r |
200 | /*\r |
201 | * Called to set up the serial connection.\r |
202 | * \r |
203 | * Returns an error message, or NULL on success.\r |
204 | *\r |
205 | * Also places the canonical host name into `realhost'. It must be\r |
206 | * freed by the caller.\r |
207 | */\r |
208 | static const char *serial_init(void *frontend_handle, void **backend_handle,\r |
209 | Config *cfg,\r |
210 | char *host, int port, char **realhost, int nodelay,\r |
211 | int keepalive)\r |
212 | {\r |
213 | Serial serial;\r |
214 | HANDLE serport;\r |
215 | const char *err;\r |
216 | \r |
217 | serial = snew(struct serial_backend_data);\r |
218 | serial->port = NULL;\r |
219 | serial->out = serial->in = NULL;\r |
220 | *backend_handle = serial;\r |
221 | \r |
222 | serial->frontend = frontend_handle;\r |
223 | \r |
224 | {\r |
225 | char *msg = dupprintf("Opening serial device %s", host);\r |
226 | logevent(serial->frontend, msg);\r |
227 | }\r |
228 | \r |
229 | serport = CreateFile(cfg->serline, GENERIC_READ | GENERIC_WRITE, 0, NULL,\r |
230 | OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r |
231 | if (serport == INVALID_HANDLE_VALUE)\r |
232 | return "Unable to open serial port";\r |
233 | \r |
234 | err = serial_configure(serial, serport, cfg);\r |
235 | if (err)\r |
236 | return err;\r |
237 | \r |
238 | serial->port = serport;\r |
239 | serial->out = handle_output_new(serport, serial_sentdata, serial,\r |
240 | HANDLE_FLAG_OVERLAPPED);\r |
241 | serial->in = handle_input_new(serport, serial_gotdata, serial,\r |
242 | HANDLE_FLAG_OVERLAPPED |\r |
243 | HANDLE_FLAG_IGNOREEOF);\r |
244 | \r |
245 | *realhost = dupstr(cfg->serline);\r |
246 | \r |
247 | return NULL;\r |
248 | }\r |
249 | \r |
250 | static void serial_free(void *handle)\r |
251 | {\r |
252 | Serial serial = (Serial) handle;\r |
253 | \r |
254 | serial_terminate(serial);\r |
255 | sfree(serial);\r |
256 | }\r |
257 | \r |
258 | static void serial_reconfig(void *handle, Config *cfg)\r |
259 | {\r |
260 | Serial serial = (Serial) handle;\r |
261 | const char *err;\r |
262 | \r |
263 | err = serial_configure(serial, serial->port, cfg);\r |
264 | \r |
265 | /*\r |
266 | * FIXME: what should we do if err returns something?\r |
267 | */\r |
268 | }\r |
269 | \r |
270 | /*\r |
271 | * Called to send data down the serial connection.\r |
272 | */\r |
273 | static int serial_send(void *handle, char *buf, int len)\r |
274 | {\r |
275 | Serial serial = (Serial) handle;\r |
276 | \r |
277 | if (serial->out == NULL)\r |
278 | return 0;\r |
279 | \r |
280 | serial->bufsize = handle_write(serial->out, buf, len);\r |
281 | return serial->bufsize;\r |
282 | }\r |
283 | \r |
284 | /*\r |
285 | * Called to query the current sendability status.\r |
286 | */\r |
287 | static int serial_sendbuffer(void *handle)\r |
288 | {\r |
289 | Serial serial = (Serial) handle;\r |
290 | return serial->bufsize;\r |
291 | }\r |
292 | \r |
293 | /*\r |
294 | * Called to set the size of the window\r |
295 | */\r |
296 | static void serial_size(void *handle, int width, int height)\r |
297 | {\r |
298 | /* Do nothing! */\r |
299 | return;\r |
300 | }\r |
301 | \r |
302 | /*\r |
303 | * Send serial special codes.\r |
304 | */\r |
305 | static void serial_special(void *handle, Telnet_Special code)\r |
306 | {\r |
307 | /*\r |
308 | * FIXME: serial break? XON? XOFF?\r |
309 | */\r |
310 | return;\r |
311 | }\r |
312 | \r |
313 | /*\r |
314 | * Return a list of the special codes that make sense in this\r |
315 | * protocol.\r |
316 | */\r |
317 | static const struct telnet_special *serial_get_specials(void *handle)\r |
318 | {\r |
319 | /*\r |
320 | * FIXME: serial break? XON? XOFF?\r |
321 | */\r |
322 | return NULL;\r |
323 | }\r |
324 | \r |
325 | static int serial_connected(void *handle)\r |
326 | {\r |
327 | return 1; /* always connected */\r |
328 | }\r |
329 | \r |
330 | static int serial_sendok(void *handle)\r |
331 | {\r |
332 | return 1;\r |
333 | }\r |
334 | \r |
335 | static void serial_unthrottle(void *handle, int backlog)\r |
336 | {\r |
337 | Serial serial = (Serial) handle;\r |
338 | if (serial->in)\r |
339 | handle_unthrottle(serial->in, backlog);\r |
340 | }\r |
341 | \r |
342 | static int serial_ldisc(void *handle, int option)\r |
343 | {\r |
344 | /*\r |
345 | * Local editing and local echo are off by default.\r |
346 | */\r |
347 | return 0;\r |
348 | }\r |
349 | \r |
350 | static void serial_provide_ldisc(void *handle, void *ldisc)\r |
351 | {\r |
352 | /* This is a stub. */\r |
353 | }\r |
354 | \r |
355 | static void serial_provide_logctx(void *handle, void *logctx)\r |
356 | {\r |
357 | /* This is a stub. */\r |
358 | }\r |
359 | \r |
360 | static int serial_exitcode(void *handle)\r |
361 | {\r |
362 | Serial serial = (Serial) handle;\r |
363 | if (serial->port != NULL)\r |
364 | return -1; /* still connected */\r |
365 | else\r |
366 | /* Exit codes are a meaningless concept with serial ports */\r |
367 | return INT_MAX;\r |
368 | }\r |
369 | \r |
370 | /*\r |
371 | * cfg_info for Serial does nothing at all.\r |
372 | */\r |
373 | static int serial_cfg_info(void *handle)\r |
374 | {\r |
375 | return 0;\r |
376 | }\r |
377 | \r |
378 | Backend serial_backend = {\r |
379 | serial_init,\r |
380 | serial_free,\r |
381 | serial_reconfig,\r |
382 | serial_send,\r |
383 | serial_sendbuffer,\r |
384 | serial_size,\r |
385 | serial_special,\r |
386 | serial_get_specials,\r |
387 | serial_connected,\r |
388 | serial_exitcode,\r |
389 | serial_sendok,\r |
390 | serial_ldisc,\r |
391 | serial_provide_ldisc,\r |
392 | serial_provide_logctx,\r |
393 | serial_unthrottle,\r |
394 | serial_cfg_info,\r |
395 | 1\r |
396 | };\r |