Annotation of tme/host/posix/posix-serial.c, revision 1.1.1.3

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.