2 * Serial back end (Unix-specific).
18 #define SERIAL_MAX_BACKLOG 4096
20 typedef struct serial_backend_data {
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.
33 static int serial_compare_by_fd(void *av, void *bv)
35 Serial a = (Serial)av;
36 Serial b = (Serial)bv;
40 else if (a->fd > b->fd)
45 static int serial_find_by_fd(void *av, void *bv)
48 Serial b = (Serial)bv;
57 static tree234 *serial_by_fd = NULL;
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);
63 static const char *serial_configure(Serial serial, Conf *conf)
65 struct termios options;
66 int bflag, bval, speed, flow, parity;
71 return "Unable to reconfigure already-closed serial connection";
73 tcgetattr(serial->fd, &options);
76 * Find the appropriate baud rate flag.
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)
180 cfsetispeed(&options, bflag);
181 cfsetospeed(&options, bflag);
182 msg = dupprintf("Configuring baud rate %d", bval);
183 logevent(serial->frontend, msg);
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)";
194 msg = dupprintf("Configuring %d data bits",
195 conf_get_int(conf, CONF_serdatabits));
196 logevent(serial->frontend, msg);
199 if (conf_get_int(conf, CONF_serstopbits) >= 4) {
200 options.c_cflag |= CSTOPB;
202 options.c_cflag &= ~CSTOPB;
204 msg = dupprintf("Configuring %d stop bits",
205 (options.c_cflag & CSTOPB ? 2 : 1));
206 logevent(serial->frontend, msg);
209 options.c_iflag &= ~(IXON|IXOFF);
211 options.c_cflag &= ~CRTSCTS;
214 options.c_cflag &= ~CNEW_RTSCTS;
216 flow = conf_get_int(conf, CONF_serflow);
217 if (flow == SER_FLOW_XONXOFF) {
218 options.c_iflag |= IXON | IXOFF;
220 } else if (flow == SER_FLOW_RTSCTS) {
222 options.c_cflag |= CRTSCTS;
225 options.c_cflag |= CNEW_RTSCTS;
230 msg = dupprintf("Configuring %s flow control", str);
231 logevent(serial->frontend, msg);
235 parity = conf_get_int(conf, CONF_serparity);
236 if (parity == SER_PAR_ODD) {
237 options.c_cflag |= PARENB;
238 options.c_cflag |= PARODD;
240 } else if (parity == SER_PAR_EVEN) {
241 options.c_cflag |= PARENB;
242 options.c_cflag &= ~PARODD;
245 options.c_cflag &= ~PARENB;
248 msg = dupprintf("Configuring %s parity", str);
249 logevent(serial->frontend, msg);
252 options.c_cflag |= CLOCAL | CREAD;
253 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
254 options.c_iflag &= ~(ISTRIP | IGNCR | INLCR | ICRNL
259 options.c_oflag &= ~(OPOST
273 options.c_cc[VMIN] = 1;
274 options.c_cc[VTIME] = 0;
276 if (tcsetattr(serial->fd, TCSANOW, &options) < 0)
277 return "Unable to configure serial port";
283 * Called to set up the serial connection.
285 * Returns an error message, or NULL on success.
287 * Also places the canonical host name into `realhost'. It must be
288 * freed by the caller.
290 static const char *serial_init(void *frontend_handle, void **backend_handle,
292 char *host, int port, char **realhost, int nodelay,
299 serial = snew(struct serial_backend_data);
300 *backend_handle = serial;
302 serial->frontend = frontend_handle;
303 serial->finished = FALSE;
304 serial->inbufsize = 0;
305 bufchain_init(&serial->output_data);
307 line = conf_get_str(conf, CONF_serline);
309 char *msg = dupprintf("Opening serial device %s", line);
310 logevent(serial->frontend, msg);
313 serial->fd = open(line, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
315 return "Unable to open serial port";
319 err = serial_configure(serial, conf);
323 *realhost = dupstr(line);
326 serial_by_fd = newtree234(serial_compare_by_fd);
327 add234(serial_by_fd, serial);
329 serial_uxsel_setup(serial);
332 * Specials are always available.
334 update_specials_menu(serial->frontend);
339 static void serial_close(Serial serial)
341 if (serial->fd >= 0) {
347 static void serial_free(void *handle)
349 Serial serial = (Serial) handle;
351 serial_close(serial);
353 bufchain_clear(&serial->output_data);
358 static void serial_reconfig(void *handle, Conf *conf)
360 Serial serial = (Serial) handle;
363 * FIXME: what should we do if this returns an error?
365 serial_configure(serial, conf);
368 static int serial_select_result(int fd, int event)
373 int finished = FALSE;
375 serial = find234(serial_by_fd, &fd, serial_find_by_fd);
378 return 1; /* spurious event; keep going */
381 ret = read(serial->fd, buf, sizeof(buf));
385 * Shouldn't happen on a real serial port, but I'm open
386 * to the idea that there might be two-way devices we
387 * can treat _like_ serial ports which can return EOF.
390 } else if (ret < 0) {
393 return 1; /* spurious */
396 if (errno == EWOULDBLOCK)
397 return 1; /* spurious */
399 perror("read serial port");
401 } else if (ret > 0) {
402 serial->inbufsize = from_backend(serial->frontend, 0, buf, ret);
403 serial_uxsel_setup(serial); /* might acquire backlog and freeze */
405 } else if (event == 2) {
407 * Attempt to send data down the pty.
409 serial_try_write(serial);
413 serial_close(serial);
415 serial->finished = TRUE;
417 notify_remote_exit(serial->frontend);
423 static void serial_uxsel_setup(Serial serial)
427 if (serial->inbufsize <= SERIAL_MAX_BACKLOG)
429 if (bufchain_size(&serial->output_data))
430 rwx |= 2; /* might also want to write to it */
431 uxsel_set(serial->fd, rwx, serial_select_result);
434 static void serial_try_write(Serial serial)
439 assert(serial->fd >= 0);
441 while (bufchain_size(&serial->output_data) > 0) {
442 bufchain_prefix(&serial->output_data, &data, &len);
443 ret = write(serial->fd, data, len);
445 if (ret < 0 && (errno == EWOULDBLOCK)) {
447 * We've sent all we can for the moment.
452 perror("write serial port");
455 bufchain_consume(&serial->output_data, ret);
458 serial_uxsel_setup(serial);
462 * Called to send data down the serial connection.
464 static int serial_send(void *handle, char *buf, int len)
466 Serial serial = (Serial) handle;
471 bufchain_add(&serial->output_data, buf, len);
472 serial_try_write(serial);
474 return bufchain_size(&serial->output_data);
478 * Called to query the current sendability status.
480 static int serial_sendbuffer(void *handle)
482 Serial serial = (Serial) handle;
483 return bufchain_size(&serial->output_data);
487 * Called to set the size of the window
489 static void serial_size(void *handle, int width, int height)
496 * Send serial special codes.
498 static void serial_special(void *handle, Telnet_Special code)
500 Serial serial = (Serial) handle;
502 if (serial->fd >= 0 && code == TS_BRK) {
503 tcsendbreak(serial->fd, 0);
504 logevent(serial->frontend, "Sending serial break at user request");
511 * Return a list of the special codes that make sense in this
514 static const struct telnet_special *serial_get_specials(void *handle)
516 static const struct telnet_special specials[] = {
523 static int serial_connected(void *handle)
525 return 1; /* always connected */
528 static int serial_sendok(void *handle)
533 static void serial_unthrottle(void *handle, int backlog)
535 Serial serial = (Serial) handle;
536 serial->inbufsize = backlog;
537 serial_uxsel_setup(serial);
540 static int serial_ldisc(void *handle, int option)
543 * Local editing and local echo are off by default.
548 static void serial_provide_ldisc(void *handle, void *ldisc)
550 /* This is a stub. */
553 static void serial_provide_logctx(void *handle, void *logctx)
555 /* This is a stub. */
558 static int serial_exitcode(void *handle)
560 Serial serial = (Serial) handle;
562 return -1; /* still connected */
564 /* Exit codes are a meaningless concept with serial ports */
569 * cfg_info for Serial does nothing at all.
571 static int serial_cfg_info(void *handle)
576 Backend serial_backend = {
589 serial_provide_ldisc,
590 serial_provide_logctx,