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