Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:15

0001 /**
0002  * @file
0003  * TERMIOS serial line support
0004  */
0005 
0006 /*
0007  * Copyright (c) 1997 Eric Norum <eric@norum.ca>
0008  *
0009  * The license and distribution terms for this file may be
0010  * found in the file LICENSE in this distribution or at
0011  * http://www.rtems.org/license/LICENSE.
0012  */
0013 
0014 #ifdef HAVE_CONFIG_H
0015 #include "config.h"
0016 #endif
0017 
0018 #include <rtems.h>
0019 #include <rtems/libio.h>
0020 #include <rtems/imfs.h>
0021 #include <rtems/score/assert.h>
0022 #include <ctype.h>
0023 #include <errno.h>
0024 #include <stdio.h>
0025 #include <stdlib.h>
0026 #include <string.h>
0027 #include <termios.h>
0028 #include <unistd.h>
0029 #include <sys/fcntl.h>
0030 #include <sys/filio.h>
0031 #include <sys/ttycom.h>
0032 
0033 #include <rtems/termiostypes.h>
0034 
0035 /*
0036  * The size of the cooked buffer
0037  */
0038 #define CBUFSIZE  (rtems_termios_cbufsize)
0039 
0040 /*
0041  * The sizes of the raw message buffers.
0042  * On most architectures it is quite a bit more
0043  * efficient if these are powers of two.
0044  */
0045 #define RAW_INPUT_BUFFER_SIZE  (rtems_termios_raw_input_size)
0046 #define RAW_OUTPUT_BUFFER_SIZE  (rtems_termios_raw_output_size)
0047 
0048 /* fields for "flow_ctrl" status */
0049 #define FL_IREQXOF 1U        /* input queue requests stop of incoming data */
0050 #define FL_ISNTXOF 2U        /* XOFF has been sent to other side of line   */
0051 #define FL_IRTSOFF 4U        /* RTS has been turned off for other side..   */
0052 
0053 #define FL_ORCVXOF 0x10U     /* XOFF has been received                     */
0054 #define FL_OSTOP   0x20U     /* output has been stopped due to XOFF        */
0055 
0056 #define FL_MDRTS   0x100U    /* input controlled with RTS/CTS handshake    */
0057 #define FL_MDXON   0x200U    /* input controlled with XON/XOFF protocol    */
0058 #define FL_MDXOF   0x400U    /* output controlled with XON/XOFF protocol   */
0059 
0060 #define NODISC(n) \
0061   { NULL,  NULL,  NULL,  NULL, \
0062     NULL,  NULL,  NULL,  NULL }
0063 /*
0064  * FIXME: change rtems_termios_linesw entries consistent
0065  *        with rtems_termios_linesw entry usage...
0066  */
0067 struct  rtems_termios_linesw rtems_termios_linesw[MAXLDISC] =
0068 {
0069   NODISC(0),    /* 0- termios-built-in */
0070   NODISC(1),    /* 1- defunct */
0071   NODISC(2),    /* 2- NTTYDISC */
0072   NODISC(3),    /* TABLDISC */
0073   NODISC(4),    /* SLIPDISC */
0074   NODISC(5),    /* PPPDISC */
0075   NODISC(6),    /* loadable */
0076   NODISC(7),    /* loadable */
0077 };
0078 
0079 int  rtems_termios_nlinesw =
0080        sizeof (rtems_termios_linesw) / sizeof (rtems_termios_linesw[0]);
0081 
0082 static size_t rtems_termios_cbufsize = 256;
0083 static size_t rtems_termios_raw_input_size = 256;
0084 static size_t rtems_termios_raw_output_size = 64;
0085 
0086 static const IMFS_node_control rtems_termios_imfs_node_control;
0087 
0088 static struct rtems_termios_tty *rtems_termios_ttyHead;
0089 static struct rtems_termios_tty *rtems_termios_ttyTail;
0090 
0091 static RTEMS_CHAIN_DEFINE_EMPTY(rtems_termios_devices);
0092 
0093 static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument);
0094 static rtems_task rtems_termios_txdaemon(rtems_task_argument argument);
0095 /*
0096  * some constants for I/O daemon task creation
0097  */
0098 #define TERMIOS_TXTASK_PRIO 10
0099 #define TERMIOS_RXTASK_PRIO 9
0100 #define TERMIOS_TXTASK_STACKSIZE 1024
0101 #define TERMIOS_RXTASK_STACKSIZE 1024
0102 /*
0103  * some events to be sent to the I/O tasks
0104  */
0105 #define TERMIOS_TX_START_EVENT     RTEMS_EVENT_1
0106 #define TERMIOS_TX_TERMINATE_EVENT RTEMS_EVENT_0
0107 
0108 #define TERMIOS_RX_PROC_EVENT      RTEMS_EVENT_1
0109 #define TERMIOS_RX_TERMINATE_EVENT RTEMS_EVENT_0
0110 
0111 static rtems_mutex rtems_termios_ttyMutex =
0112   RTEMS_MUTEX_INITIALIZER( "termios" );
0113 
0114 static void
0115 rtems_termios_obtain (void)
0116 {
0117   rtems_mutex_lock (&rtems_termios_ttyMutex);
0118 }
0119 
0120 static void
0121 rtems_termios_release (void)
0122 {
0123   rtems_mutex_unlock (&rtems_termios_ttyMutex);
0124 }
0125 
0126 rtems_status_code rtems_termios_device_install(
0127   const char                         *device_file,
0128   const rtems_termios_device_handler *handler,
0129   const rtems_termios_device_flow    *flow,
0130   rtems_termios_device_context       *context
0131 )
0132 {
0133   rtems_termios_device_node *new_device_node;
0134   int rv;
0135 
0136   new_device_node = calloc (1, sizeof(*new_device_node));
0137   if (new_device_node == NULL) {
0138     return RTEMS_NO_MEMORY;
0139   }
0140 
0141   rtems_chain_initialize_node (&new_device_node->node);
0142   new_device_node->handler = handler;
0143   new_device_node->flow = flow;
0144   new_device_node->context = context;
0145   new_device_node->tty = NULL;
0146 
0147   rv = IMFS_make_generic_node(
0148     device_file,
0149     S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
0150     &rtems_termios_imfs_node_control,
0151     new_device_node
0152   );
0153   if (rv != 0) {
0154     free (new_device_node);
0155     return RTEMS_UNSATISFIED;
0156   }
0157 
0158   return RTEMS_SUCCESSFUL;
0159 }
0160 
0161 static rtems_termios_tty *
0162 legacyContextToTTY (rtems_termios_device_context *ctx)
0163 {
0164   return RTEMS_CONTAINER_OF (ctx, rtems_termios_tty, legacy_device_context);
0165 }
0166 
0167 static bool
0168 rtems_termios_callback_firstOpen(
0169   rtems_termios_tty             *tty,
0170   rtems_termios_device_context  *ctx,
0171   struct termios                *term,
0172   rtems_libio_open_close_args_t *args
0173 )
0174 {
0175   (void) ctx;
0176   (void) term;
0177 
0178   (*tty->device.firstOpen) (tty->major, tty->minor, args);
0179 
0180   return true;
0181 }
0182 
0183 static void
0184 rtems_termios_callback_lastClose(
0185   rtems_termios_tty             *tty,
0186   rtems_termios_device_context  *ctx,
0187   rtems_libio_open_close_args_t *args
0188 )
0189 {
0190   (void) ctx;
0191 
0192   (*tty->device.lastClose) (tty->major, tty->minor, args);
0193 }
0194 
0195 static int
0196 rtems_termios_callback_pollRead (rtems_termios_device_context *ctx)
0197 {
0198   rtems_termios_tty *tty = legacyContextToTTY (ctx);
0199 
0200   return (*tty->device.pollRead) (tty->minor);
0201 }
0202 
0203 static void
0204 rtems_termios_callback_write(
0205   rtems_termios_device_context *ctx,
0206   const char                   *buf,
0207   size_t                        len
0208 )
0209 {
0210   rtems_termios_tty *tty = legacyContextToTTY (ctx);
0211 
0212   (*tty->device.write) (tty->minor, buf, len);
0213 }
0214 
0215 static bool
0216 rtems_termios_callback_setAttributes(
0217   rtems_termios_device_context *ctx,
0218   const struct termios         *term
0219 )
0220 {
0221   rtems_termios_tty *tty = legacyContextToTTY (ctx);
0222 
0223   (*tty->device.setAttributes) (tty->minor, term);
0224 
0225   return true;
0226 }
0227 
0228 static void
0229 rtems_termios_callback_stopRemoteTx (rtems_termios_device_context *ctx)
0230 {
0231   rtems_termios_tty *tty = legacyContextToTTY (ctx);
0232 
0233   (*tty->device.stopRemoteTx) (tty->minor);
0234 }
0235 
0236 static void
0237 rtems_termios_callback_startRemoteTx (rtems_termios_device_context *ctx)
0238 {
0239   rtems_termios_tty *tty = legacyContextToTTY (ctx);
0240 
0241   (*tty->device.startRemoteTx) (tty->minor);
0242 }
0243 
0244 /*
0245  * Drain output queue
0246  */
0247 static void
0248 drainOutput (struct rtems_termios_tty *tty)
0249 {
0250   rtems_termios_device_context *ctx = tty->device_context;
0251   rtems_interrupt_lock_context lock_context;
0252 
0253   if (tty->handler.mode != TERMIOS_POLLED) {
0254     rtems_termios_device_lock_acquire (ctx, &lock_context);
0255     while (tty->rawOutBuf.Tail != tty->rawOutBuf.Head) {
0256       tty->rawOutBufState = rob_wait;
0257       rtems_termios_device_lock_release (ctx, &lock_context);
0258       rtems_binary_semaphore_wait (&tty->rawOutBuf.Semaphore);
0259       rtems_termios_device_lock_acquire (ctx, &lock_context);
0260     }
0261     rtems_termios_device_lock_release (ctx, &lock_context);
0262   }
0263 }
0264 
0265 static bool
0266 needDeviceMutex (rtems_termios_tty *tty)
0267 {
0268   return tty->handler.mode == TERMIOS_IRQ_SERVER_DRIVEN
0269     || tty->handler.mode == TERMIOS_TASK_DRIVEN;
0270 }
0271 
0272 static void
0273 rtems_termios_destroy_tty (rtems_termios_tty *tty, void *arg, bool last_close)
0274 {
0275   if (rtems_termios_linesw[tty->t_line].l_close != NULL) {
0276     /*
0277      * call discipline-specific close
0278      */
0279     (void) rtems_termios_linesw[tty->t_line].l_close(tty);
0280   } else if (last_close) {
0281     /*
0282      * default: just flush output buffer
0283      */
0284     rtems_mutex_lock (&tty->osem);
0285     drainOutput (tty);
0286     rtems_mutex_unlock (&tty->osem);
0287   }
0288 
0289   if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
0290     rtems_status_code sc;
0291 
0292     /*
0293      * send "terminate" to I/O tasks
0294      */
0295     sc = rtems_event_send( tty->rxTaskId, TERMIOS_RX_TERMINATE_EVENT );
0296     if (sc != RTEMS_SUCCESSFUL)
0297       rtems_fatal_error_occurred (sc);
0298     sc = rtems_event_send( tty->txTaskId, TERMIOS_TX_TERMINATE_EVENT );
0299     if (sc != RTEMS_SUCCESSFUL)
0300       rtems_fatal_error_occurred (sc);
0301   }
0302   if (last_close && tty->handler.last_close)
0303      (*tty->handler.last_close)(tty, tty->device_context, arg);
0304 
0305   if (tty->device_node != NULL)
0306     tty->device_node->tty = NULL;
0307 
0308   rtems_mutex_destroy (&tty->isem);
0309   rtems_mutex_destroy (&tty->osem);
0310   rtems_binary_semaphore_destroy (&tty->rawOutBuf.Semaphore);
0311   if ((tty->handler.poll_read == NULL) ||
0312       (tty->handler.mode == TERMIOS_TASK_DRIVEN))
0313     rtems_binary_semaphore_destroy (&tty->rawInBuf.Semaphore);
0314 
0315   if (needDeviceMutex (tty)) {
0316     rtems_mutex_destroy (&tty->device_context->lock.mutex);
0317   } else if (tty->device_context == &tty->legacy_device_context) {
0318     rtems_interrupt_lock_destroy (&tty->legacy_device_context.lock.interrupt);
0319   }
0320 
0321   free (tty->rawInBuf.theBuf);
0322   free (tty->rawOutBuf.theBuf);
0323   free (tty->cbuf);
0324   free (tty);
0325 }
0326 
0327 static void
0328 deviceAcquireMutex(
0329   rtems_termios_device_context *ctx,
0330   rtems_interrupt_lock_context *lock_context
0331 )
0332 {
0333   rtems_mutex_lock (&ctx->lock.mutex);
0334 }
0335 
0336 static void
0337 deviceReleaseMutex(
0338   rtems_termios_device_context *ctx,
0339   rtems_interrupt_lock_context *lock_context
0340 )
0341 {
0342   rtems_mutex_unlock (&ctx->lock.mutex);
0343 }
0344 
0345 static rtems_termios_tty *
0346 rtems_termios_open_tty(
0347   rtems_device_major_number      major,
0348   rtems_device_minor_number      minor,
0349   rtems_libio_open_close_args_t *args,
0350   rtems_termios_tty             *tty,
0351   rtems_termios_device_node     *device_node,
0352   const rtems_termios_callbacks *callbacks
0353 )
0354 {
0355   if (tty == NULL) {
0356     static char c = 'a';
0357     rtems_termios_device_context *ctx;
0358 
0359     /*
0360      * Create a new device
0361      */
0362     tty = calloc (1, sizeof (struct rtems_termios_tty));
0363     if (tty == NULL) {
0364       return NULL;
0365     }
0366     /*
0367      * allocate raw input buffer
0368      */
0369     tty->rawInBuf.Size = RAW_INPUT_BUFFER_SIZE;
0370     tty->rawInBuf.theBuf = malloc (tty->rawInBuf.Size);
0371     if (tty->rawInBuf.theBuf == NULL) {
0372             free(tty);
0373       return NULL;
0374     }
0375     /*
0376      * allocate raw output buffer
0377      */
0378     tty->rawOutBuf.Size = RAW_OUTPUT_BUFFER_SIZE;
0379     tty->rawOutBuf.theBuf = malloc (tty->rawOutBuf.Size);
0380     if (tty->rawOutBuf.theBuf == NULL) {
0381             free((void *)(tty->rawInBuf.theBuf));
0382             free(tty);
0383       return NULL;
0384     }
0385     /*
0386      * allocate cooked buffer
0387      */
0388     tty->cbuf  = malloc (CBUFSIZE);
0389     if (tty->cbuf == NULL) {
0390             free((void *)(tty->rawOutBuf.theBuf));
0391             free((void *)(tty->rawInBuf.theBuf));
0392             free(tty);
0393       return NULL;
0394     }
0395     /*
0396      * Initialize wakeup callbacks
0397      */
0398     tty->tty_snd.sw_pfn = NULL;
0399     tty->tty_snd.sw_arg = NULL;
0400     tty->tty_rcv.sw_pfn = NULL;
0401     tty->tty_rcv.sw_arg = NULL;
0402     tty->tty_rcvwakeup  = false;
0403 
0404     tty->minor = minor;
0405     tty->major = major;
0406 
0407     /*
0408      * Set up mutex semaphores
0409      */
0410     rtems_mutex_init (&tty->isem, "termios input");
0411     rtems_mutex_init (&tty->osem, "termios output");
0412     rtems_binary_semaphore_init (&tty->rawOutBuf.Semaphore,
0413                                  "termios raw output");
0414     tty->rawOutBufState = rob_idle;
0415 
0416     /*
0417      * Set callbacks
0418      */
0419     if (device_node != NULL) {
0420       device_node->tty = tty;
0421       tty->handler = *device_node->handler;
0422 
0423       if (device_node->flow != NULL) {
0424         tty->flow = *device_node->flow;
0425       }
0426 
0427       tty->device_node = device_node;
0428       tty->device_context = device_node->context;
0429       memset(&tty->device, 0, sizeof(tty->device));
0430     } else {
0431       tty->handler.first_open = callbacks->firstOpen != NULL ?
0432         rtems_termios_callback_firstOpen : NULL;
0433       tty->handler.last_close = callbacks->lastClose != NULL ?
0434         rtems_termios_callback_lastClose : NULL;
0435       tty->handler.poll_read = callbacks->pollRead != NULL ?
0436         rtems_termios_callback_pollRead : NULL;
0437       tty->handler.write = callbacks->write != NULL ?
0438         rtems_termios_callback_write : NULL;
0439       tty->handler.set_attributes = callbacks->setAttributes != NULL ?
0440         rtems_termios_callback_setAttributes : NULL;
0441       tty->flow.stop_remote_tx = callbacks->stopRemoteTx != NULL ?
0442         rtems_termios_callback_stopRemoteTx : NULL;
0443       tty->flow.start_remote_tx = callbacks->startRemoteTx != NULL ?
0444         rtems_termios_callback_startRemoteTx : NULL;
0445       tty->handler.mode = callbacks->outputUsesInterrupts;
0446       tty->device_context = NULL;
0447       tty->device_node = NULL;
0448       tty->device = *callbacks;
0449     }
0450 
0451     if (tty->device_context == NULL) {
0452       tty->device_context = &tty->legacy_device_context;
0453       rtems_termios_device_context_initialize (tty->device_context, "Termios");
0454     }
0455 
0456     ctx = tty->device_context;
0457 
0458     if (needDeviceMutex (tty)) {
0459       rtems_mutex_init (&ctx->lock.mutex, "termios device");
0460       ctx->lock_acquire = deviceAcquireMutex;
0461       ctx->lock_release = deviceReleaseMutex;
0462     } else {
0463       ctx->lock_acquire = rtems_termios_device_lock_acquire_default;
0464       ctx->lock_release = rtems_termios_device_lock_release_default;
0465     }
0466 
0467     /*
0468      * Create I/O tasks
0469      */
0470     if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
0471       rtems_status_code sc;
0472 
0473       sc = rtems_task_create (
0474                                    rtems_build_name ('T', 'x', 'T', c),
0475            TERMIOS_TXTASK_PRIO,
0476            TERMIOS_TXTASK_STACKSIZE,
0477            RTEMS_NO_PREEMPT | RTEMS_NO_TIMESLICE |
0478            RTEMS_NO_ASR,
0479            RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
0480            &tty->txTaskId);
0481       if (sc != RTEMS_SUCCESSFUL)
0482         rtems_fatal_error_occurred (sc);
0483       sc = rtems_task_create (
0484                                    rtems_build_name ('R', 'x', 'T', c),
0485            TERMIOS_RXTASK_PRIO,
0486            TERMIOS_RXTASK_STACKSIZE,
0487            RTEMS_NO_PREEMPT | RTEMS_NO_TIMESLICE |
0488            RTEMS_NO_ASR,
0489            RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
0490            &tty->rxTaskId);
0491       if (sc != RTEMS_SUCCESSFUL)
0492         rtems_fatal_error_occurred (sc);
0493 
0494     }
0495     if ((tty->handler.poll_read == NULL) ||
0496         (tty->handler.mode == TERMIOS_TASK_DRIVEN)){
0497       rtems_binary_semaphore_init (&tty->rawInBuf.Semaphore,
0498                                    "termios raw input");
0499     }
0500 
0501     /*
0502      * Set default parameters
0503      */
0504     tty->termios.c_iflag = BRKINT | ICRNL | IXON | IMAXBEL;
0505     tty->termios.c_oflag = OPOST | ONLCR | OXTABS;
0506     tty->termios.c_cflag = CS8 | CREAD | CLOCAL;
0507     tty->termios.c_lflag =
0508        ISIG | ICANON | IEXTEN | ECHO | ECHOK | ECHOE | ECHOCTL;
0509 
0510     tty->termios.c_ispeed = B9600;
0511     tty->termios.c_ospeed = B9600;
0512 
0513     tty->termios.c_cc[VINTR] = '\003';
0514     tty->termios.c_cc[VQUIT] = '\034';
0515     tty->termios.c_cc[VERASE] = '\177';
0516     tty->termios.c_cc[VKILL] = '\025';
0517     tty->termios.c_cc[VEOF] = '\004';
0518     tty->termios.c_cc[VEOL] = '\000';
0519     tty->termios.c_cc[VEOL2] = '\000';
0520     tty->termios.c_cc[VSTART] = '\021';
0521     tty->termios.c_cc[VSTOP] = '\023';
0522     tty->termios.c_cc[VSUSP] = '\032';
0523     tty->termios.c_cc[VREPRINT] = '\022';
0524     tty->termios.c_cc[VDISCARD] = '\017';
0525     tty->termios.c_cc[VWERASE] = '\027';
0526     tty->termios.c_cc[VLNEXT] = '\026';
0527 
0528     /* start with no flow control, clear flow control flags */
0529     tty->flow_ctrl = 0;
0530     /*
0531      * set low/highwater mark for XON/XOFF support
0532      */
0533     tty->lowwater  = tty->rawInBuf.Size * 1/2;
0534     tty->highwater = tty->rawInBuf.Size * 3/4;
0535     /*
0536      * Bump name characer
0537      */
0538     if (c++ == 'z')
0539       c = 'a';
0540 
0541   }
0542   args->iop->data1 = tty;
0543   if (!tty->refcount++) {
0544     if (tty->handler.first_open && !(*tty->handler.first_open)(
0545         tty, tty->device_context, &tty->termios, args)) {
0546       rtems_termios_destroy_tty(tty, args, false);
0547       return NULL;
0548     }
0549 
0550     /*
0551      * start I/O tasks, if needed
0552      */
0553     if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
0554       rtems_status_code sc;
0555 
0556       sc = rtems_task_start(
0557         tty->rxTaskId, rtems_termios_rxdaemon, (rtems_task_argument)tty);
0558       if (sc != RTEMS_SUCCESSFUL)
0559         rtems_fatal_error_occurred (sc);
0560 
0561       sc = rtems_task_start(
0562         tty->txTaskId, rtems_termios_txdaemon, (rtems_task_argument)tty);
0563       if (sc != RTEMS_SUCCESSFUL)
0564         rtems_fatal_error_occurred (sc);
0565     }
0566   }
0567 
0568   return tty;
0569 }
0570 
0571 /*
0572  * Open a termios device
0573  */
0574 rtems_status_code
0575 rtems_termios_open (
0576   rtems_device_major_number      major,
0577   rtems_device_minor_number      minor,
0578   void                          *arg,
0579   const rtems_termios_callbacks *callbacks
0580 )
0581 {
0582   struct rtems_termios_tty *tty;
0583 
0584   /*
0585    * See if the device has already been opened
0586    */
0587   rtems_termios_obtain ();
0588 
0589   for (tty = rtems_termios_ttyHead ; tty != NULL ; tty = tty->forw) {
0590     if ((tty->major == major) && (tty->minor == minor))
0591       break;
0592   }
0593 
0594   tty = rtems_termios_open_tty(
0595     major, minor, arg, tty, NULL, callbacks);
0596   if (tty == NULL) {
0597     rtems_termios_release ();
0598     return RTEMS_NO_MEMORY;
0599   }
0600 
0601   if (tty->refcount == 1) {
0602     /*
0603      * link tty
0604      */
0605     tty->forw = rtems_termios_ttyHead;
0606     tty->back = NULL;
0607     if (rtems_termios_ttyHead != NULL)
0608       rtems_termios_ttyHead->back = tty;
0609     rtems_termios_ttyHead = tty;
0610     if (rtems_termios_ttyTail == NULL)
0611       rtems_termios_ttyTail = tty;
0612   }
0613 
0614   rtems_termios_release ();
0615 
0616   return RTEMS_SUCCESSFUL;
0617 }
0618 
0619 static void
0620 flushOutput (struct rtems_termios_tty *tty)
0621 {
0622   rtems_termios_device_context *ctx = tty->device_context;
0623   rtems_interrupt_lock_context lock_context;
0624 
0625   rtems_termios_device_lock_acquire (ctx, &lock_context);
0626   tty->rawOutBuf.Tail = 0;
0627   tty->rawOutBuf.Head = 0;
0628   tty->rawOutBufState = rob_idle;
0629   rtems_termios_device_lock_release (ctx, &lock_context);
0630 }
0631 
0632 static void
0633 flushInput (struct rtems_termios_tty *tty)
0634 {
0635   rtems_termios_device_context *ctx = tty->device_context;
0636   rtems_interrupt_lock_context lock_context;
0637 
0638   rtems_termios_device_lock_acquire (ctx, &lock_context);
0639   tty->rawInBuf.Tail = 0;
0640   tty->rawInBuf.Head = 0;
0641   rtems_termios_device_lock_release (ctx, &lock_context);
0642 }
0643 
0644 static void
0645 rtems_termios_close_tty (rtems_termios_tty *tty, void *arg)
0646 {
0647   if (--tty->refcount == 0) {
0648     rtems_termios_destroy_tty (tty, arg, true);
0649   }
0650 }
0651 
0652 rtems_status_code
0653 rtems_termios_close (void *arg)
0654 {
0655   rtems_libio_open_close_args_t *args = arg;
0656   struct rtems_termios_tty *tty = args->iop->data1;
0657 
0658   rtems_termios_obtain ();
0659 
0660   if (tty->refcount == 1) {
0661     if (tty->forw == NULL) {
0662       rtems_termios_ttyTail = tty->back;
0663       if ( rtems_termios_ttyTail != NULL ) {
0664         rtems_termios_ttyTail->forw = NULL;
0665       }
0666     } else {
0667       tty->forw->back = tty->back;
0668     }
0669 
0670     if (tty->back == NULL) {
0671       rtems_termios_ttyHead = tty->forw;
0672       if ( rtems_termios_ttyHead != NULL ) {
0673         rtems_termios_ttyHead->back = NULL;
0674       }
0675     } else {
0676       tty->back->forw = tty->forw;
0677     }
0678   }
0679 
0680   rtems_termios_close_tty (tty, arg);
0681 
0682   rtems_termios_release ();
0683 
0684   return RTEMS_SUCCESSFUL;
0685 }
0686 
0687 rtems_status_code rtems_termios_bufsize (
0688   size_t cbufsize,
0689   size_t raw_input,
0690   size_t raw_output
0691 )
0692 {
0693   rtems_termios_cbufsize        = cbufsize;
0694   rtems_termios_raw_input_size  = raw_input;
0695   rtems_termios_raw_output_size = raw_output;
0696   return RTEMS_SUCCESSFUL;
0697 }
0698 
0699 static void
0700 termios_set_flowctrl(struct rtems_termios_tty *tty)
0701 {
0702   rtems_termios_device_context *ctx = tty->device_context;
0703   rtems_interrupt_lock_context lock_context;
0704   /*
0705    * check for flow control options to be switched off
0706    */
0707 
0708   /* check for outgoing XON/XOFF flow control switched off */
0709   if (( tty->flow_ctrl & FL_MDXON) &&
0710       !(tty->termios.c_iflag & IXON)) {
0711     /* clear related flags in flow_ctrl */
0712     tty->flow_ctrl &= ~(FL_MDXON | FL_ORCVXOF);
0713 
0714     /* has output been stopped due to received XOFF? */
0715     if (tty->flow_ctrl & FL_OSTOP) {
0716       /* disable interrupts    */
0717       rtems_termios_device_lock_acquire (ctx, &lock_context);
0718       tty->flow_ctrl &= ~FL_OSTOP;
0719       /* check for chars in output buffer (or rob_state?) */
0720       if (tty->rawOutBufState != rob_idle) {
0721         /* if chars available, call write function... */
0722         (*tty->handler.write)(
0723           ctx, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
0724       }
0725       /* reenable interrupts */
0726       rtems_termios_device_lock_release (ctx, &lock_context);
0727     }
0728   }
0729   /* check for incoming XON/XOFF flow control switched off */
0730   if (( tty->flow_ctrl & FL_MDXOF) && !(tty->termios.c_iflag & IXOFF)) {
0731     /* clear related flags in flow_ctrl */
0732     tty->flow_ctrl &= ~(FL_MDXOF);
0733     /* FIXME: what happens, if we had sent XOFF but not yet XON? */
0734     tty->flow_ctrl &= ~(FL_ISNTXOF);
0735   }
0736 
0737   /* check for incoming RTS/CTS flow control switched off */
0738   if (( tty->flow_ctrl & FL_MDRTS) && !(tty->termios.c_cflag & CRTSCTS)) {
0739     /* clear related flags in flow_ctrl */
0740     tty->flow_ctrl &= ~(FL_MDRTS);
0741 
0742     /* restart remote Tx, if it was stopped */
0743     if ((tty->flow_ctrl & FL_IRTSOFF) &&
0744         (tty->flow.start_remote_tx != NULL)) {
0745       tty->flow.start_remote_tx(ctx);
0746     }
0747     tty->flow_ctrl &= ~(FL_IRTSOFF);
0748   }
0749 
0750   /*
0751    * check for flow control options to be switched on
0752    */
0753   /* check for incoming RTS/CTS flow control switched on */
0754   if (tty->termios.c_cflag & CRTSCTS) {
0755     tty->flow_ctrl |= FL_MDRTS;
0756   }
0757   /* check for incoming XON/XOF flow control switched on */
0758   if (tty->termios.c_iflag & IXOFF) {
0759     tty->flow_ctrl |= FL_MDXOF;
0760   }
0761   /* check for outgoing XON/XOF flow control switched on */
0762   if (tty->termios.c_iflag & IXON) {
0763     tty->flow_ctrl |= FL_MDXON;
0764   }
0765 }
0766 
0767 rtems_status_code
0768 rtems_termios_ioctl (void *arg)
0769 {
0770   rtems_libio_ioctl_args_t *args = arg;
0771   struct rtems_termios_tty *tty = args->iop->data1;
0772   struct ttywakeup         *wakeup = (struct ttywakeup *)args->buffer;
0773   rtems_status_code sc;
0774   int flags;
0775 
0776   sc = RTEMS_SUCCESSFUL;
0777   args->ioctl_return = 0;
0778   rtems_mutex_lock (&tty->osem);
0779   switch (args->command) {
0780   default:
0781     if (rtems_termios_linesw[tty->t_line].l_ioctl != NULL) {
0782       sc = rtems_termios_linesw[tty->t_line].l_ioctl(tty,args);
0783     } else if (tty->handler.ioctl) {
0784       args->ioctl_return = (*tty->handler.ioctl) (tty->device_context,
0785         args->command, args->buffer);
0786       sc = RTEMS_SUCCESSFUL;
0787     } else {
0788       sc = RTEMS_INVALID_NUMBER;
0789     }
0790     break;
0791 
0792   case TIOCGETA:
0793     *(struct termios *)args->buffer = tty->termios;
0794     break;
0795 
0796   case TIOCSETA:
0797   case TIOCSETAW:
0798   case TIOCSETAF:
0799     tty->termios = *(struct termios *)args->buffer;
0800 
0801     if (args->command == TIOCSETAW || args->command == TIOCSETAF) {
0802       drainOutput (tty);
0803       if (args->command == TIOCSETAF) {
0804         flushInput (tty);
0805       }
0806     }
0807     /* check for and process change in flow control options */
0808     termios_set_flowctrl(tty);
0809 
0810     if (tty->termios.c_lflag & ICANON) {
0811       tty->rawInBufSemaphoreWait = true;
0812       tty->rawInBufSemaphoreTimeout = 0;
0813       tty->rawInBufSemaphoreFirstTimeout = 0;
0814     } else {
0815       tty->vtimeTicks = tty->termios.c_cc[VTIME] *
0816                     rtems_clock_get_ticks_per_second() / 10;
0817       if (tty->termios.c_cc[VTIME]) {
0818         tty->rawInBufSemaphoreWait = true;
0819         tty->rawInBufSemaphoreTimeout = tty->vtimeTicks;
0820         if (tty->termios.c_cc[VMIN])
0821           tty->rawInBufSemaphoreFirstTimeout = 0;
0822         else
0823           tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks;
0824       } else {
0825         if (tty->termios.c_cc[VMIN]) {
0826           tty->rawInBufSemaphoreWait = true;
0827           tty->rawInBufSemaphoreTimeout = 0;
0828           tty->rawInBufSemaphoreFirstTimeout = 0;
0829         } else {
0830           tty->rawInBufSemaphoreWait = false;
0831         }
0832       }
0833     }
0834     if (tty->handler.set_attributes) {
0835       sc = (*tty->handler.set_attributes)(tty->device_context, &tty->termios) ?
0836         RTEMS_SUCCESSFUL : RTEMS_IO_ERROR;
0837     }
0838     break;
0839 
0840   case TIOCDRAIN:
0841     drainOutput (tty);
0842     break;
0843 
0844   case TIOCFLUSH:
0845     flags = *((int *)args->buffer);
0846 
0847     if (flags == 0) {
0848       flags = FREAD | FWRITE;
0849     } else {
0850       flags &= FREAD | FWRITE;
0851     }
0852     if (flags & FWRITE) {
0853       flushOutput (tty);
0854     }
0855     if (flags & FREAD) {
0856       flushInput (tty);
0857     }
0858     break;
0859 
0860   case RTEMS_IO_SNDWAKEUP:
0861     tty->tty_snd = *wakeup;
0862     break;
0863 
0864   case RTEMS_IO_RCVWAKEUP:
0865     tty->tty_rcv = *wakeup;
0866     break;
0867 
0868     /*
0869      * FIXME: add various ioctl code handlers
0870      */
0871 
0872 #if 1 /* FIXME */
0873   case TIOCSETD:
0874     /*
0875      * close old line discipline
0876      */
0877     if (rtems_termios_linesw[tty->t_line].l_close != NULL) {
0878       sc = rtems_termios_linesw[tty->t_line].l_close(tty);
0879     }
0880     tty->t_line=*(int*)(args->buffer);
0881     tty->t_sc = NULL; /* ensure that no more valid data */
0882     /*
0883      * open new line discipline
0884      */
0885     if (rtems_termios_linesw[tty->t_line].l_open != NULL) {
0886       sc = rtems_termios_linesw[tty->t_line].l_open(tty);
0887     }
0888     break;
0889   case TIOCGETD:
0890     *(int*)(args->buffer)=tty->t_line;
0891     break;
0892 #endif
0893    case FIONREAD: {
0894       int rawnc = tty->rawInBuf.Tail - tty->rawInBuf.Head;
0895       if ( rawnc < 0 )
0896         rawnc += tty->rawInBuf.Size;
0897       /* Half guess that this is the right operation */
0898       *(int *)args->buffer = tty->ccount - tty->cindex + rawnc;
0899     }
0900     break;
0901   }
0902 
0903   rtems_mutex_unlock (&tty->osem);
0904   return sc;
0905 }
0906 
0907 /*
0908  * Send as many chars at once as possible to device-specific code.
0909  * If transmitting==true then assume transmission is already running and
0910  * an explicit write(0) is needed if output has to stop for flow control.
0911  */
0912 static unsigned int
0913 startXmit (
0914   struct rtems_termios_tty *tty,
0915   unsigned int newTail,
0916   bool transmitting
0917 )
0918 {
0919   unsigned int nToSend;
0920 
0921   tty->rawOutBufState = rob_busy;
0922 
0923   /* if XOFF was received, do not (re)start output */
0924   if (tty->flow_ctrl & FL_ORCVXOF) {
0925     /* set flag, that output has been stopped */
0926     tty->flow_ctrl |= FL_OSTOP;
0927     nToSend = 0;
0928     /* stop transmitter */
0929     if (transmitting) {
0930       (*tty->handler.write) (tty->device_context, NULL, 0);
0931     }
0932   } else {
0933     /* when flow control XON or XOF, don't send blocks of data     */
0934     /* to allow fast reaction on incoming flow ctrl and low latency*/
0935     /* for outgoing flow control                                   */
0936     if (tty->flow_ctrl & (FL_MDXON | FL_MDXOF))
0937       nToSend = 1;
0938     else if (newTail > tty->rawOutBuf.Head)
0939       nToSend = tty->rawOutBuf.Size - newTail;
0940     else
0941       nToSend = tty->rawOutBuf.Head - newTail;
0942 
0943     (*tty->handler.write)(
0944         tty->device_context, &tty->rawOutBuf.theBuf[newTail], nToSend);
0945   }
0946 
0947   return nToSend;
0948 }
0949 
0950 /*
0951  * Send characters to device-specific code
0952  */
0953 static size_t
0954 doTransmit (const char *buf, size_t len, rtems_termios_tty *tty,
0955             bool wait, bool nextWait)
0956 {
0957   unsigned int newHead;
0958   rtems_termios_device_context *ctx = tty->device_context;
0959   rtems_interrupt_lock_context lock_context;
0960   size_t todo;
0961 
0962   if (tty->handler.mode == TERMIOS_POLLED) {
0963     (*tty->handler.write)(ctx, buf, len);
0964     return len;
0965   }
0966 
0967   todo = len;
0968 
0969   while (todo > 0) {
0970     size_t nToCopy;
0971     size_t nAvail;
0972 
0973     /* Check space for at least one char */
0974     newHead = tty->rawOutBuf.Head + 1;
0975     if (newHead >= tty->rawOutBuf.Size)
0976       newHead -= tty->rawOutBuf.Size;
0977 
0978     rtems_termios_device_lock_acquire (ctx, &lock_context);
0979     if (newHead == tty->rawOutBuf.Tail) {
0980       if (wait) {
0981         do {
0982           tty->rawOutBufState = rob_wait;
0983           rtems_termios_device_lock_release (ctx, &lock_context);
0984           rtems_binary_semaphore_wait (&tty->rawOutBuf.Semaphore);
0985           rtems_termios_device_lock_acquire (ctx, &lock_context);
0986         } while (newHead == tty->rawOutBuf.Tail);
0987       } else {
0988         rtems_termios_device_lock_release (ctx, &lock_context);
0989         return len - todo;
0990       }
0991     }
0992 
0993     /* Determine free space up to current tail or end of ring buffer */
0994     nToCopy = todo;
0995     if (tty->rawOutBuf.Tail > tty->rawOutBuf.Head) {
0996       /* Available space is contiguous from Head to Tail */
0997       nAvail = tty->rawOutBuf.Tail - tty->rawOutBuf.Head - 1;
0998     } else {
0999       /* Available space wraps at buffer end. To keep it simple, utilize
1000          only the free space from Head to end during this iteration */
1001       nAvail = tty->rawOutBuf.Size - tty->rawOutBuf.Head;
1002       /* Head may not touch Tail after wraparound */
1003       if (tty->rawOutBuf.Tail == 0)
1004         nAvail--;
1005     }
1006     if (nToCopy > nAvail)
1007       nToCopy = nAvail;
1008 
1009     /* To minimize latency, the memcpy could be done
1010      * with interrupts enabled or with limit on nToCopy (TBD)
1011      */
1012     memcpy(&tty->rawOutBuf.theBuf[tty->rawOutBuf.Head], buf, nToCopy);
1013 
1014     newHead = tty->rawOutBuf.Head + nToCopy;
1015     if (newHead >= tty->rawOutBuf.Size)
1016       newHead -= tty->rawOutBuf.Size;
1017     tty->rawOutBuf.Head = newHead;
1018 
1019     if (tty->rawOutBufState == rob_idle) {
1020       startXmit (tty, tty->rawOutBuf.Tail, false);
1021     }
1022 
1023     rtems_termios_device_lock_release (ctx, &lock_context);
1024 
1025     buf += nToCopy;
1026     todo -= nToCopy;
1027     wait = nextWait;
1028   }
1029 
1030   return len;
1031 }
1032 
1033 void
1034 rtems_termios_puts (
1035   const void *_buf, size_t len, struct rtems_termios_tty *tty)
1036 {
1037   doTransmit (_buf, len, tty, true, true);
1038 }
1039 
1040 static bool
1041 canTransmit (rtems_termios_tty *tty, bool wait, size_t len)
1042 {
1043   rtems_termios_device_context *ctx;
1044   rtems_interrupt_lock_context lock_context;
1045   unsigned int capacity;
1046 
1047   if (wait || tty->handler.mode == TERMIOS_POLLED) {
1048     return true;
1049   }
1050 
1051   ctx = tty->device_context;
1052   rtems_termios_device_lock_acquire (ctx, &lock_context);
1053   capacity = (tty->rawOutBuf.Tail - tty->rawOutBuf.Head - 1) %
1054     tty->rawOutBuf.Size;
1055   rtems_termios_device_lock_release (ctx, &lock_context);
1056   return capacity >= len;
1057 }
1058 
1059 /*
1060  * Handle output processing
1061  */
1062 static bool
1063 oproc (unsigned char c, rtems_termios_tty *tty, bool wait)
1064 {
1065   char buf[8];
1066   size_t len;
1067 
1068   buf[0] = c;
1069   len = 1;
1070 
1071   if (tty->termios.c_oflag & OPOST) {
1072     int oldColumn = tty->column;
1073     int columnAdj = 0;
1074 
1075     switch (c) {
1076     case '\n':
1077       if (tty->termios.c_oflag & ONLRET)
1078         columnAdj = -oldColumn;
1079       if (tty->termios.c_oflag & ONLCR) {
1080         len = 2;
1081 
1082         if (!canTransmit (tty, wait, len)) {
1083           return false;
1084         }
1085 
1086         columnAdj = -oldColumn;
1087         buf[0] = '\r';
1088         buf[1] = c;
1089       }
1090       break;
1091 
1092     case '\r':
1093       if ((tty->termios.c_oflag & ONOCR) && (oldColumn == 0))
1094         return true;
1095       if (tty->termios.c_oflag & OCRNL) {
1096         buf[0] = '\n';
1097         if (tty->termios.c_oflag & ONLRET)
1098           columnAdj = -oldColumn;
1099       } else {
1100         columnAdj = -oldColumn;
1101       }
1102       break;
1103 
1104     case '\t':
1105       columnAdj = 8 - (oldColumn & 7);
1106       if ((tty->termios.c_oflag & TABDLY) == OXTABS) {
1107         int i;
1108 
1109         len = (size_t) columnAdj;
1110 
1111         if (!canTransmit (tty, wait, len)) {
1112           return false;
1113         }
1114 
1115         for (i = 0; i < columnAdj; ++i) {
1116           buf[i] = ' ';
1117         }
1118       }
1119       break;
1120 
1121     case '\b':
1122       if (oldColumn > 0)
1123         columnAdj = -1;
1124       break;
1125 
1126     default:
1127       if (tty->termios.c_oflag & OLCUC) {
1128         c = toupper(c);
1129         buf[0] = c;
1130       }
1131       if (!iscntrl(c))
1132         columnAdj = 1;
1133       break;
1134     }
1135 
1136     tty->column = oldColumn + columnAdj;
1137   }
1138 
1139   return doTransmit (buf, len, tty, wait, true) > 0;
1140 }
1141 
1142 static uint32_t
1143 rtems_termios_write_tty (rtems_libio_t *iop, rtems_termios_tty *tty,
1144                          const char *buf, uint32_t len)
1145 {
1146   bool wait = !rtems_libio_iop_is_no_delay (iop);
1147 
1148   if (tty->termios.c_oflag & OPOST) {
1149     uint32_t todo = len;
1150 
1151     while (todo > 0) {
1152       if (!oproc (*buf, tty, wait)) {
1153         break;
1154       }
1155 
1156       ++buf;
1157       --todo;
1158       wait = false;
1159     }
1160 
1161     return len - todo;
1162   } else {
1163     return doTransmit (buf, len, tty, wait, false);
1164   }
1165 }
1166 
1167 rtems_status_code
1168 rtems_termios_write (void *arg)
1169 {
1170   rtems_libio_rw_args_t *args = arg;
1171   struct rtems_termios_tty *tty = args->iop->data1;
1172 
1173   rtems_mutex_lock (&tty->osem);
1174   if (rtems_termios_linesw[tty->t_line].l_write != NULL) {
1175     rtems_status_code sc;
1176 
1177     sc = rtems_termios_linesw[tty->t_line].l_write(tty,args);
1178     rtems_mutex_unlock (&tty->osem);
1179     return sc;
1180   }
1181   args->bytes_moved = rtems_termios_write_tty (args->iop, tty,
1182                                                args->buffer, args->count);
1183   rtems_mutex_unlock (&tty->osem);
1184   return RTEMS_SUCCESSFUL;
1185 }
1186 
1187 /*
1188  * Echo a typed character
1189  */
1190 static void
1191 echo (unsigned char c, struct rtems_termios_tty *tty)
1192 {
1193   if ((tty->termios.c_lflag & ECHOCTL) &&
1194        iscntrl(c) && (c != '\t') && (c != '\n')) {
1195     char echobuf[2];
1196 
1197     echobuf[0] = '^';
1198     echobuf[1] = c ^ 0x40;
1199     doTransmit (echobuf, 2, tty, true, true);
1200     tty->column += 2;
1201   } else {
1202     oproc (c, tty, true);
1203   }
1204 }
1205 
1206 /*
1207  * Erase a character or line
1208  * FIXME: Needs support for WERASE and ECHOPRT.
1209  * FIXME: Some of the tests should check for IEXTEN, too.
1210  */
1211 static void
1212 erase (struct rtems_termios_tty *tty, int lineFlag)
1213 {
1214   if (tty->ccount == 0)
1215     return;
1216   if (lineFlag) {
1217     if (!(tty->termios.c_lflag & ECHO)) {
1218       tty->ccount = 0;
1219       return;
1220     }
1221     if (!(tty->termios.c_lflag & ECHOE)) {
1222       tty->ccount = 0;
1223       echo (tty->termios.c_cc[VKILL], tty);
1224       if (tty->termios.c_lflag & ECHOK)
1225         echo ('\n', tty);
1226       return;
1227     }
1228   }
1229 
1230   while (tty->ccount) {
1231     unsigned char c = tty->cbuf[--tty->ccount];
1232 
1233     if (tty->termios.c_lflag & ECHO) {
1234       if (!lineFlag && !(tty->termios.c_lflag & ECHOE)) {
1235         echo (tty->termios.c_cc[VERASE], tty);
1236       } else if (c == '\t') {
1237         int col = tty->read_start_column;
1238         int i = 0;
1239 
1240         /*
1241          * Find the character before the tab
1242          */
1243         while (i != tty->ccount) {
1244           c = tty->cbuf[i++];
1245           if (c == '\t') {
1246             col = (col | 7) + 1;
1247           } else if (iscntrl (c)) {
1248             if (tty->termios.c_lflag & ECHOCTL)
1249               col += 2;
1250           } else {
1251             col++;
1252           }
1253         }
1254 
1255         /*
1256          * Back up over the tab
1257          */
1258         while (tty->column > col) {
1259           doTransmit ("\b", 1, tty, true, true);
1260           tty->column--;
1261         }
1262       }
1263       else {
1264         if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
1265           doTransmit ("\b \b", 3, tty, true, true);
1266           if (tty->column)
1267             tty->column--;
1268         }
1269         if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
1270           doTransmit ("\b \b", 3, tty, true, true);
1271           if (tty->column)
1272             tty->column--;
1273         }
1274       }
1275     }
1276     if (!lineFlag)
1277       break;
1278   }
1279 }
1280 
1281 static unsigned char
1282 iprocEarly (unsigned char c, rtems_termios_tty *tty)
1283 {
1284   if (tty->termios.c_iflag & ISTRIP)
1285     c &= 0x7f;
1286 
1287   if (tty->termios.c_iflag & IUCLC)
1288     c = tolower (c);
1289 
1290   if (c == '\r') {
1291     if (tty->termios.c_iflag & ICRNL)
1292       c = '\n';
1293   } else if (c == '\n') {
1294     if (tty->termios.c_iflag & INLCR)
1295       c = '\r';
1296   }
1297 
1298   return c;
1299 }
1300 
1301 /*
1302  * This points to the currently registered method to perform
1303  * ISIG processing of VKILL and VQUIT characters.
1304  */
1305 static rtems_termios_isig_handler termios_isig_handler =
1306    rtems_termios_default_isig_handler;
1307 
1308 /*
1309  * This is the default method to process VKILL or VQUIT characters if
1310  * ISIG processing is enabled. Note that it does nothing.
1311  */
1312 rtems_termios_iproc_status_code rtems_termios_default_isig_handler(
1313   unsigned char c,
1314   struct rtems_termios_tty *tty
1315 )
1316 {
1317   return RTEMS_TERMIOS_IPROC_CONTINUE;
1318 }
1319 
1320 /*
1321  * Register a method to process VKILL or VQUIT characters if
1322  * ISIG processing is enabled.
1323  */
1324 rtems_status_code rtems_termios_register_isig_handler(
1325   rtems_termios_isig_handler handler
1326 )
1327 {
1328   if (handler == NULL) {
1329     return RTEMS_INVALID_ADDRESS;
1330   }
1331 
1332   termios_isig_handler = handler;
1333   return RTEMS_SUCCESSFUL;
1334 }
1335 
1336 /*
1337  * Process a single input character
1338  */
1339 static rtems_termios_iproc_status_code
1340 iproc (unsigned char c, struct rtems_termios_tty *tty)
1341 {
1342   /*
1343    * If signals are enabled, then allow possibility of VINTR causing
1344    * SIGINTR and VQUIT causing SIGQUIT. Invoke user provided isig handler.
1345    */
1346   if ((tty->termios.c_lflag & ISIG)) {
1347     if ((c == tty->termios.c_cc[VINTR]) || (c == tty->termios.c_cc[VQUIT])) {
1348       return (*termios_isig_handler)(c, tty);
1349     }
1350   }
1351 
1352   /*
1353    * Perform canonical character processing.
1354    */
1355   if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
1356     if (c == tty->termios.c_cc[VERASE]) {
1357       erase (tty, 0);
1358       return RTEMS_TERMIOS_IPROC_CONTINUE;
1359     }
1360     else if (c == tty->termios.c_cc[VKILL]) {
1361       erase (tty, 1);
1362       return RTEMS_TERMIOS_IPROC_CONTINUE;
1363     }
1364     else if (c == tty->termios.c_cc[VEOF]) {
1365       return RTEMS_TERMIOS_IPROC_DONE;
1366     } else if (c == '\n') {
1367       if (tty->termios.c_lflag & (ECHO | ECHONL))
1368         echo (c, tty);
1369       tty->cbuf[tty->ccount++] = c;
1370       return RTEMS_TERMIOS_IPROC_DONE;
1371     } else if ((c == tty->termios.c_cc[VEOL]) ||
1372                (c == tty->termios.c_cc[VEOL2])) {
1373       if (tty->termios.c_lflag & ECHO)
1374         echo (c, tty);
1375       tty->cbuf[tty->ccount++] = c;
1376       return RTEMS_TERMIOS_IPROC_DONE;
1377     }
1378   }
1379 
1380   /*
1381    * Perform non-canonical character processing.
1382    *
1383    * FIXME: Should do IMAXBEL handling somehow
1384    */
1385   if (tty->ccount < (CBUFSIZE-1)) {
1386     if (tty->termios.c_lflag & ECHO)
1387       echo (c, tty);
1388     tty->cbuf[tty->ccount++] = c;
1389   }
1390   return RTEMS_TERMIOS_IPROC_CONTINUE;
1391 }
1392 
1393 /*
1394  * Process input character, with semaphore.
1395  */
1396 static rtems_termios_iproc_status_code
1397 siproc (unsigned char c, struct rtems_termios_tty *tty)
1398 {
1399   rtems_termios_iproc_status_code rc;
1400 
1401   /*
1402    * Obtain output semaphore if character will be echoed
1403    */
1404   if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) {
1405     rtems_mutex_lock (&tty->osem);
1406     rc = iproc (c, tty);
1407     rtems_mutex_unlock (&tty->osem);
1408   }
1409   else {
1410     rc = iproc (c, tty);
1411   }
1412   return rc;
1413 }
1414 
1415 static rtems_termios_iproc_status_code
1416 siprocPoll (unsigned char c, rtems_termios_tty *tty)
1417 {
1418   if (c == '\r' && (tty->termios.c_iflag & IGNCR) != 0) {
1419     return RTEMS_TERMIOS_IPROC_CONTINUE;
1420   }
1421 
1422   /*
1423    * iprocEarly is done at the interrupt level for interrupt driven
1424    * devices so we need to perform those actions before processing
1425    * the character.
1426    */
1427   c = iprocEarly (c, tty);
1428   return siproc (c, tty);
1429 }
1430 
1431 /*
1432  * Fill the input buffer by polling the device
1433  */
1434 static rtems_termios_iproc_status_code
1435 fillBufferPoll (struct rtems_termios_tty *tty)
1436 {
1437   int                             n;
1438   rtems_termios_iproc_status_code rc;
1439 
1440   if (tty->termios.c_lflag & ICANON) {
1441     for (;;) {
1442       n = (*tty->handler.poll_read)(tty->device_context);
1443       if (n < 0) {
1444         rtems_task_wake_after (1);
1445       } else {
1446         rc = siprocPoll (n, tty);
1447         if (rc != RTEMS_TERMIOS_IPROC_CONTINUE) {
1448           return rc;
1449         }
1450       }
1451     }
1452   } else {
1453     rtems_interval then, now;
1454 
1455     then = rtems_clock_get_ticks_since_boot();
1456     for (;;) {
1457       n = (*tty->handler.poll_read)(tty->device_context);
1458       if (n < 0) {
1459         if (tty->termios.c_cc[VMIN]) {
1460           if (tty->termios.c_cc[VTIME] && tty->ccount) {
1461             now = rtems_clock_get_ticks_since_boot();
1462             if ((now - then) > tty->vtimeTicks) {
1463               break;
1464             }
1465           }
1466         } else {
1467           if (!tty->termios.c_cc[VTIME])
1468             break;
1469           now = rtems_clock_get_ticks_since_boot();
1470           if ((now - then) > tty->vtimeTicks) {
1471             break;
1472           }
1473         }
1474         rtems_task_wake_after (1);
1475       } else {
1476         rc = siprocPoll (n, tty);
1477         if (rc != RTEMS_TERMIOS_IPROC_CONTINUE) {
1478           return rc;
1479         }
1480         if (tty->ccount >= tty->termios.c_cc[VMIN])
1481           break;
1482         if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
1483           then = rtems_clock_get_ticks_since_boot();
1484       }
1485     }
1486   }
1487   return RTEMS_TERMIOS_IPROC_CONTINUE;
1488 }
1489 
1490 /*
1491  * Fill the input buffer from the raw input queue
1492  */
1493 static rtems_termios_iproc_status_code
1494 fillBufferQueue (struct rtems_termios_tty *tty)
1495 {
1496   rtems_termios_device_context *ctx = tty->device_context;
1497   rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout;
1498   bool wait = true;
1499 
1500   while ( wait ) {
1501     rtems_interrupt_lock_context lock_context;
1502 
1503     /*
1504      * Process characters read from raw queue
1505      */
1506 
1507     rtems_termios_device_lock_acquire (ctx, &lock_context);
1508 
1509     while ((tty->rawInBuf.Head != tty->rawInBuf.Tail) &&
1510                        (tty->ccount < (CBUFSIZE-1))) {
1511       unsigned char                    c;
1512       unsigned int                     newHead;
1513       rtems_termios_iproc_status_code  rc;
1514 
1515       newHead = (tty->rawInBuf.Head + 1) % tty->rawInBuf.Size;
1516       c = tty->rawInBuf.theBuf[newHead];
1517       tty->rawInBuf.Head = newHead;
1518 
1519       if(((tty->rawInBuf.Tail - newHead) % tty->rawInBuf.Size)
1520          < tty->lowwater) {
1521         tty->flow_ctrl &= ~FL_IREQXOF;
1522         /* if tx stopped and XON should be sent... */
1523         if (((tty->flow_ctrl & (FL_MDXON | FL_ISNTXOF))
1524              ==                (FL_MDXON | FL_ISNTXOF))
1525             && ((tty->rawOutBufState == rob_idle)
1526           || (tty->flow_ctrl & FL_OSTOP))) {
1527           /* XON should be sent now... */
1528           (*tty->handler.write)(
1529             tty->device_context, (void *)&(tty->termios.c_cc[VSTART]), 1);
1530         } else if (tty->flow_ctrl & FL_MDRTS) {
1531           tty->flow_ctrl &= ~FL_IRTSOFF;
1532           /* activate RTS line */
1533           if (tty->flow.start_remote_tx != NULL) {
1534             tty->flow.start_remote_tx(tty->device_context);
1535           }
1536         }
1537       }
1538 
1539       rtems_termios_device_lock_release (ctx, &lock_context);
1540 
1541       /* continue processing new character */
1542       if (tty->termios.c_lflag & ICANON) {
1543         rc = siproc (c, tty);
1544         if (rc != RTEMS_TERMIOS_IPROC_CONTINUE) {
1545           return rc;
1546         }
1547       } else {
1548         rc = siproc (c, tty);
1549 
1550         /* in non-canonical mode only stop on interrupt */
1551         if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT) {
1552           return rc;
1553         }
1554 
1555         if (tty->ccount >= tty->termios.c_cc[VMIN])
1556           wait = false;
1557       }
1558       timeout = tty->rawInBufSemaphoreTimeout;
1559 
1560       rtems_termios_device_lock_acquire (ctx, &lock_context);
1561     }
1562 
1563     rtems_termios_device_lock_release (ctx, &lock_context);
1564 
1565     /*
1566      * Wait for characters
1567      */
1568     if (wait) {
1569       if (tty->ccount < CBUFSIZE - 1) {
1570         rtems_binary_semaphore *sem;
1571         int eno;
1572 
1573         sem = &tty->rawInBuf.Semaphore;
1574 
1575         if (tty->rawInBufSemaphoreWait) {
1576           eno = rtems_binary_semaphore_wait_timed_ticks (sem, timeout);
1577         } else {
1578           eno = rtems_binary_semaphore_try_wait (sem);
1579         }
1580 
1581         if (eno != 0) {
1582           break;
1583         }
1584       } else {
1585         break;
1586       }
1587     }
1588   }
1589   return RTEMS_TERMIOS_IPROC_CONTINUE;
1590 }
1591 
1592 static rtems_status_code
1593 rtems_termios_read_tty (
1594   struct rtems_termios_tty *tty,
1595   char                     *buffer,
1596   uint32_t                  initial_count,
1597   uint32_t                 *count_read
1598 )
1599 {
1600   uint32_t                         count;
1601   rtems_termios_iproc_status_code  rc;
1602 
1603   count = initial_count;
1604 
1605   if (tty->cindex == tty->ccount) {
1606     tty->cindex = tty->ccount = 0;
1607     tty->read_start_column = tty->column;
1608     if (tty->handler.poll_read != NULL && tty->handler.mode == TERMIOS_POLLED)
1609       rc = fillBufferPoll (tty);
1610     else
1611       rc = fillBufferQueue (tty);
1612   } else {
1613     rc = RTEMS_TERMIOS_IPROC_CONTINUE;
1614   }
1615 
1616   /*
1617    * If there are characters in the buffer, then copy them to the caller.
1618    */
1619   while (count && (tty->cindex < tty->ccount)) {
1620     *buffer++ = tty->cbuf[tty->cindex++];
1621     count--;
1622    }
1623   tty->tty_rcvwakeup = false;
1624   *count_read = initial_count - count;
1625 
1626   /*
1627    * fillBufferPoll and fillBufferQueue can indicate that the operation
1628    * was interrupted. The isig handler can do nothing, have POSIX behavior
1629    * and cause a signal, or a user defined action.
1630    */
1631   if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT) {
1632     return RTEMS_INTERRUPTED;
1633   }
1634 
1635   return RTEMS_SUCCESSFUL;
1636 }
1637 
1638 rtems_status_code
1639 rtems_termios_read (void *arg)
1640 {
1641   rtems_libio_rw_args_t    *args = arg;
1642   struct rtems_termios_tty *tty = args->iop->data1;
1643   rtems_status_code         sc;
1644 
1645   rtems_mutex_lock (&tty->isem);
1646 
1647   if (rtems_termios_linesw[tty->t_line].l_read != NULL) {
1648 
1649     sc = rtems_termios_linesw[tty->t_line].l_read(tty,args);
1650     tty->tty_rcvwakeup = false;
1651     rtems_mutex_unlock (&tty->isem);
1652     return sc;
1653   }
1654 
1655   sc = rtems_termios_read_tty(
1656     tty,
1657     args->buffer,
1658     args->count,
1659     &args->bytes_moved
1660   );
1661   rtems_mutex_unlock (&tty->isem);
1662   return sc;
1663 }
1664 
1665 /*
1666  * signal receive interrupt to rx daemon
1667  * NOTE: This routine runs in the context of the
1668  *       device receive interrupt handler.
1669  */
1670 void rtems_termios_rxirq_occured(struct rtems_termios_tty *tty)
1671 {
1672   /*
1673    * send event to rx daemon task
1674    */
1675   rtems_event_send(tty->rxTaskId,TERMIOS_RX_PROC_EVENT);
1676 }
1677 
1678 static bool
1679 mustCallReceiveCallback (const rtems_termios_tty *tty, unsigned char c,
1680                          unsigned int newTail, unsigned int head)
1681 {
1682   if ((tty->termios.c_lflag & ICANON) != 0) {
1683     return c == '\n' || c == tty->termios.c_cc[VEOF] ||
1684       c == tty->termios.c_cc[VEOL] || c == tty->termios.c_cc[VEOL2];
1685   } else {
1686     unsigned int rawContentSize = (newTail - head) % tty->rawInBuf.Size;
1687 
1688     return rawContentSize >= tty->termios.c_cc[VMIN];
1689   }
1690 }
1691 
1692 /*
1693  * Place characters on raw queue.
1694  * NOTE: This routine runs in the context of the
1695  *       device receive interrupt handler.
1696  * Returns the number of characters dropped because of overflow.
1697  */
1698 int
1699 rtems_termios_enqueue_raw_characters (void *ttyp, const char *buf, int len)
1700 {
1701   struct rtems_termios_tty *tty = ttyp;
1702   char c;
1703   int dropped = 0;
1704   bool flow_rcv = false; /* true, if flow control char received */
1705   rtems_termios_device_context *ctx = tty->device_context;
1706   rtems_interrupt_lock_context lock_context;
1707 
1708   if (rtems_termios_linesw[tty->t_line].l_rint != NULL) {
1709     while (len--) {
1710       c = *buf++;
1711       rtems_termios_linesw[tty->t_line].l_rint(c,tty);
1712     }
1713 
1714     /*
1715      * check to see if rcv wakeup callback was set
1716      */
1717     if (tty->tty_rcv.sw_pfn != NULL && !tty->tty_rcvwakeup) {
1718       tty->tty_rcvwakeup = true;
1719       (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
1720     }
1721     return 0;
1722   }
1723 
1724   while (len--) {
1725     c = *buf++;
1726     /* FIXME: implement IXANY: any character restarts output */
1727     /* if incoming XON/XOFF controls outgoing stream: */
1728     if (tty->flow_ctrl & FL_MDXON) {
1729       /* if received char is V_STOP and V_START (both are equal value) */
1730       if (c == tty->termios.c_cc[VSTOP]) {
1731         if (c == tty->termios.c_cc[VSTART]) {
1732           /* received VSTOP and VSTART==VSTOP? */
1733           /* then toggle "stop output" status  */
1734           tty->flow_ctrl = tty->flow_ctrl ^ FL_ORCVXOF;
1735         }
1736         else {
1737           /* VSTOP received (other code than VSTART) */
1738           /* stop output                             */
1739           tty->flow_ctrl |= FL_ORCVXOF;
1740         }
1741         flow_rcv = true;
1742       }
1743       else if (c == tty->termios.c_cc[VSTART]) {
1744         /* VSTART received */
1745         /* restart output  */
1746         tty->flow_ctrl &= ~FL_ORCVXOF;
1747         flow_rcv = true;
1748       }
1749     }
1750     if (flow_rcv) {
1751       /* restart output according to FL_ORCVXOF flag */
1752       if ((tty->flow_ctrl & (FL_ORCVXOF | FL_OSTOP)) == FL_OSTOP) {
1753         /* disable interrupts    */
1754         rtems_termios_device_lock_acquire (ctx, &lock_context);
1755         tty->flow_ctrl &= ~FL_OSTOP;
1756         /* check for chars in output buffer (or rob_state?) */
1757         if (tty->rawOutBufState != rob_idle) {
1758           /* if chars available, call write function... */
1759           (*tty->handler.write)(
1760             ctx, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail], 1);
1761         }
1762         /* reenable interrupts */
1763         rtems_termios_device_lock_release (ctx, &lock_context);
1764       }
1765     } else {
1766       unsigned int head;
1767       unsigned int oldTail;
1768       unsigned int newTail;
1769       bool callReciveCallback;
1770 
1771       if (c == '\r' && (tty->termios.c_iflag & IGNCR) != 0) {
1772         continue;
1773       }
1774 
1775       c = iprocEarly (c, tty);
1776 
1777       rtems_termios_device_lock_acquire (ctx, &lock_context);
1778 
1779       head = tty->rawInBuf.Head;
1780       oldTail = tty->rawInBuf.Tail;
1781       newTail = (oldTail + 1) % tty->rawInBuf.Size;
1782 
1783       /* if chars_in_buffer > highwater                */
1784       if ((tty->flow_ctrl & FL_IREQXOF) != 0 && (((newTail - head) %
1785           tty->rawInBuf.Size) > tty->highwater)) {
1786         /* incoming data stream should be stopped */
1787         tty->flow_ctrl |= FL_IREQXOF;
1788         if ((tty->flow_ctrl & (FL_MDXOF | FL_ISNTXOF))
1789             ==                (FL_MDXOF             ) ) {
1790           if ((tty->flow_ctrl & FL_OSTOP) ||
1791               (tty->rawOutBufState == rob_idle)) {
1792             /* if tx is stopped due to XOFF or out of data */
1793             /*    call write function here                 */
1794             tty->flow_ctrl |= FL_ISNTXOF;
1795             (*tty->handler.write)(ctx,
1796                 (void *)&(tty->termios.c_cc[VSTOP]), 1);
1797           }
1798         } else if ((tty->flow_ctrl & (FL_MDRTS | FL_IRTSOFF)) == (FL_MDRTS) ) {
1799           tty->flow_ctrl |= FL_IRTSOFF;
1800           /* deactivate RTS line */
1801           if (tty->flow.stop_remote_tx != NULL) {
1802             tty->flow.stop_remote_tx(ctx);
1803           }
1804         }
1805       }
1806 
1807       callReciveCallback = false;
1808 
1809       if (newTail != head) {
1810         tty->rawInBuf.theBuf[newTail] = c;
1811         tty->rawInBuf.Tail = newTail;
1812 
1813         /*
1814          * check to see if rcv wakeup callback was set
1815          */
1816         if (tty->tty_rcv.sw_pfn != NULL && !tty->tty_rcvwakeup) {
1817           if (mustCallReceiveCallback (tty, c, newTail, head)) {
1818             tty->tty_rcvwakeup = true;
1819             callReciveCallback = true;
1820           }
1821         }
1822       } else {
1823         ++dropped;
1824 
1825         if (tty->tty_rcv.sw_pfn != NULL && !tty->tty_rcvwakeup) {
1826           tty->tty_rcvwakeup = true;
1827           callReciveCallback = true;
1828         }
1829       }
1830 
1831       rtems_termios_device_lock_release (ctx, &lock_context);
1832 
1833       if (callReciveCallback) {
1834         (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
1835       }
1836     }
1837   }
1838 
1839   tty->rawInBufDropped += dropped;
1840   rtems_binary_semaphore_post (&tty->rawInBuf.Semaphore);
1841   return dropped;
1842 }
1843 
1844 /*
1845  * in task-driven mode, this function is called in Tx task context
1846  * in interrupt-driven mode, this function is called in TxIRQ context
1847  */
1848 static int
1849 rtems_termios_refill_transmitter (struct rtems_termios_tty *tty)
1850 {
1851   bool wakeUpWriterTask = false;
1852   unsigned int newTail;
1853   int nToSend;
1854   rtems_termios_device_context *ctx = tty->device_context;
1855   rtems_interrupt_lock_context lock_context;
1856   int len;
1857 
1858   rtems_termios_device_lock_acquire (ctx, &lock_context);
1859 
1860   /* check for XOF/XON to send */
1861   if ((tty->flow_ctrl & (FL_MDXOF | FL_IREQXOF | FL_ISNTXOF))
1862       == (FL_MDXOF | FL_IREQXOF)) {
1863     /* XOFF should be sent now... */
1864     (*tty->handler.write)(ctx, (void *)&(tty->termios.c_cc[VSTOP]), 1);
1865 
1866     tty->t_dqlen--;
1867     tty->flow_ctrl |= FL_ISNTXOF;
1868 
1869     nToSend = 1;
1870 
1871   } else if ((tty->flow_ctrl & (FL_IREQXOF | FL_ISNTXOF)) == FL_ISNTXOF) {
1872     /* NOTE: send XON even, if no longer in XON/XOFF mode... */
1873     /* XON should be sent now... */
1874     /*
1875      * FIXME: this .write call will generate another
1876      * dequeue callback. This will advance the "Tail" in the data
1877      * buffer, although the corresponding data is not yet out!
1878      * Therefore the dequeue "length" should be reduced by 1
1879      */
1880     (*tty->handler.write)(ctx, (void *)&(tty->termios.c_cc[VSTART]), 1);
1881 
1882     tty->t_dqlen--;
1883     tty->flow_ctrl &= ~FL_ISNTXOF;
1884 
1885     nToSend = 1;
1886   } else if ( tty->rawOutBuf.Head == tty->rawOutBuf.Tail ) {
1887     /*
1888      * buffer was empty
1889      */
1890     if (tty->rawOutBufState == rob_wait) {
1891       /*
1892        * this should never happen...
1893        */
1894       wakeUpWriterTask = true;
1895     }
1896 
1897     (*tty->handler.write) (ctx, NULL, 0);
1898     nToSend = 0;
1899   } else {
1900     len = tty->t_dqlen;
1901     tty->t_dqlen = 0;
1902 
1903     newTail = (tty->rawOutBuf.Tail + len) % tty->rawOutBuf.Size;
1904     tty->rawOutBuf.Tail = newTail;
1905     if (tty->rawOutBufState == rob_wait) {
1906       /*
1907        * wake up any pending writer task
1908        */
1909       wakeUpWriterTask = true;
1910     }
1911 
1912     if (newTail == tty->rawOutBuf.Head) {
1913       /*
1914        * Buffer has become empty
1915        */
1916       tty->rawOutBufState = rob_idle;
1917       (*tty->handler.write) (ctx, NULL, 0);
1918       nToSend = 0;
1919 
1920       /*
1921        * check to see if snd wakeup callback was set
1922        */
1923       if ( tty->tty_snd.sw_pfn != NULL) {
1924         (*tty->tty_snd.sw_pfn)(&tty->termios, tty->tty_snd.sw_arg);
1925       }
1926     } else {
1927       /*
1928        * Buffer not empty, check flow control, start transmitter
1929        */
1930       nToSend = startXmit (tty, newTail, true);
1931     }
1932   }
1933 
1934   rtems_termios_device_lock_release (ctx, &lock_context);
1935 
1936   if (wakeUpWriterTask) {
1937     rtems_binary_semaphore_post (&tty->rawOutBuf.Semaphore);
1938   }
1939 
1940   return nToSend;
1941 }
1942 
1943 /*
1944  * Characters have been transmitted
1945  * NOTE: This routine runs in the context of the
1946  *       device transmit interrupt handler.
1947  * The second argument is the number of characters transmitted so far.
1948  * This value will always be 1 for devices which generate an interrupt
1949  * for each transmitted character.
1950  * It returns number of characters left to transmit
1951  */
1952 int
1953 rtems_termios_dequeue_characters (void *ttyp, int len)
1954 {
1955   struct rtems_termios_tty *tty = ttyp;
1956   rtems_status_code sc;
1957 
1958   /*
1959    * sum up character count already sent
1960    */
1961   tty->t_dqlen += len;
1962 
1963   if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
1964     /*
1965      * send wake up to transmitter task
1966      */
1967     tty->txTaskCharsDequeued = len;
1968     sc = rtems_event_send(tty->txTaskId, TERMIOS_TX_START_EVENT);
1969     if (sc != RTEMS_SUCCESSFUL)
1970       rtems_fatal_error_occurred (sc);
1971     return 0; /* nothing to output in IRQ... */
1972   }
1973 
1974   if (tty->t_line == PPPDISC ) {
1975     /*
1976      * call PPP line discipline start function
1977      */
1978     if (rtems_termios_linesw[tty->t_line].l_start != NULL) {
1979       rtems_termios_linesw[tty->t_line].l_start(tty, len);
1980     }
1981     return 0; /* nothing to output in IRQ... */
1982   }
1983 
1984   return rtems_termios_refill_transmitter(tty);
1985 }
1986 
1987 /*
1988  * this task actually processes any transmit events
1989  */
1990 static rtems_task rtems_termios_txdaemon(rtems_task_argument argument)
1991 {
1992   struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
1993   rtems_event_set the_event;
1994 
1995   while (1) {
1996     /*
1997      * wait for rtems event
1998      */
1999     rtems_event_receive(
2000        (TERMIOS_TX_START_EVENT | TERMIOS_TX_TERMINATE_EVENT),
2001        RTEMS_EVENT_ANY | RTEMS_WAIT,
2002        RTEMS_NO_TIMEOUT,
2003        &the_event
2004     );
2005     if ((the_event & TERMIOS_TX_TERMINATE_EVENT) != 0) {
2006       tty->txTaskId = 0;
2007       rtems_task_exit();
2008     }
2009 
2010     /*
2011      * call any line discipline start function
2012      */
2013     if (rtems_termios_linesw[tty->t_line].l_start != NULL) {
2014       rtems_termios_linesw[tty->t_line].l_start(tty, tty->txTaskCharsDequeued);
2015 
2016       if (tty->t_line == PPPDISC) {
2017         /*
2018          * Do not call rtems_termios_refill_transmitter() in this case similar
2019          * to rtems_termios_dequeue_characters().
2020          */
2021         continue;
2022       }
2023     }
2024 
2025     /*
2026      * try to push further characters to device
2027      */
2028     rtems_termios_refill_transmitter(tty);
2029   }
2030 }
2031 
2032 /*
2033  * this task actually processes any receive events
2034  */
2035 static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument)
2036 {
2037   struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
2038   rtems_termios_device_context *ctx = tty->device_context;
2039   rtems_event_set the_event;
2040   int c;
2041   char c_buf;
2042 
2043   while (1) {
2044     /*
2045      * wait for rtems event
2046      */
2047     rtems_event_receive(
2048       (TERMIOS_RX_PROC_EVENT | TERMIOS_RX_TERMINATE_EVENT),
2049       RTEMS_EVENT_ANY | RTEMS_WAIT,
2050       RTEMS_NO_TIMEOUT,
2051       &the_event
2052     );
2053     if ((the_event & TERMIOS_RX_TERMINATE_EVENT) != 0) {
2054       tty->rxTaskId = 0;
2055       rtems_task_exit();
2056     }
2057 
2058     /*
2059      * do something
2060      */
2061     c = tty->handler.poll_read(ctx);
2062     if (c != EOF) {
2063       /*
2064        * poll_read did call enqueue on its own
2065        */
2066       c_buf = c;
2067       rtems_termios_enqueue_raw_characters ( tty,&c_buf,1);
2068     }
2069   }
2070 }
2071 
2072 static int
2073 rtems_termios_imfs_open (rtems_libio_t *iop,
2074   const char *path, int oflag, mode_t mode)
2075 {
2076   rtems_termios_device_node *device_node;
2077   rtems_libio_open_close_args_t args;
2078   struct rtems_termios_tty *tty;
2079 
2080   device_node = IMFS_generic_get_context_by_iop (iop);
2081 
2082   memset (&args, 0, sizeof (args));
2083   args.iop = iop;
2084   args.flags = rtems_libio_iop_flags (iop);
2085   args.mode = mode;
2086 
2087   rtems_termios_obtain ();
2088 
2089   tty = rtems_termios_open_tty (device_node->major, device_node->minor, &args,
2090       device_node->tty, device_node, NULL);
2091   if (tty == NULL) {
2092     rtems_termios_release ();
2093     rtems_set_errno_and_return_minus_one (ENOMEM);
2094   }
2095 
2096   rtems_termios_release ();
2097   return 0;
2098 }
2099 
2100 static int
2101 rtems_termios_imfs_close (rtems_libio_t *iop)
2102 {
2103   rtems_libio_open_close_args_t args;
2104   struct rtems_termios_tty *tty;
2105 
2106   memset (&args, 0, sizeof (args));
2107   args.iop = iop;
2108 
2109   tty = iop->data1;
2110 
2111   rtems_termios_obtain ();
2112   rtems_termios_close_tty (tty, &args);
2113   rtems_termios_release ();
2114   return 0;
2115 }
2116 
2117 static ssize_t
2118 rtems_termios_imfs_read (rtems_libio_t *iop, void *buffer, size_t count)
2119 {
2120   struct rtems_termios_tty *tty;
2121   uint32_t                  bytes_moved;
2122   rtems_status_code         sc;
2123 
2124   tty = iop->data1;
2125 
2126   rtems_mutex_lock (&tty->isem);
2127 
2128   if (rtems_termios_linesw[tty->t_line].l_read != NULL) {
2129     rtems_libio_rw_args_t args;
2130     rtems_status_code sc;
2131 
2132     memset (&args, 0, sizeof (args));
2133     args.iop = iop;
2134     args.buffer = buffer;
2135     args.count = count;
2136     args.flags = rtems_libio_iop_flags (iop);
2137 
2138     sc = rtems_termios_linesw[tty->t_line].l_read (tty, &args);
2139     tty->tty_rcvwakeup = false;
2140     rtems_mutex_unlock (&tty->isem);
2141 
2142     if (sc != RTEMS_SUCCESSFUL) {
2143       return rtems_status_code_to_errno (sc);
2144     }
2145 
2146     return (ssize_t) args.bytes_moved;
2147   }
2148 
2149   sc = rtems_termios_read_tty(tty, buffer, count, &bytes_moved);
2150   rtems_mutex_unlock (&tty->isem);
2151   if (sc != RTEMS_SUCCESSFUL) {
2152      return rtems_status_code_to_errno (sc);
2153   }
2154   return (ssize_t) bytes_moved;
2155   
2156 }
2157 
2158 static ssize_t
2159 rtems_termios_imfs_write (rtems_libio_t *iop, const void *buffer, size_t count)
2160 {
2161   struct rtems_termios_tty *tty;
2162   uint32_t bytes_moved;
2163 
2164   tty = iop->data1;
2165 
2166   rtems_mutex_lock (&tty->osem);
2167 
2168   if (rtems_termios_linesw[tty->t_line].l_write != NULL) {
2169     rtems_libio_rw_args_t args;
2170     rtems_status_code sc;
2171 
2172     memset (&args, 0, sizeof (args));
2173     args.iop = iop;
2174     args.buffer = RTEMS_DECONST (void *, buffer);
2175     args.count = count;
2176     args.flags = rtems_libio_iop_flags (iop);
2177 
2178     sc = rtems_termios_linesw[tty->t_line].l_write (tty, &args);
2179     rtems_mutex_unlock (&tty->osem);
2180 
2181     if (sc != RTEMS_SUCCESSFUL) {
2182       return rtems_status_code_to_errno (sc);
2183     }
2184 
2185     return (ssize_t) args.bytes_moved;
2186   }
2187 
2188   bytes_moved = rtems_termios_write_tty (iop, tty, buffer, count);
2189   rtems_mutex_unlock (&tty->osem);
2190   return (ssize_t) bytes_moved;
2191 }
2192 
2193 static int
2194 rtems_termios_imfs_ioctl (rtems_libio_t *iop, ioctl_command_t request,
2195   void *buffer)
2196 {
2197   rtems_status_code sc;
2198   rtems_libio_ioctl_args_t args;
2199 
2200   memset (&args, 0, sizeof (args));
2201   args.iop = iop;
2202   args.command = request;
2203   args.buffer = buffer;
2204 
2205   sc = rtems_termios_ioctl (&args);
2206   if ( sc == RTEMS_SUCCESSFUL ) {
2207     return args.ioctl_return;
2208   } else {
2209     return rtems_status_code_to_errno (sc);
2210   }
2211 }
2212 
2213 static const rtems_filesystem_file_handlers_r rtems_termios_imfs_handler = {
2214   .open_h = rtems_termios_imfs_open,
2215   .close_h = rtems_termios_imfs_close,
2216   .read_h = rtems_termios_imfs_read,
2217   .write_h = rtems_termios_imfs_write,
2218   .ioctl_h = rtems_termios_imfs_ioctl,
2219   .lseek_h = rtems_filesystem_default_lseek,
2220   .fstat_h = IMFS_stat,
2221   .ftruncate_h = rtems_filesystem_default_ftruncate,
2222   .fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
2223   .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
2224   .fcntl_h = rtems_filesystem_default_fcntl,
2225   .kqfilter_h = rtems_termios_kqfilter,
2226   .mmap_h = rtems_termios_mmap,
2227   .poll_h = rtems_termios_poll,
2228   .readv_h = rtems_filesystem_default_readv,
2229   .writev_h = rtems_filesystem_default_writev
2230 };
2231 
2232 static IMFS_jnode_t *
2233 rtems_termios_imfs_node_initialize (IMFS_jnode_t *node, void *arg)
2234 {
2235   rtems_termios_device_node *device_node;
2236   dev_t dev;
2237 
2238   node = IMFS_node_initialize_generic (node, arg);
2239   device_node = IMFS_generic_get_context_by_node (node);
2240   dev = IMFS_generic_get_device_identifier_by_node (node);
2241   device_node->major = rtems_filesystem_dev_major_t (dev);
2242   device_node->minor = rtems_filesystem_dev_minor_t (dev);
2243 
2244   return node;
2245 }
2246 
2247 static void
2248 rtems_termios_imfs_node_destroy (IMFS_jnode_t *node)
2249 {
2250   rtems_termios_device_node *device_node;
2251 
2252   device_node = IMFS_generic_get_context_by_node (node);
2253   free (device_node);
2254   IMFS_node_destroy_default (node);
2255 }
2256 
2257 static const IMFS_node_control rtems_termios_imfs_node_control =
2258   IMFS_GENERIC_INITIALIZER(
2259     &rtems_termios_imfs_handler,
2260     rtems_termios_imfs_node_initialize,
2261     rtems_termios_imfs_node_destroy
2262   );