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

1.1     ! root        1: /* $Id: posix-serial.c,v 1.7 2003/05/16 21:48:09 fredette Exp $ */
        !             2: 
        !             3: /* host/posix/serial.c - implementation of serial ports on a POSIX system: */
        !             4: 
        !             5: /*
        !             6:  * Copyright (c) 2003 Matt Fredette
        !             7:  * All rights reserved.
        !             8:  *
        !             9:  * Redistribution and use in source and binary forms, with or without
        !            10:  * modification, are permitted provided that the following conditions
        !            11:  * are met:
        !            12:  * 1. Redistributions of source code must retain the above copyright
        !            13:  *    notice, this list of conditions and the following disclaimer.
        !            14:  * 2. Redistributions in binary form must reproduce the above copyright
        !            15:  *    notice, this list of conditions and the following disclaimer in the
        !            16:  *    documentation and/or other materials provided with the distribution.
        !            17:  * 3. All advertising materials mentioning features or use of this software
        !            18:  *    must display the following acknowledgement:
        !            19:  *      This product includes software developed by Matt Fredette.
        !            20:  * 4. The name of the author may not be used to endorse or promote products
        !            21:  *    derived from this software without specific prior written permission.
        !            22:  *
        !            23:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
        !            24:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
        !            25:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
        !            26:  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
        !            27:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
        !            28:  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
        !            29:  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            30:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
        !            31:  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
        !            32:  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
        !            33:  * POSSIBILITY OF SUCH DAMAGE.
        !            34:  */
        !            35: 
        !            36: #include <tme/common.h>
        !            37: _TME_RCSID("$Id: posix-serial.c,v 1.7 2003/05/16 21:48:09 fredette Exp $");
        !            38: 
        !            39: /* includes: */
        !            40: #include <tme/generic/serial.h>
        !            41: #include <tme/threads.h>
        !            42: #include <unistd.h>
        !            43: #include <fcntl.h>
        !            44: #include <stdio.h>
        !            45: #include <termios.h>
        !            46: #include <sys/types.h>
        !            47: #include <sys/time.h>
        !            48: #include <sys/ioctl.h>
        !            49: 
        !            50: /* macros: */
        !            51: #define TME_POSIX_SERIAL_BUFFER_SIZE   (4096)
        !            52: 
        !            53: /* structures: */
        !            54: struct tme_posix_serial {
        !            55: 
        !            56:   /* our mutex: */
        !            57:   tme_mutex_t tme_posix_serial_mutex;
        !            58: 
        !            59:   /* backpointer to our element: */
        !            60:   struct tme_element *tme_posix_serial_element;
        !            61: 
        !            62:   /* our connection: */
        !            63:   struct tme_serial_connection *tme_posix_serial_connection;
        !            64: 
        !            65:   /* our writer thread condition: */
        !            66:   tme_cond_t tme_posix_serial_cond_writer;
        !            67: 
        !            68:   /* this is nonzero iff callouts are running: */
        !            69:   int tme_posix_serial_callouts_running;
        !            70: 
        !            71:   /* our input file descriptor: */
        !            72:   int tme_posix_serial_fd_in;
        !            73: 
        !            74:   /* our output file descriptor: */
        !            75:   int tme_posix_serial_fd_out;
        !            76: 
        !            77:   /* if we're emulating break: */
        !            78:   int tme_posix_serial_emulate_break;
        !            79: 
        !            80:   /* our current control inputs: */
        !            81:   unsigned int tme_posix_serial_ctrl_callin;
        !            82: 
        !            83:   /* the number of control output cycles to assert BREAK: */
        !            84:   int tme_posix_serial_ctrl_callout_break;
        !            85: 
        !            86:   /* our current control outputs: */
        !            87:   unsigned int tme_posix_serial_ctrl_callout;
        !            88: 
        !            89:   /* the last control outputs we successfully called out: */
        !            90:   unsigned int tme_posix_serial_ctrl_callout_last;
        !            91:   
        !            92:   /* our input and output buffers: */
        !            93:   struct tme_serial_buffer tme_posix_serial_buffer_in;
        !            94:   struct tme_serial_buffer tme_posix_serial_buffer_out;
        !            95: 
        !            96:   /* our input scanner state: */
        !            97:   int tme_posix_serial_input_scanner_state;
        !            98: };
        !            99: 
        !           100: /* the serial callout function.  it must be called with the mutex locked: */
        !           101: static void
        !           102: _tme_posix_serial_callout(struct tme_posix_serial *serial)
        !           103: {
        !           104:   struct tme_serial_connection *conn_serial;
        !           105:   unsigned int ctrl;
        !           106:   tme_uint8_t buffer_input[1024];
        !           107:   unsigned int buffer_input_size;
        !           108:   tme_serial_data_flags_t data_flags;
        !           109:   int rc;
        !           110:   int again;
        !           111: 
        !           112:   /* if this function is already running in another thread, return
        !           113:      now.  the other thread will do our work: */
        !           114:   if (serial->tme_posix_serial_callouts_running) {
        !           115:     return;
        !           116:   }
        !           117: 
        !           118:   /* callouts are now running: */
        !           119:   serial->tme_posix_serial_callouts_running = TRUE;
        !           120: 
        !           121:   /* loop running callouts until there is nothing to do: */
        !           122:   for (again = TRUE; again;) {
        !           123:     again = FALSE;
        !           124: 
        !           125:     /* if we're connected: */
        !           126:     conn_serial = serial->tme_posix_serial_connection;
        !           127:     if (conn_serial != NULL) {
        !           128: 
        !           129:       /* if we need to notify our connection of new ctrl bits: */
        !           130:       ctrl = serial->tme_posix_serial_ctrl_callout;
        !           131:       if (ctrl != serial->tme_posix_serial_ctrl_callout_last) {
        !           132: 
        !           133:        /* unlock the mutex: */
        !           134:        tme_mutex_unlock(&serial->tme_posix_serial_mutex);
        !           135: 
        !           136:        /* try to do the notify: */
        !           137:        rc = (*conn_serial->tme_serial_connection_ctrl)(conn_serial, ctrl);
        !           138: 
        !           139:        /* lock the mutex: */
        !           140:        tme_mutex_lock(&serial->tme_posix_serial_mutex);
        !           141: 
        !           142:        /* if the notify was successful, note what we sent, and allow
        !           143:           the outermost loop to run again: */
        !           144:        if (rc == TME_OK) {
        !           145:          serial->tme_posix_serial_ctrl_callout_last = ctrl;
        !           146:          again = TRUE;
        !           147:        }
        !           148:       }
        !           149: 
        !           150:       /* if our connection is readable, and our output buffer isn't full,
        !           151:         read the connection: */
        !           152:       if ((serial->tme_posix_serial_ctrl_callin & TME_SERIAL_CTRL_OK_READ)
        !           153:          && !tme_serial_buffer_is_full(&serial->tme_posix_serial_buffer_out)) {
        !           154: 
        !           155:        /* get the minimum of the free space in the output buffer and
        !           156:           the size of our stack buffer: */
        !           157:        buffer_input_size = tme_serial_buffer_space_free(&serial->tme_posix_serial_buffer_out);
        !           158:        buffer_input_size = TME_MIN(buffer_input_size, sizeof(buffer_input));
        !           159: 
        !           160:        /* unlock the mutex: */
        !           161:        tme_mutex_unlock(&serial->tme_posix_serial_mutex);
        !           162: 
        !           163:        /* do the read: */
        !           164:        rc = (*conn_serial->tme_serial_connection_read)
        !           165:          (conn_serial,
        !           166:           buffer_input, 
        !           167:           buffer_input_size,
        !           168:           &data_flags);
        !           169: 
        !           170:        /* lock the mutex: */
        !           171:        tme_mutex_lock(&serial->tme_posix_serial_mutex);
        !           172: 
        !           173:        /* if the read was successful, add what we got and notify the
        !           174:           writer so that it may try to write: */
        !           175:        if (rc > 0) {
        !           176:          (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_out,
        !           177:                                          buffer_input,
        !           178:                                          rc,
        !           179:                                          data_flags,
        !           180:                                          TME_SERIAL_COPY_NORMAL);
        !           181:          tme_cond_notify(&serial->tme_posix_serial_cond_writer, TRUE);
        !           182: 
        !           183:          /* allow the outermost loop to run again: */
        !           184:          again = TRUE;
        !           185:        }
        !           186: 
        !           187:        /* otherwise, the read failed, and the convention dictates
        !           188:           that we forget this connection was readable: */
        !           189:        else {
        !           190:          serial->tme_posix_serial_ctrl_callin &= ~TME_SERIAL_CTRL_OK_READ;
        !           191:        }
        !           192:       }
        !           193:     }
        !           194:   }
        !           195: 
        !           196:   /* there are no more callouts to make: */
        !           197:   serial->tme_posix_serial_callouts_running = FALSE;
        !           198: }
        !           199: 
        !           200: /* the serial control thread: */
        !           201: static void
        !           202: _tme_posix_serial_th_ctrl(struct tme_posix_serial *serial)
        !           203: {
        !           204:   int modem_state, modem_state_out;
        !           205:   unsigned int ctrl;
        !           206: 
        !           207:   /* loop forever: */
        !           208:   for (;;) {
        !           209:    
        !           210:     /* get the modem state of the input device: */
        !           211:     ioctl(serial->tme_posix_serial_fd_in, TIOCMGET, &modem_state);
        !           212: 
        !           213:     /* if the output device is different, get the modem state of the
        !           214:        output device and merge it in: */
        !           215:     if (serial->tme_posix_serial_fd_out
        !           216:        != serial->tme_posix_serial_fd_in) {
        !           217:       ioctl(serial->tme_posix_serial_fd_in, TIOCMGET, &modem_state_out);
        !           218:       modem_state &= ~(TIOCM_DTR | TIOCM_RTS | TIOCM_CTS);
        !           219:       modem_state |= modem_state_out & ~(TIOCM_CD | TIOCM_RI | TIOCM_DSR);
        !           220:     }
        !           221:     
        !           222:     /* lock the mutex: */
        !           223:     tme_mutex_lock(&serial->tme_posix_serial_mutex);
        !           224:     
        !           225:     /* update the control outputs: */
        !           226:     ctrl = (serial->tme_posix_serial_ctrl_callout
        !           227:            & ~(TME_SERIAL_CTRL_CTS
        !           228:                | TME_SERIAL_CTRL_DCD
        !           229:                | TME_SERIAL_CTRL_RI
        !           230:                | TME_SERIAL_CTRL_BREAK));
        !           231:     if (modem_state & TIOCM_CTS) {
        !           232:       ctrl |= TME_SERIAL_CTRL_CTS;
        !           233:     }
        !           234:     if (modem_state & TIOCM_CD) {
        !           235:       ctrl |= TME_SERIAL_CTRL_DCD;
        !           236:     }
        !           237:     if (modem_state & TIOCM_RI) {
        !           238:       ctrl |= TME_SERIAL_CTRL_RI;
        !           239:     }
        !           240:     if (serial->tme_posix_serial_ctrl_callout_break > 0) {
        !           241:       ctrl |= TME_SERIAL_CTRL_BREAK;
        !           242:       serial->tme_posix_serial_ctrl_callout_break--;
        !           243:     }
        !           244:     
        !           245:     /* if the control outputs have changed, call out the change: */
        !           246:     if (ctrl != serial->tme_posix_serial_ctrl_callout) {
        !           247:       serial->tme_posix_serial_ctrl_callout = ctrl;
        !           248:       _tme_posix_serial_callout(serial);
        !           249:     }
        !           250:     
        !           251:     /* unlock the mutex: */
        !           252:     tme_mutex_unlock(&serial->tme_posix_serial_mutex);
        !           253: 
        !           254:     /* check the controls again in .5 seconds: */
        !           255:     tme_thread_sleep_yield(0, 500000);
        !           256:   }
        !           257:   /* NOTREACHED */
        !           258: }
        !           259: 
        !           260: /* the serial writer thread: */
        !           261: static void
        !           262: _tme_posix_serial_th_writer(struct tme_posix_serial *serial)
        !           263: {
        !           264:   tme_uint8_t buffer_output[1024];
        !           265:   unsigned int buffer_output_size;
        !           266:   int rc;
        !           267: 
        !           268:   /* lock the mutex: */
        !           269:   tme_mutex_lock(&serial->tme_posix_serial_mutex);
        !           270: 
        !           271:   /* loop forever: */
        !           272:   for (;;) {
        !           273: 
        !           274:     /* if there is no data to write, wait on the writer condition,
        !           275:        after which there must be data to write: */
        !           276:     if (tme_serial_buffer_is_empty(&serial->tme_posix_serial_buffer_out)) {
        !           277:       tme_cond_wait_yield(&serial->tme_posix_serial_cond_writer,
        !           278:                          &serial->tme_posix_serial_mutex);
        !           279:     }
        !           280: 
        !           281:     /* get the data to write: */
        !           282:     buffer_output_size = 
        !           283:       tme_serial_buffer_copyout(&serial->tme_posix_serial_buffer_out,
        !           284:                                buffer_output,
        !           285:                                sizeof(buffer_output),
        !           286:                                NULL,
        !           287:                                TME_SERIAL_COPY_PEEK);
        !           288:     assert(buffer_output_size > 0);
        !           289: 
        !           290:     /* unlock the mutex: */
        !           291:     tme_mutex_unlock(&serial->tme_posix_serial_mutex);
        !           292: 
        !           293:     /* try to write the device: */
        !           294:     rc = tme_thread_write_yield(serial->tme_posix_serial_fd_out,
        !           295:                                buffer_output,
        !           296:                                buffer_output_size);
        !           297: 
        !           298:     /* lock the mutex: */
        !           299:     tme_mutex_lock(&serial->tme_posix_serial_mutex);
        !           300: 
        !           301:     /* if the write was successful: */
        !           302:     if (rc > 0) {
        !           303: 
        !           304:       /* remove the written data from the output buffer: */
        !           305:       tme_serial_buffer_copyout(&serial->tme_posix_serial_buffer_out,
        !           306:                                NULL,
        !           307:                                rc,
        !           308:                                NULL,
        !           309:                                TME_SERIAL_COPY_NORMAL);
        !           310: 
        !           311:       /* call out for more data: */
        !           312:       _tme_posix_serial_callout(serial);
        !           313:     }
        !           314:   }
        !           315:   /* NOTREACHED */
        !           316: }
        !           317:     
        !           318: /* the serial reader thread: */
        !           319: static void
        !           320: _tme_posix_serial_th_reader(struct tme_posix_serial *serial)
        !           321: {
        !           322:   tme_uint8_t buffer_input[1024];
        !           323:   tme_uint8_t buffer_slack[10];
        !           324:   tme_uint8_t byte, *byte_head, *byte_tail;
        !           325:   int scanner_state;
        !           326:   int buffer_was_empty;
        !           327:   int rc;
        !           328: 
        !           329:   /* loop forever: */
        !           330:   for (;;) {
        !           331: 
        !           332:     /* try to read the device: */
        !           333:     rc = tme_thread_read_yield(serial->tme_posix_serial_fd_in,
        !           334:                               buffer_input,
        !           335:                               sizeof(buffer_input));
        !           336: 
        !           337:     /* if the read failed: */
        !           338:     if (rc <= 0) {
        !           339:       /* XXX diagnostic */
        !           340:       continue;
        !           341:     }
        !           342: 
        !           343:     /* lock the mutex: */
        !           344:     tme_mutex_lock(&serial->tme_posix_serial_mutex);
        !           345: 
        !           346:     /* remember if this buffer was empty: */
        !           347:     buffer_was_empty =
        !           348:       tme_serial_buffer_is_empty(&serial->tme_posix_serial_buffer_in);
        !           349: 
        !           350:     /* scan the input: */
        !           351:     byte_head = buffer_input;
        !           352:     scanner_state = serial->tme_posix_serial_input_scanner_state;
        !           353:     for (; rc > 0; ) {
        !           354:          
        !           355:       /* in state zero, we haven't seen anything interesting: */
        !           356:       if (scanner_state == 0) {
        !           357:        
        !           358:        /* scan quickly, stopping only when we see an escape: */
        !           359:        byte_tail = byte_head;
        !           360:        if (serial->tme_posix_serial_emulate_break) {
        !           361:          do {
        !           362:            byte = *(byte_head++);
        !           363:            if (byte == 0xff
        !           364:                || byte == '^') {
        !           365:              break;
        !           366:            }
        !           367:          } while (--rc > 0);
        !           368:        }
        !           369:        else {
        !           370:          do {
        !           371:            byte = *(byte_head++);
        !           372:            if (byte == 0xff) {
        !           373:              break;
        !           374:            }
        !           375:          } while (--rc > 0);
        !           376:        }
        !           377:        
        !           378:        /* if we stopped scanning because we saw an escape, back up: */
        !           379:        if (rc > 0) {
        !           380:          byte_head--;
        !           381:        }
        !           382: 
        !           383:        /* add in the normal data we scanned quickly: */
        !           384:        if (byte_head > byte_tail) {
        !           385:          (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_in,
        !           386:                                          byte_tail,
        !           387:                                          (byte_head - byte_tail),
        !           388:                                          TME_SERIAL_DATA_NORMAL,
        !           389:                                          TME_SERIAL_COPY_FULL_IS_OVERRUN);
        !           390:        }
        !           391: 
        !           392:        /* if this byte is an 0xff, move to state one: */
        !           393:        if (byte == 0xff) {
        !           394:          byte_head++;
        !           395:          --rc;
        !           396:          scanner_state = 1;
        !           397:        }
        !           398: 
        !           399:        /* if we're emulating breaks, and this is a carat, move to
        !           400:           state eight: */
        !           401:        else if (serial->tme_posix_serial_emulate_break
        !           402:                 && byte == '^') {
        !           403:          byte_head++;
        !           404:          --rc;
        !           405:          scanner_state = 8;
        !           406:        }
        !           407:        
        !           408:        /* otherwise, we must have drained everything: */
        !           409:        else {
        !           410:          assert(rc == 0);
        !           411:        }
        !           412:       }
        !           413: 
        !           414:       /* in state one, we have seen 0xff: */
        !           415:       else if (scanner_state == 1) {
        !           416: 
        !           417:        /* get the next byte: */
        !           418:        byte = *(byte_head++);
        !           419:        --rc;
        !           420:        
        !           421:        /* if this is an 0x00, move to state two: */
        !           422:        if (byte == 0x00) {
        !           423:          scanner_state = 2;
        !           424:        }
        !           425:        
        !           426:        /* otherwise, receive 0xff, and if this is not an 0xff, return
        !           427:           it to the buffer.  move to state zero: */
        !           428:        else {
        !           429:          buffer_slack[0] = 0xff;
        !           430:          (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_in,
        !           431:                                          buffer_slack,
        !           432:                                          1,
        !           433:                                          TME_SERIAL_DATA_NORMAL,
        !           434:                                          TME_SERIAL_COPY_FULL_IS_OVERRUN);
        !           435:          if (byte != 0xff) {
        !           436:            byte_head--;
        !           437:            ++rc;
        !           438:          }
        !           439:          scanner_state = 0;
        !           440:        }
        !           441:       }
        !           442: 
        !           443:       /* in state two, we have seen 0xff 0x00: */
        !           444:       else if (scanner_state == 2) {
        !           445: 
        !           446:        /* get the next byte: */
        !           447:        byte = *(byte_head++);
        !           448:        --rc;
        !           449:        
        !           450:        /* if this is an 0x00, this is a break: */
        !           451:        if (byte == 0x00) {
        !           452: 
        !           453:          /* if break isn't already being asserted, assert it and call
        !           454:             out the change.  always reset the assertion time: */
        !           455:          if (!(serial->tme_posix_serial_ctrl_callout & TME_SERIAL_CTRL_BREAK)) {
        !           456:            serial->tme_posix_serial_ctrl_callout |= TME_SERIAL_CTRL_BREAK;
        !           457:            _tme_posix_serial_callout(serial);
        !           458:          }
        !           459:          serial->tme_posix_serial_ctrl_callout_break = 2;
        !           460:        }
        !           461: 
        !           462:        /* otherwise, this byte was received with bad framing or
        !           463:           parity.  we can't tell which, so call it bad parity: */
        !           464:        else {
        !           465:          (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_in,
        !           466:                                          byte_head - 1,
        !           467:                                          1,
        !           468:                                          TME_SERIAL_DATA_BAD_PARITY,
        !           469:                                          TME_SERIAL_COPY_FULL_IS_OVERRUN);
        !           470:        }
        !           471:        
        !           472:        /* move to state zero: */
        !           473:        scanner_state = 0;
        !           474:       }
        !           475: 
        !           476:       /* in states eight and nine, we have seen one or two break escapes,
        !           477:         respectively: */
        !           478:       else if (scanner_state == 8
        !           479:               || scanner_state == 9) {
        !           480:        assert(serial->tme_posix_serial_emulate_break);
        !           481:        
        !           482:        /* get the next byte: */
        !           483:        byte = *(byte_head++);
        !           484:        --rc;
        !           485:        
        !           486:        /* if this isn't also a break escape, return it to the
        !           487:           buffer, receive the break escapes, and move to state
        !           488:           zero: */
        !           489:        if (byte != '^') {
        !           490:          byte_head--;
        !           491:          ++rc;
        !           492:          buffer_slack[0] = buffer_slack[1] = '^';
        !           493:          (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_in,
        !           494:                                          buffer_slack,
        !           495:                                          scanner_state - 7,
        !           496:                                          TME_SERIAL_DATA_NORMAL,
        !           497:                                          TME_SERIAL_COPY_FULL_IS_OVERRUN);
        !           498:          scanner_state = 0;
        !           499:        }
        !           500:        
        !           501:        /* otherwise, this is a break escape.  if we have now seen
        !           502:           three total, this is a break, and move to state zero: */
        !           503:        else if (++scanner_state == 10) {
        !           504: 
        !           505:          /* if break isn't already being asserted, assert it and call
        !           506:             out the change.  always reset the assertion time: */
        !           507:          if (!(serial->tme_posix_serial_ctrl_callout & TME_SERIAL_CTRL_BREAK)) {
        !           508:            serial->tme_posix_serial_ctrl_callout |= TME_SERIAL_CTRL_BREAK;
        !           509:            _tme_posix_serial_callout(serial);
        !           510:          }
        !           511:          serial->tme_posix_serial_ctrl_callout_break = 2;
        !           512:          scanner_state = 0;
        !           513:        }
        !           514:       }
        !           515:       
        !           516:       /* any other state is impossible: */
        !           517:       else {
        !           518:        assert(FALSE);
        !           519:       }
        !           520:     }
        !           521:              
        !           522:     /* update the scanner state: */
        !           523:     serial->tme_posix_serial_input_scanner_state = scanner_state;
        !           524: 
        !           525:     /* if the input buffer was previously empty, and it is now not
        !           526:        empty, call out that we can be read again: */
        !           527:     if (buffer_was_empty
        !           528:        && !tme_serial_buffer_is_empty(&serial->tme_posix_serial_buffer_in)) {
        !           529:       serial->tme_posix_serial_ctrl_callout |= TME_SERIAL_CTRL_OK_READ;
        !           530:       _tme_posix_serial_callout(serial);
        !           531:     }
        !           532: 
        !           533:     /* unlock the mutex: */
        !           534:     tme_mutex_unlock(&serial->tme_posix_serial_mutex);
        !           535:   }
        !           536:   /* NOTREACHED */
        !           537: }
        !           538: 
        !           539: /* the serial configuration callin function: */
        !           540: static int
        !           541: _tme_posix_serial_config(struct tme_serial_connection *conn_serial, struct tme_serial_config *config)
        !           542: {
        !           543:   struct tme_posix_serial *serial;
        !           544:   struct termios serial_termios;
        !           545:   tme_uint32_t config_baud;
        !           546:   speed_t termios_baud;
        !           547:   int is_input, rc;
        !           548: 
        !           549:   /* recover our data structure: */
        !           550:   serial = conn_serial->tme_serial_connection.tme_connection_element->tme_element_private;
        !           551: 
        !           552:   /* lock our mutex: */
        !           553:   tme_mutex_lock(&serial->tme_posix_serial_mutex);
        !           554: 
        !           555:   /* loop over the input and output devices: */
        !           556:   for (is_input = 2; is_input-- > 0; ) {
        !           557: 
        !           558:     /* get the current configuration of the device: */
        !           559:     rc = tcgetattr((is_input
        !           560:                    ? serial->tme_posix_serial_fd_in
        !           561:                    : serial->tme_posix_serial_fd_out),
        !           562:                   &serial_termios);
        !           563:     
        !           564:     /* update the configuration: */
        !           565:     
        !           566:     /* baud rate: */
        !           567:     config_baud = config->tme_serial_config_baud;
        !           568:     termios_baud = (config_baud == 0 ? B0
        !           569:                    : config_baud <= 50 ? B50
        !           570:                    : config_baud <= 75 ? B75
        !           571:                    : config_baud <= 110 ? B110
        !           572:                    : config_baud <= 134 ? B134
        !           573:                    : config_baud <= 150 ? B150
        !           574:                    : config_baud <= 200 ? B200
        !           575:                    : config_baud <= 300 ? B300
        !           576:                    : config_baud <= 600 ? B600
        !           577:                    : config_baud <= 1200 ? B1200
        !           578:                    : config_baud <= 1800 ? B1800
        !           579:                    : config_baud <= 2400 ? B2400
        !           580:                    : config_baud <= 4800 ? B4800
        !           581:                    : config_baud <= 9600 ? B9600
        !           582:                    : config_baud <= 19200 ? B19200
        !           583:                    : config_baud <= 38400 ? B38400
        !           584:                    : (speed_t) -1);
        !           585:     if (termios_baud == (speed_t) -1) {
        !           586:       /* XXX diagnostic */
        !           587:       termios_baud = B38400;
        !           588:     }
        !           589:     rc = cfsetspeed(&serial_termios, termios_baud);
        !           590: 
        !           591:     /* input mode or output mode: */
        !           592:     if (is_input) {
        !           593:       serial_termios.c_iflag = PARMRK;
        !           594:       if (config->tme_serial_config_flags & TME_SERIAL_FLAGS_CHECK_PARITY) {
        !           595:        serial_termios.c_iflag = INPCK;
        !           596:       }
        !           597:     }
        !           598:     else {
        !           599:       serial_termios.c_oflag = 0;
        !           600:     }
        !           601: 
        !           602:     /* control mode: */
        !           603:     serial_termios.c_cflag = CREAD | CLOCAL;
        !           604:     switch (config->tme_serial_config_bits_data) {
        !           605:     case 5: serial_termios.c_cflag |= CS5; break;
        !           606:     case 6: serial_termios.c_cflag |= CS6; break;
        !           607:     case 7: serial_termios.c_cflag |= CS7; break;
        !           608:     default: assert(FALSE);
        !           609:     case 8: serial_termios.c_cflag |= CS8; break;
        !           610:     }
        !           611:     switch (config->tme_serial_config_bits_stop) {
        !           612:     case 1: break;
        !           613:     default: assert(FALSE);
        !           614:     case 2: serial_termios.c_cflag |= CSTOPB; break;
        !           615:     }
        !           616:     switch (config->tme_serial_config_parity) {
        !           617:     default: assert(FALSE);
        !           618:     case TME_SERIAL_PARITY_NONE: break;
        !           619:     case TME_SERIAL_PARITY_ODD: serial_termios.c_cflag |= PARENB | PARODD; break;
        !           620:     case TME_SERIAL_PARITY_EVEN: serial_termios.c_cflag |= PARENB; break;
        !           621:     }
        !           622:   
        !           623:     /* local mode: */
        !           624:     serial_termios.c_lflag = 0;
        !           625: 
        !           626:     /* set the configuration on the devices: */
        !           627:     rc = tcsetattr((is_input
        !           628:                    ? serial->tme_posix_serial_fd_in
        !           629:                    : serial->tme_posix_serial_fd_out),
        !           630:                   TCSADRAIN,
        !           631:                   &serial_termios);
        !           632:   }
        !           633: 
        !           634:   /* unlock our mutex: */
        !           635:   tme_mutex_unlock(&serial->tme_posix_serial_mutex);
        !           636: 
        !           637:   /* done: */
        !           638:   return (TME_OK);
        !           639: }
        !           640: 
        !           641: /* the serial control callin function: */
        !           642: static int
        !           643: _tme_posix_serial_ctrl(struct tme_serial_connection *conn_serial, unsigned int control)
        !           644: {
        !           645:   struct tme_posix_serial *serial;
        !           646:   int modem_state;
        !           647:   int rc;
        !           648: 
        !           649:   /* recover our data structure: */
        !           650:   serial = conn_serial->tme_serial_connection.tme_connection_element->tme_element_private;
        !           651: 
        !           652:   /* lock our mutex: */
        !           653:   tme_mutex_lock(&serial->tme_posix_serial_mutex);
        !           654: 
        !           655:   /* get the current output device modem state: */
        !           656:   rc = ioctl(serial->tme_posix_serial_fd_out, TIOCMGET, &modem_state);
        !           657: 
        !           658:   /* update the modem state: */
        !           659:   if (control & TME_SERIAL_CTRL_DTR) {
        !           660:     modem_state |= TIOCM_DTR;
        !           661:   }
        !           662:   else {
        !           663:     modem_state &= TIOCM_DTR;
        !           664:   }
        !           665:   if (control & TME_SERIAL_CTRL_RTS) {
        !           666:     modem_state |= TIOCM_RTS;
        !           667:   }
        !           668:   else {
        !           669:     modem_state &= TIOCM_RTS;
        !           670:   }
        !           671: 
        !           672:   /* set the new modem state: */
        !           673:   rc = ioctl(serial->tme_posix_serial_fd_out, TIOCMSET, &modem_state);
        !           674: 
        !           675:   /* send a break: */
        !           676:   if (control & TME_SERIAL_CTRL_BREAK) {
        !           677:     tcsendbreak(serial->tme_posix_serial_fd_out, 0);
        !           678:   }
        !           679: 
        !           680:   /* remember these controls.  if the OK_READ is set, call out a read: */
        !           681:   serial->tme_posix_serial_ctrl_callin = control;
        !           682:   if (control & TME_SERIAL_CTRL_OK_READ) {
        !           683:     _tme_posix_serial_callout(serial);
        !           684:   }
        !           685: 
        !           686:   /* unlock our mutex: */
        !           687:   tme_mutex_unlock(&serial->tme_posix_serial_mutex);
        !           688: 
        !           689:   /* done: */
        !           690:   return (TME_OK);
        !           691: }
        !           692: 
        !           693: /* the serial read callin function: */
        !           694: static int
        !           695: _tme_posix_serial_read(struct tme_serial_connection *conn_serial, 
        !           696:                       tme_uint8_t *data, unsigned int count,
        !           697:                       tme_serial_data_flags_t *_data_flags)
        !           698: {
        !           699:   struct tme_posix_serial *serial;
        !           700:   unsigned int rc;
        !           701: 
        !           702:   /* recover our data structure: */
        !           703:   serial = conn_serial->tme_serial_connection.tme_connection_element->tme_element_private;
        !           704: 
        !           705:   /* lock the mutex: */
        !           706:   tme_mutex_lock(&serial->tme_posix_serial_mutex);
        !           707: 
        !           708:   /* copy data out of the input buffer: */
        !           709:   rc = tme_serial_buffer_copyout(&serial->tme_posix_serial_buffer_in,
        !           710:                                 data, count,
        !           711:                                 _data_flags,
        !           712:                                 TME_SERIAL_COPY_NORMAL);
        !           713: 
        !           714:   /* if the input buffer is now empty, our connection shouldn't read
        !           715:      again until we have filled some of the buffer.  we don't call
        !           716:      this transition out, because the convention dictates that
        !           717:      our connection forget that we're readable: */
        !           718:   if (rc < count) {
        !           719:     serial->tme_posix_serial_ctrl_callout &= ~TME_SERIAL_CTRL_OK_READ;
        !           720:     serial->tme_posix_serial_ctrl_callout_last &= ~TME_SERIAL_CTRL_OK_READ;
        !           721:   }
        !           722: 
        !           723:   /* unlock the mutex: */
        !           724:   tme_mutex_unlock(&serial->tme_posix_serial_mutex);
        !           725: 
        !           726:   /* done: */
        !           727:   return (rc);
        !           728: }
        !           729: 
        !           730: /* this scores a connection: */
        !           731: static int
        !           732: _tme_posix_serial_connection_score(struct tme_connection *conn, unsigned int *_score)
        !           733: {
        !           734:   struct tme_posix_serial *serial;
        !           735: 
        !           736:   /* recover our serial: */
        !           737:   serial = conn->tme_connection_element->tme_element_private;
        !           738: 
        !           739:   /* both sides must be serial connections: */
        !           740:   assert(conn->tme_connection_type == TME_CONNECTION_SERIAL);
        !           741:   assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_SERIAL);
        !           742: 
        !           743:   /* serial connections are always good: */
        !           744:   *_score = 1;
        !           745:   return (TME_OK);
        !           746: }
        !           747: 
        !           748: /* this makes a new connection: */
        !           749: static int
        !           750: _tme_posix_serial_connection_make(struct tme_connection *conn, unsigned int state)
        !           751: {
        !           752:   struct tme_posix_serial *serial;
        !           753: 
        !           754:   /* recover our serial: */
        !           755:   serial = conn->tme_connection_element->tme_element_private;
        !           756: 
        !           757:   /* both sides must be generic bus connections: */
        !           758:   assert(conn->tme_connection_type == TME_CONNECTION_SERIAL);
        !           759:   assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_SERIAL);
        !           760: 
        !           761:   /* serials are always set up to answer calls across the connection,
        !           762:      so we only have to do work when the connection has gone full,
        !           763:      namely taking the other side of the connection: */
        !           764:   if (state == TME_CONNECTION_FULL) {
        !           765: 
        !           766:     /* lock our mutex: */
        !           767:     tme_mutex_lock(&serial->tme_posix_serial_mutex);
        !           768: 
        !           769:     /* save our connection: */
        !           770:     serial->tme_posix_serial_connection =
        !           771:       (struct tme_serial_connection *) conn->tme_connection_other;
        !           772: 
        !           773:     /* do any pending callouts: */
        !           774:     _tme_posix_serial_callout(serial);
        !           775: 
        !           776:     /* unlock our mutex: */
        !           777:     tme_mutex_unlock(&serial->tme_posix_serial_mutex);
        !           778:   }
        !           779: 
        !           780:   return (TME_OK);
        !           781: }
        !           782: 
        !           783: /* this breaks a connection: */
        !           784: static int 
        !           785: _tme_posix_serial_connection_break(struct tme_connection *conn, unsigned int state)
        !           786: {
        !           787:   abort();
        !           788: }
        !           789: 
        !           790: /* this makes a new connection side for a serial: */
        !           791: static int
        !           792: _tme_posix_serial_connections_new(struct tme_element *element,
        !           793:                                  const char * const *args,
        !           794:                                  struct tme_connection **_conns,
        !           795:                                  char **_output)
        !           796: {
        !           797:   struct tme_posix_serial *serial;
        !           798:   struct tme_serial_connection *conn_serial;
        !           799:   struct tme_connection *conn;
        !           800: 
        !           801:   /* recover our serial: */
        !           802:   serial = (struct tme_posix_serial *) element->tme_element_private;
        !           803: 
        !           804:   /* if this serial is already connected, we can do nothing: */
        !           805:   if (TME_ATOMIC_READ(struct tme_serial_connection *,
        !           806:                      serial->tme_posix_serial_connection) != NULL) {
        !           807:     return (EISCONN);
        !           808:   }
        !           809: 
        !           810:   /* create our side of a serial connection: */
        !           811:   conn_serial = tme_new0(struct tme_serial_connection, 1);
        !           812:   conn = &conn_serial->tme_serial_connection;
        !           813: 
        !           814:   /* fill in the generic connection: */
        !           815:   conn->tme_connection_next = *_conns;
        !           816:   conn->tme_connection_type = TME_CONNECTION_SERIAL;
        !           817:   conn->tme_connection_score = _tme_posix_serial_connection_score;
        !           818:   conn->tme_connection_make = _tme_posix_serial_connection_make;
        !           819:   conn->tme_connection_break = _tme_posix_serial_connection_break;
        !           820: 
        !           821:   /* fill in the serial connection: */
        !           822:   conn_serial->tme_serial_connection_config = _tme_posix_serial_config;
        !           823:   conn_serial->tme_serial_connection_ctrl = _tme_posix_serial_ctrl;
        !           824:   conn_serial->tme_serial_connection_read = _tme_posix_serial_read;
        !           825: 
        !           826:   /* return the connection side possibility: */
        !           827:   *_conns = conn;
        !           828:   return (TME_OK);
        !           829: }
        !           830: 
        !           831: /* the new serial function: */
        !           832: TME_ELEMENT_SUB_NEW_DECL(tme_host_posix,serial) {
        !           833:   struct tme_posix_serial *serial;
        !           834:   const char *filename_in;
        !           835:   const char *filename_out;
        !           836:   int fd_in, fd_out;
        !           837:   int usage;
        !           838:   int arg_i;
        !           839:   int saved_errno;
        !           840:   int emulate_break;
        !           841: 
        !           842:   /* initialize: */
        !           843:   filename_in = NULL;
        !           844:   filename_out = NULL;
        !           845:   emulate_break = FALSE;
        !           846:   arg_i = 1;
        !           847:   usage = FALSE;
        !           848: 
        !           849:   /* loop reading our arguments: */
        !           850:   for (;;) {
        !           851: 
        !           852:     /* the device we're supposed to use for input: */
        !           853:     if (TME_ARG_IS(args[arg_i + 0], "device-input")
        !           854:        && args[arg_i + 1] != NULL
        !           855:        && filename_in == NULL) {
        !           856:       filename_in = args[arg_i + 1];
        !           857:       arg_i += 2;
        !           858:     }
        !           859: 
        !           860:     /* the device we're supposed to use for output: */
        !           861:     else if (TME_ARG_IS(args[arg_i + 0], "device-output")
        !           862:             && args[arg_i + 1] != NULL
        !           863:             && filename_out == NULL) {
        !           864:       filename_out = args[arg_i + 1];
        !           865:       arg_i += 2;
        !           866:     }
        !           867: 
        !           868:     /* the device we're supposed to use for input and output: */
        !           869:     else if (TME_ARG_IS(args[arg_i + 0], "device")
        !           870:             && args[arg_i + 1] != NULL
        !           871:             && filename_in == NULL
        !           872:             && filename_out == NULL) {
        !           873:       filename_in = filename_out = args[arg_i + 1];
        !           874:       arg_i += 2;
        !           875:     }
        !           876: 
        !           877:     /* if we're supposed to emulate break: */
        !           878:     else if (TME_ARG_IS(args[arg_i + 0], "break-carats")) {
        !           879:       emulate_break = TRUE;
        !           880:       arg_i++;
        !           881:     }
        !           882: 
        !           883:     /* if we've run out of arguments: */
        !           884:     else if (args[arg_i + 0] == NULL) {
        !           885: 
        !           886:       /* we must have been given input and output devices: */
        !           887:       if (filename_in == NULL
        !           888:          || filename_out == NULL) {
        !           889:        usage = TRUE;
        !           890:       }
        !           891:       break;
        !           892:     }
        !           893: 
        !           894:     /* this is a bad argument: */
        !           895:     else {
        !           896:       tme_output_append_error(_output,
        !           897:                              "%s %s", 
        !           898:                              args[arg_i],
        !           899:                              _("unexpected"));
        !           900:       usage = TRUE;
        !           901:       break;
        !           902:     }
        !           903:   }
        !           904: 
        !           905:   if (usage) {
        !           906:     tme_output_append_error(_output, 
        !           907:                            "%s %s { device %s | { device-input %s device-output %s } } [break-carats]",
        !           908:                            _("usage:"),
        !           909:                            args[0],
        !           910:                            _("DEVICE"),
        !           911:                            _("DEVICE"),
        !           912:                            _("DEVICE"));
        !           913:     return (EINVAL);
        !           914:   }
        !           915: 
        !           916:   /* open the devices: */
        !           917:   fd_in = fd_out = -1;
        !           918:   if (fd_in < 0
        !           919:       && !strcmp(filename_in, "-")) {
        !           920:     fd_in = STDIN_FILENO;
        !           921:   }
        !           922:   if (fd_out < 0
        !           923:       && !strcmp(filename_out, "-")) {
        !           924:     fd_out = STDOUT_FILENO;
        !           925:   }
        !           926:   if (fd_in < 0) {
        !           927:     if (strcmp(filename_in, filename_out) == 0) {
        !           928:       fd_in = fd_out = open(filename_in, O_RDWR | O_NONBLOCK);
        !           929:     }
        !           930:     else {
        !           931:       fd_in = open(filename_in, O_RDONLY | O_NONBLOCK);
        !           932:     }
        !           933:     if (fd_in < 0) {
        !           934:       tme_output_append_error(_output, "%s", filename_in);
        !           935:       return (errno);
        !           936:     }
        !           937:   }
        !           938:   if (fd_out < 0) {
        !           939:     fd_out = open(filename_out, O_WRONLY | O_NONBLOCK);
        !           940:     if (fd_out < 0) {
        !           941:       saved_errno = errno;
        !           942:       close(fd_in);
        !           943:       tme_output_append_error(_output, "%s", filename_out);
        !           944:       return (saved_errno);
        !           945:     }
        !           946:   }
        !           947: 
        !           948:   /* start the serial structure: */
        !           949:   serial = tme_new0(struct tme_posix_serial, 1);
        !           950:   serial->tme_posix_serial_element = element;
        !           951:   serial->tme_posix_serial_fd_in = fd_in;
        !           952:   serial->tme_posix_serial_fd_out = fd_out;
        !           953:   serial->tme_posix_serial_emulate_break = emulate_break;
        !           954:   serial->tme_posix_serial_ctrl_callout = 0;
        !           955:   serial->tme_posix_serial_ctrl_callout_last = 0;
        !           956:   tme_serial_buffer_init(&serial->tme_posix_serial_buffer_in, 
        !           957:                         TME_POSIX_SERIAL_BUFFER_SIZE);
        !           958:   tme_serial_buffer_init(&serial->tme_posix_serial_buffer_out, 
        !           959:                         TME_POSIX_SERIAL_BUFFER_SIZE);
        !           960: 
        !           961:   /* start the threads: */
        !           962:   tme_mutex_init(&serial->tme_posix_serial_mutex);
        !           963:   tme_cond_init(&serial->tme_posix_serial_cond_writer);
        !           964:   tme_thread_create((tme_thread_t) _tme_posix_serial_th_writer, serial);
        !           965:   tme_thread_create((tme_thread_t) _tme_posix_serial_th_reader, serial);
        !           966:   tme_thread_create((tme_thread_t) _tme_posix_serial_th_ctrl, serial);
        !           967: 
        !           968:   /* fill the element: */
        !           969:   element->tme_element_private = serial;
        !           970:   element->tme_element_connections_new = _tme_posix_serial_connections_new;
        !           971: 
        !           972:   return (TME_OK);
        !           973: }

unix.superglobalmegacorp.com

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