chiark / gitweb /
Follow new transport configuration interface. Add parameters for flow
[jog] / tx-serial-unix.c
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
64 typedef 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
73 struct baudmap { unsigned long baud; unsigned long magic; };
74
75 static 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
133 static unsigned long csize[] = { CS5, CS6, CS7, CS8 };
134
135 static 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
148 void 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
166 txport *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
248 fail_1:
249   close(fd);
250 fail_0:
251   return (0);
252
253 conferr:
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
270 ssize_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
286 void *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
337 void 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 -------------------------------------------------*/