New audio subsystem.
[jog] / tx-serial-unix.c
CommitLineData
2ec1e693 1/* -*-c-*-
2 *
09d225f8 3 * $Id: tx-serial-unix.c,v 1.3 2002/02/02 19:17:33 mdw Exp $
2ec1e693 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 $
09d225f8 32 * Revision 1.3 2002/02/02 19:17:33 mdw
33 * Missing header.
34 *
f43b7748 35 * Revision 1.2 2002/01/30 09:24:24 mdw
36 * Restructure for new transport configuration interface.
37 *
2ec1e693 38 * Revision 1.1 2002/01/25 19:34:45 mdw
39 * Initial revision
40 *
41 */
42
43/*----- Header files ------------------------------------------------------*/
44
09d225f8 45#ifdef HAVE_CONFIG_H
46# include "config.h"
47#endif
48
2ec1e693 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
74typedef struct txsu {
75 txport tx; /* Transport base */
76 struct txsu *next, **prev; /* Chain of serial transports */
77 int fd; /* File descriptor */
f43b7748 78 serial_config sc; /* Internal serial config */
79 struct termios ta, old_ta; /* External serial configs */
2ec1e693 80} txsu;
81
82/*----- Static variables --------------------------------------------------*/
83
84struct baudmap { unsigned long baud; unsigned long magic; };
85
f43b7748 86static const struct baudmap baudmap[] = {
2ec1e693 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
144static unsigned long csize[] = { CS5, CS6, CS7, CS8 };
145
146static 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
159void 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
f43b7748 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
178static 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
2ec1e693 236/* --- @txsu_create@ --- *
237 *
238 * Arguments: @const char *file@ = filename for serial port
2ec1e693 239 *
240 * Returns: Pointer to created transport block.
241 *
242 * Use: Creates a serial port transport.
243 */
244
f43b7748 245txport *txsu_create(const char *file)
2ec1e693 246{
f43b7748 247 txsu *tx = CREATE(txsu);
2ec1e693 248 serial_config sc = SERIAL_INIT;
2ec1e693 249
f43b7748 250 if ((tx->fd = open(file, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) {
2ec1e693 251 err_report(ERR_TXPORT, ERRTX_CREATE, errno,
252 "couldn't open device `%s': %s", file, strerror(errno));
253 goto fail_0;
254 }
f43b7748 255 if (fdflags(tx->fd, O_NONBLOCK, 0, 0, 0)) {
2ec1e693 256 err_report(ERR_TXPORT, ERRTX_CREATE, errno,
257 "fcntl(clear O_NONBLOCK): %s", file, strerror(errno));
258 goto fail_1;
f43b7748 259 }
2ec1e693 260
f43b7748 261 if (tcgetattr(tx->fd, &tx->old_ta)) {
2ec1e693 262 err_report(ERR_TXPORT, ERRTX_CREATE, errno,
263 "couldn't get terminal attributes: %s", strerror(errno));
264 goto fail_1;
265 }
f43b7748 266 tx->ta = tx->old_ta;
267 if (setconfig(tx, &sc))
2ec1e693 268 goto fail_1;
2ec1e693 269
2ec1e693 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
277fail_1:
f43b7748 278 close(tx->fd);
2ec1e693 279fail_0:
f43b7748 280 DESTROY(tx);
2ec1e693 281 return (0);
f43b7748 282}
2ec1e693 283
f43b7748 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
295int 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);
2ec1e693 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
326ssize_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
342void *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
393void 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 -------------------------------------------------*/