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