Fixes (mostly from Colin Watson, a couple redone by me) to make Unix
[u/mdw/putty] / unix / uxser.c
CommitLineData
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
20typedef 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 */
33static 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
45static 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
57static tree234 *serial_by_fd = NULL;
58
59static int serial_select_result(int fd, int event);
60static void serial_uxsel_setup(Serial serial);
61static void serial_try_write(Serial serial);
62
63static 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 */
286static 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
333static void serial_close(Serial serial)
334{
335 if (serial->fd >= 0) {
336 close(serial->fd);
337 serial->fd = -1;
338 }
339}
340
341static 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
352static 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
362static 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
417static 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
428static 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 */
458static 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 */
474static 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 */
483static void serial_size(void *handle, int width, int height)
484{
485 /* Do nothing! */
486 return;
487}
488
489/*
490 * Send serial special codes.
491 */
492static 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 */
508static 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
517static int serial_connected(void *handle)
518{
519 return 1; /* always connected */
520}
521
522static int serial_sendok(void *handle)
523{
524 return 1;
525}
526
527static void serial_unthrottle(void *handle, int backlog)
528{
529 Serial serial = (Serial) handle;
530 serial->inbufsize = backlog;
531 serial_uxsel_setup(serial);
532}
533
534static 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
542static void serial_provide_ldisc(void *handle, void *ldisc)
543{
544 /* This is a stub. */
545}
546
547static void serial_provide_logctx(void *handle, void *logctx)
548{
549 /* This is a stub. */
550}
551
552static 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 */
565static int serial_cfg_info(void *handle)
566{
567 return 0;
568}
569
570Backend 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};