infra: Clean up project setup
[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
2ec1e693 29/*----- Header files ------------------------------------------------------*/
30
09d225f8 31#ifdef HAVE_CONFIG_H
32# include "config.h"
33#endif
34
2ec1e693 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
60typedef struct txsu {
61 txport tx; /* Transport base */
62 struct txsu *next, **prev; /* Chain of serial transports */
63 int fd; /* File descriptor */
f43b7748 64 serial_config sc; /* Internal serial config */
65 struct termios ta, old_ta; /* External serial configs */
2ec1e693 66} txsu;
67
68/*----- Static variables --------------------------------------------------*/
69
70struct baudmap { unsigned long baud; unsigned long magic; };
71
f43b7748 72static const struct baudmap baudmap[] = {
2ec1e693 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
130static unsigned long csize[] = { CS5, CS6, CS7, CS8 };
131
132static 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
145void 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
f43b7748 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
164static 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
2ec1e693 222/* --- @txsu_create@ --- *
223 *
224 * Arguments: @const char *file@ = filename for serial port
2ec1e693 225 *
226 * Returns: Pointer to created transport block.
227 *
228 * Use: Creates a serial port transport.
229 */
230
f43b7748 231txport *txsu_create(const char *file)
2ec1e693 232{
f43b7748 233 txsu *tx = CREATE(txsu);
2ec1e693 234 serial_config sc = SERIAL_INIT;
2ec1e693 235
f43b7748 236 if ((tx->fd = open(file, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) {
2ec1e693 237 err_report(ERR_TXPORT, ERRTX_CREATE, errno,
238 "couldn't open device `%s': %s", file, strerror(errno));
239 goto fail_0;
240 }
f43b7748 241 if (fdflags(tx->fd, O_NONBLOCK, 0, 0, 0)) {
2ec1e693 242 err_report(ERR_TXPORT, ERRTX_CREATE, errno,
243 "fcntl(clear O_NONBLOCK): %s", file, strerror(errno));
244 goto fail_1;
f43b7748 245 }
2ec1e693 246
f43b7748 247 if (tcgetattr(tx->fd, &tx->old_ta)) {
2ec1e693 248 err_report(ERR_TXPORT, ERRTX_CREATE, errno,
249 "couldn't get terminal attributes: %s", strerror(errno));
250 goto fail_1;
251 }
f43b7748 252 tx->ta = tx->old_ta;
253 if (setconfig(tx, &sc))
2ec1e693 254 goto fail_1;
2ec1e693 255
2ec1e693 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
263fail_1:
f43b7748 264 close(tx->fd);
2ec1e693 265fail_0:
f43b7748 266 DESTROY(tx);
2ec1e693 267 return (0);
f43b7748 268}
2ec1e693 269
f43b7748 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
281int 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);
2ec1e693 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
312ssize_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
328void *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
379void 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 -------------------------------------------------*/