2 * Serial back end (Unix-specific).
24 #define SERIAL_MAX_BACKLOG 4096
26 typedef struct serial_backend_data
{
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.
39 static int serial_compare_by_fd(void *av
, void *bv
)
41 Serial a
= (Serial
)av
;
42 Serial b
= (Serial
)bv
;
46 else if (a
->fd
> b
->fd
)
51 static int serial_find_by_fd(void *av
, void *bv
)
54 Serial b
= (Serial
)bv
;
63 static tree234
*serial_by_fd
= NULL
;
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
);
69 static const char *serial_configure(Serial serial
, Config
*cfg
)
71 struct termios options
;
77 return "Unable to reconfigure already-closed serial connection";
79 tcgetattr(serial
->fd
, &options
);
82 * Find the appropriate baud rate flag.
84 #define SETBAUD(x) (bflag = B ## x, bval = x)
85 #define CHECKBAUD(x) do { if (cfg->serspeed >= x) SETBAUD(x); } while (0)
143 cfsetispeed(&options
, bflag
);
144 cfsetospeed(&options
, bflag
);
145 msg
= dupprintf("Configuring baud rate %d", bval
);
146 logevent(serial
->frontend
, msg
);
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)";
157 msg
= dupprintf("Configuring %d data bits", cfg
->serdatabits
);
158 logevent(serial
->frontend
, msg
);
161 if (cfg
->serstopbits
>= 4) {
162 options
.c_cflag
|= CSTOPB
;
164 options
.c_cflag
&= ~CSTOPB
;
166 msg
= dupprintf("Configuring %d stop bits",
167 (options
.c_cflag
& CSTOPB ?
2 : 1));
168 logevent(serial
->frontend
, msg
);
171 options
.c_cflag
&= ~(IXON
|IXOFF
);
172 if (cfg
->serflow
== SER_FLOW_XONXOFF
) {
173 options
.c_cflag
|= 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_oflag
&= ~OPOST
;
209 options
.c_cc
[VMIN
] = 1;
210 options
.c_cc
[VTIME
] = 0;
212 if (tcsetattr(serial
->fd
, TCSANOW
, &options
) < 0)
213 return "Unable to configure serial port";
219 * Called to set up the serial connection.
221 * Returns an error message, or NULL on success.
223 * Also places the canonical host name into `realhost'. It must be
224 * freed by the caller.
226 static const char *serial_init(void *frontend_handle
, void **backend_handle
,
228 char *host
, int port
, char **realhost
, int nodelay
,
234 serial
= snew(struct serial_backend_data
);
235 *backend_handle
= serial
;
237 serial
->frontend
= frontend_handle
;
238 serial
->finished
= FALSE
;
239 serial
->inbufsize
= 0;
240 bufchain_init(&serial
->output_data
);
243 char *msg
= dupprintf("Opening serial device %s", cfg
->serline
);
244 logevent(serial
->frontend
, msg
);
247 serial
->fd
= open(cfg
->serline
, O_RDWR
| O_NOCTTY
| O_NDELAY
| O_NONBLOCK
);
249 return "Unable to open serial port";
251 err
= serial_configure(serial
, cfg
);
255 *realhost
= dupstr(cfg
->serline
);
258 serial_by_fd
= newtree234(serial_compare_by_fd
);
259 add234(serial_by_fd
, serial
);
261 serial_uxsel_setup(serial
);
266 static void serial_close(Serial serial
)
268 if (serial
->fd
>= 0) {
274 static void serial_free(void *handle
)
276 Serial serial
= (Serial
) handle
;
278 serial_close(serial
);
280 bufchain_clear(&serial
->output_data
);
285 static void serial_reconfig(void *handle
, Config
*cfg
)
287 Serial serial
= (Serial
) handle
;
290 err
= serial_configure(serial
, cfg
);
293 * FIXME: what should we do if err returns something?
297 static int serial_select_result(int fd
, int event
)
302 int finished
= FALSE
;
304 serial
= find234(serial_by_fd
, &fd
, serial_find_by_fd
);
307 return 1; /* spurious event; keep going */
310 ret
= read(serial
->fd
, buf
, sizeof(buf
));
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.
319 } else if (ret
< 0) {
320 perror("read serial port");
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 */
326 } else if (event
== 2) {
328 * Attempt to send data down the pty.
330 serial_try_write(serial
);
334 serial_close(serial
);
336 serial
->finished
= TRUE
;
338 notify_remote_exit(serial
->frontend
);
344 static void serial_uxsel_setup(Serial serial
)
348 if (serial
->inbufsize
<= SERIAL_MAX_BACKLOG
)
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
);
355 static void serial_try_write(Serial serial
)
360 assert(serial
->fd
>= 0);
362 while (bufchain_size(&serial
->output_data
) > 0) {
363 bufchain_prefix(&serial
->output_data
, &data
, &len
);
364 ret
= write(serial
->fd
, data
, len
);
366 if (ret
< 0 && (errno
== EWOULDBLOCK
)) {
368 * We've sent all we can for the moment.
373 perror("write serial port");
376 bufchain_consume(&serial
->output_data
, ret
);
379 serial_uxsel_setup(serial
);
383 * Called to send data down the serial connection.
385 static int serial_send(void *handle
, char *buf
, int len
)
387 Serial serial
= (Serial
) handle
;
392 bufchain_add(&serial
->output_data
, buf
, len
);
393 serial_try_write(serial
);
395 return bufchain_size(&serial
->output_data
);
399 * Called to query the current sendability status.
401 static int serial_sendbuffer(void *handle
)
403 Serial serial
= (Serial
) handle
;
404 return bufchain_size(&serial
->output_data
);
408 * Called to set the size of the window
410 static void serial_size(void *handle
, int width
, int height
)
417 * Send serial special codes.
419 static void serial_special(void *handle
, Telnet_Special code
)
422 * FIXME: serial break? XON? XOFF?
428 * Return a list of the special codes that make sense in this
431 static const struct telnet_special
*serial_get_specials(void *handle
)
434 * FIXME: serial break? XON? XOFF?
439 static int serial_connected(void *handle
)
441 return 1; /* always connected */
444 static int serial_sendok(void *handle
)
449 static void serial_unthrottle(void *handle
, int backlog
)
451 Serial serial
= (Serial
) handle
;
452 serial
->inbufsize
= backlog
;
453 serial_uxsel_setup(serial
);
456 static int serial_ldisc(void *handle
, int option
)
459 * Local editing and local echo are off by default.
464 static void serial_provide_ldisc(void *handle
, void *ldisc
)
466 /* This is a stub. */
469 static void serial_provide_logctx(void *handle
, void *logctx
)
471 /* This is a stub. */
474 static int serial_exitcode(void *handle
)
476 Serial serial
= (Serial
) handle
;
478 return -1; /* still connected */
480 /* Exit codes are a meaningless concept with serial ports */
485 * cfg_info for Serial does nothing at all.
487 static int serial_cfg_info(void *handle
)
492 Backend serial_backend
= {
505 serial_provide_ldisc
,
506 serial_provide_logctx
,