|
|
1.1 root 1: /* $Id: posix-serial.c,v 1.7 2003/05/16 21:48:09 fredette Exp $ */
2:
3: /* host/posix/serial.c - implementation of serial ports on a POSIX system: */
4:
5: /*
6: * Copyright (c) 2003 Matt Fredette
7: * All rights reserved.
8: *
9: * Redistribution and use in source and binary forms, with or without
10: * modification, are permitted provided that the following conditions
11: * are met:
12: * 1. Redistributions of source code must retain the above copyright
13: * notice, this list of conditions and the following disclaimer.
14: * 2. Redistributions in binary form must reproduce the above copyright
15: * notice, this list of conditions and the following disclaimer in the
16: * documentation and/or other materials provided with the distribution.
17: * 3. All advertising materials mentioning features or use of this software
18: * must display the following acknowledgement:
19: * This product includes software developed by Matt Fredette.
20: * 4. The name of the author may not be used to endorse or promote products
21: * derived from this software without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33: * POSSIBILITY OF SUCH DAMAGE.
34: */
35:
36: #include <tme/common.h>
37: _TME_RCSID("$Id: posix-serial.c,v 1.7 2003/05/16 21:48:09 fredette Exp $");
38:
39: /* includes: */
40: #include <tme/generic/serial.h>
41: #include <tme/threads.h>
42: #include <unistd.h>
43: #include <fcntl.h>
44: #include <stdio.h>
45: #include <termios.h>
46: #include <sys/types.h>
47: #include <sys/time.h>
48: #include <sys/ioctl.h>
49:
50: /* macros: */
51: #define TME_POSIX_SERIAL_BUFFER_SIZE (4096)
52:
53: /* structures: */
54: struct tme_posix_serial {
55:
56: /* our mutex: */
57: tme_mutex_t tme_posix_serial_mutex;
58:
59: /* backpointer to our element: */
60: struct tme_element *tme_posix_serial_element;
61:
62: /* our connection: */
63: struct tme_serial_connection *tme_posix_serial_connection;
64:
65: /* our writer thread condition: */
66: tme_cond_t tme_posix_serial_cond_writer;
67:
68: /* this is nonzero iff callouts are running: */
69: int tme_posix_serial_callouts_running;
70:
71: /* our input file descriptor: */
72: int tme_posix_serial_fd_in;
73:
74: /* our output file descriptor: */
75: int tme_posix_serial_fd_out;
76:
77: /* if we're emulating break: */
78: int tme_posix_serial_emulate_break;
79:
80: /* our current control inputs: */
81: unsigned int tme_posix_serial_ctrl_callin;
82:
83: /* the number of control output cycles to assert BREAK: */
84: int tme_posix_serial_ctrl_callout_break;
85:
86: /* our current control outputs: */
87: unsigned int tme_posix_serial_ctrl_callout;
88:
89: /* the last control outputs we successfully called out: */
90: unsigned int tme_posix_serial_ctrl_callout_last;
91:
92: /* our input and output buffers: */
93: struct tme_serial_buffer tme_posix_serial_buffer_in;
94: struct tme_serial_buffer tme_posix_serial_buffer_out;
95:
96: /* our input scanner state: */
97: int tme_posix_serial_input_scanner_state;
98: };
99:
100: /* the serial callout function. it must be called with the mutex locked: */
101: static void
102: _tme_posix_serial_callout(struct tme_posix_serial *serial)
103: {
104: struct tme_serial_connection *conn_serial;
105: unsigned int ctrl;
106: tme_uint8_t buffer_input[1024];
107: unsigned int buffer_input_size;
108: tme_serial_data_flags_t data_flags;
109: int rc;
110: int again;
111:
112: /* if this function is already running in another thread, return
113: now. the other thread will do our work: */
114: if (serial->tme_posix_serial_callouts_running) {
115: return;
116: }
117:
118: /* callouts are now running: */
119: serial->tme_posix_serial_callouts_running = TRUE;
120:
121: /* loop running callouts until there is nothing to do: */
122: for (again = TRUE; again;) {
123: again = FALSE;
124:
125: /* if we're connected: */
126: conn_serial = serial->tme_posix_serial_connection;
127: if (conn_serial != NULL) {
128:
129: /* if we need to notify our connection of new ctrl bits: */
130: ctrl = serial->tme_posix_serial_ctrl_callout;
131: if (ctrl != serial->tme_posix_serial_ctrl_callout_last) {
132:
133: /* unlock the mutex: */
134: tme_mutex_unlock(&serial->tme_posix_serial_mutex);
135:
136: /* try to do the notify: */
137: rc = (*conn_serial->tme_serial_connection_ctrl)(conn_serial, ctrl);
138:
139: /* lock the mutex: */
140: tme_mutex_lock(&serial->tme_posix_serial_mutex);
141:
142: /* if the notify was successful, note what we sent, and allow
143: the outermost loop to run again: */
144: if (rc == TME_OK) {
145: serial->tme_posix_serial_ctrl_callout_last = ctrl;
146: again = TRUE;
147: }
148: }
149:
150: /* if our connection is readable, and our output buffer isn't full,
151: read the connection: */
152: if ((serial->tme_posix_serial_ctrl_callin & TME_SERIAL_CTRL_OK_READ)
153: && !tme_serial_buffer_is_full(&serial->tme_posix_serial_buffer_out)) {
154:
155: /* get the minimum of the free space in the output buffer and
156: the size of our stack buffer: */
157: buffer_input_size = tme_serial_buffer_space_free(&serial->tme_posix_serial_buffer_out);
158: buffer_input_size = TME_MIN(buffer_input_size, sizeof(buffer_input));
159:
160: /* unlock the mutex: */
161: tme_mutex_unlock(&serial->tme_posix_serial_mutex);
162:
163: /* do the read: */
164: rc = (*conn_serial->tme_serial_connection_read)
165: (conn_serial,
166: buffer_input,
167: buffer_input_size,
168: &data_flags);
169:
170: /* lock the mutex: */
171: tme_mutex_lock(&serial->tme_posix_serial_mutex);
172:
173: /* if the read was successful, add what we got and notify the
174: writer so that it may try to write: */
175: if (rc > 0) {
176: (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_out,
177: buffer_input,
178: rc,
179: data_flags,
180: TME_SERIAL_COPY_NORMAL);
181: tme_cond_notify(&serial->tme_posix_serial_cond_writer, TRUE);
182:
183: /* allow the outermost loop to run again: */
184: again = TRUE;
185: }
186:
187: /* otherwise, the read failed, and the convention dictates
188: that we forget this connection was readable: */
189: else {
190: serial->tme_posix_serial_ctrl_callin &= ~TME_SERIAL_CTRL_OK_READ;
191: }
192: }
193: }
194: }
195:
196: /* there are no more callouts to make: */
197: serial->tme_posix_serial_callouts_running = FALSE;
198: }
199:
200: /* the serial control thread: */
201: static void
202: _tme_posix_serial_th_ctrl(struct tme_posix_serial *serial)
203: {
204: int modem_state, modem_state_out;
205: unsigned int ctrl;
206:
207: /* loop forever: */
208: for (;;) {
209:
210: /* get the modem state of the input device: */
211: ioctl(serial->tme_posix_serial_fd_in, TIOCMGET, &modem_state);
212:
213: /* if the output device is different, get the modem state of the
214: output device and merge it in: */
215: if (serial->tme_posix_serial_fd_out
216: != serial->tme_posix_serial_fd_in) {
217: ioctl(serial->tme_posix_serial_fd_in, TIOCMGET, &modem_state_out);
218: modem_state &= ~(TIOCM_DTR | TIOCM_RTS | TIOCM_CTS);
219: modem_state |= modem_state_out & ~(TIOCM_CD | TIOCM_RI | TIOCM_DSR);
220: }
221:
222: /* lock the mutex: */
223: tme_mutex_lock(&serial->tme_posix_serial_mutex);
224:
225: /* update the control outputs: */
226: ctrl = (serial->tme_posix_serial_ctrl_callout
227: & ~(TME_SERIAL_CTRL_CTS
228: | TME_SERIAL_CTRL_DCD
229: | TME_SERIAL_CTRL_RI
230: | TME_SERIAL_CTRL_BREAK));
231: if (modem_state & TIOCM_CTS) {
232: ctrl |= TME_SERIAL_CTRL_CTS;
233: }
234: if (modem_state & TIOCM_CD) {
235: ctrl |= TME_SERIAL_CTRL_DCD;
236: }
237: if (modem_state & TIOCM_RI) {
238: ctrl |= TME_SERIAL_CTRL_RI;
239: }
240: if (serial->tme_posix_serial_ctrl_callout_break > 0) {
241: ctrl |= TME_SERIAL_CTRL_BREAK;
242: serial->tme_posix_serial_ctrl_callout_break--;
243: }
244:
245: /* if the control outputs have changed, call out the change: */
246: if (ctrl != serial->tme_posix_serial_ctrl_callout) {
247: serial->tme_posix_serial_ctrl_callout = ctrl;
248: _tme_posix_serial_callout(serial);
249: }
250:
251: /* unlock the mutex: */
252: tme_mutex_unlock(&serial->tme_posix_serial_mutex);
253:
254: /* check the controls again in .5 seconds: */
255: tme_thread_sleep_yield(0, 500000);
256: }
257: /* NOTREACHED */
258: }
259:
260: /* the serial writer thread: */
261: static void
262: _tme_posix_serial_th_writer(struct tme_posix_serial *serial)
263: {
264: tme_uint8_t buffer_output[1024];
265: unsigned int buffer_output_size;
266: int rc;
267:
268: /* lock the mutex: */
269: tme_mutex_lock(&serial->tme_posix_serial_mutex);
270:
271: /* loop forever: */
272: for (;;) {
273:
274: /* if there is no data to write, wait on the writer condition,
275: after which there must be data to write: */
276: if (tme_serial_buffer_is_empty(&serial->tme_posix_serial_buffer_out)) {
277: tme_cond_wait_yield(&serial->tme_posix_serial_cond_writer,
278: &serial->tme_posix_serial_mutex);
279: }
280:
281: /* get the data to write: */
282: buffer_output_size =
283: tme_serial_buffer_copyout(&serial->tme_posix_serial_buffer_out,
284: buffer_output,
285: sizeof(buffer_output),
286: NULL,
287: TME_SERIAL_COPY_PEEK);
288: assert(buffer_output_size > 0);
289:
290: /* unlock the mutex: */
291: tme_mutex_unlock(&serial->tme_posix_serial_mutex);
292:
293: /* try to write the device: */
294: rc = tme_thread_write_yield(serial->tme_posix_serial_fd_out,
295: buffer_output,
296: buffer_output_size);
297:
298: /* lock the mutex: */
299: tme_mutex_lock(&serial->tme_posix_serial_mutex);
300:
301: /* if the write was successful: */
302: if (rc > 0) {
303:
304: /* remove the written data from the output buffer: */
305: tme_serial_buffer_copyout(&serial->tme_posix_serial_buffer_out,
306: NULL,
307: rc,
308: NULL,
309: TME_SERIAL_COPY_NORMAL);
310:
311: /* call out for more data: */
312: _tme_posix_serial_callout(serial);
313: }
314: }
315: /* NOTREACHED */
316: }
317:
318: /* the serial reader thread: */
319: static void
320: _tme_posix_serial_th_reader(struct tme_posix_serial *serial)
321: {
322: tme_uint8_t buffer_input[1024];
323: tme_uint8_t buffer_slack[10];
324: tme_uint8_t byte, *byte_head, *byte_tail;
325: int scanner_state;
326: int buffer_was_empty;
327: int rc;
328:
329: /* loop forever: */
330: for (;;) {
331:
332: /* try to read the device: */
333: rc = tme_thread_read_yield(serial->tme_posix_serial_fd_in,
334: buffer_input,
335: sizeof(buffer_input));
336:
337: /* if the read failed: */
338: if (rc <= 0) {
339: /* XXX diagnostic */
340: continue;
341: }
342:
343: /* lock the mutex: */
344: tme_mutex_lock(&serial->tme_posix_serial_mutex);
345:
346: /* remember if this buffer was empty: */
347: buffer_was_empty =
348: tme_serial_buffer_is_empty(&serial->tme_posix_serial_buffer_in);
349:
350: /* scan the input: */
351: byte_head = buffer_input;
352: scanner_state = serial->tme_posix_serial_input_scanner_state;
353: for (; rc > 0; ) {
354:
355: /* in state zero, we haven't seen anything interesting: */
356: if (scanner_state == 0) {
357:
358: /* scan quickly, stopping only when we see an escape: */
359: byte_tail = byte_head;
360: if (serial->tme_posix_serial_emulate_break) {
361: do {
362: byte = *(byte_head++);
363: if (byte == 0xff
364: || byte == '^') {
365: break;
366: }
367: } while (--rc > 0);
368: }
369: else {
370: do {
371: byte = *(byte_head++);
372: if (byte == 0xff) {
373: break;
374: }
375: } while (--rc > 0);
376: }
377:
378: /* if we stopped scanning because we saw an escape, back up: */
379: if (rc > 0) {
380: byte_head--;
381: }
382:
383: /* add in the normal data we scanned quickly: */
384: if (byte_head > byte_tail) {
385: (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_in,
386: byte_tail,
387: (byte_head - byte_tail),
388: TME_SERIAL_DATA_NORMAL,
389: TME_SERIAL_COPY_FULL_IS_OVERRUN);
390: }
391:
392: /* if this byte is an 0xff, move to state one: */
393: if (byte == 0xff) {
394: byte_head++;
395: --rc;
396: scanner_state = 1;
397: }
398:
399: /* if we're emulating breaks, and this is a carat, move to
400: state eight: */
401: else if (serial->tme_posix_serial_emulate_break
402: && byte == '^') {
403: byte_head++;
404: --rc;
405: scanner_state = 8;
406: }
407:
408: /* otherwise, we must have drained everything: */
409: else {
410: assert(rc == 0);
411: }
412: }
413:
414: /* in state one, we have seen 0xff: */
415: else if (scanner_state == 1) {
416:
417: /* get the next byte: */
418: byte = *(byte_head++);
419: --rc;
420:
421: /* if this is an 0x00, move to state two: */
422: if (byte == 0x00) {
423: scanner_state = 2;
424: }
425:
426: /* otherwise, receive 0xff, and if this is not an 0xff, return
427: it to the buffer. move to state zero: */
428: else {
429: buffer_slack[0] = 0xff;
430: (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_in,
431: buffer_slack,
432: 1,
433: TME_SERIAL_DATA_NORMAL,
434: TME_SERIAL_COPY_FULL_IS_OVERRUN);
435: if (byte != 0xff) {
436: byte_head--;
437: ++rc;
438: }
439: scanner_state = 0;
440: }
441: }
442:
443: /* in state two, we have seen 0xff 0x00: */
444: else if (scanner_state == 2) {
445:
446: /* get the next byte: */
447: byte = *(byte_head++);
448: --rc;
449:
450: /* if this is an 0x00, this is a break: */
451: if (byte == 0x00) {
452:
453: /* if break isn't already being asserted, assert it and call
454: out the change. always reset the assertion time: */
455: if (!(serial->tme_posix_serial_ctrl_callout & TME_SERIAL_CTRL_BREAK)) {
456: serial->tme_posix_serial_ctrl_callout |= TME_SERIAL_CTRL_BREAK;
457: _tme_posix_serial_callout(serial);
458: }
459: serial->tme_posix_serial_ctrl_callout_break = 2;
460: }
461:
462: /* otherwise, this byte was received with bad framing or
463: parity. we can't tell which, so call it bad parity: */
464: else {
465: (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_in,
466: byte_head - 1,
467: 1,
468: TME_SERIAL_DATA_BAD_PARITY,
469: TME_SERIAL_COPY_FULL_IS_OVERRUN);
470: }
471:
472: /* move to state zero: */
473: scanner_state = 0;
474: }
475:
476: /* in states eight and nine, we have seen one or two break escapes,
477: respectively: */
478: else if (scanner_state == 8
479: || scanner_state == 9) {
480: assert(serial->tme_posix_serial_emulate_break);
481:
482: /* get the next byte: */
483: byte = *(byte_head++);
484: --rc;
485:
486: /* if this isn't also a break escape, return it to the
487: buffer, receive the break escapes, and move to state
488: zero: */
489: if (byte != '^') {
490: byte_head--;
491: ++rc;
492: buffer_slack[0] = buffer_slack[1] = '^';
493: (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_in,
494: buffer_slack,
495: scanner_state - 7,
496: TME_SERIAL_DATA_NORMAL,
497: TME_SERIAL_COPY_FULL_IS_OVERRUN);
498: scanner_state = 0;
499: }
500:
501: /* otherwise, this is a break escape. if we have now seen
502: three total, this is a break, and move to state zero: */
503: else if (++scanner_state == 10) {
504:
505: /* if break isn't already being asserted, assert it and call
506: out the change. always reset the assertion time: */
507: if (!(serial->tme_posix_serial_ctrl_callout & TME_SERIAL_CTRL_BREAK)) {
508: serial->tme_posix_serial_ctrl_callout |= TME_SERIAL_CTRL_BREAK;
509: _tme_posix_serial_callout(serial);
510: }
511: serial->tme_posix_serial_ctrl_callout_break = 2;
512: scanner_state = 0;
513: }
514: }
515:
516: /* any other state is impossible: */
517: else {
518: assert(FALSE);
519: }
520: }
521:
522: /* update the scanner state: */
523: serial->tme_posix_serial_input_scanner_state = scanner_state;
524:
525: /* if the input buffer was previously empty, and it is now not
526: empty, call out that we can be read again: */
527: if (buffer_was_empty
528: && !tme_serial_buffer_is_empty(&serial->tme_posix_serial_buffer_in)) {
529: serial->tme_posix_serial_ctrl_callout |= TME_SERIAL_CTRL_OK_READ;
530: _tme_posix_serial_callout(serial);
531: }
532:
533: /* unlock the mutex: */
534: tme_mutex_unlock(&serial->tme_posix_serial_mutex);
535: }
536: /* NOTREACHED */
537: }
538:
539: /* the serial configuration callin function: */
540: static int
541: _tme_posix_serial_config(struct tme_serial_connection *conn_serial, struct tme_serial_config *config)
542: {
543: struct tme_posix_serial *serial;
544: struct termios serial_termios;
545: tme_uint32_t config_baud;
546: speed_t termios_baud;
547: int is_input, rc;
548:
549: /* recover our data structure: */
550: serial = conn_serial->tme_serial_connection.tme_connection_element->tme_element_private;
551:
552: /* lock our mutex: */
553: tme_mutex_lock(&serial->tme_posix_serial_mutex);
554:
555: /* loop over the input and output devices: */
556: for (is_input = 2; is_input-- > 0; ) {
557:
558: /* get the current configuration of the device: */
559: rc = tcgetattr((is_input
560: ? serial->tme_posix_serial_fd_in
561: : serial->tme_posix_serial_fd_out),
562: &serial_termios);
563:
564: /* update the configuration: */
565:
566: /* baud rate: */
567: config_baud = config->tme_serial_config_baud;
568: termios_baud = (config_baud == 0 ? B0
569: : config_baud <= 50 ? B50
570: : config_baud <= 75 ? B75
571: : config_baud <= 110 ? B110
572: : config_baud <= 134 ? B134
573: : config_baud <= 150 ? B150
574: : config_baud <= 200 ? B200
575: : config_baud <= 300 ? B300
576: : config_baud <= 600 ? B600
577: : config_baud <= 1200 ? B1200
578: : config_baud <= 1800 ? B1800
579: : config_baud <= 2400 ? B2400
580: : config_baud <= 4800 ? B4800
581: : config_baud <= 9600 ? B9600
582: : config_baud <= 19200 ? B19200
583: : config_baud <= 38400 ? B38400
584: : (speed_t) -1);
585: if (termios_baud == (speed_t) -1) {
586: /* XXX diagnostic */
587: termios_baud = B38400;
588: }
589: rc = cfsetspeed(&serial_termios, termios_baud);
590:
591: /* input mode or output mode: */
592: if (is_input) {
593: serial_termios.c_iflag = PARMRK;
594: if (config->tme_serial_config_flags & TME_SERIAL_FLAGS_CHECK_PARITY) {
595: serial_termios.c_iflag = INPCK;
596: }
597: }
598: else {
599: serial_termios.c_oflag = 0;
600: }
601:
602: /* control mode: */
603: serial_termios.c_cflag = CREAD | CLOCAL;
604: switch (config->tme_serial_config_bits_data) {
605: case 5: serial_termios.c_cflag |= CS5; break;
606: case 6: serial_termios.c_cflag |= CS6; break;
607: case 7: serial_termios.c_cflag |= CS7; break;
608: default: assert(FALSE);
609: case 8: serial_termios.c_cflag |= CS8; break;
610: }
611: switch (config->tme_serial_config_bits_stop) {
612: case 1: break;
613: default: assert(FALSE);
614: case 2: serial_termios.c_cflag |= CSTOPB; break;
615: }
616: switch (config->tme_serial_config_parity) {
617: default: assert(FALSE);
618: case TME_SERIAL_PARITY_NONE: break;
619: case TME_SERIAL_PARITY_ODD: serial_termios.c_cflag |= PARENB | PARODD; break;
620: case TME_SERIAL_PARITY_EVEN: serial_termios.c_cflag |= PARENB; break;
621: }
622:
623: /* local mode: */
624: serial_termios.c_lflag = 0;
625:
626: /* set the configuration on the devices: */
627: rc = tcsetattr((is_input
628: ? serial->tme_posix_serial_fd_in
629: : serial->tme_posix_serial_fd_out),
630: TCSADRAIN,
631: &serial_termios);
632: }
633:
634: /* unlock our mutex: */
635: tme_mutex_unlock(&serial->tme_posix_serial_mutex);
636:
637: /* done: */
638: return (TME_OK);
639: }
640:
641: /* the serial control callin function: */
642: static int
643: _tme_posix_serial_ctrl(struct tme_serial_connection *conn_serial, unsigned int control)
644: {
645: struct tme_posix_serial *serial;
646: int modem_state;
647: int rc;
648:
649: /* recover our data structure: */
650: serial = conn_serial->tme_serial_connection.tme_connection_element->tme_element_private;
651:
652: /* lock our mutex: */
653: tme_mutex_lock(&serial->tme_posix_serial_mutex);
654:
655: /* get the current output device modem state: */
656: rc = ioctl(serial->tme_posix_serial_fd_out, TIOCMGET, &modem_state);
657:
658: /* update the modem state: */
659: if (control & TME_SERIAL_CTRL_DTR) {
660: modem_state |= TIOCM_DTR;
661: }
662: else {
663: modem_state &= TIOCM_DTR;
664: }
665: if (control & TME_SERIAL_CTRL_RTS) {
666: modem_state |= TIOCM_RTS;
667: }
668: else {
669: modem_state &= TIOCM_RTS;
670: }
671:
672: /* set the new modem state: */
673: rc = ioctl(serial->tme_posix_serial_fd_out, TIOCMSET, &modem_state);
674:
675: /* send a break: */
676: if (control & TME_SERIAL_CTRL_BREAK) {
677: tcsendbreak(serial->tme_posix_serial_fd_out, 0);
678: }
679:
680: /* remember these controls. if the OK_READ is set, call out a read: */
681: serial->tme_posix_serial_ctrl_callin = control;
682: if (control & TME_SERIAL_CTRL_OK_READ) {
683: _tme_posix_serial_callout(serial);
684: }
685:
686: /* unlock our mutex: */
687: tme_mutex_unlock(&serial->tme_posix_serial_mutex);
688:
689: /* done: */
690: return (TME_OK);
691: }
692:
693: /* the serial read callin function: */
694: static int
695: _tme_posix_serial_read(struct tme_serial_connection *conn_serial,
696: tme_uint8_t *data, unsigned int count,
697: tme_serial_data_flags_t *_data_flags)
698: {
699: struct tme_posix_serial *serial;
700: unsigned int rc;
701:
702: /* recover our data structure: */
703: serial = conn_serial->tme_serial_connection.tme_connection_element->tme_element_private;
704:
705: /* lock the mutex: */
706: tme_mutex_lock(&serial->tme_posix_serial_mutex);
707:
708: /* copy data out of the input buffer: */
709: rc = tme_serial_buffer_copyout(&serial->tme_posix_serial_buffer_in,
710: data, count,
711: _data_flags,
712: TME_SERIAL_COPY_NORMAL);
713:
714: /* if the input buffer is now empty, our connection shouldn't read
715: again until we have filled some of the buffer. we don't call
716: this transition out, because the convention dictates that
717: our connection forget that we're readable: */
718: if (rc < count) {
719: serial->tme_posix_serial_ctrl_callout &= ~TME_SERIAL_CTRL_OK_READ;
720: serial->tme_posix_serial_ctrl_callout_last &= ~TME_SERIAL_CTRL_OK_READ;
721: }
722:
723: /* unlock the mutex: */
724: tme_mutex_unlock(&serial->tme_posix_serial_mutex);
725:
726: /* done: */
727: return (rc);
728: }
729:
730: /* this scores a connection: */
731: static int
732: _tme_posix_serial_connection_score(struct tme_connection *conn, unsigned int *_score)
733: {
734: struct tme_posix_serial *serial;
735:
736: /* recover our serial: */
737: serial = conn->tme_connection_element->tme_element_private;
738:
739: /* both sides must be serial connections: */
740: assert(conn->tme_connection_type == TME_CONNECTION_SERIAL);
741: assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_SERIAL);
742:
743: /* serial connections are always good: */
744: *_score = 1;
745: return (TME_OK);
746: }
747:
748: /* this makes a new connection: */
749: static int
750: _tme_posix_serial_connection_make(struct tme_connection *conn, unsigned int state)
751: {
752: struct tme_posix_serial *serial;
753:
754: /* recover our serial: */
755: serial = conn->tme_connection_element->tme_element_private;
756:
757: /* both sides must be generic bus connections: */
758: assert(conn->tme_connection_type == TME_CONNECTION_SERIAL);
759: assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_SERIAL);
760:
761: /* serials are always set up to answer calls across the connection,
762: so we only have to do work when the connection has gone full,
763: namely taking the other side of the connection: */
764: if (state == TME_CONNECTION_FULL) {
765:
766: /* lock our mutex: */
767: tme_mutex_lock(&serial->tme_posix_serial_mutex);
768:
769: /* save our connection: */
770: serial->tme_posix_serial_connection =
771: (struct tme_serial_connection *) conn->tme_connection_other;
772:
773: /* do any pending callouts: */
774: _tme_posix_serial_callout(serial);
775:
776: /* unlock our mutex: */
777: tme_mutex_unlock(&serial->tme_posix_serial_mutex);
778: }
779:
780: return (TME_OK);
781: }
782:
783: /* this breaks a connection: */
784: static int
785: _tme_posix_serial_connection_break(struct tme_connection *conn, unsigned int state)
786: {
787: abort();
788: }
789:
790: /* this makes a new connection side for a serial: */
791: static int
792: _tme_posix_serial_connections_new(struct tme_element *element,
793: const char * const *args,
794: struct tme_connection **_conns,
795: char **_output)
796: {
797: struct tme_posix_serial *serial;
798: struct tme_serial_connection *conn_serial;
799: struct tme_connection *conn;
800:
801: /* recover our serial: */
802: serial = (struct tme_posix_serial *) element->tme_element_private;
803:
804: /* if this serial is already connected, we can do nothing: */
805: if (TME_ATOMIC_READ(struct tme_serial_connection *,
806: serial->tme_posix_serial_connection) != NULL) {
807: return (EISCONN);
808: }
809:
810: /* create our side of a serial connection: */
811: conn_serial = tme_new0(struct tme_serial_connection, 1);
812: conn = &conn_serial->tme_serial_connection;
813:
814: /* fill in the generic connection: */
815: conn->tme_connection_next = *_conns;
816: conn->tme_connection_type = TME_CONNECTION_SERIAL;
817: conn->tme_connection_score = _tme_posix_serial_connection_score;
818: conn->tme_connection_make = _tme_posix_serial_connection_make;
819: conn->tme_connection_break = _tme_posix_serial_connection_break;
820:
821: /* fill in the serial connection: */
822: conn_serial->tme_serial_connection_config = _tme_posix_serial_config;
823: conn_serial->tme_serial_connection_ctrl = _tme_posix_serial_ctrl;
824: conn_serial->tme_serial_connection_read = _tme_posix_serial_read;
825:
826: /* return the connection side possibility: */
827: *_conns = conn;
828: return (TME_OK);
829: }
830:
831: /* the new serial function: */
832: TME_ELEMENT_SUB_NEW_DECL(tme_host_posix,serial) {
833: struct tme_posix_serial *serial;
834: const char *filename_in;
835: const char *filename_out;
836: int fd_in, fd_out;
837: int usage;
838: int arg_i;
839: int saved_errno;
840: int emulate_break;
841:
842: /* initialize: */
843: filename_in = NULL;
844: filename_out = NULL;
845: emulate_break = FALSE;
846: arg_i = 1;
847: usage = FALSE;
848:
849: /* loop reading our arguments: */
850: for (;;) {
851:
852: /* the device we're supposed to use for input: */
853: if (TME_ARG_IS(args[arg_i + 0], "device-input")
854: && args[arg_i + 1] != NULL
855: && filename_in == NULL) {
856: filename_in = args[arg_i + 1];
857: arg_i += 2;
858: }
859:
860: /* the device we're supposed to use for output: */
861: else if (TME_ARG_IS(args[arg_i + 0], "device-output")
862: && args[arg_i + 1] != NULL
863: && filename_out == NULL) {
864: filename_out = args[arg_i + 1];
865: arg_i += 2;
866: }
867:
868: /* the device we're supposed to use for input and output: */
869: else if (TME_ARG_IS(args[arg_i + 0], "device")
870: && args[arg_i + 1] != NULL
871: && filename_in == NULL
872: && filename_out == NULL) {
873: filename_in = filename_out = args[arg_i + 1];
874: arg_i += 2;
875: }
876:
877: /* if we're supposed to emulate break: */
878: else if (TME_ARG_IS(args[arg_i + 0], "break-carats")) {
879: emulate_break = TRUE;
880: arg_i++;
881: }
882:
883: /* if we've run out of arguments: */
884: else if (args[arg_i + 0] == NULL) {
885:
886: /* we must have been given input and output devices: */
887: if (filename_in == NULL
888: || filename_out == NULL) {
889: usage = TRUE;
890: }
891: break;
892: }
893:
894: /* this is a bad argument: */
895: else {
896: tme_output_append_error(_output,
897: "%s %s",
898: args[arg_i],
899: _("unexpected"));
900: usage = TRUE;
901: break;
902: }
903: }
904:
905: if (usage) {
906: tme_output_append_error(_output,
907: "%s %s { device %s | { device-input %s device-output %s } } [break-carats]",
908: _("usage:"),
909: args[0],
910: _("DEVICE"),
911: _("DEVICE"),
912: _("DEVICE"));
913: return (EINVAL);
914: }
915:
916: /* open the devices: */
917: fd_in = fd_out = -1;
918: if (fd_in < 0
919: && !strcmp(filename_in, "-")) {
920: fd_in = STDIN_FILENO;
921: }
922: if (fd_out < 0
923: && !strcmp(filename_out, "-")) {
924: fd_out = STDOUT_FILENO;
925: }
926: if (fd_in < 0) {
927: if (strcmp(filename_in, filename_out) == 0) {
928: fd_in = fd_out = open(filename_in, O_RDWR | O_NONBLOCK);
929: }
930: else {
931: fd_in = open(filename_in, O_RDONLY | O_NONBLOCK);
932: }
933: if (fd_in < 0) {
934: tme_output_append_error(_output, "%s", filename_in);
935: return (errno);
936: }
937: }
938: if (fd_out < 0) {
939: fd_out = open(filename_out, O_WRONLY | O_NONBLOCK);
940: if (fd_out < 0) {
941: saved_errno = errno;
942: close(fd_in);
943: tme_output_append_error(_output, "%s", filename_out);
944: return (saved_errno);
945: }
946: }
947:
948: /* start the serial structure: */
949: serial = tme_new0(struct tme_posix_serial, 1);
950: serial->tme_posix_serial_element = element;
951: serial->tme_posix_serial_fd_in = fd_in;
952: serial->tme_posix_serial_fd_out = fd_out;
953: serial->tme_posix_serial_emulate_break = emulate_break;
954: serial->tme_posix_serial_ctrl_callout = 0;
955: serial->tme_posix_serial_ctrl_callout_last = 0;
956: tme_serial_buffer_init(&serial->tme_posix_serial_buffer_in,
957: TME_POSIX_SERIAL_BUFFER_SIZE);
958: tme_serial_buffer_init(&serial->tme_posix_serial_buffer_out,
959: TME_POSIX_SERIAL_BUFFER_SIZE);
960:
961: /* start the threads: */
962: tme_mutex_init(&serial->tme_posix_serial_mutex);
963: tme_cond_init(&serial->tme_posix_serial_cond_writer);
964: tme_thread_create((tme_thread_t) _tme_posix_serial_th_writer, serial);
965: tme_thread_create((tme_thread_t) _tme_posix_serial_th_reader, serial);
966: tme_thread_create((tme_thread_t) _tme_posix_serial_th_ctrl, serial);
967:
968: /* fill the element: */
969: element->tme_element_private = serial;
970: element->tme_element_connections_new = _tme_posix_serial_connections_new;
971:
972: return (TME_OK);
973: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.