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