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
);
314 serial
->fd
= open(line
, O_RDWR
| O_NOCTTY
| O_NDELAY
| O_NONBLOCK
);
316 return "Unable to open serial port";
320 err
= serial_configure(serial
, conf
);
324 *realhost
= dupstr(line
);
327 serial_by_fd
= newtree234(serial_compare_by_fd
);
328 add234(serial_by_fd
, serial
);
330 serial_uxsel_setup(serial
);
333 * Specials are always available.
335 update_specials_menu(serial
->frontend
);
340 static void serial_close(Serial serial
)
342 if (serial
->fd
>= 0) {
348 static void serial_free(void *handle
)
350 Serial serial
= (Serial
) handle
;
352 serial_close(serial
);
354 bufchain_clear(&serial
->output_data
);
359 static void serial_reconfig(void *handle
, Conf
*conf
)
361 Serial serial
= (Serial
) handle
;
364 * FIXME: what should we do if this returns an error?
366 serial_configure(serial
, conf
);
369 static int serial_select_result(int fd
, int event
)
374 int finished
= FALSE
;
376 serial
= find234(serial_by_fd
, &fd
, serial_find_by_fd
);
379 return 1; /* spurious event; keep going */
382 ret
= read(serial
->fd
, buf
, sizeof(buf
));
386 * Shouldn't happen on a real serial port, but I'm open
387 * to the idea that there might be two-way devices we
388 * can treat _like_ serial ports which can return EOF.
391 } else if (ret
< 0) {
394 return 1; /* spurious */
397 if (errno
== EWOULDBLOCK
)
398 return 1; /* spurious */
400 perror("read serial port");
402 } else if (ret
> 0) {
403 serial
->inbufsize
= from_backend(serial
->frontend
, 0, buf
, ret
);
404 serial_uxsel_setup(serial
); /* might acquire backlog and freeze */
406 } else if (event
== 2) {
408 * Attempt to send data down the pty.
410 serial_try_write(serial
);
414 serial_close(serial
);
416 serial
->finished
= TRUE
;
418 notify_remote_exit(serial
->frontend
);
424 static void serial_uxsel_setup(Serial serial
)
428 if (serial
->inbufsize
<= SERIAL_MAX_BACKLOG
)
430 if (bufchain_size(&serial
->output_data
))
431 rwx
|= 2; /* might also want to write to it */
432 uxsel_set(serial
->fd
, rwx
, serial_select_result
);
435 static void serial_try_write(Serial serial
)
440 assert(serial
->fd
>= 0);
442 while (bufchain_size(&serial
->output_data
) > 0) {
443 bufchain_prefix(&serial
->output_data
, &data
, &len
);
444 ret
= write(serial
->fd
, data
, len
);
446 if (ret
< 0 && (errno
== EWOULDBLOCK
)) {
448 * We've sent all we can for the moment.
453 perror("write serial port");
456 bufchain_consume(&serial
->output_data
, ret
);
459 serial_uxsel_setup(serial
);
463 * Called to send data down the serial connection.
465 static int serial_send(void *handle
, char *buf
, int len
)
467 Serial serial
= (Serial
) handle
;
472 bufchain_add(&serial
->output_data
, buf
, len
);
473 serial_try_write(serial
);
475 return bufchain_size(&serial
->output_data
);
479 * Called to query the current sendability status.
481 static int serial_sendbuffer(void *handle
)
483 Serial serial
= (Serial
) handle
;
484 return bufchain_size(&serial
->output_data
);
488 * Called to set the size of the window
490 static void serial_size(void *handle
, int width
, int height
)
497 * Send serial special codes.
499 static void serial_special(void *handle
, Telnet_Special code
)
501 Serial serial
= (Serial
) handle
;
503 if (serial
->fd
>= 0 && code
== TS_BRK
) {
504 tcsendbreak(serial
->fd
, 0);
505 logevent(serial
->frontend
, "Sending serial break at user request");
512 * Return a list of the special codes that make sense in this
515 static const struct telnet_special
*serial_get_specials(void *handle
)
517 static const struct telnet_special specials
[] = {
524 static int serial_connected(void *handle
)
526 return 1; /* always connected */
529 static int serial_sendok(void *handle
)
534 static void serial_unthrottle(void *handle
, int backlog
)
536 Serial serial
= (Serial
) handle
;
537 serial
->inbufsize
= backlog
;
538 serial_uxsel_setup(serial
);
541 static int serial_ldisc(void *handle
, int option
)
544 * Local editing and local echo are off by default.
549 static void serial_provide_ldisc(void *handle
, void *ldisc
)
551 /* This is a stub. */
554 static void serial_provide_logctx(void *handle
, void *logctx
)
556 /* This is a stub. */
559 static int serial_exitcode(void *handle
)
561 Serial serial
= (Serial
) handle
;
563 return -1; /* still connected */
565 /* Exit codes are a meaningless concept with serial ports */
570 * cfg_info for Serial does nothing at all.
572 static int serial_cfg_info(void *handle
)
577 Backend serial_backend
= {
590 serial_provide_ldisc
,
591 serial_provide_logctx
,