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, Config *cfg)
65
struct termios options;
71
return "Unable to reconfigure already-closed serial connection";
73
tcgetattr(serial->fd, &options);
76
* Find the appropriate baud rate flag.
78
#define SETBAUD(x) (bflag = B ## x, bval = x)
79
#define CHECKBAUD(x) do { if (cfg->serspeed >= x) SETBAUD(x); } while (0)
137
cfsetispeed(&options, bflag);
138
cfsetospeed(&options, bflag);
139
msg = dupprintf("Configuring baud rate %d", bval);
140
logevent(serial->frontend, msg);
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)";
151
msg = dupprintf("Configuring %d data bits", cfg->serdatabits);
152
logevent(serial->frontend, msg);
155
if (cfg->serstopbits >= 4) {
156
options.c_cflag |= CSTOPB;
158
options.c_cflag &= ~CSTOPB;
160
msg = dupprintf("Configuring %d stop bits",
161
(options.c_cflag & CSTOPB ? 2 : 1));
162
logevent(serial->frontend, msg);
165
options.c_iflag &= ~(IXON|IXOFF);
167
options.c_cflag &= ~CRTSCTS;
170
options.c_cflag &= ~CNEW_RTSCTS;
172
if (cfg->serflow == SER_FLOW_XONXOFF) {
173
options.c_iflag |= IXON | IXOFF;
175
} else if (cfg->serflow == SER_FLOW_RTSCTS) {
177
options.c_cflag |= CRTSCTS;
180
options.c_cflag |= CNEW_RTSCTS;
185
msg = dupprintf("Configuring %s flow control", str);
186
logevent(serial->frontend, msg);
190
if (cfg->serparity == SER_PAR_ODD) {
191
options.c_cflag |= PARENB;
192
options.c_cflag |= PARODD;
194
} else if (cfg->serparity == SER_PAR_EVEN) {
195
options.c_cflag |= PARENB;
196
options.c_cflag &= ~PARODD;
199
options.c_cflag &= ~PARENB;
202
msg = dupprintf("Configuring %s parity", str);
203
logevent(serial->frontend, msg);
206
options.c_cflag |= CLOCAL | CREAD;
207
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
208
options.c_iflag &= ~(ISTRIP | IGNCR | INLCR | ICRNL
213
options.c_oflag &= ~(OPOST
227
options.c_cc[VMIN] = 1;
228
options.c_cc[VTIME] = 0;
230
if (tcsetattr(serial->fd, TCSANOW, &options) < 0)
231
return "Unable to configure serial port";
237
* Called to set up the serial connection.
239
* Returns an error message, or NULL on success.
241
* Also places the canonical host name into `realhost'. It must be
242
* freed by the caller.
244
static const char *serial_init(void *frontend_handle, void **backend_handle,
246
char *host, int port, char **realhost, int nodelay,
252
serial = snew(struct serial_backend_data);
253
*backend_handle = serial;
255
serial->frontend = frontend_handle;
256
serial->finished = FALSE;
257
serial->inbufsize = 0;
258
bufchain_init(&serial->output_data);
261
char *msg = dupprintf("Opening serial device %s", cfg->serline);
262
logevent(serial->frontend, msg);
265
serial->fd = open(cfg->serline, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
267
return "Unable to open serial port";
271
err = serial_configure(serial, cfg);
275
*realhost = dupstr(cfg->serline);
278
serial_by_fd = newtree234(serial_compare_by_fd);
279
add234(serial_by_fd, serial);
281
serial_uxsel_setup(serial);
284
* Specials are always available.
286
update_specials_menu(serial->frontend);
291
static void serial_close(Serial serial)
293
if (serial->fd >= 0) {
299
static void serial_free(void *handle)
301
Serial serial = (Serial) handle;
303
serial_close(serial);
305
bufchain_clear(&serial->output_data);
310
static void serial_reconfig(void *handle, Config *cfg)
312
Serial serial = (Serial) handle;
315
err = serial_configure(serial, cfg);
318
* FIXME: what should we do if err returns something?
322
static int serial_select_result(int fd, int event)
327
int finished = FALSE;
329
serial = find234(serial_by_fd, &fd, serial_find_by_fd);
332
return 1; /* spurious event; keep going */
335
ret = read(serial->fd, buf, sizeof(buf));
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.
344
} else if (ret < 0) {
345
perror("read serial port");
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 */
351
} else if (event == 2) {
353
* Attempt to send data down the pty.
355
serial_try_write(serial);
359
serial_close(serial);
361
serial->finished = TRUE;
363
notify_remote_exit(serial->frontend);
369
static void serial_uxsel_setup(Serial serial)
373
if (serial->inbufsize <= SERIAL_MAX_BACKLOG)
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);
380
static void serial_try_write(Serial serial)
385
assert(serial->fd >= 0);
387
while (bufchain_size(&serial->output_data) > 0) {
388
bufchain_prefix(&serial->output_data, &data, &len);
389
ret = write(serial->fd, data, len);
391
if (ret < 0 && (errno == EWOULDBLOCK)) {
393
* We've sent all we can for the moment.
398
perror("write serial port");
401
bufchain_consume(&serial->output_data, ret);
404
serial_uxsel_setup(serial);
408
* Called to send data down the serial connection.
410
static int serial_send(void *handle, char *buf, int len)
412
Serial serial = (Serial) handle;
417
bufchain_add(&serial->output_data, buf, len);
418
serial_try_write(serial);
420
return bufchain_size(&serial->output_data);
424
* Called to query the current sendability status.
426
static int serial_sendbuffer(void *handle)
428
Serial serial = (Serial) handle;
429
return bufchain_size(&serial->output_data);
433
* Called to set the size of the window
435
static void serial_size(void *handle, int width, int height)
442
* Send serial special codes.
444
static void serial_special(void *handle, Telnet_Special code)
446
Serial serial = (Serial) handle;
448
if (serial->fd >= 0 && code == TS_BRK) {
449
tcsendbreak(serial->fd, 0);
450
logevent(serial->frontend, "Sending serial break at user request");
457
* Return a list of the special codes that make sense in this
460
static const struct telnet_special *serial_get_specials(void *handle)
462
static const struct telnet_special specials[] = {
469
static int serial_connected(void *handle)
471
return 1; /* always connected */
474
static int serial_sendok(void *handle)
479
static void serial_unthrottle(void *handle, int backlog)
481
Serial serial = (Serial) handle;
482
serial->inbufsize = backlog;
483
serial_uxsel_setup(serial);
486
static int serial_ldisc(void *handle, int option)
489
* Local editing and local echo are off by default.
494
static void serial_provide_ldisc(void *handle, void *ldisc)
496
/* This is a stub. */
499
static void serial_provide_logctx(void *handle, void *logctx)
501
/* This is a stub. */
504
static int serial_exitcode(void *handle)
506
Serial serial = (Serial) handle;
508
return -1; /* still connected */
510
/* Exit codes are a meaningless concept with serial ports */
515
* cfg_info for Serial does nothing at all.
517
static int serial_cfg_info(void *handle)
522
Backend serial_backend = {
535
serial_provide_ldisc,
536
serial_provide_logctx,