chiark / gitweb /
infra: Clean up project setup
[jog] / tx-serial-unix.c
1 /* -*-c-*-
2  *
3  * $Id: tx-serial-unix.c,v 1.3 2002/02/02 19:17:33 mdw Exp $
4  *
5  * Unix/POSIX serial transport
6  *
7  * (c) 2001 Mark Wooding
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Jog: Programming for a jogging machine.
13  *
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.
18  * 
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.
23  * 
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.
27  */
28
29 /*----- Header files ------------------------------------------------------*/
30
31 #ifdef HAVE_CONFIG_H
32 #  include "config.h"
33 #endif
34
35 #include <assert.h>
36 #include <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #include <sys/types.h>
42 #include <termios.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45
46 #include <mLib/darray.h>
47 #include <mLib/fdflags.h>
48 #include <mLib/sub.h>
49
50 #include "err.h"
51 #include "serial.h"
52 #include "txport.h"
53
54 #ifndef O_NOCTTY
55 #  define O_NOCTTY 0
56 #endif
57
58 /*----- Data structures ---------------------------------------------------*/
59
60 typedef struct txsu {
61   txport tx;                            /* Transport base */
62   struct txsu *next, **prev;            /* Chain of serial transports */
63   int fd;                               /* File descriptor */
64   serial_config sc;                     /* Internal serial config */
65   struct termios ta, old_ta;            /* External serial configs */
66 } txsu;
67
68 /*----- Static variables --------------------------------------------------*/
69
70 struct baudmap { unsigned long baud; unsigned long magic; };
71
72 static const struct baudmap baudmap[] = {
73 #ifdef B50
74   {     50,     B50 },
75 #endif
76 #ifdef B75
77   {     75,     B75 },
78 #endif
79 #ifdef B110
80   {    110,    B110 },
81 #endif
82 #ifdef B134
83   {    134,    B134 },
84 #endif
85 #ifdef B150
86   {    150,    B150 },
87 #endif
88 #ifdef B200
89   {    200,    B200 },
90 #endif
91 #ifdef B300
92   {    300,    B300 },
93 #endif
94 #ifdef B600
95   {    600,    B600 },
96 #endif
97 #ifdef B1200
98   {   1200,   B1200 },
99 #endif
100 #ifdef B1800
101   {   1800,   B1800 },
102 #endif
103 #ifdef B2400
104   {   2400,   B2400 },
105 #endif
106 #ifdef B4800
107   {   4800,   B4800 },
108 #endif
109 #ifdef B9600
110   {   9600,   B9600 },
111 #endif
112 #ifdef B19200
113   {  19200,  B19200 },
114 #endif
115 #ifdef B38400
116   {  38400,  B38400 },
117 #endif
118 #ifdef B57600
119   {  57600,  B57600 },
120 #endif
121 #ifdef B115200
122   { 115200, B115200 },
123 #endif
124 #ifdef B230400
125   { 230400, B230400 },
126 #endif
127   {      0,       0 }
128 };
129
130 static unsigned long csize[] = { CS5, CS6, CS7, CS8 };
131
132 static txsu *active = 0;
133
134 /*----- Main code ---------------------------------------------------------*/
135
136 /* --- @txsu_shutdown@ --- *
137  *
138  * Arguments:   ---
139  *
140  * Returns:     ---
141  *
142  * Use:         Restores terminal settings on exit.
143  */
144
145 void txsu_shutdown(void)
146 {
147   txsu *tx;
148
149   for (tx = active; tx; tx = tx->next)
150     tcsetattr(tx->fd, TCSAFLUSH, &tx->old_ta);
151 }
152
153 /* --- @setconfig@ --- *
154  *
155  * Arguments:   @txsu *tx@ = pointer to serial transport
156  *              @serial_config *sc@ = pointer to configuration to set
157  *
158  * Returns:     Zero if OK, nonzero on error.
159  *
160  * Use:         Updates the external configuration from an internal
161  *              representation.
162  */
163
164 static int setconfig(txsu *tx, serial_config *sc)
165 {
166   struct termios *ta = &tx->ta;
167   const struct baudmap *b;
168
169   for (b = baudmap; b->baud && b->baud != sc->baud; b++)
170     ;
171   if (!b->baud || 
172       sc->wordlen < 5 || sc->wordlen > 8 ||
173       sc->stopbits < 1 || sc->stopbits > 2) {
174     err_report(ERR_TXPORT, ERRTX_CONFIG, 0, "bad serial configuration");
175     return (-1);
176   }
177
178   ta->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL);
179   ta->c_oflag &= ~OPOST;
180   ta->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
181   ta->c_cc[VMIN] = 1;
182   ta->c_cc[VTIME] = 0;
183
184   cfsetospeed(ta, b->magic);
185   cfsetispeed(ta, b->magic);
186   ta->c_cflag = (ta->c_cflag & ~CSIZE) | csize[sc->wordlen - 5];
187   switch (sc->parity) {
188     case PARITY_NONE: ta->c_cflag &= ~PARENB; break;
189     case PARITY_ODD:  ta->c_cflag |= PARENB | PARODD; break;
190     case PARITY_EVEN: ta->c_cflag |= PARENB; ta->c_cflag &= ~PARODD; break;
191   }
192   switch (sc->stopbits) {
193     case 1: ta->c_cflag &= ~CSTOPB; break;
194     case 2: ta->c_cflag |= CSTOPB; break;
195   }
196
197   switch (sc->flow) {
198     case FLOW_NONE:
199       ta->c_cflag &= ~CRTSCTS;
200       ta->c_iflag &= ~(IXON | IXOFF);
201       break;
202     case FLOW_XONXOFF:
203       ta->c_cflag &= ~CRTSCTS;
204       ta->c_iflag |= IXON | IXOFF;
205       break;
206     case FLOW_RTSCTS:
207       ta->c_cflag |= CRTSCTS;
208       ta->c_iflag &= ~(IXON | IXOFF);
209       break;
210   }
211       
212   if (tcsetattr(tx->fd, TCSAFLUSH, ta)) {
213     err_report(ERR_TXPORT, ERRTX_CREATE, errno,
214                "couldn't set terminal attributes: %s", strerror(errno));
215     return (-1);
216   }
217
218   tx->sc = *sc;
219   return (0);
220 }
221
222 /* --- @txsu_create@ --- *
223  *
224  * Arguments:   @const char *file@ = filename for serial port
225  *
226  * Returns:     Pointer to created transport block.
227  *
228  * Use:         Creates a serial port transport.
229  */
230
231 txport *txsu_create(const char *file)
232 {
233   txsu *tx = CREATE(txsu);
234   serial_config sc = SERIAL_INIT;
235
236   if ((tx->fd = open(file, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) {
237     err_report(ERR_TXPORT, ERRTX_CREATE, errno,
238                "couldn't open device `%s': %s", file, strerror(errno));
239     goto fail_0;
240   }
241   if (fdflags(tx->fd, O_NONBLOCK, 0, 0, 0)) {
242     err_report(ERR_TXPORT, ERRTX_CREATE, errno,
243                "fcntl(clear O_NONBLOCK): %s", file, strerror(errno));
244     goto fail_1;
245   }
246
247   if (tcgetattr(tx->fd, &tx->old_ta)) {
248     err_report(ERR_TXPORT, ERRTX_CREATE, errno,
249                "couldn't get terminal attributes: %s", strerror(errno));
250     goto fail_1;
251   }
252   tx->ta = tx->old_ta;
253   if (setconfig(tx, &sc))
254     goto fail_1;
255
256   tx->next = active;
257   tx->prev = &active;
258   active = tx;
259   return (&tx->tx);
260
261   /* --- Tidy up because it all went horribly wrong --- */
262
263 fail_1:
264   close(tx->fd);
265 fail_0:
266   DESTROY(tx);
267   return (0);
268 }
269
270 /* --- @txsu_configure@ --- *
271  *
272  * Arguments:   @txport *txg@ = pointer to transport block
273  *              @const char *k@ = configuration keyword
274  *              @const char *v@ = value
275  *
276  * Returns:     Nonzero if handled, zero otherwise.
277  *
278  * Use:         Configures a serial port.
279  */
280
281 int txsu_configure(txport *txg, const char *k, const char *v)
282 {
283   txsu *tx = (txsu *)txg;
284   serial_config sc = tx->sc;
285
286   if (!v) {
287     err_report(ERR_TXPORT, ERRTX_CONFIG, 0,
288                "syntax error in serial config `%s'", k);
289     return (-1);
290   }
291   if (serial_parse(&sc, k, v)) {
292     err_report(ERR_TXPORT, ERRTX_CONFIG, 0,
293                "syntax error in serial config `%s=%s'", k, v);
294     return (-1);
295   }
296   if (setconfig(tx, &sc))
297     return (-1);
298   return (1);
299 }
300
301 /* --- @txsu_write@ --- *
302  *
303  * Arguments:   @txport *txg@ = pointer to transport context
304  *              @const void *p@ = pointer to buffer
305  *              @size_t sz@ = size of the buffer
306  *
307  * Returns:     Number of bytes written, or @-1@ on error.
308  *
309  * Use:         Writes data to a transport.
310  */
311
312 ssize_t txsu_write(txport *txg, const void *p, size_t sz)
313 {
314   txsu *tx = (txsu *)txg;
315
316   return (write(tx->fd, p, sz));
317 }
318
319 /* --- @txsu_fetch@ --- *
320  *
321  * Arguments:   @void *txv@ = pointer to transport context
322  *
323  * Returns:     Nothing of interest.
324  *
325  * Use:         Thread to fetch data from a serial port.
326  */
327
328 void *txsu_fetch(void *txv)
329 {
330   txsu *tx = txv;
331   unsigned char buf[BUFSIZ];
332   ssize_t n;
333   int e;
334
335   /* --- Read data while it arrives --- */
336
337   for (;;) {
338     n = read(tx->fd, buf, sizeof(buf));
339     if (n < 0) {
340       err_report(ERR_TXPORT, ERRTX_READ, errno,
341                  "error reading from serial port: %s", strerror(errno));
342       break;
343     }
344 #ifdef TERMINAL_EOF
345     if (!n) break;
346 #else
347     if (!n) continue;
348 #endif
349     if ((e = pthread_mutex_lock(&tx->tx.mx)) != 0) {
350       err_report(ERR_TXPORT, ERRTX_READ, e,
351                  "error locking mutex: %s", strerror(e));
352       break;
353     }
354     DA_ENSURE(&tx->tx.buf, n);
355     memcpy(DA(&tx->tx.buf) + DA_LEN(&tx->tx.buf), buf, n);
356     DA_EXTEND(&tx->tx.buf, n);
357     pthread_cond_signal(&tx->tx.cv);
358     pthread_mutex_unlock(&tx->tx.mx);
359   }
360
361   /* --- Deal with crapness --- */
362
363   e = pthread_mutex_lock(&tx->tx.mx);
364   tx->tx.s = TX_CLOSE;
365   pthread_cond_signal(&tx->tx.cv);
366   pthread_mutex_unlock(&tx->tx.mx);
367   return (0);
368 }
369
370 /* --- @txsu_destroy@ --- *
371  *
372  * Arguments:   @txport *txg@ = pointer to transport context
373  *
374  * Returns:     ---
375  *
376  * Use:         Destroys a serial port transport.
377  */
378
379 void txsu_destroy(txport *txg)
380 {
381   txsu *tx = (txsu *)txg;
382
383   tcsetattr(tx->fd, TCSAFLUSH, &tx->old_ta);
384   close(tx->fd);
385   *tx->prev = tx->next;
386   if (tx->next)
387     tx->next->prev = tx->prev;
388   DESTROY(tx);
389 }
390
391 /*----- That's all, folks -------------------------------------------------*/