|
|
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.