3 * $Id: txport.c,v 1.1 2002/01/25 19:34:45 mdw Exp $
5 * Transport switch glue
7 * (c) 2001 Mark Wooding
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of Jog: Programming for a jogging machine.
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.
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.
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.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.1 2002/01/25 19:34:45 mdw
37 /*----- Header files ------------------------------------------------------*/
47 #include <sys/types.h>
52 #include <mLib/darray.h>
53 #include <mLib/dstr.h>
54 #include <mLib/lbuf.h>
56 #include <mLib/trace.h>
62 /*----- Global variables --------------------------------------------------*/
65 #include "tx-socket.h"
66 #include "tx-serial-unix.h"
67 txport_ops
*txlist
= TX_LIST
;
69 const char *txname
= 0;
70 const char *txfile
= 0;
71 const char *txconf
= 0;
73 /*----- Main code ---------------------------------------------------------*/
75 /* --- @newline@ --- *
77 * Arguments: @char *s@ = pointer to line
78 * @size_t len@ = length of line
79 * @void *txv@ = pointer to transport context
83 * Use: Adds a line to the list.
86 static void newline(char *s
, size_t len
, void *txv
)
94 l
->s
= xmalloc(len
+ 1);
95 memcpy(l
->s
, s
, len
+ 1);
99 l
->prev
= tx
->ll_tail
;
101 tx
->ll_tail
->next
= l
;
107 /* --- @tx_create@ --- *
109 * Arguments: @const char *name@ = name of transport to instantiate
110 * @const char *file@ = filename for transport
111 * @const char *config@ = config string
113 * Returns: A pointer to the transport context, or null on error.
115 * Use: Creates a new transport.
118 txport
*tx_create(const char *name
, const char *file
, const char *config
)
127 /* --- Look up the transport by name --- */
134 for (o
= txlist
; o
; o
= o
->next
) {
135 if (strncmp(name
, o
->name
, len
) == 0)
138 err_report(ERR_TXPORT
, ERRTX_BADTX
, 0, "unknown transport `%s'", name
);
141 /* --- Set up the transport block --- */
145 const struct txfile
*fv
;
146 for (fv
= o
->fv
; fv
->env
|| fv
->name
; fv
++) {
147 if (fv
->env
&& (file
= getenv(fv
->env
)) == 0)
152 if (file
&& fv
->name
)
162 if ((tx
= o
->create(file
, config
)) == 0)
168 if ((e
= pthread_mutex_init(&tx
->mx
, 0)) != 0) {
169 err_report(ERR_TXPORT
, ERRTX_CREATE
, e
,
170 "mutex creation failed: %s", strerror(e
));
173 if ((e
= pthread_cond_init(&tx
->cv
, 0)) != 0) {
174 err_report(ERR_TXPORT
, ERRTX_CREATE
, e
,
175 "condvar creation failed: %s", strerror(e
));
178 if ((e
= pthread_attr_init(&ta
)) != 0) {
179 err_report(ERR_TXPORT
, ERRTX_CREATE
, e
,
180 "thread attribute creation failed: %s", strerror(e
));
183 if ((e
= pthread_attr_setdetachstate(&ta
, PTHREAD_CREATE_DETACHED
)) ||
184 (e
= pthread_create(&tx
->tid
, &ta
, tx
->ops
->fetch
, tx
)) != 0) {
185 err_report(ERR_TXPORT
, ERRTX_CREATE
, e
,
186 "thread creation failed: %s", strerror(e
));
189 pthread_attr_destroy(&ta
);
190 lbuf_init(&tx
->lb
, newline
, tx
);
196 /* --- Something went wrong --- */
199 pthread_attr_destroy(&ta
);
201 pthread_cond_destroy(&tx
->cv
);
203 pthread_mutex_destroy(&tx
->mx
);
205 tx
->ops
->destroy(tx
);
211 /* --- @tx_write@ --- *
213 * Arguments: @txport *tx@ = pointer to transport context
214 * @const void *p@ = pointer to buffer to write
215 * @size_t sz@ = size of buffer
217 * Returns: Zero if OK, or @-1@ on error.
219 * Use: Writes some data to a transport.
222 int tx_write(txport
*tx
, const void *p
, size_t sz
)
224 if (tx
->ops
->write(tx
, p
, sz
) < 0) {
225 err_report(ERR_TXPORT
, ERRTX_WRITE
, errno
,
226 "error writing to transport: %s", strerror(errno
));
232 /* --- @tx_printf@ --- *
234 * Arguments: @txport *tx@ = pointer to transport context
235 * @const char *p@ = pointer to string to write
237 * Returns: The number of characters printed, or @EOF@ on error.
239 * Use: Writes a textual message to a transport.
242 int tx_vprintf(txport
*tx
, const char *p
, va_list *ap
)
247 dstr_vputf(&d
, p
, *ap
);
249 rc
= tx_write(tx
, d
.buf
, d
.len
);
254 int tx_printf(txport
*tx
, const char *p
, ...)
260 rc
= tx_vprintf(tx
, p
, &ap
);
265 /* --- @tx_read@, @tx_readx@ --- *
267 * Arguments: @txport *tx@ = pointer to transport context
268 * @unsigned long t@ = time to wait for data (ms)
269 * @int (*filter)(const char *s, void *p)@ = filtering function
270 * @void *p@ = pointer argument for filter
272 * Returns: A pointer to a line block, which must be freed using
275 * Use: Fetches a line from the buffer. Each line is passed to the
276 * filter function in oldest-to-newest order; the filter
277 * function returns nonzero to choose a line. If no suitable
278 * line is waiting in the raw buffer, the program blocks while
279 * more data is fetched, until the time limit @t@ is exceeded,
280 * in which case a null pointer is returned. A null filter
281 * function is equivalent to one which always selects its line.
284 txline
*tx_readx(txport
*tx
, unsigned long t
,
285 int (*filter
)(const char *s
, void *p
), void *p
)
287 txline
*l
, **ll
= &tx
->ll
;
289 struct timeval now
, tv
;
295 /* --- Get the time to wait until --- */
298 gettimeofday(&now
, 0);
299 tv_addl(&tv
, &now
, t
/ 1000, (t
% 1000) * 1000);
300 ts
.tv_sec
= tv
.tv_sec
;
301 ts
.tv_nsec
= tv
.tv_usec
* 1000;
304 /* --- Check for a matching line --- */
307 for (; *ll
; ll
= &l
->next
) {
309 if (!filter
|| filter(l
->s
, p
))
314 /* --- Lock the buffer --- *
316 * The following operations require a lock on the buffer, so we obtain that
321 if ((e
= pthread_mutex_lock(&tx
->mx
)) != 0) {
322 err_report(ERR_TXPORT
, ERRTX_READ
, e
,
323 "error locking mutex: %s", strerror(errno
));
329 /* --- Push more stuff through the line buffer --- */
332 if (DA_LEN(&tx
->buf
)) {
333 trace_block(1u, "incoming data", DA(&tx
->buf
), DA_LEN(&tx
->buf
));
334 lbuf_snarf(&tx
->lb
, DA(&tx
->buf
), DA_LEN(&tx
->buf
));
335 DA_SHRINK(&tx
->buf
, DA_LEN(&tx
->buf
));
339 /* --- If nothing else can arrive, give up --- */
341 if (tx
->s
== TX_CLOSE
) {
346 if (!t
|| tx
->s
== TX_CLOSED
)
348 gettimeofday(&now
, 0);
349 if (TV_CMP(&now
, >=, &tv
))
352 /* --- Wait for some more data to arrive --- */
355 e
= pthread_cond_wait(&tx
->cv
, &tx
->mx
);
357 e
= pthread_cond_timedwait(&tx
->cv
, &tx
->mx
, &ts
);
358 if (e
&& e
!= ETIMEDOUT
&& e
!= EINTR
) {
359 err_report(ERR_TXPORT
, ERRTX_READ
, e
,
360 "error waiting on condvar: %s", strerror(errno
));
365 /* --- Everything is finished --- */
369 pthread_mutex_unlock(&tx
->mx
);
375 txline
*tx_read(txport
*tx
, unsigned long t
)
377 return (tx_readx(tx
, t
, 0, 0));
380 /* --- @tx_freeline@ --- *
382 * Arguments: @txline *l@ = pointer to line
386 * Use: Frees a line block.
389 void tx_freeline(txline
*l
)
393 l
->next
->prev
= l
->prev
;
395 tx
->ll_tail
= l
->prev
;
397 l
->prev
->next
= l
->next
;
404 /* --- @tx_destroy@ --- *
406 * Arguments: @txport *tx@ = transport context
410 * Use: Destroys a transport.
413 void tx_destroy(txport
*tx
)
417 if (tx
->s
== TX_READY
) {
418 pthread_mutex_lock(&tx
->mx
);
419 if (tx
->s
== TX_READY
)
420 pthread_cancel(tx
->tid
);
421 pthread_mutex_unlock(&tx
->mx
);
423 pthread_mutex_destroy(&tx
->mx
);
424 pthread_cond_destroy(&tx
->cv
);
425 DA_DESTROY(&tx
->buf
);
426 lbuf_destroy(&tx
->lb
);
427 for (l
= tx
->ll
; l
; l
= ll
) {
432 tx
->ops
->destroy(tx
);
435 /*----- That's all, folks -------------------------------------------------*/