3 * $Id: tx-serial-unix.c,v 1.3 2002/02/02 19:17:33 mdw Exp $
5 * Unix/POSIX serial transport
7 * (c) 2001 Mark Wooding
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of Jog: Programming for a jogging machine.
14 * Jog is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * Jog is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with Jog; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 /*----- Revision history --------------------------------------------------*
31 * $Log: tx-serial-unix.c,v $
32 * Revision 1.3 2002/02/02 19:17:33 mdw
35 * Revision 1.2 2002/01/30 09:24:24 mdw
36 * Restructure for new transport configuration interface.
38 * Revision 1.1 2002/01/25 19:34:45 mdw
43 /*----- Header files ------------------------------------------------------*/
55 #include <sys/types.h>
60 #include <mLib/darray.h>
61 #include <mLib/fdflags.h>
72 /*----- Data structures ---------------------------------------------------*/
75 txport tx
; /* Transport base */
76 struct txsu
*next
, **prev
; /* Chain of serial transports */
77 int fd
; /* File descriptor */
78 serial_config sc
; /* Internal serial config */
79 struct termios ta
, old_ta
; /* External serial configs */
82 /*----- Static variables --------------------------------------------------*/
84 struct baudmap
{ unsigned long baud
; unsigned long magic
; };
86 static const struct baudmap baudmap
[] = {
144 static unsigned long csize
[] = { CS5
, CS6
, CS7
, CS8
};
146 static txsu
*active
= 0;
148 /*----- Main code ---------------------------------------------------------*/
150 /* --- @txsu_shutdown@ --- *
156 * Use: Restores terminal settings on exit.
159 void txsu_shutdown(void)
163 for (tx
= active
; tx
; tx
= tx
->next
)
164 tcsetattr(tx
->fd
, TCSAFLUSH
, &tx
->old_ta
);
167 /* --- @setconfig@ --- *
169 * Arguments: @txsu *tx@ = pointer to serial transport
170 * @serial_config *sc@ = pointer to configuration to set
172 * Returns: Zero if OK, nonzero on error.
174 * Use: Updates the external configuration from an internal
178 static int setconfig(txsu
*tx
, serial_config
*sc
)
180 struct termios
*ta
= &tx
->ta
;
181 const struct baudmap
*b
;
183 for (b
= baudmap
; b
->baud
&& b
->baud
!= sc
->baud
; b
++)
186 sc
->wordlen
< 5 || sc
->wordlen
> 8 ||
187 sc
->stopbits
< 1 || sc
->stopbits
> 2) {
188 err_report(ERR_TXPORT
, ERRTX_CONFIG
, 0, "bad serial configuration");
192 ta
->c_iflag
&= ~(IGNBRK
|BRKINT
|PARMRK
|ISTRIP
|INLCR
|IGNCR
|ICRNL
);
193 ta
->c_oflag
&= ~OPOST
;
194 ta
->c_lflag
&= ~(ECHO
|ECHONL
|ICANON
|ISIG
|IEXTEN
);
198 cfsetospeed(ta
, b
->magic
);
199 cfsetispeed(ta
, b
->magic
);
200 ta
->c_cflag
= (ta
->c_cflag
& ~CSIZE
) | csize
[sc
->wordlen
- 5];
201 switch (sc
->parity
) {
202 case PARITY_NONE
: ta
->c_cflag
&= ~PARENB
; break;
203 case PARITY_ODD
: ta
->c_cflag
|= PARENB
| PARODD
; break;
204 case PARITY_EVEN
: ta
->c_cflag
|= PARENB
; ta
->c_cflag
&= ~PARODD
; break;
206 switch (sc
->stopbits
) {
207 case 1: ta
->c_cflag
&= ~CSTOPB
; break;
208 case 2: ta
->c_cflag
|= CSTOPB
; break;
213 ta
->c_cflag
&= ~CRTSCTS
;
214 ta
->c_iflag
&= ~(IXON
| IXOFF
);
217 ta
->c_cflag
&= ~CRTSCTS
;
218 ta
->c_iflag
|= IXON
| IXOFF
;
221 ta
->c_cflag
|= CRTSCTS
;
222 ta
->c_iflag
&= ~(IXON
| IXOFF
);
226 if (tcsetattr(tx
->fd
, TCSAFLUSH
, ta
)) {
227 err_report(ERR_TXPORT
, ERRTX_CREATE
, errno
,
228 "couldn't set terminal attributes: %s", strerror(errno
));
236 /* --- @txsu_create@ --- *
238 * Arguments: @const char *file@ = filename for serial port
240 * Returns: Pointer to created transport block.
242 * Use: Creates a serial port transport.
245 txport
*txsu_create(const char *file
)
247 txsu
*tx
= CREATE(txsu
);
248 serial_config sc
= SERIAL_INIT
;
250 if ((tx
->fd
= open(file
, O_RDWR
| O_NOCTTY
| O_NONBLOCK
)) < 0) {
251 err_report(ERR_TXPORT
, ERRTX_CREATE
, errno
,
252 "couldn't open device `%s': %s", file
, strerror(errno
));
255 if (fdflags(tx
->fd
, O_NONBLOCK
, 0, 0, 0)) {
256 err_report(ERR_TXPORT
, ERRTX_CREATE
, errno
,
257 "fcntl(clear O_NONBLOCK): %s", file
, strerror(errno
));
261 if (tcgetattr(tx
->fd
, &tx
->old_ta
)) {
262 err_report(ERR_TXPORT
, ERRTX_CREATE
, errno
,
263 "couldn't get terminal attributes: %s", strerror(errno
));
267 if (setconfig(tx
, &sc
))
275 /* --- Tidy up because it all went horribly wrong --- */
284 /* --- @txsu_configure@ --- *
286 * Arguments: @txport *txg@ = pointer to transport block
287 * @const char *k@ = configuration keyword
288 * @const char *v@ = value
290 * Returns: Nonzero if handled, zero otherwise.
292 * Use: Configures a serial port.
295 int txsu_configure(txport
*txg
, const char *k
, const char *v
)
297 txsu
*tx
= (txsu
*)txg
;
298 serial_config sc
= tx
->sc
;
301 err_report(ERR_TXPORT
, ERRTX_CONFIG
, 0,
302 "syntax error in serial config `%s'", k
);
305 if (serial_parse(&sc
, k
, v
)) {
306 err_report(ERR_TXPORT
, ERRTX_CONFIG
, 0,
307 "syntax error in serial config `%s=%s'", k
, v
);
310 if (setconfig(tx
, &sc
))
315 /* --- @txsu_write@ --- *
317 * Arguments: @txport *txg@ = pointer to transport context
318 * @const void *p@ = pointer to buffer
319 * @size_t sz@ = size of the buffer
321 * Returns: Number of bytes written, or @-1@ on error.
323 * Use: Writes data to a transport.
326 ssize_t
txsu_write(txport
*txg
, const void *p
, size_t sz
)
328 txsu
*tx
= (txsu
*)txg
;
330 return (write(tx
->fd
, p
, sz
));
333 /* --- @txsu_fetch@ --- *
335 * Arguments: @void *txv@ = pointer to transport context
337 * Returns: Nothing of interest.
339 * Use: Thread to fetch data from a serial port.
342 void *txsu_fetch(void *txv
)
345 unsigned char buf
[BUFSIZ
];
349 /* --- Read data while it arrives --- */
352 n
= read(tx
->fd
, buf
, sizeof(buf
));
354 err_report(ERR_TXPORT
, ERRTX_READ
, errno
,
355 "error reading from serial port: %s", strerror(errno
));
363 if ((e
= pthread_mutex_lock(&tx
->tx
.mx
)) != 0) {
364 err_report(ERR_TXPORT
, ERRTX_READ
, e
,
365 "error locking mutex: %s", strerror(e
));
368 DA_ENSURE(&tx
->tx
.buf
, n
);
369 memcpy(DA(&tx
->tx
.buf
) + DA_LEN(&tx
->tx
.buf
), buf
, n
);
370 DA_EXTEND(&tx
->tx
.buf
, n
);
371 pthread_cond_signal(&tx
->tx
.cv
);
372 pthread_mutex_unlock(&tx
->tx
.mx
);
375 /* --- Deal with crapness --- */
377 e
= pthread_mutex_lock(&tx
->tx
.mx
);
379 pthread_cond_signal(&tx
->tx
.cv
);
380 pthread_mutex_unlock(&tx
->tx
.mx
);
384 /* --- @txsu_destroy@ --- *
386 * Arguments: @txport *txg@ = pointer to transport context
390 * Use: Destroys a serial port transport.
393 void txsu_destroy(txport
*txg
)
395 txsu
*tx
= (txsu
*)txg
;
397 tcsetattr(tx
->fd
, TCSAFLUSH
, &tx
->old_ta
);
399 *tx
->prev
= tx
->next
;
401 tx
->next
->prev
= tx
->prev
;
405 /*----- That's all, folks -------------------------------------------------*/