]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/uxser.c
Compile fix for GTK 3.18: avoid gtk_adjustment_changed().
[PuTTY.git] / 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                                const char *host, int port, char **realhost,
293                                int nodelay, 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         sfree(msg);
312     }
313
314     serial->fd = open(line, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
315     if (serial->fd < 0)
316         return "Unable to open serial port";
317
318     cloexec(serial->fd);
319
320     err = serial_configure(serial, conf);
321     if (err)
322         return err;
323
324     *realhost = dupstr(line);
325
326     if (!serial_by_fd)
327         serial_by_fd = newtree234(serial_compare_by_fd);
328     add234(serial_by_fd, serial);
329
330     serial_uxsel_setup(serial);
331
332     /*
333      * Specials are always available.
334      */
335     update_specials_menu(serial->frontend);
336
337     return NULL;
338 }
339
340 static void serial_close(Serial serial)
341 {
342     if (serial->fd >= 0) {
343         close(serial->fd);
344         serial->fd = -1;
345     }
346 }
347
348 static void serial_free(void *handle)
349 {
350     Serial serial = (Serial) handle;
351
352     serial_close(serial);
353
354     bufchain_clear(&serial->output_data);
355
356     sfree(serial);
357 }
358
359 static void serial_reconfig(void *handle, Conf *conf)
360 {
361     Serial serial = (Serial) handle;
362
363     /*
364      * FIXME: what should we do if this returns an error?
365      */
366     serial_configure(serial, conf);
367 }
368
369 static int serial_select_result(int fd, int event)
370 {
371     Serial serial;
372     char buf[4096];
373     int ret;
374     int finished = FALSE;
375
376     serial = find234(serial_by_fd, &fd, serial_find_by_fd);
377
378     if (!serial)
379         return 1;                      /* spurious event; keep going */
380
381     if (event == 1) {
382         ret = read(serial->fd, buf, sizeof(buf));
383
384         if (ret == 0) {
385             /*
386              * Shouldn't happen on a real serial port, but I'm open
387              * to the idea that there might be two-way devices we
388              * can treat _like_ serial ports which can return EOF.
389              */
390             finished = TRUE;
391         } else if (ret < 0) {
392 #ifdef EAGAIN
393             if (errno == EAGAIN)
394                 return 1;              /* spurious */
395 #endif
396 #ifdef EWOULDBLOCK
397             if (errno == EWOULDBLOCK)
398                 return 1;              /* spurious */
399 #endif
400             perror("read serial port");
401             exit(1);
402         } else if (ret > 0) {
403             serial->inbufsize = from_backend(serial->frontend, 0, buf, ret);
404             serial_uxsel_setup(serial); /* might acquire backlog and freeze */
405         }
406     } else if (event == 2) {
407         /*
408          * Attempt to send data down the pty.
409          */
410         serial_try_write(serial);
411     }
412
413     if (finished) {
414         serial_close(serial);
415
416         serial->finished = TRUE;
417
418         notify_remote_exit(serial->frontend);
419     }
420
421     return !finished;
422 }
423
424 static void serial_uxsel_setup(Serial serial)
425 {
426     int rwx = 0;
427
428     if (serial->inbufsize <= SERIAL_MAX_BACKLOG)
429         rwx |= 1;
430     if (bufchain_size(&serial->output_data))
431         rwx |= 2;                      /* might also want to write to it */
432     uxsel_set(serial->fd, rwx, serial_select_result);
433 }
434
435 static void serial_try_write(Serial serial)
436 {
437     void *data;
438     int len, ret;
439
440     assert(serial->fd >= 0);
441
442     while (bufchain_size(&serial->output_data) > 0) {
443         bufchain_prefix(&serial->output_data, &data, &len);
444         ret = write(serial->fd, data, len);
445
446         if (ret < 0 && (errno == EWOULDBLOCK)) {
447             /*
448              * We've sent all we can for the moment.
449              */
450             break;
451         }
452         if (ret < 0) {
453             perror("write serial port");
454             exit(1);
455         }
456         bufchain_consume(&serial->output_data, ret);
457     }
458
459     serial_uxsel_setup(serial);
460 }
461
462 /*
463  * Called to send data down the serial connection.
464  */
465 static int serial_send(void *handle, const char *buf, int len)
466 {
467     Serial serial = (Serial) handle;
468
469     if (serial->fd < 0)
470         return 0;
471
472     bufchain_add(&serial->output_data, buf, len);
473     serial_try_write(serial);
474
475     return bufchain_size(&serial->output_data);
476 }
477
478 /*
479  * Called to query the current sendability status.
480  */
481 static int serial_sendbuffer(void *handle)
482 {
483     Serial serial = (Serial) handle;
484     return bufchain_size(&serial->output_data);
485 }
486
487 /*
488  * Called to set the size of the window
489  */
490 static void serial_size(void *handle, int width, int height)
491 {
492     /* Do nothing! */
493     return;
494 }
495
496 /*
497  * Send serial special codes.
498  */
499 static void serial_special(void *handle, Telnet_Special code)
500 {
501     Serial serial = (Serial) handle;
502
503     if (serial->fd >= 0 && code == TS_BRK) {
504         tcsendbreak(serial->fd, 0);
505         logevent(serial->frontend, "Sending serial break at user request");
506     }
507
508     return;
509 }
510
511 /*
512  * Return a list of the special codes that make sense in this
513  * protocol.
514  */
515 static const struct telnet_special *serial_get_specials(void *handle)
516 {
517     static const struct telnet_special specials[] = {
518         {"Break", TS_BRK},
519         {NULL, TS_EXITMENU}
520     };
521     return specials;
522 }
523
524 static int serial_connected(void *handle)
525 {
526     return 1;                          /* always connected */
527 }
528
529 static int serial_sendok(void *handle)
530 {
531     return 1;
532 }
533
534 static void serial_unthrottle(void *handle, int backlog)
535 {
536     Serial serial = (Serial) handle;
537     serial->inbufsize = backlog;
538     serial_uxsel_setup(serial);
539 }
540
541 static int serial_ldisc(void *handle, int option)
542 {
543     /*
544      * Local editing and local echo are off by default.
545      */
546     return 0;
547 }
548
549 static void serial_provide_ldisc(void *handle, void *ldisc)
550 {
551     /* This is a stub. */
552 }
553
554 static void serial_provide_logctx(void *handle, void *logctx)
555 {
556     /* This is a stub. */
557 }
558
559 static int serial_exitcode(void *handle)
560 {
561     Serial serial = (Serial) handle;
562     if (serial->fd >= 0)
563         return -1;                     /* still connected */
564     else
565         /* Exit codes are a meaningless concept with serial ports */
566         return INT_MAX;
567 }
568
569 /*
570  * cfg_info for Serial does nothing at all.
571  */
572 static int serial_cfg_info(void *handle)
573 {
574     return 0;
575 }
576
577 Backend serial_backend = {
578     serial_init,
579     serial_free,
580     serial_reconfig,
581     serial_send,
582     serial_sendbuffer,
583     serial_size,
584     serial_special,
585     serial_get_specials,
586     serial_connected,
587     serial_exitcode,
588     serial_sendok,
589     serial_ldisc,
590     serial_provide_ldisc,
591     serial_provide_logctx,
592     serial_unthrottle,
593     serial_cfg_info,
594     NULL /* test_for_upstream */,
595     "serial",
596     PROT_SERIAL,
597     0
598 };