|
|
1.1.1.2 ! root 1: /* $Id: posix-serial.c,v 1.8 2003/07/29 18:20:30 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.2 ! root 37: _TME_RCSID("$Id: posix-serial.c,v 1.8 2003/07/29 18:20:30 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: */ 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.