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

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

unix.superglobalmegacorp.com

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