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 -------------------------------------------------*/