All the new audio stuff.
[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 -------------------------------------------------*/