2 * Serial back end (Unix-specific).
\r
13 #include <termios.h>
\r
16 #include "tree234.h"
\r
18 #define SERIAL_MAX_BACKLOG 4096
\r
20 typedef struct serial_backend_data {
\r
25 bufchain output_data;
\r
29 * We store our serial backends in a tree sorted by fd, so that
\r
30 * when we get an uxsel notification we know which backend instance
\r
31 * is the owner of the serial port that caused it.
\r
33 static int serial_compare_by_fd(void *av, void *bv)
\r
35 Serial a = (Serial)av;
\r
36 Serial b = (Serial)bv;
\r
40 else if (a->fd > b->fd)
\r
45 static int serial_find_by_fd(void *av, void *bv)
\r
48 Serial b = (Serial)bv;
\r
57 static tree234 *serial_by_fd = NULL;
\r
59 static int serial_select_result(int fd, int event);
\r
60 static void serial_uxsel_setup(Serial serial);
\r
61 static void serial_try_write(Serial serial);
\r
63 static const char *serial_configure(Serial serial, Config *cfg)
\r
65 struct termios options;
\r
71 return "Unable to reconfigure already-closed serial connection";
\r
73 tcgetattr(serial->fd, &options);
\r
76 * Find the appropriate baud rate flag.
\r
78 #define SETBAUD(x) (bflag = B ## x, bval = x)
\r
79 #define CHECKBAUD(x) do { if (cfg->serspeed >= x) SETBAUD(x); } while (0)
\r
154 CHECKBAUD(1000000);
\r
157 CHECKBAUD(1152000);
\r
160 CHECKBAUD(1500000);
\r
163 CHECKBAUD(2000000);
\r
166 CHECKBAUD(2500000);
\r
169 CHECKBAUD(3000000);
\r
172 CHECKBAUD(3500000);
\r
175 CHECKBAUD(4000000);
\r
179 cfsetispeed(&options, bflag);
\r
180 cfsetospeed(&options, bflag);
\r
181 msg = dupprintf("Configuring baud rate %d", bval);
\r
182 logevent(serial->frontend, msg);
\r
185 options.c_cflag &= ~CSIZE;
\r
186 switch (cfg->serdatabits) {
\r
187 case 5: options.c_cflag |= CS5; break;
\r
188 case 6: options.c_cflag |= CS6; break;
\r
189 case 7: options.c_cflag |= CS7; break;
\r
190 case 8: options.c_cflag |= CS8; break;
\r
191 default: return "Invalid number of data bits (need 5, 6, 7 or 8)";
\r
193 msg = dupprintf("Configuring %d data bits", cfg->serdatabits);
\r
194 logevent(serial->frontend, msg);
\r
197 if (cfg->serstopbits >= 4) {
\r
198 options.c_cflag |= CSTOPB;
\r
200 options.c_cflag &= ~CSTOPB;
\r
202 msg = dupprintf("Configuring %d stop bits",
\r
203 (options.c_cflag & CSTOPB ? 2 : 1));
\r
204 logevent(serial->frontend, msg);
\r
207 options.c_iflag &= ~(IXON|IXOFF);
\r
209 options.c_cflag &= ~CRTSCTS;
\r
212 options.c_cflag &= ~CNEW_RTSCTS;
\r
214 if (cfg->serflow == SER_FLOW_XONXOFF) {
\r
215 options.c_iflag |= IXON | IXOFF;
\r
217 } else if (cfg->serflow == SER_FLOW_RTSCTS) {
\r
219 options.c_cflag |= CRTSCTS;
\r
222 options.c_cflag |= CNEW_RTSCTS;
\r
227 msg = dupprintf("Configuring %s flow control", str);
\r
228 logevent(serial->frontend, msg);
\r
232 if (cfg->serparity == SER_PAR_ODD) {
\r
233 options.c_cflag |= PARENB;
\r
234 options.c_cflag |= PARODD;
\r
236 } else if (cfg->serparity == SER_PAR_EVEN) {
\r
237 options.c_cflag |= PARENB;
\r
238 options.c_cflag &= ~PARODD;
\r
241 options.c_cflag &= ~PARENB;
\r
244 msg = dupprintf("Configuring %s parity", str);
\r
245 logevent(serial->frontend, msg);
\r
248 options.c_cflag |= CLOCAL | CREAD;
\r
249 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
\r
250 options.c_iflag &= ~(ISTRIP | IGNCR | INLCR | ICRNL
\r
255 options.c_oflag &= ~(OPOST
\r
269 options.c_cc[VMIN] = 1;
\r
270 options.c_cc[VTIME] = 0;
\r
272 if (tcsetattr(serial->fd, TCSANOW, &options) < 0)
\r
273 return "Unable to configure serial port";
\r
279 * Called to set up the serial connection.
\r
281 * Returns an error message, or NULL on success.
\r
283 * Also places the canonical host name into `realhost'. It must be
\r
284 * freed by the caller.
\r
286 static const char *serial_init(void *frontend_handle, void **backend_handle,
\r
288 char *host, int port, char **realhost, int nodelay,
\r
294 serial = snew(struct serial_backend_data);
\r
295 *backend_handle = serial;
\r
297 serial->frontend = frontend_handle;
\r
298 serial->finished = FALSE;
\r
299 serial->inbufsize = 0;
\r
300 bufchain_init(&serial->output_data);
\r
303 char *msg = dupprintf("Opening serial device %s", cfg->serline);
\r
304 logevent(serial->frontend, msg);
\r
307 serial->fd = open(cfg->serline, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
\r
308 if (serial->fd < 0)
\r
309 return "Unable to open serial port";
\r
311 cloexec(serial->fd);
\r
313 err = serial_configure(serial, cfg);
\r
317 *realhost = dupstr(cfg->serline);
\r
320 serial_by_fd = newtree234(serial_compare_by_fd);
\r
321 add234(serial_by_fd, serial);
\r
323 serial_uxsel_setup(serial);
\r
326 * Specials are always available.
\r
328 update_specials_menu(serial->frontend);
\r
333 static void serial_close(Serial serial)
\r
335 if (serial->fd >= 0) {
\r
341 static void serial_free(void *handle)
\r
343 Serial serial = (Serial) handle;
\r
345 serial_close(serial);
\r
347 bufchain_clear(&serial->output_data);
\r
352 static void serial_reconfig(void *handle, Config *cfg)
\r
354 Serial serial = (Serial) handle;
\r
357 * FIXME: what should we do if this returns an error?
\r
359 serial_configure(serial, cfg);
\r
362 static int serial_select_result(int fd, int event)
\r
367 int finished = FALSE;
\r
369 serial = find234(serial_by_fd, &fd, serial_find_by_fd);
\r
372 return 1; /* spurious event; keep going */
\r
375 ret = read(serial->fd, buf, sizeof(buf));
\r
379 * Shouldn't happen on a real serial port, but I'm open
\r
380 * to the idea that there might be two-way devices we
\r
381 * can treat _like_ serial ports which can return EOF.
\r
384 } else if (ret < 0) {
\r
386 if (errno == EAGAIN)
\r
387 return 1; /* spurious */
\r
390 if (errno == EWOULDBLOCK)
\r
391 return 1; /* spurious */
\r
393 perror("read serial port");
\r
395 } else if (ret > 0) {
\r
396 serial->inbufsize = from_backend(serial->frontend, 0, buf, ret);
\r
397 serial_uxsel_setup(serial); /* might acquire backlog and freeze */
\r
399 } else if (event == 2) {
\r
401 * Attempt to send data down the pty.
\r
403 serial_try_write(serial);
\r
407 serial_close(serial);
\r
409 serial->finished = TRUE;
\r
411 notify_remote_exit(serial->frontend);
\r
417 static void serial_uxsel_setup(Serial serial)
\r
421 if (serial->inbufsize <= SERIAL_MAX_BACKLOG)
\r
423 if (bufchain_size(&serial->output_data))
\r
424 rwx |= 2; /* might also want to write to it */
\r
425 uxsel_set(serial->fd, rwx, serial_select_result);
\r
428 static void serial_try_write(Serial serial)
\r
433 assert(serial->fd >= 0);
\r
435 while (bufchain_size(&serial->output_data) > 0) {
\r
436 bufchain_prefix(&serial->output_data, &data, &len);
\r
437 ret = write(serial->fd, data, len);
\r
439 if (ret < 0 && (errno == EWOULDBLOCK)) {
\r
441 * We've sent all we can for the moment.
\r
446 perror("write serial port");
\r
449 bufchain_consume(&serial->output_data, ret);
\r
452 serial_uxsel_setup(serial);
\r
456 * Called to send data down the serial connection.
\r
458 static int serial_send(void *handle, char *buf, int len)
\r
460 Serial serial = (Serial) handle;
\r
462 if (serial->fd < 0)
\r
465 bufchain_add(&serial->output_data, buf, len);
\r
466 serial_try_write(serial);
\r
468 return bufchain_size(&serial->output_data);
\r
472 * Called to query the current sendability status.
\r
474 static int serial_sendbuffer(void *handle)
\r
476 Serial serial = (Serial) handle;
\r
477 return bufchain_size(&serial->output_data);
\r
481 * Called to set the size of the window
\r
483 static void serial_size(void *handle, int width, int height)
\r
490 * Send serial special codes.
\r
492 static void serial_special(void *handle, Telnet_Special code)
\r
494 Serial serial = (Serial) handle;
\r
496 if (serial->fd >= 0 && code == TS_BRK) {
\r
497 tcsendbreak(serial->fd, 0);
\r
498 logevent(serial->frontend, "Sending serial break at user request");
\r
505 * Return a list of the special codes that make sense in this
\r
508 static const struct telnet_special *serial_get_specials(void *handle)
\r
510 static const struct telnet_special specials[] = {
\r
512 {NULL, TS_EXITMENU}
\r
517 static int serial_connected(void *handle)
\r
519 return 1; /* always connected */
\r
522 static int serial_sendok(void *handle)
\r
527 static void serial_unthrottle(void *handle, int backlog)
\r
529 Serial serial = (Serial) handle;
\r
530 serial->inbufsize = backlog;
\r
531 serial_uxsel_setup(serial);
\r
534 static int serial_ldisc(void *handle, int option)
\r
537 * Local editing and local echo are off by default.
\r
542 static void serial_provide_ldisc(void *handle, void *ldisc)
\r
544 /* This is a stub. */
\r
547 static void serial_provide_logctx(void *handle, void *logctx)
\r
549 /* This is a stub. */
\r
552 static int serial_exitcode(void *handle)
\r
554 Serial serial = (Serial) handle;
\r
555 if (serial->fd >= 0)
\r
556 return -1; /* still connected */
\r
558 /* Exit codes are a meaningless concept with serial ports */
\r
563 * cfg_info for Serial does nothing at all.
\r
565 static int serial_cfg_info(void *handle)
\r
570 Backend serial_backend = {
\r
578 serial_get_specials,
\r
583 serial_provide_ldisc,
\r
584 serial_provide_logctx,
\r