David Laight reports that sometimes reads on a serial port will
[u/mdw/putty] / unix / uxser.c
1 /*
2 * Serial back end (Unix-specific).
3 */
4
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
132 #ifdef B230400
133 CHECKBAUD(230400);
134 #endif
135 #undef CHECKBAUD
136 #undef SETBAUD
137 cfsetispeed(&options, bflag);
138 cfsetospeed(&options, bflag);
139 msg = dupprintf("Configuring baud rate %d", bval);
140 logevent(serial->frontend, msg);
141 sfree(msg);
142
143 options.c_cflag &= ~CSIZE;
144 switch (cfg->serdatabits) {
145 case 5: options.c_cflag |= CS5; break;
146 case 6: options.c_cflag |= CS6; break;
147 case 7: options.c_cflag |= CS7; break;
148 case 8: options.c_cflag |= CS8; break;
149 default: return "Invalid number of data bits (need 5, 6, 7 or 8)";
150 }
151 msg = dupprintf("Configuring %d data bits", cfg->serdatabits);
152 logevent(serial->frontend, msg);
153 sfree(msg);
154
155 if (cfg->serstopbits >= 4) {
156 options.c_cflag |= CSTOPB;
157 } else {
158 options.c_cflag &= ~CSTOPB;
159 }
160 msg = dupprintf("Configuring %d stop bits",
161 (options.c_cflag & CSTOPB ? 2 : 1));
162 logevent(serial->frontend, msg);
163 sfree(msg);
164
165 options.c_iflag &= ~(IXON|IXOFF);
166 #ifdef CRTSCTS
167 options.c_cflag &= ~CRTSCTS;
168 #endif
169 #ifdef CNEW_RTSCTS
170 options.c_cflag &= ~CNEW_RTSCTS;
171 #endif
172 if (cfg->serflow == SER_FLOW_XONXOFF) {
173 options.c_iflag |= IXON | IXOFF;
174 str = "XON/XOFF";
175 } else if (cfg->serflow == SER_FLOW_RTSCTS) {
176 #ifdef CRTSCTS
177 options.c_cflag |= CRTSCTS;
178 #endif
179 #ifdef CNEW_RTSCTS
180 options.c_cflag |= CNEW_RTSCTS;
181 #endif
182 str = "RTS/CTS";
183 } else
184 str = "no";
185 msg = dupprintf("Configuring %s flow control", str);
186 logevent(serial->frontend, msg);
187 sfree(msg);
188
189 /* Parity */
190 if (cfg->serparity == SER_PAR_ODD) {
191 options.c_cflag |= PARENB;
192 options.c_cflag |= PARODD;
193 str = "odd";
194 } else if (cfg->serparity == SER_PAR_EVEN) {
195 options.c_cflag |= PARENB;
196 options.c_cflag &= ~PARODD;
197 str = "even";
198 } else {
199 options.c_cflag &= ~PARENB;
200 str = "no";
201 }
202 msg = dupprintf("Configuring %s parity", str);
203 logevent(serial->frontend, msg);
204 sfree(msg);
205
206 options.c_cflag |= CLOCAL | CREAD;
207 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
208 options.c_iflag &= ~(ISTRIP | IGNCR | INLCR | ICRNL
209 #ifdef IUCLC
210 | IUCLC
211 #endif
212 );
213 options.c_oflag &= ~(OPOST
214 #ifdef ONLCR
215 | ONLCR
216 #endif
217 #ifdef OCRNL
218 | OCRNL
219 #endif
220 #ifdef ONOCR
221 | ONOCR
222 #endif
223 #ifdef ONLRET
224 | ONLRET
225 #endif
226 );
227 options.c_cc[VMIN] = 1;
228 options.c_cc[VTIME] = 0;
229
230 if (tcsetattr(serial->fd, TCSANOW, &options) < 0)
231 return "Unable to configure serial port";
232
233 return NULL;
234 }
235
236 /*
237 * Called to set up the serial connection.
238 *
239 * Returns an error message, or NULL on success.
240 *
241 * Also places the canonical host name into `realhost'. It must be
242 * freed by the caller.
243 */
244 static const char *serial_init(void *frontend_handle, void **backend_handle,
245 Config *cfg,
246 char *host, int port, char **realhost, int nodelay,
247 int keepalive)
248 {
249 Serial serial;
250 const char *err;
251
252 serial = snew(struct serial_backend_data);
253 *backend_handle = serial;
254
255 serial->frontend = frontend_handle;
256 serial->finished = FALSE;
257 serial->inbufsize = 0;
258 bufchain_init(&serial->output_data);
259
260 {
261 char *msg = dupprintf("Opening serial device %s", cfg->serline);
262 logevent(serial->frontend, msg);
263 }
264
265 serial->fd = open(cfg->serline, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
266 if (serial->fd < 0)
267 return "Unable to open serial port";
268
269 cloexec(serial->fd);
270
271 err = serial_configure(serial, cfg);
272 if (err)
273 return err;
274
275 *realhost = dupstr(cfg->serline);
276
277 if (!serial_by_fd)
278 serial_by_fd = newtree234(serial_compare_by_fd);
279 add234(serial_by_fd, serial);
280
281 serial_uxsel_setup(serial);
282
283 /*
284 * Specials are always available.
285 */
286 update_specials_menu(serial->frontend);
287
288 return NULL;
289 }
290
291 static void serial_close(Serial serial)
292 {
293 if (serial->fd >= 0) {
294 close(serial->fd);
295 serial->fd = -1;
296 }
297 }
298
299 static void serial_free(void *handle)
300 {
301 Serial serial = (Serial) handle;
302
303 serial_close(serial);
304
305 bufchain_clear(&serial->output_data);
306
307 sfree(serial);
308 }
309
310 static void serial_reconfig(void *handle, Config *cfg)
311 {
312 Serial serial = (Serial) handle;
313 const char *err;
314
315 err = serial_configure(serial, cfg);
316
317 /*
318 * FIXME: what should we do if err returns something?
319 */
320 }
321
322 static int serial_select_result(int fd, int event)
323 {
324 Serial serial;
325 char buf[4096];
326 int ret;
327 int finished = FALSE;
328
329 serial = find234(serial_by_fd, &fd, serial_find_by_fd);
330
331 if (!serial)
332 return 1; /* spurious event; keep going */
333
334 if (event == 1) {
335 ret = read(serial->fd, buf, sizeof(buf));
336
337 if (ret == 0) {
338 /*
339 * Shouldn't happen on a real serial port, but I'm open
340 * to the idea that there might be two-way devices we
341 * can treat _like_ serial ports which can return EOF.
342 */
343 finished = TRUE;
344 } else if (ret < 0) {
345 #ifdef EAGAIN
346 if (errno == EAGAIN)
347 return 1; /* spurious */
348 #endif
349 #ifdef EWOULDBLOCK
350 if (errno == EWOULDBLOCK)
351 return 1; /* spurious */
352 #endif
353 perror("read serial port");
354 exit(1);
355 } else if (ret > 0) {
356 serial->inbufsize = from_backend(serial->frontend, 0, buf, ret);
357 serial_uxsel_setup(serial); /* might acquire backlog and freeze */
358 }
359 } else if (event == 2) {
360 /*
361 * Attempt to send data down the pty.
362 */
363 serial_try_write(serial);
364 }
365
366 if (finished) {
367 serial_close(serial);
368
369 serial->finished = TRUE;
370
371 notify_remote_exit(serial->frontend);
372 }
373
374 return !finished;
375 }
376
377 static void serial_uxsel_setup(Serial serial)
378 {
379 int rwx = 0;
380
381 if (serial->inbufsize <= SERIAL_MAX_BACKLOG)
382 rwx |= 1;
383 if (bufchain_size(&serial->output_data))
384 rwx |= 2; /* might also want to write to it */
385 uxsel_set(serial->fd, rwx, serial_select_result);
386 }
387
388 static void serial_try_write(Serial serial)
389 {
390 void *data;
391 int len, ret;
392
393 assert(serial->fd >= 0);
394
395 while (bufchain_size(&serial->output_data) > 0) {
396 bufchain_prefix(&serial->output_data, &data, &len);
397 ret = write(serial->fd, data, len);
398
399 if (ret < 0 && (errno == EWOULDBLOCK)) {
400 /*
401 * We've sent all we can for the moment.
402 */
403 break;
404 }
405 if (ret < 0) {
406 perror("write serial port");
407 exit(1);
408 }
409 bufchain_consume(&serial->output_data, ret);
410 }
411
412 serial_uxsel_setup(serial);
413 }
414
415 /*
416 * Called to send data down the serial connection.
417 */
418 static int serial_send(void *handle, char *buf, int len)
419 {
420 Serial serial = (Serial) handle;
421
422 if (serial->fd < 0)
423 return 0;
424
425 bufchain_add(&serial->output_data, buf, len);
426 serial_try_write(serial);
427
428 return bufchain_size(&serial->output_data);
429 }
430
431 /*
432 * Called to query the current sendability status.
433 */
434 static int serial_sendbuffer(void *handle)
435 {
436 Serial serial = (Serial) handle;
437 return bufchain_size(&serial->output_data);
438 }
439
440 /*
441 * Called to set the size of the window
442 */
443 static void serial_size(void *handle, int width, int height)
444 {
445 /* Do nothing! */
446 return;
447 }
448
449 /*
450 * Send serial special codes.
451 */
452 static void serial_special(void *handle, Telnet_Special code)
453 {
454 Serial serial = (Serial) handle;
455
456 if (serial->fd >= 0 && code == TS_BRK) {
457 tcsendbreak(serial->fd, 0);
458 logevent(serial->frontend, "Sending serial break at user request");
459 }
460
461 return;
462 }
463
464 /*
465 * Return a list of the special codes that make sense in this
466 * protocol.
467 */
468 static const struct telnet_special *serial_get_specials(void *handle)
469 {
470 static const struct telnet_special specials[] = {
471 {"Break", TS_BRK},
472 {NULL, TS_EXITMENU}
473 };
474 return specials;
475 }
476
477 static int serial_connected(void *handle)
478 {
479 return 1; /* always connected */
480 }
481
482 static int serial_sendok(void *handle)
483 {
484 return 1;
485 }
486
487 static void serial_unthrottle(void *handle, int backlog)
488 {
489 Serial serial = (Serial) handle;
490 serial->inbufsize = backlog;
491 serial_uxsel_setup(serial);
492 }
493
494 static int serial_ldisc(void *handle, int option)
495 {
496 /*
497 * Local editing and local echo are off by default.
498 */
499 return 0;
500 }
501
502 static void serial_provide_ldisc(void *handle, void *ldisc)
503 {
504 /* This is a stub. */
505 }
506
507 static void serial_provide_logctx(void *handle, void *logctx)
508 {
509 /* This is a stub. */
510 }
511
512 static int serial_exitcode(void *handle)
513 {
514 Serial serial = (Serial) handle;
515 if (serial->fd >= 0)
516 return -1; /* still connected */
517 else
518 /* Exit codes are a meaningless concept with serial ports */
519 return INT_MAX;
520 }
521
522 /*
523 * cfg_info for Serial does nothing at all.
524 */
525 static int serial_cfg_info(void *handle)
526 {
527 return 0;
528 }
529
530 Backend serial_backend = {
531 serial_init,
532 serial_free,
533 serial_reconfig,
534 serial_send,
535 serial_sendbuffer,
536 serial_size,
537 serial_special,
538 serial_get_specials,
539 serial_connected,
540 serial_exitcode,
541 serial_sendok,
542 serial_ldisc,
543 serial_provide_ldisc,
544 serial_provide_logctx,
545 serial_unthrottle,
546 serial_cfg_info,
547 "serial",
548 PROT_SERIAL,
549 0
550 };