Serial back end for Unix. Due to hardware limitations (no Linux box
[u/mdw/putty] / unix / uxser.c
1 /*
2 * Serial back end (Unix-specific).
3 */
4
5 /*
6 * TODO:
7 *
8 * - send break.
9 */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <assert.h>
14 #include <limits.h>
15
16 #include <errno.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 #include <termios.h>
20
21 #include "putty.h"
22 #include "tree234.h"
23
24 #define SERIAL_MAX_BACKLOG 4096
25
26 typedef struct serial_backend_data {
27 void *frontend;
28 int fd;
29 int finished;
30 int inbufsize;
31 bufchain output_data;
32 } *Serial;
33
34 /*
35 * We store our serial backends in a tree sorted by fd, so that
36 * when we get an uxsel notification we know which backend instance
37 * is the owner of the serial port that caused it.
38 */
39 static int serial_compare_by_fd(void *av, void *bv)
40 {
41 Serial a = (Serial)av;
42 Serial b = (Serial)bv;
43
44 if (a->fd < b->fd)
45 return -1;
46 else if (a->fd > b->fd)
47 return +1;
48 return 0;
49 }
50
51 static int serial_find_by_fd(void *av, void *bv)
52 {
53 int a = *(int *)av;
54 Serial b = (Serial)bv;
55
56 if (a < b->fd)
57 return -1;
58 else if (a > b->fd)
59 return +1;
60 return 0;
61 }
62
63 static tree234 *serial_by_fd = NULL;
64
65 static int serial_select_result(int fd, int event);
66 static void serial_uxsel_setup(Serial serial);
67 static void serial_try_write(Serial serial);
68
69 static const char *serial_configure(Serial serial, Config *cfg)
70 {
71 struct termios options;
72 int bflag, bval;
73 const char *str;
74 char *msg;
75
76 if (serial->fd < 0)
77 return "Unable to reconfigure already-closed serial connection";
78
79 tcgetattr(serial->fd, &options);
80
81 /*
82 * Find the appropriate baud rate flag.
83 */
84 #define SETBAUD(x) (bflag = B ## x, bval = x)
85 #define CHECKBAUD(x) do { if (cfg->serspeed >= x) SETBAUD(x); } while (0)
86 SETBAUD(50);
87 #ifdef B75
88 CHECKBAUD(75);
89 #endif
90 #ifdef B110
91 CHECKBAUD(110);
92 #endif
93 #ifdef B134
94 CHECKBAUD(134);
95 #endif
96 #ifdef B150
97 CHECKBAUD(150);
98 #endif
99 #ifdef B200
100 CHECKBAUD(200);
101 #endif
102 #ifdef B300
103 CHECKBAUD(300);
104 #endif
105 #ifdef B600
106 CHECKBAUD(600);
107 #endif
108 #ifdef B1200
109 CHECKBAUD(1200);
110 #endif
111 #ifdef B1800
112 CHECKBAUD(1800);
113 #endif
114 #ifdef B2400
115 CHECKBAUD(2400);
116 #endif
117 #ifdef B4800
118 CHECKBAUD(4800);
119 #endif
120 #ifdef B9600
121 CHECKBAUD(9600);
122 #endif
123 #ifdef B19200
124 CHECKBAUD(19200);
125 #endif
126 #ifdef B38400
127 CHECKBAUD(38400);
128 #endif
129 #ifdef B57600
130 CHECKBAUD(57600);
131 #endif
132 #ifdef B76800
133 CHECKBAUD(76800);
134 #endif
135 #ifdef B115200
136 CHECKBAUD(115200);
137 #endif
138 #ifdef B230400
139 CHECKBAUD(230400);
140 #endif
141 #undef CHECKBAUD
142 #undef SETBAUD
143 cfsetispeed(&options, bflag);
144 cfsetospeed(&options, bflag);
145 msg = dupprintf("Configuring baud rate %d", bval);
146 logevent(serial->frontend, msg);
147 sfree(msg);
148
149 options.c_cflag &= ~CSIZE;
150 switch (cfg->serdatabits) {
151 case 5: options.c_cflag |= CS5; break;
152 case 6: options.c_cflag |= CS6; break;
153 case 7: options.c_cflag |= CS7; break;
154 case 8: options.c_cflag |= CS8; break;
155 default: return "Invalid number of data bits (need 5, 6, 7 or 8)";
156 }
157 msg = dupprintf("Configuring %d data bits", cfg->serdatabits);
158 logevent(serial->frontend, msg);
159 sfree(msg);
160
161 if (cfg->serstopbits >= 4) {
162 options.c_cflag |= CSTOPB;
163 } else {
164 options.c_cflag &= ~CSTOPB;
165 }
166 msg = dupprintf("Configuring %d stop bits",
167 (options.c_cflag & CSTOPB ? 2 : 1));
168 logevent(serial->frontend, msg);
169 sfree(msg);
170
171 options.c_cflag &= ~(IXON|IXOFF);
172 if (cfg->serflow == SER_FLOW_XONXOFF) {
173 options.c_cflag |= 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_oflag &= ~OPOST;
209 options.c_cc[VMIN] = 1;
210 options.c_cc[VTIME] = 0;
211
212 if (tcsetattr(serial->fd, TCSANOW, &options) < 0)
213 return "Unable to configure serial port";
214
215 return NULL;
216 }
217
218 /*
219 * Called to set up the serial connection.
220 *
221 * Returns an error message, or NULL on success.
222 *
223 * Also places the canonical host name into `realhost'. It must be
224 * freed by the caller.
225 */
226 static const char *serial_init(void *frontend_handle, void **backend_handle,
227 Config *cfg,
228 char *host, int port, char **realhost, int nodelay,
229 int keepalive)
230 {
231 Serial serial;
232 const char *err;
233
234 serial = snew(struct serial_backend_data);
235 *backend_handle = serial;
236
237 serial->frontend = frontend_handle;
238 serial->finished = FALSE;
239 serial->inbufsize = 0;
240 bufchain_init(&serial->output_data);
241
242 {
243 char *msg = dupprintf("Opening serial device %s", cfg->serline);
244 logevent(serial->frontend, msg);
245 }
246
247 serial->fd = open(cfg->serline, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
248 if (serial->fd < 0)
249 return "Unable to open serial port";
250
251 err = serial_configure(serial, cfg);
252 if (err)
253 return err;
254
255 *realhost = dupstr(cfg->serline);
256
257 if (!serial_by_fd)
258 serial_by_fd = newtree234(serial_compare_by_fd);
259 add234(serial_by_fd, serial);
260
261 serial_uxsel_setup(serial);
262
263 return NULL;
264 }
265
266 static void serial_close(Serial serial)
267 {
268 if (serial->fd >= 0) {
269 close(serial->fd);
270 serial->fd = -1;
271 }
272 }
273
274 static void serial_free(void *handle)
275 {
276 Serial serial = (Serial) handle;
277
278 serial_close(serial);
279
280 bufchain_clear(&serial->output_data);
281
282 sfree(serial);
283 }
284
285 static void serial_reconfig(void *handle, Config *cfg)
286 {
287 Serial serial = (Serial) handle;
288 const char *err;
289
290 err = serial_configure(serial, cfg);
291
292 /*
293 * FIXME: what should we do if err returns something?
294 */
295 }
296
297 static int serial_select_result(int fd, int event)
298 {
299 Serial serial;
300 char buf[4096];
301 int ret;
302 int finished = FALSE;
303
304 serial = find234(serial_by_fd, &fd, serial_find_by_fd);
305
306 if (!serial)
307 return 1; /* spurious event; keep going */
308
309 if (event == 1) {
310 ret = read(serial->fd, buf, sizeof(buf));
311
312 if (ret == 0) {
313 /*
314 * Shouldn't happen on a real serial port, but I'm open
315 * to the idea that there might be two-way devices we
316 * can treat _like_ serial ports which can return EOF.
317 */
318 finished = TRUE;
319 } else if (ret < 0) {
320 perror("read serial port");
321 exit(1);
322 } else if (ret > 0) {
323 serial->inbufsize = from_backend(serial->frontend, 0, buf, ret);
324 serial_uxsel_setup(serial); /* might acquire backlog and freeze */
325 }
326 } else if (event == 2) {
327 /*
328 * Attempt to send data down the pty.
329 */
330 serial_try_write(serial);
331 }
332
333 if (finished) {
334 serial_close(serial);
335
336 serial->finished = TRUE;
337
338 notify_remote_exit(serial->frontend);
339 }
340
341 return !finished;
342 }
343
344 static void serial_uxsel_setup(Serial serial)
345 {
346 int rwx = 0;
347
348 if (serial->inbufsize <= SERIAL_MAX_BACKLOG)
349 rwx |= 1;
350 if (bufchain_size(&serial->output_data))
351 rwx |= 2; /* might also want to write to it */
352 uxsel_set(serial->fd, rwx, serial_select_result);
353 }
354
355 static void serial_try_write(Serial serial)
356 {
357 void *data;
358 int len, ret;
359
360 assert(serial->fd >= 0);
361
362 while (bufchain_size(&serial->output_data) > 0) {
363 bufchain_prefix(&serial->output_data, &data, &len);
364 ret = write(serial->fd, data, len);
365
366 if (ret < 0 && (errno == EWOULDBLOCK)) {
367 /*
368 * We've sent all we can for the moment.
369 */
370 break;
371 }
372 if (ret < 0) {
373 perror("write serial port");
374 exit(1);
375 }
376 bufchain_consume(&serial->output_data, ret);
377 }
378
379 serial_uxsel_setup(serial);
380 }
381
382 /*
383 * Called to send data down the serial connection.
384 */
385 static int serial_send(void *handle, char *buf, int len)
386 {
387 Serial serial = (Serial) handle;
388
389 if (serial->fd < 0)
390 return 0;
391
392 bufchain_add(&serial->output_data, buf, len);
393 serial_try_write(serial);
394
395 return bufchain_size(&serial->output_data);
396 }
397
398 /*
399 * Called to query the current sendability status.
400 */
401 static int serial_sendbuffer(void *handle)
402 {
403 Serial serial = (Serial) handle;
404 return bufchain_size(&serial->output_data);
405 }
406
407 /*
408 * Called to set the size of the window
409 */
410 static void serial_size(void *handle, int width, int height)
411 {
412 /* Do nothing! */
413 return;
414 }
415
416 /*
417 * Send serial special codes.
418 */
419 static void serial_special(void *handle, Telnet_Special code)
420 {
421 /*
422 * FIXME: serial break? XON? XOFF?
423 */
424 return;
425 }
426
427 /*
428 * Return a list of the special codes that make sense in this
429 * protocol.
430 */
431 static const struct telnet_special *serial_get_specials(void *handle)
432 {
433 /*
434 * FIXME: serial break? XON? XOFF?
435 */
436 return NULL;
437 }
438
439 static int serial_connected(void *handle)
440 {
441 return 1; /* always connected */
442 }
443
444 static int serial_sendok(void *handle)
445 {
446 return 1;
447 }
448
449 static void serial_unthrottle(void *handle, int backlog)
450 {
451 Serial serial = (Serial) handle;
452 serial->inbufsize = backlog;
453 serial_uxsel_setup(serial);
454 }
455
456 static int serial_ldisc(void *handle, int option)
457 {
458 /*
459 * Local editing and local echo are off by default.
460 */
461 return 0;
462 }
463
464 static void serial_provide_ldisc(void *handle, void *ldisc)
465 {
466 /* This is a stub. */
467 }
468
469 static void serial_provide_logctx(void *handle, void *logctx)
470 {
471 /* This is a stub. */
472 }
473
474 static int serial_exitcode(void *handle)
475 {
476 Serial serial = (Serial) handle;
477 if (serial->fd >= 0)
478 return -1; /* still connected */
479 else
480 /* Exit codes are a meaningless concept with serial ports */
481 return INT_MAX;
482 }
483
484 /*
485 * cfg_info for Serial does nothing at all.
486 */
487 static int serial_cfg_info(void *handle)
488 {
489 return 0;
490 }
491
492 Backend serial_backend = {
493 serial_init,
494 serial_free,
495 serial_reconfig,
496 serial_send,
497 serial_sendbuffer,
498 serial_size,
499 serial_special,
500 serial_get_specials,
501 serial_connected,
502 serial_exitcode,
503 serial_sendok,
504 serial_ldisc,
505 serial_provide_ldisc,
506 serial_provide_logctx,
507 serial_unthrottle,
508 serial_cfg_info,
509 1
510 };