|
|
1.1.1.3 ! root 1: /* $Id: posix-serial.c,v 1.11 2007/08/24 00:57:01 fredette Exp $ */ 1.1 root 2: 1.1.1.2 root 3: /* host/posix/posix-serial.c - implementation of serial ports on a POSIX system: */ 1.1 root 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> 1.1.1.3 ! root 37: _TME_RCSID("$Id: posix-serial.c,v 1.11 2007/08/24 00:57:01 fredette Exp $"); 1.1 root 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: */ 1.1.1.3 ! root 211: if (ioctl(serial->tme_posix_serial_fd_in, TIOCMGET, &modem_state) < 0) { ! 212: modem_state = 0; ! 213: } 1.1 root 214: 215: /* if the output device is different, get the modem state of the 216: output device and merge it in: */ 217: if (serial->tme_posix_serial_fd_out 218: != serial->tme_posix_serial_fd_in) { 1.1.1.3 ! root 219: if (ioctl(serial->tme_posix_serial_fd_in, TIOCMGET, &modem_state_out) < 0) { ! 220: modem_state_out = 0; ! 221: } 1.1 root 222: modem_state &= ~(TIOCM_DTR | TIOCM_RTS | TIOCM_CTS); 223: modem_state |= modem_state_out & ~(TIOCM_CD | TIOCM_RI | TIOCM_DSR); 224: } 225: 226: /* lock the mutex: */ 227: tme_mutex_lock(&serial->tme_posix_serial_mutex); 228: 229: /* update the control outputs: */ 230: ctrl = (serial->tme_posix_serial_ctrl_callout 231: & ~(TME_SERIAL_CTRL_CTS 232: | TME_SERIAL_CTRL_DCD 233: | TME_SERIAL_CTRL_RI 234: | TME_SERIAL_CTRL_BREAK)); 235: if (modem_state & TIOCM_CTS) { 236: ctrl |= TME_SERIAL_CTRL_CTS; 237: } 238: if (modem_state & TIOCM_CD) { 239: ctrl |= TME_SERIAL_CTRL_DCD; 240: } 241: if (modem_state & TIOCM_RI) { 242: ctrl |= TME_SERIAL_CTRL_RI; 243: } 244: if (serial->tme_posix_serial_ctrl_callout_break > 0) { 245: ctrl |= TME_SERIAL_CTRL_BREAK; 246: serial->tme_posix_serial_ctrl_callout_break--; 247: } 248: 249: /* if the control outputs have changed, call out the change: */ 250: if (ctrl != serial->tme_posix_serial_ctrl_callout) { 251: serial->tme_posix_serial_ctrl_callout = ctrl; 252: _tme_posix_serial_callout(serial); 253: } 254: 255: /* unlock the mutex: */ 256: tme_mutex_unlock(&serial->tme_posix_serial_mutex); 257: 258: /* check the controls again in .5 seconds: */ 259: tme_thread_sleep_yield(0, 500000); 260: } 261: /* NOTREACHED */ 262: } 263: 264: /* the serial writer thread: */ 265: static void 266: _tme_posix_serial_th_writer(struct tme_posix_serial *serial) 267: { 268: tme_uint8_t buffer_output[1024]; 269: unsigned int buffer_output_size; 270: int rc; 271: 272: /* lock the mutex: */ 273: tme_mutex_lock(&serial->tme_posix_serial_mutex); 274: 275: /* loop forever: */ 276: for (;;) { 277: 278: /* if there is no data to write, wait on the writer condition, 279: after which there must be data to write: */ 280: if (tme_serial_buffer_is_empty(&serial->tme_posix_serial_buffer_out)) { 281: tme_cond_wait_yield(&serial->tme_posix_serial_cond_writer, 282: &serial->tme_posix_serial_mutex); 283: } 284: 285: /* get the data to write: */ 286: buffer_output_size = 287: tme_serial_buffer_copyout(&serial->tme_posix_serial_buffer_out, 288: buffer_output, 289: sizeof(buffer_output), 290: NULL, 291: TME_SERIAL_COPY_PEEK); 292: assert(buffer_output_size > 0); 293: 294: /* unlock the mutex: */ 295: tme_mutex_unlock(&serial->tme_posix_serial_mutex); 296: 297: /* try to write the device: */ 298: rc = tme_thread_write_yield(serial->tme_posix_serial_fd_out, 299: buffer_output, 300: buffer_output_size); 301: 302: /* lock the mutex: */ 303: tme_mutex_lock(&serial->tme_posix_serial_mutex); 304: 305: /* if the write was successful: */ 306: if (rc > 0) { 307: 308: /* remove the written data from the output buffer: */ 309: tme_serial_buffer_copyout(&serial->tme_posix_serial_buffer_out, 310: NULL, 311: rc, 312: NULL, 313: TME_SERIAL_COPY_NORMAL); 314: 315: /* call out for more data: */ 316: _tme_posix_serial_callout(serial); 317: } 318: } 319: /* NOTREACHED */ 320: } 321: 322: /* the serial reader thread: */ 323: static void 324: _tme_posix_serial_th_reader(struct tme_posix_serial *serial) 325: { 326: tme_uint8_t buffer_input[1024]; 327: tme_uint8_t buffer_slack[10]; 328: tme_uint8_t byte, *byte_head, *byte_tail; 329: int scanner_state; 330: int buffer_was_empty; 331: int rc; 332: 333: /* loop forever: */ 334: for (;;) { 335: 336: /* try to read the device: */ 337: rc = tme_thread_read_yield(serial->tme_posix_serial_fd_in, 338: buffer_input, 339: sizeof(buffer_input)); 340: 341: /* if the read failed: */ 1.1.1.3 ! root 342: if (rc < 0) { 1.1 root 343: /* XXX diagnostic */ 344: continue; 345: } 346: 1.1.1.3 ! root 347: /* if we hit EOF: */ ! 348: if (rc == 0) { ! 349: return; ! 350: } ! 351: 1.1 root 352: /* lock the mutex: */ 353: tme_mutex_lock(&serial->tme_posix_serial_mutex); 354: 355: /* remember if this buffer was empty: */ 356: buffer_was_empty = 357: tme_serial_buffer_is_empty(&serial->tme_posix_serial_buffer_in); 358: 359: /* scan the input: */ 360: byte_head = buffer_input; 361: scanner_state = serial->tme_posix_serial_input_scanner_state; 362: for (; rc > 0; ) { 363: 364: /* in state zero, we haven't seen anything interesting: */ 365: if (scanner_state == 0) { 366: 367: /* scan quickly, stopping only when we see an escape: */ 368: byte_tail = byte_head; 369: if (serial->tme_posix_serial_emulate_break) { 370: do { 371: byte = *(byte_head++); 372: if (byte == 0xff 373: || byte == '^') { 374: break; 375: } 376: } while (--rc > 0); 377: } 378: else { 379: do { 380: byte = *(byte_head++); 381: if (byte == 0xff) { 382: break; 383: } 384: } while (--rc > 0); 385: } 386: 387: /* if we stopped scanning because we saw an escape, back up: */ 388: if (rc > 0) { 389: byte_head--; 390: } 391: 392: /* add in the normal data we scanned quickly: */ 393: if (byte_head > byte_tail) { 394: (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_in, 395: byte_tail, 396: (byte_head - byte_tail), 397: TME_SERIAL_DATA_NORMAL, 398: TME_SERIAL_COPY_FULL_IS_OVERRUN); 399: } 400: 401: /* if this byte is an 0xff, move to state one: */ 402: if (byte == 0xff) { 403: byte_head++; 404: --rc; 405: scanner_state = 1; 406: } 407: 408: /* if we're emulating breaks, and this is a carat, move to 409: state eight: */ 410: else if (serial->tme_posix_serial_emulate_break 411: && byte == '^') { 412: byte_head++; 413: --rc; 414: scanner_state = 8; 415: } 416: 417: /* otherwise, we must have drained everything: */ 418: else { 419: assert(rc == 0); 420: } 421: } 422: 423: /* in state one, we have seen 0xff: */ 424: else if (scanner_state == 1) { 425: 426: /* get the next byte: */ 427: byte = *(byte_head++); 428: --rc; 429: 430: /* if this is an 0x00, move to state two: */ 431: if (byte == 0x00) { 432: scanner_state = 2; 433: } 434: 435: /* otherwise, receive 0xff, and if this is not an 0xff, return 436: it to the buffer. move to state zero: */ 437: else { 438: buffer_slack[0] = 0xff; 439: (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_in, 440: buffer_slack, 441: 1, 442: TME_SERIAL_DATA_NORMAL, 443: TME_SERIAL_COPY_FULL_IS_OVERRUN); 444: if (byte != 0xff) { 445: byte_head--; 446: ++rc; 447: } 448: scanner_state = 0; 449: } 450: } 451: 452: /* in state two, we have seen 0xff 0x00: */ 453: else if (scanner_state == 2) { 454: 455: /* get the next byte: */ 456: byte = *(byte_head++); 457: --rc; 458: 459: /* if this is an 0x00, this is a break: */ 460: if (byte == 0x00) { 461: 462: /* if break isn't already being asserted, assert it and call 463: out the change. always reset the assertion time: */ 464: if (!(serial->tme_posix_serial_ctrl_callout & TME_SERIAL_CTRL_BREAK)) { 465: serial->tme_posix_serial_ctrl_callout |= TME_SERIAL_CTRL_BREAK; 466: _tme_posix_serial_callout(serial); 467: } 468: serial->tme_posix_serial_ctrl_callout_break = 2; 469: } 470: 471: /* otherwise, this byte was received with bad framing or 472: parity. we can't tell which, so call it bad parity: */ 473: else { 474: (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_in, 475: byte_head - 1, 476: 1, 477: TME_SERIAL_DATA_BAD_PARITY, 478: TME_SERIAL_COPY_FULL_IS_OVERRUN); 479: } 480: 481: /* move to state zero: */ 482: scanner_state = 0; 483: } 484: 485: /* in states eight and nine, we have seen one or two break escapes, 486: respectively: */ 487: else if (scanner_state == 8 488: || scanner_state == 9) { 489: assert(serial->tme_posix_serial_emulate_break); 490: 491: /* get the next byte: */ 492: byte = *(byte_head++); 493: --rc; 494: 495: /* if this isn't also a break escape, return it to the 496: buffer, receive the break escapes, and move to state 497: zero: */ 498: if (byte != '^') { 499: byte_head--; 500: ++rc; 501: buffer_slack[0] = buffer_slack[1] = '^'; 502: (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_in, 503: buffer_slack, 504: scanner_state - 7, 505: TME_SERIAL_DATA_NORMAL, 506: TME_SERIAL_COPY_FULL_IS_OVERRUN); 507: scanner_state = 0; 508: } 509: 510: /* otherwise, this is a break escape. if we have now seen 511: three total, this is a break, and move to state zero: */ 512: else if (++scanner_state == 10) { 513: 514: /* if break isn't already being asserted, assert it and call 515: out the change. always reset the assertion time: */ 516: if (!(serial->tme_posix_serial_ctrl_callout & TME_SERIAL_CTRL_BREAK)) { 517: serial->tme_posix_serial_ctrl_callout |= TME_SERIAL_CTRL_BREAK; 518: _tme_posix_serial_callout(serial); 519: } 520: serial->tme_posix_serial_ctrl_callout_break = 2; 521: scanner_state = 0; 522: } 523: } 524: 525: /* any other state is impossible: */ 526: else { 527: assert(FALSE); 528: } 529: } 530: 531: /* update the scanner state: */ 532: serial->tme_posix_serial_input_scanner_state = scanner_state; 533: 534: /* if the input buffer was previously empty, and it is now not 535: empty, call out that we can be read again: */ 536: if (buffer_was_empty 537: && !tme_serial_buffer_is_empty(&serial->tme_posix_serial_buffer_in)) { 538: serial->tme_posix_serial_ctrl_callout |= TME_SERIAL_CTRL_OK_READ; 539: _tme_posix_serial_callout(serial); 540: } 541: 542: /* unlock the mutex: */ 543: tme_mutex_unlock(&serial->tme_posix_serial_mutex); 544: } 545: /* NOTREACHED */ 546: } 547: 548: /* the serial configuration callin function: */ 549: static int 550: _tme_posix_serial_config(struct tme_serial_connection *conn_serial, struct tme_serial_config *config) 551: { 552: struct tme_posix_serial *serial; 553: struct termios serial_termios; 554: tme_uint32_t config_baud; 555: speed_t termios_baud; 556: int is_input, rc; 557: 558: /* recover our data structure: */ 559: serial = conn_serial->tme_serial_connection.tme_connection_element->tme_element_private; 560: 561: /* lock our mutex: */ 562: tme_mutex_lock(&serial->tme_posix_serial_mutex); 563: 564: /* loop over the input and output devices: */ 565: for (is_input = 2; is_input-- > 0; ) { 566: 567: /* get the current configuration of the device: */ 568: rc = tcgetattr((is_input 569: ? serial->tme_posix_serial_fd_in 570: : serial->tme_posix_serial_fd_out), 571: &serial_termios); 572: 573: /* update the configuration: */ 574: 575: /* baud rate: */ 576: config_baud = config->tme_serial_config_baud; 577: termios_baud = (config_baud == 0 ? B0 578: : config_baud <= 50 ? B50 579: : config_baud <= 75 ? B75 580: : config_baud <= 110 ? B110 581: : config_baud <= 134 ? B134 582: : config_baud <= 150 ? B150 583: : config_baud <= 200 ? B200 584: : config_baud <= 300 ? B300 585: : config_baud <= 600 ? B600 586: : config_baud <= 1200 ? B1200 587: : config_baud <= 1800 ? B1800 588: : config_baud <= 2400 ? B2400 589: : config_baud <= 4800 ? B4800 590: : config_baud <= 9600 ? B9600 591: : config_baud <= 19200 ? B19200 592: : config_baud <= 38400 ? B38400 593: : (speed_t) -1); 594: if (termios_baud == (speed_t) -1) { 595: /* XXX diagnostic */ 596: termios_baud = B38400; 597: } 598: rc = cfsetspeed(&serial_termios, termios_baud); 599: 600: /* input mode or output mode: */ 601: if (is_input) { 602: serial_termios.c_iflag = PARMRK; 603: if (config->tme_serial_config_flags & TME_SERIAL_FLAGS_CHECK_PARITY) { 604: serial_termios.c_iflag = INPCK; 605: } 606: } 607: else { 608: serial_termios.c_oflag = 0; 609: } 610: 611: /* control mode: */ 612: serial_termios.c_cflag = CREAD | CLOCAL; 613: switch (config->tme_serial_config_bits_data) { 614: case 5: serial_termios.c_cflag |= CS5; break; 615: case 6: serial_termios.c_cflag |= CS6; break; 616: case 7: serial_termios.c_cflag |= CS7; break; 617: default: assert(FALSE); 618: case 8: serial_termios.c_cflag |= CS8; break; 619: } 620: switch (config->tme_serial_config_bits_stop) { 621: case 1: break; 622: default: assert(FALSE); 623: case 2: serial_termios.c_cflag |= CSTOPB; break; 624: } 625: switch (config->tme_serial_config_parity) { 626: default: assert(FALSE); 627: case TME_SERIAL_PARITY_NONE: break; 628: case TME_SERIAL_PARITY_ODD: serial_termios.c_cflag |= PARENB | PARODD; break; 629: case TME_SERIAL_PARITY_EVEN: serial_termios.c_cflag |= PARENB; break; 630: } 631: 632: /* local mode: */ 633: serial_termios.c_lflag = 0; 634: 635: /* set the configuration on the devices: */ 636: rc = tcsetattr((is_input 637: ? serial->tme_posix_serial_fd_in 638: : serial->tme_posix_serial_fd_out), 639: TCSADRAIN, 640: &serial_termios); 641: } 642: 643: /* unlock our mutex: */ 644: tme_mutex_unlock(&serial->tme_posix_serial_mutex); 645: 646: /* done: */ 647: return (TME_OK); 648: } 649: 650: /* the serial control callin function: */ 651: static int 652: _tme_posix_serial_ctrl(struct tme_serial_connection *conn_serial, unsigned int control) 653: { 654: struct tme_posix_serial *serial; 655: int modem_state; 656: int rc; 657: 658: /* recover our data structure: */ 659: serial = conn_serial->tme_serial_connection.tme_connection_element->tme_element_private; 660: 661: /* lock our mutex: */ 662: tme_mutex_lock(&serial->tme_posix_serial_mutex); 663: 664: /* get the current output device modem state: */ 665: rc = ioctl(serial->tme_posix_serial_fd_out, TIOCMGET, &modem_state); 666: 667: /* update the modem state: */ 668: if (control & TME_SERIAL_CTRL_DTR) { 669: modem_state |= TIOCM_DTR; 670: } 671: else { 672: modem_state &= TIOCM_DTR; 673: } 674: if (control & TME_SERIAL_CTRL_RTS) { 675: modem_state |= TIOCM_RTS; 676: } 677: else { 678: modem_state &= TIOCM_RTS; 679: } 680: 681: /* set the new modem state: */ 682: rc = ioctl(serial->tme_posix_serial_fd_out, TIOCMSET, &modem_state); 683: 684: /* send a break: */ 685: if (control & TME_SERIAL_CTRL_BREAK) { 686: tcsendbreak(serial->tme_posix_serial_fd_out, 0); 687: } 688: 689: /* remember these controls. if the OK_READ is set, call out a read: */ 690: serial->tme_posix_serial_ctrl_callin = control; 691: if (control & TME_SERIAL_CTRL_OK_READ) { 692: _tme_posix_serial_callout(serial); 693: } 694: 695: /* unlock our mutex: */ 696: tme_mutex_unlock(&serial->tme_posix_serial_mutex); 697: 698: /* done: */ 699: return (TME_OK); 700: } 701: 702: /* the serial read callin function: */ 703: static int 704: _tme_posix_serial_read(struct tme_serial_connection *conn_serial, 705: tme_uint8_t *data, unsigned int count, 706: tme_serial_data_flags_t *_data_flags) 707: { 708: struct tme_posix_serial *serial; 709: unsigned int rc; 710: 711: /* recover our data structure: */ 712: serial = conn_serial->tme_serial_connection.tme_connection_element->tme_element_private; 713: 714: /* lock the mutex: */ 715: tme_mutex_lock(&serial->tme_posix_serial_mutex); 716: 717: /* copy data out of the input buffer: */ 718: rc = tme_serial_buffer_copyout(&serial->tme_posix_serial_buffer_in, 719: data, count, 720: _data_flags, 721: TME_SERIAL_COPY_NORMAL); 722: 723: /* if the input buffer is now empty, our connection shouldn't read 724: again until we have filled some of the buffer. we don't call 725: this transition out, because the convention dictates that 726: our connection forget that we're readable: */ 727: if (rc < count) { 728: serial->tme_posix_serial_ctrl_callout &= ~TME_SERIAL_CTRL_OK_READ; 729: serial->tme_posix_serial_ctrl_callout_last &= ~TME_SERIAL_CTRL_OK_READ; 730: } 731: 732: /* unlock the mutex: */ 733: tme_mutex_unlock(&serial->tme_posix_serial_mutex); 734: 735: /* done: */ 736: return (rc); 737: } 738: 739: /* this scores a connection: */ 740: static int 741: _tme_posix_serial_connection_score(struct tme_connection *conn, unsigned int *_score) 742: { 743: struct tme_posix_serial *serial; 744: 745: /* recover our serial: */ 746: serial = conn->tme_connection_element->tme_element_private; 747: 748: /* both sides must be serial connections: */ 749: assert(conn->tme_connection_type == TME_CONNECTION_SERIAL); 750: assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_SERIAL); 751: 752: /* serial connections are always good: */ 753: *_score = 1; 754: return (TME_OK); 755: } 756: 757: /* this makes a new connection: */ 758: static int 759: _tme_posix_serial_connection_make(struct tme_connection *conn, unsigned int state) 760: { 761: struct tme_posix_serial *serial; 762: 763: /* recover our serial: */ 764: serial = conn->tme_connection_element->tme_element_private; 765: 766: /* both sides must be generic bus connections: */ 767: assert(conn->tme_connection_type == TME_CONNECTION_SERIAL); 768: assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_SERIAL); 769: 770: /* serials are always set up to answer calls across the connection, 771: so we only have to do work when the connection has gone full, 772: namely taking the other side of the connection: */ 773: if (state == TME_CONNECTION_FULL) { 774: 775: /* lock our mutex: */ 776: tme_mutex_lock(&serial->tme_posix_serial_mutex); 777: 778: /* save our connection: */ 779: serial->tme_posix_serial_connection = 780: (struct tme_serial_connection *) conn->tme_connection_other; 781: 782: /* do any pending callouts: */ 783: _tme_posix_serial_callout(serial); 784: 785: /* unlock our mutex: */ 786: tme_mutex_unlock(&serial->tme_posix_serial_mutex); 787: } 788: 789: return (TME_OK); 790: } 791: 792: /* this breaks a connection: */ 793: static int 794: _tme_posix_serial_connection_break(struct tme_connection *conn, unsigned int state) 795: { 796: abort(); 797: } 798: 799: /* this makes a new connection side for a serial: */ 800: static int 801: _tme_posix_serial_connections_new(struct tme_element *element, 802: const char * const *args, 803: struct tme_connection **_conns, 804: char **_output) 805: { 806: struct tme_posix_serial *serial; 807: struct tme_serial_connection *conn_serial; 808: struct tme_connection *conn; 809: 810: /* recover our serial: */ 811: serial = (struct tme_posix_serial *) element->tme_element_private; 812: 813: /* if this serial is already connected, we can do nothing: */ 1.1.1.3 ! root 814: if (serial->tme_posix_serial_connection != NULL) { 1.1 root 815: return (EISCONN); 816: } 817: 818: /* create our side of a serial connection: */ 819: conn_serial = tme_new0(struct tme_serial_connection, 1); 820: conn = &conn_serial->tme_serial_connection; 821: 822: /* fill in the generic connection: */ 823: conn->tme_connection_next = *_conns; 824: conn->tme_connection_type = TME_CONNECTION_SERIAL; 825: conn->tme_connection_score = _tme_posix_serial_connection_score; 826: conn->tme_connection_make = _tme_posix_serial_connection_make; 827: conn->tme_connection_break = _tme_posix_serial_connection_break; 828: 829: /* fill in the serial connection: */ 830: conn_serial->tme_serial_connection_config = _tme_posix_serial_config; 831: conn_serial->tme_serial_connection_ctrl = _tme_posix_serial_ctrl; 832: conn_serial->tme_serial_connection_read = _tme_posix_serial_read; 833: 834: /* return the connection side possibility: */ 835: *_conns = conn; 836: return (TME_OK); 837: } 838: 839: /* the new serial function: */ 840: TME_ELEMENT_SUB_NEW_DECL(tme_host_posix,serial) { 841: struct tme_posix_serial *serial; 842: const char *filename_in; 843: const char *filename_out; 844: int fd_in, fd_out; 845: int usage; 846: int arg_i; 847: int saved_errno; 848: int emulate_break; 849: 850: /* initialize: */ 851: filename_in = NULL; 852: filename_out = NULL; 853: emulate_break = FALSE; 854: arg_i = 1; 855: usage = FALSE; 856: 857: /* loop reading our arguments: */ 858: for (;;) { 859: 860: /* the device we're supposed to use for input: */ 861: if (TME_ARG_IS(args[arg_i + 0], "device-input") 862: && args[arg_i + 1] != NULL 863: && filename_in == NULL) { 864: filename_in = args[arg_i + 1]; 865: arg_i += 2; 866: } 867: 868: /* the device we're supposed to use for output: */ 869: else if (TME_ARG_IS(args[arg_i + 0], "device-output") 870: && args[arg_i + 1] != NULL 871: && filename_out == NULL) { 872: filename_out = args[arg_i + 1]; 873: arg_i += 2; 874: } 875: 876: /* the device we're supposed to use for input and output: */ 877: else if (TME_ARG_IS(args[arg_i + 0], "device") 878: && args[arg_i + 1] != NULL 879: && filename_in == NULL 880: && filename_out == NULL) { 881: filename_in = filename_out = args[arg_i + 1]; 882: arg_i += 2; 883: } 884: 885: /* if we're supposed to emulate break: */ 886: else if (TME_ARG_IS(args[arg_i + 0], "break-carats")) { 887: emulate_break = TRUE; 888: arg_i++; 889: } 890: 891: /* if we've run out of arguments: */ 892: else if (args[arg_i + 0] == NULL) { 893: 894: /* we must have been given input and output devices: */ 895: if (filename_in == NULL 896: || filename_out == NULL) { 897: usage = TRUE; 898: } 899: break; 900: } 901: 902: /* this is a bad argument: */ 903: else { 904: tme_output_append_error(_output, 905: "%s %s", 906: args[arg_i], 907: _("unexpected")); 908: usage = TRUE; 909: break; 910: } 911: } 912: 913: if (usage) { 914: tme_output_append_error(_output, 915: "%s %s { device %s | { device-input %s device-output %s } } [break-carats]", 916: _("usage:"), 917: args[0], 918: _("DEVICE"), 919: _("DEVICE"), 920: _("DEVICE")); 921: return (EINVAL); 922: } 923: 924: /* open the devices: */ 925: fd_in = fd_out = -1; 926: if (fd_in < 0 927: && !strcmp(filename_in, "-")) { 928: fd_in = STDIN_FILENO; 929: } 930: if (fd_out < 0 931: && !strcmp(filename_out, "-")) { 932: fd_out = STDOUT_FILENO; 933: } 934: if (fd_in < 0) { 935: if (strcmp(filename_in, filename_out) == 0) { 936: fd_in = fd_out = open(filename_in, O_RDWR | O_NONBLOCK); 937: } 938: else { 939: fd_in = open(filename_in, O_RDONLY | O_NONBLOCK); 940: } 941: if (fd_in < 0) { 942: tme_output_append_error(_output, "%s", filename_in); 943: return (errno); 944: } 945: } 946: if (fd_out < 0) { 947: fd_out = open(filename_out, O_WRONLY | O_NONBLOCK); 948: if (fd_out < 0) { 949: saved_errno = errno; 950: close(fd_in); 951: tme_output_append_error(_output, "%s", filename_out); 952: return (saved_errno); 953: } 954: } 955: 956: /* start the serial structure: */ 957: serial = tme_new0(struct tme_posix_serial, 1); 958: serial->tme_posix_serial_element = element; 959: serial->tme_posix_serial_fd_in = fd_in; 960: serial->tme_posix_serial_fd_out = fd_out; 961: serial->tme_posix_serial_emulate_break = emulate_break; 962: serial->tme_posix_serial_ctrl_callout = 0; 963: serial->tme_posix_serial_ctrl_callout_last = 0; 964: tme_serial_buffer_init(&serial->tme_posix_serial_buffer_in, 965: TME_POSIX_SERIAL_BUFFER_SIZE); 966: tme_serial_buffer_init(&serial->tme_posix_serial_buffer_out, 967: TME_POSIX_SERIAL_BUFFER_SIZE); 968: 969: /* start the threads: */ 970: tme_mutex_init(&serial->tme_posix_serial_mutex); 971: tme_cond_init(&serial->tme_posix_serial_cond_writer); 972: tme_thread_create((tme_thread_t) _tme_posix_serial_th_writer, serial); 973: tme_thread_create((tme_thread_t) _tme_posix_serial_th_reader, serial); 974: tme_thread_create((tme_thread_t) _tme_posix_serial_th_ctrl, serial); 975: 976: /* fill the element: */ 977: element->tme_element_private = serial; 978: element->tme_element_connections_new = _tme_posix_serial_connections_new; 979: 980: return (TME_OK); 981: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.