3 * $Id: tx-serial-unix.c,v 1.1 2002/01/25 19:34:45 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.1 2002/01/25 19:34:45 mdw
37 /*----- Header files ------------------------------------------------------*/
45 #include <sys/types.h>
50 #include <mLib/darray.h>
51 #include <mLib/fdflags.h>
62 /*----- Data structures ---------------------------------------------------*/
65 txport tx
; /* Transport base */
66 struct txsu
*next
, **prev
; /* Chain of serial transports */
67 int fd
; /* File descriptor */
68 struct termios old_ta
; /* Old terminal settings */
71 /*----- Static variables --------------------------------------------------*/
73 struct baudmap
{ unsigned long baud
; unsigned long magic
; };
75 static struct baudmap baudmap
[] = {
133 static unsigned long csize
[] = { CS5
, CS6
, CS7
, CS8
};
135 static txsu
*active
= 0;
137 /*----- Main code ---------------------------------------------------------*/
139 /* --- @txsu_shutdown@ --- *
145 * Use: Restores terminal settings on exit.
148 void txsu_shutdown(void)
152 for (tx
= active
; tx
; tx
= tx
->next
)
153 tcsetattr(tx
->fd
, TCSAFLUSH
, &tx
->old_ta
);
156 /* --- @txsu_create@ --- *
158 * Arguments: @const char *file@ = filename for serial port
159 * @const char *config@ = configuration string
161 * Returns: Pointer to created transport block.
163 * Use: Creates a serial port transport.
166 txport
*txsu_create(const char *file
, const char *config
)
169 serial_config sc
= SERIAL_INIT
;
170 struct termios ta
, old_ta
;
174 /* --- Parse the configuration and check it --- */
176 if (config
&& *config
&& serial_parse(config
, &sc
))
179 for (b
= baudmap
; b
->baud
&& b
->baud
!= sc
.baud
; b
++)
182 sc
.wordlen
< 5 || sc
.wordlen
> 8 ||
183 sc
.stopbits
< 1 || sc
.stopbits
> 2)
186 /* --- Open the serial port and fetch attributes --- */
188 if ((fd
= open(file
, O_RDWR
| O_NOCTTY
| O_NONBLOCK
)) < 0) {
189 err_report(ERR_TXPORT
, ERRTX_CREATE
, errno
,
190 "couldn't open device `%s': %s", file
, strerror(errno
));
193 if (fdflags(fd
, O_NONBLOCK
, 0, 0, 0)) {
194 err_report(ERR_TXPORT
, ERRTX_CREATE
, errno
,
195 "fcntl(clear O_NONBLOCK): %s", file
, strerror(errno
));
199 if (tcgetattr(fd
, &ta
)) {
200 err_report(ERR_TXPORT
, ERRTX_CREATE
, errno
,
201 "couldn't get terminal attributes: %s", strerror(errno
));
206 /* --- Fix the attributes --- */
208 ta
.c_iflag
&= ~(IGNBRK
|BRKINT
|PARMRK
|ISTRIP
|INLCR
|IGNCR
|ICRNL
|IXON
);
209 ta
.c_oflag
&= ~OPOST
;
210 ta
.c_lflag
&= ~(ECHO
|ECHONL
|ICANON
|ISIG
|IEXTEN
);
211 ta
.c_cflag
&= ~(CRTSCTS
);
215 cfsetospeed(&ta
, b
->magic
);
216 cfsetispeed(&ta
, b
->magic
);
217 ta
.c_cflag
= (ta
.c_cflag
& ~CSIZE
) | csize
[sc
.wordlen
- 5];
219 case PARITY_NONE
: ta
.c_cflag
&= ~PARENB
; break;
220 case PARITY_ODD
: ta
.c_cflag
|= PARENB
| PARODD
; break;
221 case PARITY_EVEN
: ta
.c_cflag
|= PARENB
; ta
.c_cflag
&= ~PARODD
; break;
223 switch (sc
.stopbits
) {
224 case 1: ta
.c_cflag
&= ~CSTOPB
; break;
225 case 2: ta
.c_cflag
|= CSTOPB
; break;
228 /* --- Set attributes --- */
230 if (tcsetattr(fd
, TCSAFLUSH
, &ta
)) {
231 err_report(ERR_TXPORT
, ERRTX_CREATE
, errno
,
232 "couldn't set terminal attributes: %s", strerror(errno
));
246 /* --- Tidy up because it all went horribly wrong --- */
254 err_report(ERR_TXPORT
, ERRTX_CONFIG
, 0,
255 "bad configuration for serial port transport");
259 /* --- @txsu_write@ --- *
261 * Arguments: @txport *txg@ = pointer to transport context
262 * @const void *p@ = pointer to buffer
263 * @size_t sz@ = size of the buffer
265 * Returns: Number of bytes written, or @-1@ on error.
267 * Use: Writes data to a transport.
270 ssize_t
txsu_write(txport
*txg
, const void *p
, size_t sz
)
272 txsu
*tx
= (txsu
*)txg
;
274 return (write(tx
->fd
, p
, sz
));
277 /* --- @txsu_fetch@ --- *
279 * Arguments: @void *txv@ = pointer to transport context
281 * Returns: Nothing of interest.
283 * Use: Thread to fetch data from a serial port.
286 void *txsu_fetch(void *txv
)
289 unsigned char buf
[BUFSIZ
];
293 /* --- Read data while it arrives --- */
296 n
= read(tx
->fd
, buf
, sizeof(buf
));
298 err_report(ERR_TXPORT
, ERRTX_READ
, errno
,
299 "error reading from serial port: %s", strerror(errno
));
307 if ((e
= pthread_mutex_lock(&tx
->tx
.mx
)) != 0) {
308 err_report(ERR_TXPORT
, ERRTX_READ
, e
,
309 "error locking mutex: %s", strerror(e
));
312 DA_ENSURE(&tx
->tx
.buf
, n
);
313 memcpy(DA(&tx
->tx
.buf
) + DA_LEN(&tx
->tx
.buf
), buf
, n
);
314 DA_EXTEND(&tx
->tx
.buf
, n
);
315 pthread_cond_signal(&tx
->tx
.cv
);
316 pthread_mutex_unlock(&tx
->tx
.mx
);
319 /* --- Deal with crapness --- */
321 e
= pthread_mutex_lock(&tx
->tx
.mx
);
323 pthread_cond_signal(&tx
->tx
.cv
);
324 pthread_mutex_unlock(&tx
->tx
.mx
);
328 /* --- @txsu_destroy@ --- *
330 * Arguments: @txport *txg@ = pointer to transport context
334 * Use: Destroys a serial port transport.
337 void txsu_destroy(txport
*txg
)
339 txsu
*tx
= (txsu
*)txg
;
341 tcsetattr(tx
->fd
, TCSAFLUSH
, &tx
->old_ta
);
343 *tx
->prev
= tx
->next
;
345 tx
->next
->prev
= tx
->prev
;
349 /*----- That's all, folks -------------------------------------------------*/