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