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