]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/uxser.c
24b0124cb4aba9fad9735f1a3901bd8e6b8c2477
[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, 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             perror("read serial port");
346             exit(1);
347         } else if (ret > 0) {
348             serial->inbufsize = from_backend(serial->frontend, 0, buf, ret);
349             serial_uxsel_setup(serial); /* might acquire backlog and freeze */
350         }
351     } else if (event == 2) {
352         /*
353          * Attempt to send data down the pty.
354          */
355         serial_try_write(serial);
356     }
357
358     if (finished) {
359         serial_close(serial);
360
361         serial->finished = TRUE;
362
363         notify_remote_exit(serial->frontend);
364     }
365
366     return !finished;
367 }
368
369 static void serial_uxsel_setup(Serial serial)
370 {
371     int rwx = 0;
372
373     if (serial->inbufsize <= SERIAL_MAX_BACKLOG)
374         rwx |= 1;
375     if (bufchain_size(&serial->output_data))
376         rwx |= 2;                      /* might also want to write to it */
377     uxsel_set(serial->fd, rwx, serial_select_result);
378 }
379
380 static void serial_try_write(Serial serial)
381 {
382     void *data;
383     int len, ret;
384
385     assert(serial->fd >= 0);
386
387     while (bufchain_size(&serial->output_data) > 0) {
388         bufchain_prefix(&serial->output_data, &data, &len);
389         ret = write(serial->fd, data, len);
390
391         if (ret < 0 && (errno == EWOULDBLOCK)) {
392             /*
393              * We've sent all we can for the moment.
394              */
395             break;
396         }
397         if (ret < 0) {
398             perror("write serial port");
399             exit(1);
400         }
401         bufchain_consume(&serial->output_data, ret);
402     }
403
404     serial_uxsel_setup(serial);
405 }
406
407 /*
408  * Called to send data down the serial connection.
409  */
410 static int serial_send(void *handle, char *buf, int len)
411 {
412     Serial serial = (Serial) handle;
413
414     if (serial->fd < 0)
415         return 0;
416
417     bufchain_add(&serial->output_data, buf, len);
418     serial_try_write(serial);
419
420     return bufchain_size(&serial->output_data);
421 }
422
423 /*
424  * Called to query the current sendability status.
425  */
426 static int serial_sendbuffer(void *handle)
427 {
428     Serial serial = (Serial) handle;
429     return bufchain_size(&serial->output_data);
430 }
431
432 /*
433  * Called to set the size of the window
434  */
435 static void serial_size(void *handle, int width, int height)
436 {
437     /* Do nothing! */
438     return;
439 }
440
441 /*
442  * Send serial special codes.
443  */
444 static void serial_special(void *handle, Telnet_Special code)
445 {
446     Serial serial = (Serial) handle;
447
448     if (serial->fd >= 0 && code == TS_BRK) {
449         tcsendbreak(serial->fd, 0);
450         logevent(serial->frontend, "Sending serial break at user request");
451     }
452
453     return;
454 }
455
456 /*
457  * Return a list of the special codes that make sense in this
458  * protocol.
459  */
460 static const struct telnet_special *serial_get_specials(void *handle)
461 {
462     static const struct telnet_special specials[] = {
463         {"Break", TS_BRK},
464         {NULL, TS_EXITMENU}
465     };
466     return specials;
467 }
468
469 static int serial_connected(void *handle)
470 {
471     return 1;                          /* always connected */
472 }
473
474 static int serial_sendok(void *handle)
475 {
476     return 1;
477 }
478
479 static void serial_unthrottle(void *handle, int backlog)
480 {
481     Serial serial = (Serial) handle;
482     serial->inbufsize = backlog;
483     serial_uxsel_setup(serial);
484 }
485
486 static int serial_ldisc(void *handle, int option)
487 {
488     /*
489      * Local editing and local echo are off by default.
490      */
491     return 0;
492 }
493
494 static void serial_provide_ldisc(void *handle, void *ldisc)
495 {
496     /* This is a stub. */
497 }
498
499 static void serial_provide_logctx(void *handle, void *logctx)
500 {
501     /* This is a stub. */
502 }
503
504 static int serial_exitcode(void *handle)
505 {
506     Serial serial = (Serial) handle;
507     if (serial->fd >= 0)
508         return -1;                     /* still connected */
509     else
510         /* Exit codes are a meaningless concept with serial ports */
511         return INT_MAX;
512 }
513
514 /*
515  * cfg_info for Serial does nothing at all.
516  */
517 static int serial_cfg_info(void *handle)
518 {
519     return 0;
520 }
521
522 Backend serial_backend = {
523     serial_init,
524     serial_free,
525     serial_reconfig,
526     serial_send,
527     serial_sendbuffer,
528     serial_size,
529     serial_special,
530     serial_get_specials,
531     serial_connected,
532     serial_exitcode,
533     serial_sendok,
534     serial_ldisc,
535     serial_provide_ldisc,
536     serial_provide_logctx,
537     serial_unthrottle,
538     serial_cfg_info,
539     1
540 };