aef05b78 |
1 | /* |
2 | * Serial back end (Unix-specific). |
3 | */ |
4 | |
aef05b78 |
5 | #include <stdio.h> |
6 | #include <stdlib.h> |
7 | #include <assert.h> |
8 | #include <limits.h> |
9 | |
10 | #include <errno.h> |
11 | #include <unistd.h> |
12 | #include <fcntl.h> |
13 | #include <termios.h> |
14 | |
15 | #include "putty.h" |
16 | #include "tree234.h" |
17 | |
18 | #define SERIAL_MAX_BACKLOG 4096 |
19 | |
20 | typedef struct serial_backend_data { |
21 | void *frontend; |
22 | int fd; |
23 | int finished; |
24 | int inbufsize; |
25 | bufchain output_data; |
26 | } *Serial; |
27 | |
28 | /* |
29 | * We store our serial backends in a tree sorted by fd, so that |
30 | * when we get an uxsel notification we know which backend instance |
31 | * is the owner of the serial port that caused it. |
32 | */ |
33 | static int serial_compare_by_fd(void *av, void *bv) |
34 | { |
35 | Serial a = (Serial)av; |
36 | Serial b = (Serial)bv; |
37 | |
38 | if (a->fd < b->fd) |
39 | return -1; |
40 | else if (a->fd > b->fd) |
41 | return +1; |
42 | return 0; |
43 | } |
44 | |
45 | static int serial_find_by_fd(void *av, void *bv) |
46 | { |
47 | int a = *(int *)av; |
48 | Serial b = (Serial)bv; |
49 | |
50 | if (a < b->fd) |
51 | return -1; |
52 | else if (a > b->fd) |
53 | return +1; |
54 | return 0; |
55 | } |
56 | |
57 | static tree234 *serial_by_fd = NULL; |
58 | |
59 | static int serial_select_result(int fd, int event); |
60 | static void serial_uxsel_setup(Serial serial); |
61 | static void serial_try_write(Serial serial); |
62 | |
63 | static const char *serial_configure(Serial serial, Config *cfg) |
64 | { |
65 | struct termios options; |
66 | int bflag, bval; |
67 | const char *str; |
68 | char *msg; |
69 | |
70 | if (serial->fd < 0) |
71 | return "Unable to reconfigure already-closed serial connection"; |
72 | |
73 | tcgetattr(serial->fd, &options); |
74 | |
75 | /* |
76 | * Find the appropriate baud rate flag. |
77 | */ |
78 | #define SETBAUD(x) (bflag = B ## x, bval = x) |
79 | #define CHECKBAUD(x) do { if (cfg->serspeed >= x) SETBAUD(x); } while (0) |
80 | SETBAUD(50); |
81 | #ifdef B75 |
82 | CHECKBAUD(75); |
83 | #endif |
84 | #ifdef B110 |
85 | CHECKBAUD(110); |
86 | #endif |
87 | #ifdef B134 |
88 | CHECKBAUD(134); |
89 | #endif |
90 | #ifdef B150 |
91 | CHECKBAUD(150); |
92 | #endif |
93 | #ifdef B200 |
94 | CHECKBAUD(200); |
95 | #endif |
96 | #ifdef B300 |
97 | CHECKBAUD(300); |
98 | #endif |
99 | #ifdef B600 |
100 | CHECKBAUD(600); |
101 | #endif |
102 | #ifdef B1200 |
103 | CHECKBAUD(1200); |
104 | #endif |
105 | #ifdef B1800 |
106 | CHECKBAUD(1800); |
107 | #endif |
108 | #ifdef B2400 |
109 | CHECKBAUD(2400); |
110 | #endif |
111 | #ifdef B4800 |
112 | CHECKBAUD(4800); |
113 | #endif |
114 | #ifdef B9600 |
115 | CHECKBAUD(9600); |
116 | #endif |
117 | #ifdef B19200 |
118 | CHECKBAUD(19200); |
119 | #endif |
120 | #ifdef B38400 |
121 | CHECKBAUD(38400); |
122 | #endif |
123 | #ifdef B57600 |
124 | CHECKBAUD(57600); |
125 | #endif |
126 | #ifdef B76800 |
127 | CHECKBAUD(76800); |
128 | #endif |
129 | #ifdef B115200 |
130 | CHECKBAUD(115200); |
131 | #endif |
25d0ab1e |
132 | #ifdef B153600 |
133 | CHECKBAUD(153600); |
134 | #endif |
aef05b78 |
135 | #ifdef B230400 |
136 | CHECKBAUD(230400); |
137 | #endif |
25d0ab1e |
138 | #ifdef B307200 |
139 | CHECKBAUD(307200); |
140 | #endif |
141 | #ifdef B460800 |
142 | CHECKBAUD(460800); |
143 | #endif |
144 | #ifdef B500000 |
145 | CHECKBAUD(500000); |
146 | #endif |
147 | #ifdef B576000 |
148 | CHECKBAUD(576000); |
149 | #endif |
150 | #ifdef B921600 |
151 | CHECKBAUD(921600); |
152 | #endif |
153 | #ifdef B1000000 |
154 | CHECKBAUD(1000000); |
155 | #endif |
156 | #ifdef B1152000 |
157 | CHECKBAUD(1152000); |
158 | #endif |
159 | #ifdef B1500000 |
160 | CHECKBAUD(1500000); |
161 | #endif |
162 | #ifdef B2000000 |
163 | CHECKBAUD(2000000); |
164 | #endif |
165 | #ifdef B2500000 |
166 | CHECKBAUD(2500000); |
167 | #endif |
168 | #ifdef B3000000 |
169 | CHECKBAUD(3000000); |
170 | #endif |
171 | #ifdef B3500000 |
172 | CHECKBAUD(3500000); |
173 | #endif |
174 | #ifdef B4000000 |
175 | CHECKBAUD(4000000); |
176 | #endif |
aef05b78 |
177 | #undef CHECKBAUD |
178 | #undef SETBAUD |
179 | cfsetispeed(&options, bflag); |
180 | cfsetospeed(&options, bflag); |
181 | msg = dupprintf("Configuring baud rate %d", bval); |
182 | logevent(serial->frontend, msg); |
183 | sfree(msg); |
184 | |
185 | options.c_cflag &= ~CSIZE; |
186 | switch (cfg->serdatabits) { |
187 | case 5: options.c_cflag |= CS5; break; |
188 | case 6: options.c_cflag |= CS6; break; |
189 | case 7: options.c_cflag |= CS7; break; |
190 | case 8: options.c_cflag |= CS8; break; |
191 | default: return "Invalid number of data bits (need 5, 6, 7 or 8)"; |
192 | } |
193 | msg = dupprintf("Configuring %d data bits", cfg->serdatabits); |
194 | logevent(serial->frontend, msg); |
195 | sfree(msg); |
196 | |
197 | if (cfg->serstopbits >= 4) { |
198 | options.c_cflag |= CSTOPB; |
199 | } else { |
200 | options.c_cflag &= ~CSTOPB; |
201 | } |
202 | msg = dupprintf("Configuring %d stop bits", |
203 | (options.c_cflag & CSTOPB ? 2 : 1)); |
204 | logevent(serial->frontend, msg); |
205 | sfree(msg); |
206 | |
e26f8aa1 |
207 | options.c_iflag &= ~(IXON|IXOFF); |
208 | #ifdef CRTSCTS |
209 | options.c_cflag &= ~CRTSCTS; |
210 | #endif |
211 | #ifdef CNEW_RTSCTS |
212 | options.c_cflag &= ~CNEW_RTSCTS; |
213 | #endif |
aef05b78 |
214 | if (cfg->serflow == SER_FLOW_XONXOFF) { |
e26f8aa1 |
215 | options.c_iflag |= IXON | IXOFF; |
aef05b78 |
216 | str = "XON/XOFF"; |
217 | } else if (cfg->serflow == SER_FLOW_RTSCTS) { |
218 | #ifdef CRTSCTS |
219 | options.c_cflag |= CRTSCTS; |
220 | #endif |
221 | #ifdef CNEW_RTSCTS |
222 | options.c_cflag |= CNEW_RTSCTS; |
223 | #endif |
224 | str = "RTS/CTS"; |
225 | } else |
226 | str = "no"; |
227 | msg = dupprintf("Configuring %s flow control", str); |
228 | logevent(serial->frontend, msg); |
229 | sfree(msg); |
230 | |
231 | /* Parity */ |
232 | if (cfg->serparity == SER_PAR_ODD) { |
233 | options.c_cflag |= PARENB; |
234 | options.c_cflag |= PARODD; |
235 | str = "odd"; |
236 | } else if (cfg->serparity == SER_PAR_EVEN) { |
237 | options.c_cflag |= PARENB; |
238 | options.c_cflag &= ~PARODD; |
239 | str = "even"; |
240 | } else { |
241 | options.c_cflag &= ~PARENB; |
242 | str = "no"; |
243 | } |
244 | msg = dupprintf("Configuring %s parity", str); |
245 | logevent(serial->frontend, msg); |
246 | sfree(msg); |
247 | |
248 | options.c_cflag |= CLOCAL | CREAD; |
249 | options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); |
9a41f8d3 |
250 | options.c_iflag &= ~(ISTRIP | IGNCR | INLCR | ICRNL |
251 | #ifdef IUCLC |
252 | | IUCLC |
253 | #endif |
254 | ); |
255 | options.c_oflag &= ~(OPOST |
256 | #ifdef ONLCR |
257 | | ONLCR |
258 | #endif |
e8c03e4c |
259 | #ifdef OCRNL |
260 | | OCRNL |
261 | #endif |
262 | #ifdef ONOCR |
263 | | ONOCR |
264 | #endif |
265 | #ifdef ONLRET |
266 | | ONLRET |
267 | #endif |
268 | ); |
aef05b78 |
269 | options.c_cc[VMIN] = 1; |
270 | options.c_cc[VTIME] = 0; |
271 | |
272 | if (tcsetattr(serial->fd, TCSANOW, &options) < 0) |
273 | return "Unable to configure serial port"; |
274 | |
275 | return NULL; |
276 | } |
277 | |
278 | /* |
279 | * Called to set up the serial connection. |
280 | * |
281 | * Returns an error message, or NULL on success. |
282 | * |
283 | * Also places the canonical host name into `realhost'. It must be |
284 | * freed by the caller. |
285 | */ |
286 | static const char *serial_init(void *frontend_handle, void **backend_handle, |
287 | Config *cfg, |
288 | char *host, int port, char **realhost, int nodelay, |
289 | int keepalive) |
290 | { |
291 | Serial serial; |
292 | const char *err; |
293 | |
294 | serial = snew(struct serial_backend_data); |
295 | *backend_handle = serial; |
296 | |
297 | serial->frontend = frontend_handle; |
298 | serial->finished = FALSE; |
299 | serial->inbufsize = 0; |
300 | bufchain_init(&serial->output_data); |
301 | |
302 | { |
303 | char *msg = dupprintf("Opening serial device %s", cfg->serline); |
304 | logevent(serial->frontend, msg); |
305 | } |
306 | |
307 | serial->fd = open(cfg->serline, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); |
308 | if (serial->fd < 0) |
309 | return "Unable to open serial port"; |
310 | |
db9b7dce |
311 | cloexec(serial->fd); |
89e97516 |
312 | |
aef05b78 |
313 | err = serial_configure(serial, cfg); |
314 | if (err) |
315 | return err; |
316 | |
317 | *realhost = dupstr(cfg->serline); |
318 | |
319 | if (!serial_by_fd) |
320 | serial_by_fd = newtree234(serial_compare_by_fd); |
321 | add234(serial_by_fd, serial); |
322 | |
323 | serial_uxsel_setup(serial); |
324 | |
45ef7ce3 |
325 | /* |
326 | * Specials are always available. |
327 | */ |
328 | update_specials_menu(serial->frontend); |
329 | |
aef05b78 |
330 | return NULL; |
331 | } |
332 | |
333 | static void serial_close(Serial serial) |
334 | { |
335 | if (serial->fd >= 0) { |
336 | close(serial->fd); |
337 | serial->fd = -1; |
338 | } |
339 | } |
340 | |
341 | static void serial_free(void *handle) |
342 | { |
343 | Serial serial = (Serial) handle; |
344 | |
345 | serial_close(serial); |
346 | |
347 | bufchain_clear(&serial->output_data); |
348 | |
349 | sfree(serial); |
350 | } |
351 | |
352 | static void serial_reconfig(void *handle, Config *cfg) |
353 | { |
354 | Serial serial = (Serial) handle; |
aef05b78 |
355 | |
356 | /* |
3ab79841 |
357 | * FIXME: what should we do if this returns an error? |
aef05b78 |
358 | */ |
3ab79841 |
359 | serial_configure(serial, cfg); |
aef05b78 |
360 | } |
361 | |
362 | static int serial_select_result(int fd, int event) |
363 | { |
364 | Serial serial; |
365 | char buf[4096]; |
366 | int ret; |
367 | int finished = FALSE; |
368 | |
369 | serial = find234(serial_by_fd, &fd, serial_find_by_fd); |
370 | |
371 | if (!serial) |
372 | return 1; /* spurious event; keep going */ |
373 | |
374 | if (event == 1) { |
375 | ret = read(serial->fd, buf, sizeof(buf)); |
376 | |
377 | if (ret == 0) { |
378 | /* |
379 | * Shouldn't happen on a real serial port, but I'm open |
380 | * to the idea that there might be two-way devices we |
381 | * can treat _like_ serial ports which can return EOF. |
382 | */ |
383 | finished = TRUE; |
384 | } else if (ret < 0) { |
0620c88a |
385 | #ifdef EAGAIN |
386 | if (errno == EAGAIN) |
387 | return 1; /* spurious */ |
388 | #endif |
389 | #ifdef EWOULDBLOCK |
390 | if (errno == EWOULDBLOCK) |
391 | return 1; /* spurious */ |
392 | #endif |
aef05b78 |
393 | perror("read serial port"); |
394 | exit(1); |
395 | } else if (ret > 0) { |
396 | serial->inbufsize = from_backend(serial->frontend, 0, buf, ret); |
397 | serial_uxsel_setup(serial); /* might acquire backlog and freeze */ |
398 | } |
399 | } else if (event == 2) { |
400 | /* |
401 | * Attempt to send data down the pty. |
402 | */ |
403 | serial_try_write(serial); |
404 | } |
405 | |
406 | if (finished) { |
407 | serial_close(serial); |
408 | |
409 | serial->finished = TRUE; |
410 | |
411 | notify_remote_exit(serial->frontend); |
412 | } |
413 | |
414 | return !finished; |
415 | } |
416 | |
417 | static void serial_uxsel_setup(Serial serial) |
418 | { |
419 | int rwx = 0; |
420 | |
421 | if (serial->inbufsize <= SERIAL_MAX_BACKLOG) |
422 | rwx |= 1; |
423 | if (bufchain_size(&serial->output_data)) |
424 | rwx |= 2; /* might also want to write to it */ |
425 | uxsel_set(serial->fd, rwx, serial_select_result); |
426 | } |
427 | |
428 | static void serial_try_write(Serial serial) |
429 | { |
430 | void *data; |
431 | int len, ret; |
432 | |
433 | assert(serial->fd >= 0); |
434 | |
435 | while (bufchain_size(&serial->output_data) > 0) { |
436 | bufchain_prefix(&serial->output_data, &data, &len); |
437 | ret = write(serial->fd, data, len); |
438 | |
439 | if (ret < 0 && (errno == EWOULDBLOCK)) { |
440 | /* |
441 | * We've sent all we can for the moment. |
442 | */ |
443 | break; |
444 | } |
445 | if (ret < 0) { |
446 | perror("write serial port"); |
447 | exit(1); |
448 | } |
449 | bufchain_consume(&serial->output_data, ret); |
450 | } |
451 | |
452 | serial_uxsel_setup(serial); |
453 | } |
454 | |
455 | /* |
456 | * Called to send data down the serial connection. |
457 | */ |
458 | static int serial_send(void *handle, char *buf, int len) |
459 | { |
460 | Serial serial = (Serial) handle; |
461 | |
462 | if (serial->fd < 0) |
463 | return 0; |
464 | |
465 | bufchain_add(&serial->output_data, buf, len); |
466 | serial_try_write(serial); |
467 | |
468 | return bufchain_size(&serial->output_data); |
469 | } |
470 | |
471 | /* |
472 | * Called to query the current sendability status. |
473 | */ |
474 | static int serial_sendbuffer(void *handle) |
475 | { |
476 | Serial serial = (Serial) handle; |
477 | return bufchain_size(&serial->output_data); |
478 | } |
479 | |
480 | /* |
481 | * Called to set the size of the window |
482 | */ |
483 | static void serial_size(void *handle, int width, int height) |
484 | { |
485 | /* Do nothing! */ |
486 | return; |
487 | } |
488 | |
489 | /* |
490 | * Send serial special codes. |
491 | */ |
492 | static void serial_special(void *handle, Telnet_Special code) |
493 | { |
45ef7ce3 |
494 | Serial serial = (Serial) handle; |
495 | |
496 | if (serial->fd >= 0 && code == TS_BRK) { |
497 | tcsendbreak(serial->fd, 0); |
498 | logevent(serial->frontend, "Sending serial break at user request"); |
499 | } |
500 | |
aef05b78 |
501 | return; |
502 | } |
503 | |
504 | /* |
505 | * Return a list of the special codes that make sense in this |
506 | * protocol. |
507 | */ |
508 | static const struct telnet_special *serial_get_specials(void *handle) |
509 | { |
45ef7ce3 |
510 | static const struct telnet_special specials[] = { |
511 | {"Break", TS_BRK}, |
512 | {NULL, TS_EXITMENU} |
513 | }; |
514 | return specials; |
aef05b78 |
515 | } |
516 | |
517 | static int serial_connected(void *handle) |
518 | { |
519 | return 1; /* always connected */ |
520 | } |
521 | |
522 | static int serial_sendok(void *handle) |
523 | { |
524 | return 1; |
525 | } |
526 | |
527 | static void serial_unthrottle(void *handle, int backlog) |
528 | { |
529 | Serial serial = (Serial) handle; |
530 | serial->inbufsize = backlog; |
531 | serial_uxsel_setup(serial); |
532 | } |
533 | |
534 | static int serial_ldisc(void *handle, int option) |
535 | { |
536 | /* |
537 | * Local editing and local echo are off by default. |
538 | */ |
539 | return 0; |
540 | } |
541 | |
542 | static void serial_provide_ldisc(void *handle, void *ldisc) |
543 | { |
544 | /* This is a stub. */ |
545 | } |
546 | |
547 | static void serial_provide_logctx(void *handle, void *logctx) |
548 | { |
549 | /* This is a stub. */ |
550 | } |
551 | |
552 | static int serial_exitcode(void *handle) |
553 | { |
554 | Serial serial = (Serial) handle; |
555 | if (serial->fd >= 0) |
556 | return -1; /* still connected */ |
557 | else |
558 | /* Exit codes are a meaningless concept with serial ports */ |
559 | return INT_MAX; |
560 | } |
561 | |
562 | /* |
563 | * cfg_info for Serial does nothing at all. |
564 | */ |
565 | static int serial_cfg_info(void *handle) |
566 | { |
567 | return 0; |
568 | } |
569 | |
570 | Backend serial_backend = { |
571 | serial_init, |
572 | serial_free, |
573 | serial_reconfig, |
574 | serial_send, |
575 | serial_sendbuffer, |
576 | serial_size, |
577 | serial_special, |
578 | serial_get_specials, |
579 | serial_connected, |
580 | serial_exitcode, |
581 | serial_sendok, |
582 | serial_ldisc, |
583 | serial_provide_ldisc, |
584 | serial_provide_logctx, |
585 | serial_unthrottle, |
586 | serial_cfg_info, |
9e164d82 |
587 | "serial", |
588 | PROT_SERIAL, |
a4451dd1 |
589 | 0 |
aef05b78 |
590 | }; |