source: rtems/cpukit/libcsupport/src/termios.c @ 83c5fc1

4.104.114.84.95
Last change on this file since 83c5fc1 was 83c5fc1, checked in by Ralf Corsepius <ralf.corsepius@…>, on 03/23/04 at 06:07:53

2004-03-23 Ralf Corsepius <ralf_corsepius@…>

  • libcsupport/include/chain.h, libcsupport/include/clockdrv.h, libcsupport/include/ringbuf.h, libcsupport/include/spurious.h, libcsupport/include/timerdrv.h, libcsupport/include/vmeintr.h, libcsupport/include/motorola/mc68230.h, libcsupport/include/rtems/assoc.h, libcsupport/include/rtems/libio.h, libcsupport/include/rtems/libio_.h, libcsupport/include/rtems/termiostypes.h, libcsupport/include/zilog/z8036.h, libcsupport/include/zilog/z8530.h, libcsupport/include/zilog/z8536.h, libcsupport/src/gettod.c, libcsupport/src/assoc.c, libcsupport/src/assocnamebad.c, libcsupport/src/error.c, libcsupport/src/libio.c, libcsupport/src/libio_sockets.c, libcsupport/src/malloc.c, libcsupport/src/no_libc.c, libcsupport/src/termios.c, libcsupport/src/termiosreserveresources.c: Convert to using c99 fixed-size types.
  • Property mode set to 100644
File size: 38.7 KB
Line 
1/*
2 * TERMIOS serial line support
3 *
4 *  Author:
5 *    W. Eric Norum
6 *    Saskatchewan Accelerator Laboratory
7 *    University of Saskatchewan
8 *    Saskatoon, Saskatchewan, CANADA
9 *    eric@skatter.usask.ca
10 *
11 *  The license and distribution terms for this file may be
12 *  found in the file LICENSE in this distribution or at
13 *  http://www.rtems.com/license/LICENSE.
14 *
15 *  $Id$
16 */
17
18#if HAVE_CONFIG_H
19#include "config.h"
20#endif
21
22#include <rtems.h>
23#include <rtems/libio.h>
24#include <ctype.h>
25#include <errno.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <termios.h>
29#include <unistd.h>
30#include <sys/ttycom.h>
31
32#include <rtems/termiostypes.h>
33
34/*
35 *  FreeBSD does not support a full POSIX termios so we have to help it out
36 */
37#if !HAVE_DECL_XTABS
38#define XTABS   0
39#endif
40
41#if !HAVE_DECL_ONLRET
42#define ONLRET  0
43#endif
44#if !HAVE_DECL_ONOCR
45#define ONOCR  0
46#endif
47#if !HAVE_DECL_TABDLY
48#define TABDLY  0
49#endif
50#if !HAVE_DECL_OLCUC
51#define OLCUC 0
52#endif
53#if !HAVE_DECL_ILCUC
54#define ILCUC 0
55#endif
56#if !HAVE_DECL_OCRNL
57#define OCRNL 0
58#endif
59#if !HAVE_DECL_IUCLC
60#define IUCLC   0
61#endif
62
63#if !HAVE_DECL_ECHOPRT
64#define ECHOPRT 0
65#endif
66
67/*
68 * The size of the cooked buffer
69 */
70#define CBUFSIZE        (rtems_termios_cbufsize)
71
72/*
73 * The sizes of the raw message buffers.
74 * On most architectures it is quite a bit more
75 * efficient if these are powers of two.
76 */
77#define RAW_INPUT_BUFFER_SIZE   (rtems_termios_raw_input_size)
78#define RAW_OUTPUT_BUFFER_SIZE  (rtems_termios_raw_output_size)
79
80/* fields for "flow_ctrl" status */
81#define FL_IREQXOF 1        /* input queue requests stop of incoming data */
82#define FL_ISNTXOF 2        /* XOFF has been sent to other side of line   */
83#define FL_IRTSOFF 4        /* RTS has been turned off for other side..   */
84
85#define FL_ORCVXOF 0x10     /* XOFF has been received                     */
86#define FL_OSTOP   0x20     /* output has been stopped due to XOFF        */
87
88#define FL_MDRTS   0x100    /* input controlled with RTS/CTS handshake    */
89#define FL_MDXON   0x200    /* input controlled with XON/XOFF protocol    */
90#define FL_MDXOF   0x400    /* output controlled with XON/XOFF protocol   */
91
92#define NODISC(n) \
93        { NULL, NULL,   NULL,   NULL, \
94          NULL, NULL,   NULL,   NULL }
95/*
96 * FIXME: change linesw entries consistant with linesw entry usage...
97 */
98struct  linesw linesw[MAXLDISC] =
99{
100        NODISC(0),              /* 0- termios-built-in */
101        NODISC(1),              /* 1- defunct */
102        NODISC(2),              /* 2- NTTYDISC */
103        NODISC(3),              /* TABLDISC */
104        NODISC(4),              /* SLIPDISC */
105        NODISC(5),              /* PPPDISC */
106        NODISC(6),              /* loadable */
107        NODISC(7),              /* loadable */
108};
109
110int     nlinesw = sizeof (linesw) / sizeof (linesw[0]);
111
112extern struct rtems_termios_tty *rtems_termios_ttyHead;
113extern struct rtems_termios_tty *rtems_termios_ttyTail;
114extern rtems_id rtems_termios_ttyMutex;
115
116static int rtems_termios_cbufsize = 256;
117static int rtems_termios_raw_input_size = 128;
118static int rtems_termios_raw_output_size = 64;
119
120static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument);
121static rtems_task rtems_termios_txdaemon(rtems_task_argument argument);
122/*
123 * some constants for I/O daemon task creation
124 */
125#define TERMIOS_TXTASK_PRIO 10
126#define TERMIOS_RXTASK_PRIO 9
127#define TERMIOS_TXTASK_STACKSIZE 1024
128#define TERMIOS_RXTASK_STACKSIZE 1024
129/*
130 * some events to be sent to the I/O tasks
131 */
132#define TERMIOS_TX_START_EVENT     RTEMS_EVENT_1
133#define TERMIOS_TX_TERMINATE_EVENT RTEMS_EVENT_0
134
135#define TERMIOS_RX_PROC_EVENT      RTEMS_EVENT_1
136#define TERMIOS_RX_TERMINATE_EVENT RTEMS_EVENT_0
137
138/*
139 * Open a termios device
140 */
141rtems_status_code
142rtems_termios_open (
143  rtems_device_major_number      major,
144  rtems_device_minor_number      minor,
145  void                          *arg,
146  const rtems_termios_callbacks *callbacks
147  )
148{
149        rtems_status_code sc;
150        rtems_libio_open_close_args_t *args = arg;
151        struct rtems_termios_tty *tty;
152
153        /*
154         * See if the device has already been opened
155         */
156        sc = rtems_semaphore_obtain (rtems_termios_ttyMutex,
157                                     RTEMS_WAIT, RTEMS_NO_TIMEOUT);
158        if (sc != RTEMS_SUCCESSFUL)
159                return sc;
160        for (tty = rtems_termios_ttyHead ; tty != NULL ; tty = tty->forw) {
161                if ((tty->major == major) && (tty->minor == minor))
162                        break;
163        }
164        if (tty == NULL) {
165                static char c = 'a';
166
167                /*
168                 * Create a new device
169                 */
170                tty = calloc (1, sizeof (struct rtems_termios_tty));
171                if (tty == NULL) {
172                        rtems_semaphore_release (rtems_termios_ttyMutex);
173                        return RTEMS_NO_MEMORY;
174                }
175                /*
176                 * allocate raw input buffer
177                 */
178                tty->rawInBuf.Size = RAW_INPUT_BUFFER_SIZE;
179                tty->rawInBuf.theBuf = malloc (tty->rawInBuf.Size);
180                if (tty->rawInBuf.theBuf == NULL) {
181                        free(tty);
182                        rtems_semaphore_release (rtems_termios_ttyMutex);
183                        return RTEMS_NO_MEMORY;
184                }
185                /*
186                 * allocate raw output buffer
187                 */
188                tty->rawOutBuf.Size = RAW_OUTPUT_BUFFER_SIZE;
189                tty->rawOutBuf.theBuf = malloc (tty->rawOutBuf.Size);
190                if (tty->rawOutBuf.theBuf == NULL) {
191                        free((void *)(tty->rawInBuf.theBuf));
192                        free(tty);
193                        rtems_semaphore_release (rtems_termios_ttyMutex);
194                        return RTEMS_NO_MEMORY;
195                }
196                /*
197                 * allocate cooked buffer
198                 */
199                tty->cbuf  = malloc (CBUFSIZE);
200                if (tty->cbuf == NULL) {
201                        free((void *)(tty->rawOutBuf.theBuf));
202                        free((void *)(tty->rawInBuf.theBuf));
203                        free(tty);
204                        rtems_semaphore_release (rtems_termios_ttyMutex);
205                        return RTEMS_NO_MEMORY;
206                }
207                /*
208                 * Initialize wakeup callbacks
209                 */
210                tty->tty_snd.sw_pfn = NULL;
211                tty->tty_snd.sw_arg = NULL;
212                tty->tty_rcv.sw_pfn = NULL;
213                tty->tty_rcv.sw_arg = NULL;
214                tty->tty_rcvwakeup  = 0;
215
216                /*
217                 * link tty
218                 */
219                tty->forw = rtems_termios_ttyHead;
220                tty->back = NULL;
221                if (rtems_termios_ttyHead != NULL)
222                        rtems_termios_ttyHead->back = tty;
223                rtems_termios_ttyHead = tty;
224                if (rtems_termios_ttyTail == NULL)
225                        rtems_termios_ttyTail = tty;
226
227                tty->minor = minor;
228                tty->major = major;
229
230                /*
231                 * Set up mutex semaphores
232                 */
233                sc = rtems_semaphore_create (
234                        rtems_build_name ('T', 'R', 'i', c),
235                        1,
236                        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
237                        RTEMS_NO_PRIORITY,
238                        &tty->isem);
239                if (sc != RTEMS_SUCCESSFUL)
240                        rtems_fatal_error_occurred (sc);
241                sc = rtems_semaphore_create (
242                        rtems_build_name ('T', 'R', 'o', c),
243                        1,
244                        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
245                        RTEMS_NO_PRIORITY,
246                        &tty->osem);
247                if (sc != RTEMS_SUCCESSFUL)
248                        rtems_fatal_error_occurred (sc);
249                sc = rtems_semaphore_create (
250                        rtems_build_name ('T', 'R', 'x', c),
251                        0,
252                        RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_FIFO,
253                        RTEMS_NO_PRIORITY,
254                        &tty->rawOutBuf.Semaphore);
255                if (sc != RTEMS_SUCCESSFUL)
256                        rtems_fatal_error_occurred (sc);
257                tty->rawOutBufState = rob_idle;
258
259                /*
260                 * Set callbacks
261                 */
262                tty->device = *callbacks;
263
264                /*
265                 * Create I/O tasks
266                 */
267                if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
268                        sc = rtems_task_create (
269                                   rtems_build_name ('T', 'x', 'T', c),
270                                   TERMIOS_TXTASK_PRIO,
271                                   TERMIOS_TXTASK_STACKSIZE,
272                                   RTEMS_NO_PREEMPT | RTEMS_NO_TIMESLICE |
273                                   RTEMS_NO_ASR,
274                                   RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
275                                   &tty->txTaskId);
276                        if (sc != RTEMS_SUCCESSFUL)
277                                rtems_fatal_error_occurred (sc);
278                        sc = rtems_task_create (
279                                   rtems_build_name ('R', 'x', 'T', c),
280                                   TERMIOS_RXTASK_PRIO,
281                                   TERMIOS_RXTASK_STACKSIZE,
282                                   RTEMS_NO_PREEMPT | RTEMS_NO_TIMESLICE |
283                                   RTEMS_NO_ASR,
284                                   RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
285                                   &tty->rxTaskId);
286                        if (sc != RTEMS_SUCCESSFUL)
287                                rtems_fatal_error_occurred (sc);
288                               
289                }
290                if ((tty->device.pollRead == NULL) ||
291                    (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN)){
292                        sc = rtems_semaphore_create (
293                                rtems_build_name ('T', 'R', 'r', c),
294                                0,
295                                RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY,
296                                RTEMS_NO_PRIORITY,
297                                &tty->rawInBuf.Semaphore);
298                        if (sc != RTEMS_SUCCESSFUL)
299                                rtems_fatal_error_occurred (sc);
300                }
301
302                /*
303                 * Set default parameters
304                 */
305                tty->termios.c_iflag = BRKINT | ICRNL | IXON | IMAXBEL;
306                tty->termios.c_oflag = OPOST | ONLCR | XTABS;
307                tty->termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
308                tty->termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOK | ECHOE | ECHOCTL;
309
310                tty->termios.c_cc[VINTR] = '\003';
311                tty->termios.c_cc[VQUIT] = '\034';
312                tty->termios.c_cc[VERASE] = '\177';
313                tty->termios.c_cc[VKILL] = '\025';
314                tty->termios.c_cc[VEOF] = '\004';
315                tty->termios.c_cc[VEOL] = '\000';
316                tty->termios.c_cc[VEOL2] = '\000';
317                tty->termios.c_cc[VSTART] = '\021';
318                tty->termios.c_cc[VSTOP] = '\023';
319                tty->termios.c_cc[VSUSP] = '\032';
320                tty->termios.c_cc[VREPRINT] = '\022';
321                tty->termios.c_cc[VDISCARD] = '\017';
322                tty->termios.c_cc[VWERASE] = '\027';
323                tty->termios.c_cc[VLNEXT] = '\026';
324
325                /* start with no flow control, clear flow control flags */
326                tty->flow_ctrl = 0;
327                /*
328                 * set low/highwater mark for XON/XOFF support
329                 */
330                tty->lowwater  = tty->rawInBuf.Size * 1/2;
331                tty->highwater = tty->rawInBuf.Size * 3/4;
332                /*
333                 * Bump name characer
334                 */
335                if (c++ == 'z')
336                        c = 'a';
337
338        }
339        args->iop->data1 = tty;
340        if (!tty->refcount++) {
341          if (tty->device.firstOpen)
342                (*tty->device.firstOpen)(major, minor, arg);
343          /*
344           * start I/O tasks, if needed
345           */
346          if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
347            sc = rtems_task_start(tty->rxTaskId,
348                                  rtems_termios_rxdaemon,
349                                  (rtems_task_argument)tty);
350            if (sc != RTEMS_SUCCESSFUL)
351              rtems_fatal_error_occurred (sc);
352           
353            sc = rtems_task_start(tty->txTaskId,
354                                  rtems_termios_txdaemon,
355                                  (rtems_task_argument)tty);
356            if (sc != RTEMS_SUCCESSFUL)
357              rtems_fatal_error_occurred (sc);
358          }
359        }
360        rtems_semaphore_release (rtems_termios_ttyMutex);
361        return RTEMS_SUCCESSFUL;
362}
363
364/*
365 * Drain output queue
366 */
367static void
368drainOutput (struct rtems_termios_tty *tty)
369{
370        rtems_interrupt_level level;
371        rtems_status_code sc;
372
373        if (tty->device.outputUsesInterrupts != TERMIOS_POLLED) {
374                rtems_interrupt_disable (level);
375                while (tty->rawOutBuf.Tail != tty->rawOutBuf.Head) {
376                        tty->rawOutBufState = rob_wait;
377                        rtems_interrupt_enable (level);
378                        sc = rtems_semaphore_obtain (tty->rawOutBuf.Semaphore,
379                                                        RTEMS_WAIT,
380                                                        RTEMS_NO_TIMEOUT);
381                        if (sc != RTEMS_SUCCESSFUL)
382                                rtems_fatal_error_occurred (sc);
383                        rtems_interrupt_disable (level);
384                }
385                rtems_interrupt_enable (level);
386        }
387}
388
389rtems_status_code
390rtems_termios_close (void *arg)
391{
392        rtems_libio_open_close_args_t *args = arg;
393        struct rtems_termios_tty *tty = args->iop->data1;
394        rtems_status_code sc;
395
396        sc = rtems_semaphore_obtain (rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
397        if (sc != RTEMS_SUCCESSFUL)
398                rtems_fatal_error_occurred (sc);
399        if (--tty->refcount == 0) {
400                if (linesw[tty->t_line].l_close != NULL) {
401                        /*
402                         * call discipline-specific close
403                         */
404                        sc = linesw[tty->t_line].l_close(tty);
405                }
406                else {
407                        /*
408                         * default: just flush output buffer
409                         */
410                        drainOutput (tty);
411                }
412         
413                if (tty->device.outputUsesInterrupts
414                    == TERMIOS_TASK_DRIVEN) {
415                        /*
416                         * send "terminate" to I/O tasks
417                         */
418                        sc = rtems_event_send(
419                                  tty->rxTaskId,
420                                  TERMIOS_RX_TERMINATE_EVENT);
421                        if (sc != RTEMS_SUCCESSFUL)
422                                rtems_fatal_error_occurred (sc);
423                        sc = rtems_event_send(
424                                  tty->txTaskId,
425                                  TERMIOS_TX_TERMINATE_EVENT);
426                        if (sc != RTEMS_SUCCESSFUL)
427                                rtems_fatal_error_occurred (sc);
428                }
429                if (tty->device.lastClose)
430                         (*tty->device.lastClose)(tty->major, tty->minor, arg);
431                if (tty->forw == NULL) {
432                        rtems_termios_ttyTail = tty->back;
433                        if ( rtems_termios_ttyTail != NULL ) {
434                                rtems_termios_ttyTail->forw = NULL;
435                        }
436                }
437                else {
438                        tty->forw->back = tty->back;
439                }
440                if (tty->back == NULL) {
441                        rtems_termios_ttyHead = tty->forw;
442                        if ( rtems_termios_ttyHead != NULL ) {
443                                rtems_termios_ttyHead->back = NULL;
444                        }
445                }
446                else {
447                        tty->back->forw = tty->forw;
448                }
449                rtems_semaphore_delete (tty->isem);
450                rtems_semaphore_delete (tty->osem);
451                rtems_semaphore_delete (tty->rawOutBuf.Semaphore);
452                if ((tty->device.pollRead == NULL) ||
453                    (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN))
454                        rtems_semaphore_delete (tty->rawInBuf.Semaphore);
455                free (tty->rawInBuf.theBuf);
456                free (tty->rawOutBuf.theBuf);
457                free (tty->cbuf);
458                free (tty);
459        }
460        rtems_semaphore_release (rtems_termios_ttyMutex);
461        return RTEMS_SUCCESSFUL;
462}
463
464rtems_status_code rtems_termios_bufsize (
465  int cbufsize,
466  int raw_input,
467  int raw_output
468)
469{
470  rtems_termios_cbufsize        = cbufsize;
471  rtems_termios_raw_input_size  = raw_input;
472  rtems_termios_raw_output_size = raw_output;
473  return RTEMS_SUCCESSFUL;
474}
475
476static void
477termios_set_flowctrl(struct rtems_termios_tty *tty)
478{
479  rtems_interrupt_level level;
480  /*
481   * check for flow control options to be switched off
482   */
483
484  /* check for outgoing XON/XOFF flow control switched off */
485  if (( tty->flow_ctrl & FL_MDXON) &&
486      !(tty->termios.c_iflag & IXON)) {
487    /* clear related flags in flow_ctrl */
488    tty->flow_ctrl &= ~(FL_MDXON | FL_ORCVXOF);
489
490    /* has output been stopped due to received XOFF? */
491    if (tty->flow_ctrl & FL_OSTOP) {
492      /* disable interrupts    */
493      rtems_interrupt_disable(level);
494      tty->flow_ctrl &= ~FL_OSTOP;
495      /* check for chars in output buffer (or rob_state?) */
496      if (tty->rawOutBufState != rob_idle) {
497        /* if chars available, call write function... */
498        (*tty->device.write)(tty->minor,
499                     (char *)&tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
500      }
501      /* reenable interrupts */
502      rtems_interrupt_enable(level);
503    }
504  }
505  /* check for incoming XON/XOFF flow control switched off */
506  if (( tty->flow_ctrl & FL_MDXOF) &&
507      !(tty->termios.c_iflag & IXOFF)) {
508    /* clear related flags in flow_ctrl */
509    tty->flow_ctrl &= ~(FL_MDXOF);
510    /* FIXME: what happens, if we had sent XOFF but not yet XON? */
511    tty->flow_ctrl &= ~(FL_ISNTXOF);
512  }
513
514  /* check for incoming RTS/CTS flow control switched off */
515  if (( tty->flow_ctrl & FL_MDRTS) &&
516      !(tty->termios.c_cflag & CRTSCTS)) {
517    /* clear related flags in flow_ctrl */
518    tty->flow_ctrl &= ~(FL_MDRTS);
519   
520    /* restart remote Tx, if it was stopped */
521    if ((tty->flow_ctrl & FL_IRTSOFF) &&
522        (tty->device.startRemoteTx != NULL)) {
523      tty->device.startRemoteTx(tty->minor);
524    }
525    tty->flow_ctrl &= ~(FL_IRTSOFF);
526  }   
527 
528  /*
529   * check for flow control options to be switched on 
530   */
531  /* check for incoming RTS/CTS flow control switched on */
532  if (tty->termios.c_cflag & CRTSCTS) {
533    tty->flow_ctrl |= FL_MDRTS;
534  }
535  /* check for incoming XON/XOF flow control switched on */
536  if (tty->termios.c_iflag & IXOFF) {
537    tty->flow_ctrl |= FL_MDXOF;
538  }
539  /* check for outgoing XON/XOF flow control switched on */
540  if (tty->termios.c_iflag & IXON) {
541    tty->flow_ctrl |= FL_MDXON;
542  }
543}
544
545rtems_status_code
546rtems_termios_ioctl (void *arg)
547{
548        rtems_libio_ioctl_args_t *args = arg;
549        struct rtems_termios_tty *tty = args->iop->data1;
550        struct ttywakeup         *wakeup = (struct ttywakeup *)args->buffer;
551        rtems_status_code sc;
552
553        args->ioctl_return = 0;
554        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
555        if (sc != RTEMS_SUCCESSFUL) {
556                args->ioctl_return = sc;
557                return sc;
558        }
559        switch (args->command) {
560        default:
561                if (linesw[tty->t_line].l_ioctl != NULL) {
562                        sc = linesw[tty->t_line].l_ioctl(tty,args);
563                }
564                else {
565                        sc = RTEMS_INVALID_NUMBER;
566                }
567                break;
568
569        case RTEMS_IO_GET_ATTRIBUTES:
570                *(struct termios *)args->buffer = tty->termios;
571                break;
572
573        case RTEMS_IO_SET_ATTRIBUTES:
574                tty->termios = *(struct termios *)args->buffer;
575
576                /* check for and process change in flow control options */
577                termios_set_flowctrl(tty);
578
579                if (tty->termios.c_lflag & ICANON) {
580                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
581                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
582                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
583                }
584                else {
585                        rtems_interval ticksPerSecond;
586                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
587                        tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10;
588                        if (tty->termios.c_cc[VTIME]) {
589                                tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
590                                tty->rawInBufSemaphoreTimeout = tty->vtimeTicks;
591                                if (tty->termios.c_cc[VMIN])
592                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
593                                else
594                                        tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks;
595                        }
596                        else {
597                                if (tty->termios.c_cc[VMIN]) {
598                                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
599                                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
600                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
601                                }
602                                else {
603                                        tty->rawInBufSemaphoreOptions = RTEMS_NO_WAIT;
604                                }
605                        }
606                }
607                if (tty->device.setAttributes)
608                        (*tty->device.setAttributes)(tty->minor, &tty->termios);
609                break;
610
611        case RTEMS_IO_TCDRAIN:
612                drainOutput (tty);
613                break;
614
615        case RTEMS_IO_SNDWAKEUP:
616                tty->tty_snd = *wakeup;
617                break;
618
619        case RTEMS_IO_RCVWAKEUP:
620                tty->tty_rcv = *wakeup;
621                break;
622
623                /*
624                 * FIXME: add various ioctl code handlers
625                 */
626
627#if 1 /* FIXME */
628        case TIOCSETD:
629                /*
630                 * close old line discipline
631                 */
632                if (linesw[tty->t_line].l_close != NULL) {
633                        sc = linesw[tty->t_line].l_close(tty);
634                }
635                tty->t_line=*(int*)(args->buffer);
636                tty->t_sc = NULL; /* ensure that no more valid data */
637                /*
638                 * open new line discipline
639                 */
640                if (linesw[tty->t_line].l_open != NULL) {
641                        sc = linesw[tty->t_line].l_open(tty);
642                }
643                break;
644        case TIOCGETD: 
645                *(int*)(args->buffer)=tty->t_line;
646                break;         
647#endif
648        case FIONREAD:
649                /* Half guess that this is the right operation */
650                *(int *)args->buffer = tty->ccount - tty->cindex;
651                break;
652        }
653        rtems_semaphore_release (tty->osem);
654        args->ioctl_return = sc;
655        return sc;
656}
657
658/*
659 * Send characters to device-specific code
660 */
661void
662rtems_termios_puts (const char *buf, int len, struct rtems_termios_tty *tty)
663{
664        unsigned int newHead;
665        rtems_interrupt_level level;
666        rtems_status_code sc;
667
668        if (tty->device.outputUsesInterrupts == TERMIOS_POLLED) {
669                (*tty->device.write)(tty->minor, buf, len);
670                return;
671        }
672        newHead = tty->rawOutBuf.Head;
673        while (len) {
674                /*
675                 * Performance improvement could be made here.
676                 * Copy multiple bytes to raw buffer:
677                 * if (len > 1) && (space to buffer end, or tail > 1)
678                 *      ncopy = MIN (len, space to buffer end or tail)
679                 *      memcpy (raw buffer, buf, ncopy)
680                 *      buf += ncopy
681                 *      len -= ncopy
682                 *
683                 * To minimize latency, the memcpy should be done
684                 * with interrupts enabled.
685                 */
686                newHead = (newHead + 1) % tty->rawOutBuf.Size;
687                rtems_interrupt_disable (level);
688                while (newHead == tty->rawOutBuf.Tail) {
689                        tty->rawOutBufState = rob_wait;
690                        rtems_interrupt_enable (level);
691                        sc = rtems_semaphore_obtain (tty->rawOutBuf.Semaphore,
692                                                        RTEMS_WAIT,
693                                                        RTEMS_NO_TIMEOUT);
694                        if (sc != RTEMS_SUCCESSFUL)
695                                rtems_fatal_error_occurred (sc);
696                        rtems_interrupt_disable (level);
697                }
698                tty->rawOutBuf.theBuf[tty->rawOutBuf.Head] = *buf++;
699                tty->rawOutBuf.Head = newHead;
700                if (tty->rawOutBufState == rob_idle) {
701                  /* check, whether XOFF has been received */
702                  if (!(tty->flow_ctrl & FL_ORCVXOF)) {
703                    (*tty->device.write)(tty->minor,
704                        (char *)&tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
705                  }
706                  else {
707                    /* remember that output has been stopped due to flow ctrl*/
708                    tty->flow_ctrl |= FL_OSTOP;
709                  }
710                  tty->rawOutBufState = rob_busy;
711                }
712                rtems_interrupt_enable (level);
713                len--;
714        }
715}
716
717/*
718 * Handle output processing
719 */
720static void
721oproc (unsigned char c, struct rtems_termios_tty *tty)
722{
723        int     i;
724
725        if (tty->termios.c_oflag & OPOST) {
726                switch (c) {
727                case '\n':
728                        if (tty->termios.c_oflag & ONLRET)
729                                tty->column = 0;
730                        if (tty->termios.c_oflag & ONLCR) {
731                                rtems_termios_puts ("\r", 1, tty);
732                                tty->column = 0;
733                        }
734                        break;
735
736                case '\r':
737                        if ((tty->termios.c_oflag & ONOCR) && (tty->column == 0))
738                                return;
739                        if (tty->termios.c_oflag & OCRNL) {
740                                c = '\n';
741                                if (tty->termios.c_oflag & ONLRET)
742                                        tty->column = 0;
743                                break;
744                        }
745                        tty->column = 0;
746                        break;
747
748                case '\t':
749                        i = 8 - (tty->column & 7);
750                        if ((tty->termios.c_oflag & TABDLY) == XTABS) {
751                                tty->column += i;
752                                rtems_termios_puts ( "        ",  i, tty);
753                                return;
754                        }
755                        tty->column += i;
756                        break;
757
758                case '\b':
759                        if (tty->column > 0)
760                                tty->column--;
761                        break;
762
763                default:
764                        if (tty->termios.c_oflag & OLCUC)
765                                c = toupper(c);
766                        if (!iscntrl(c))
767                                tty->column++;
768                        break;
769                }
770        }
771        rtems_termios_puts (&c, 1, tty);
772}
773
774rtems_status_code
775rtems_termios_write (void *arg)
776{
777        rtems_libio_rw_args_t *args = arg;
778        struct rtems_termios_tty *tty = args->iop->data1;
779        rtems_status_code sc;
780
781        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
782        if (sc != RTEMS_SUCCESSFUL)
783                return sc;
784        if (linesw[tty->t_line].l_write != NULL) {
785                sc = linesw[tty->t_line].l_write(tty,args);
786                rtems_semaphore_release (tty->osem);
787                return sc;
788        }
789        if (tty->termios.c_oflag & OPOST) {
790                uint32_t   count = args->count;
791                uint8_t   *buffer = args->buffer;
792                while (count--)
793                        oproc (*buffer++, tty);
794                args->bytes_moved = args->count;
795        }
796        else {
797                rtems_termios_puts (args->buffer, args->count, tty);
798                args->bytes_moved = args->count;
799        }
800        rtems_semaphore_release (tty->osem);
801        return sc;
802}
803
804/*
805 * Echo a typed character
806 */
807static void
808echo (unsigned char c, struct rtems_termios_tty *tty)
809{
810        if ((tty->termios.c_lflag & ECHOCTL) && iscntrl(c) && (c != '\t') && (c != '\n')) {
811                char echobuf[2];
812
813                echobuf[0] = '^';
814                echobuf[1] = c ^ 0x40;
815                rtems_termios_puts (echobuf, 2, tty);
816                tty->column += 2;
817        }
818        else {
819                oproc (c, tty);
820        }
821}
822
823/*
824 * Erase a character or line
825 * FIXME: Needs support for WERASE and ECHOPRT.
826 * FIXME: Some of the tests should check for IEXTEN, too.
827 */
828static void
829erase (struct rtems_termios_tty *tty, int lineFlag)
830{
831        if (tty->ccount == 0)
832                return;
833        if (lineFlag) {
834                if (!(tty->termios.c_lflag & ECHO)) {
835                        tty->ccount = 0;
836                        return;
837                }
838                if (!(tty->termios.c_lflag & ECHOE)) {
839                        tty->ccount = 0;
840                        echo (tty->termios.c_cc[VKILL], tty);
841                        if (tty->termios.c_lflag & ECHOK)
842                                echo ('\n', tty);
843                        return;
844                }
845        }
846        while (tty->ccount) {
847                unsigned char c = tty->cbuf[--tty->ccount];
848
849                if (tty->termios.c_lflag & ECHO) {
850                        if (!lineFlag && !(tty->termios.c_lflag & ECHOE)) {
851                                echo (tty->termios.c_cc[VERASE], tty);
852                        }
853                        else if (c == '\t') {
854                                int col = tty->read_start_column;
855                                int i = 0;
856
857                                /*
858                                 * Find the character before the tab
859                                 */
860                                while (i != tty->ccount) {
861                                        c = tty->cbuf[i++];
862                                        if (c == '\t') {
863                                                col = (col | 7) + 1;
864                                        }
865                                        else if (iscntrl (c)) {
866                                                if (tty->termios.c_lflag & ECHOCTL)
867                                                        col += 2;
868                                        }
869                                        else {
870                                                col++;
871                                        }
872                                }
873
874                                /*
875                                 * Back up over the tab
876                                 */
877                                while (tty->column > col) {
878                                        rtems_termios_puts ("\b", 1, tty);
879                                        tty->column--;
880                                }
881                        }
882                        else {
883                                if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
884                                        rtems_termios_puts ("\b \b", 3, tty);
885                                        if (tty->column)
886                                                tty->column--;
887                                }
888                                if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
889                                        rtems_termios_puts ("\b \b", 3, tty);
890                                        if (tty->column)
891                                                tty->column--;
892                                }
893                        }
894                }
895                if (!lineFlag)
896                        break;
897        }
898}
899
900/*
901 * Process a single input character
902 */
903static int
904iproc (unsigned char c, struct rtems_termios_tty *tty)
905{
906        if (tty->termios.c_iflag & ISTRIP)
907                c &= 0x7f;
908        if (tty->termios.c_iflag & IUCLC)
909                c = tolower (c);
910        if (c == '\r') {
911                if (tty->termios.c_iflag & IGNCR)
912                        return 0;
913                if (tty->termios.c_iflag & ICRNL)
914                        c = '\n';
915        }
916        else if ((c == '\n') && (tty->termios.c_iflag & INLCR)) {
917                c = '\r';
918        }
919        if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
920                if (c == tty->termios.c_cc[VERASE]) {
921                        erase (tty, 0);
922                        return 0;
923                }
924                else if (c == tty->termios.c_cc[VKILL]) {
925                        erase (tty, 1);
926                        return 0;
927                }
928                else if (c == tty->termios.c_cc[VEOF]) {
929                        return 1;
930                }
931                else if (c == '\n') {
932                        if (tty->termios.c_lflag & (ECHO | ECHONL))
933                                echo (c, tty);
934                        tty->cbuf[tty->ccount++] = c;
935                        return 1;
936                }
937                else if ((c == tty->termios.c_cc[VEOL])
938                      || (c == tty->termios.c_cc[VEOL2])) {
939                        if (tty->termios.c_lflag & ECHO)
940                                echo (c, tty);
941                        tty->cbuf[tty->ccount++] = c;
942                        return 1;
943                }
944        }
945
946        /*
947         * FIXME: Should do IMAXBEL handling somehow
948         */
949        if (tty->ccount < (CBUFSIZE-1)) {
950                if (tty->termios.c_lflag & ECHO)
951                        echo (c, tty);
952                tty->cbuf[tty->ccount++] = c;
953        }
954        return 0;
955}
956
957/*
958 * Process input character, with semaphore.
959 */
960static int
961siproc (unsigned char c, struct rtems_termios_tty *tty)
962{
963        int i;
964
965        /*
966         * Obtain output semaphore if character will be echoed
967         */
968        if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) {
969                rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
970                i = iproc (c, tty);
971                rtems_semaphore_release (tty->osem);
972        }
973        else {
974                i = iproc (c, tty);
975        }
976        return i;
977}
978
979/*
980 * Fill the input buffer by polling the device
981 */
982static rtems_status_code
983fillBufferPoll (struct rtems_termios_tty *tty)
984{
985        int n;
986
987        if (tty->termios.c_lflag & ICANON) {
988                for (;;) {
989                        n = (*tty->device.pollRead)(tty->minor);
990                        if (n < 0) {
991                                rtems_task_wake_after (1);
992                        }
993                        else {
994                                if  (siproc (n, tty))
995                                        break;
996                        }
997                }
998        }
999        else {
1000                rtems_interval then, now;
1001                if (!tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
1002                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
1003                for (;;) {
1004                        n = (*tty->device.pollRead)(tty->minor);
1005                        if (n < 0) {
1006                                if (tty->termios.c_cc[VMIN]) {
1007                                        if (tty->termios.c_cc[VTIME] && tty->ccount) {
1008                                                rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
1009                                                if ((now - then) > tty->vtimeTicks) {
1010                                                        break;
1011                                                }
1012                                        }
1013                                }
1014                                else {
1015                                        if (!tty->termios.c_cc[VTIME])
1016                                                break;
1017                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
1018                                        if ((now - then) > tty->vtimeTicks) {
1019                                                break;
1020                                        }
1021                                }
1022                                rtems_task_wake_after (1);
1023                        }
1024                        else {
1025                                siproc (n, tty);
1026                                if (tty->ccount >= tty->termios.c_cc[VMIN])
1027                                        break;
1028                                if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
1029                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
1030                        }
1031                }
1032        }
1033        return RTEMS_SUCCESSFUL;
1034}
1035
1036/*
1037 * Fill the input buffer from the raw input queue
1038 */
1039static rtems_status_code
1040fillBufferQueue (struct rtems_termios_tty *tty)
1041{
1042        rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout;
1043        rtems_status_code sc;
1044        int               wait = (int)1;
1045
1046        while ( wait ) {
1047                /*
1048                 * Process characters read from raw queue
1049                 */
1050                while (tty->rawInBuf.Head != tty->rawInBuf.Tail) {
1051                        unsigned char c;
1052                        unsigned int newHead;
1053
1054                        newHead = (tty->rawInBuf.Head + 1) % tty->rawInBuf.Size;
1055                        c = tty->rawInBuf.theBuf[newHead];
1056                        tty->rawInBuf.Head = newHead;
1057                        if(((tty->rawInBuf.Tail-newHead+tty->rawInBuf.Size)
1058                            % tty->rawInBuf.Size)
1059                           < tty->lowwater) {
1060                          tty->flow_ctrl &= ~FL_IREQXOF;
1061                          /* if tx stopped and XON should be sent... */
1062                          if (((tty->flow_ctrl & (FL_MDXON | FL_ISNTXOF))
1063                               ==                (FL_MDXON | FL_ISNTXOF))
1064                              && ((tty->rawOutBufState == rob_idle)
1065                                  || (tty->flow_ctrl & FL_OSTOP))) {
1066                            /* XON should be sent now... */
1067                            (*tty->device.write)(tty->minor,
1068                                                 &(tty->termios.c_cc[VSTART]),
1069                                                 1);
1070                          }
1071                          else if (tty->flow_ctrl & FL_MDRTS) {             
1072                            tty->flow_ctrl &= ~FL_IRTSOFF;             
1073                            /* activate RTS line */
1074                            if (tty->device.startRemoteTx != NULL) {
1075                              tty->device.startRemoteTx(tty->minor);
1076                            }
1077                          }
1078                        }
1079
1080                        /* continue processing new character */
1081                        if (tty->termios.c_lflag & ICANON) {
1082                                if  (siproc (c, tty))
1083                                        wait = 0;
1084                        }
1085                        else {
1086                                siproc (c, tty);
1087                                if (tty->ccount >= tty->termios.c_cc[VMIN])
1088                                        wait = 0;
1089                        }
1090                        timeout = tty->rawInBufSemaphoreTimeout;
1091                }
1092
1093                /*
1094                 * Wait for characters
1095                 */
1096                if ( wait ) {
1097                        sc = rtems_semaphore_obtain (tty->rawInBuf.Semaphore,
1098                                tty->rawInBufSemaphoreOptions,
1099                                timeout);
1100                        if (sc != RTEMS_SUCCESSFUL)
1101                                break;
1102                }
1103        }
1104        return RTEMS_SUCCESSFUL;
1105}
1106
1107rtems_status_code
1108rtems_termios_read (void *arg)
1109{
1110        rtems_libio_rw_args_t *args = arg;
1111        struct rtems_termios_tty *tty = args->iop->data1;
1112        uint32_t   count = args->count;
1113        uint8_t   *buffer = args->buffer;
1114        rtems_status_code sc;
1115
1116        sc = rtems_semaphore_obtain (tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
1117        if (sc != RTEMS_SUCCESSFUL)
1118                return sc;
1119        if (linesw[tty->t_line].l_read != NULL) {
1120                sc = linesw[tty->t_line].l_read(tty,args);
1121                tty->tty_rcvwakeup = 0;
1122                rtems_semaphore_release (tty->isem);
1123                return sc;
1124        }
1125        if (tty->cindex == tty->ccount) {
1126                tty->cindex = tty->ccount = 0;
1127                tty->read_start_column = tty->column;
1128                if (tty->device.pollRead != NULL
1129                    && tty->device.outputUsesInterrupts == TERMIOS_POLLED)
1130                        sc = fillBufferPoll (tty);
1131                else
1132                        sc = fillBufferQueue (tty);
1133                if (sc != RTEMS_SUCCESSFUL)
1134                        tty->cindex = tty->ccount = 0;
1135        }
1136        while (count && (tty->cindex < tty->ccount)) {
1137                *buffer++ = tty->cbuf[tty->cindex++];
1138                count--;
1139        }
1140        args->bytes_moved = args->count - count;
1141        tty->tty_rcvwakeup = 0;
1142        rtems_semaphore_release (tty->isem);
1143        return sc;
1144}
1145
1146/*
1147 * signal receive interrupt to rx daemon
1148 * NOTE: This routine runs in the context of the
1149 *       device receive interrupt handler.
1150 */
1151void rtems_termios_rxirq_occured(struct rtems_termios_tty *tty)
1152{
1153        /*
1154         * send event to rx daemon task
1155         */
1156        rtems_event_send(tty->rxTaskId,TERMIOS_RX_PROC_EVENT);
1157}
1158
1159/*
1160 * Place characters on raw queue.
1161 * NOTE: This routine runs in the context of the
1162 *       device receive interrupt handler.
1163 * Returns the number of characters dropped because of overflow.
1164 */
1165int
1166rtems_termios_enqueue_raw_characters (void *ttyp, char *buf, int len)
1167{
1168        struct rtems_termios_tty *tty = ttyp;
1169        unsigned int newTail;
1170        char c;
1171        int dropped = 0;
1172        boolean flow_rcv = FALSE; /* TRUE, if flow control char received */
1173        rtems_interrupt_level level;
1174
1175        if (linesw[tty->t_line].l_rint != NULL) {
1176          while (len--) {
1177            c = *buf++;
1178            linesw[tty->t_line].l_rint(c,tty);
1179          }
1180           
1181          /*
1182           * check to see if rcv wakeup callback was set
1183           */
1184          if (( !tty->tty_rcvwakeup ) && ( tty->tty_rcv.sw_pfn != NULL )) {
1185            (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
1186            tty->tty_rcvwakeup = 1;
1187          }
1188          return 0;
1189        }
1190
1191        while (len--) {
1192          c = *buf++;
1193          /* FIXME: implement IXANY: any character restarts output */
1194          /* if incoming XON/XOFF controls outgoing stream: */
1195          if (tty->flow_ctrl & FL_MDXON) {         
1196            /* if received char is V_STOP and V_START (both are equal value) */
1197            if (c == tty->termios.c_cc[VSTOP]) {
1198              if (c == tty->termios.c_cc[VSTART]) {
1199                /* received VSTOP and VSTART==VSTOP? */
1200                /* then toggle "stop output" status  */
1201                tty->flow_ctrl = tty->flow_ctrl ^ FL_ORCVXOF;
1202              }
1203              else {
1204                /* VSTOP received (other code than VSTART) */
1205                /* stop output                             */
1206                tty->flow_ctrl |= FL_ORCVXOF;
1207              }
1208              flow_rcv = TRUE;
1209            }
1210            else if (c == tty->termios.c_cc[VSTART]) {
1211              /* VSTART received */
1212              /* restart output  */
1213              tty->flow_ctrl &= ~FL_ORCVXOF;
1214              flow_rcv = TRUE;
1215            }
1216          }
1217          if (flow_rcv) {
1218            /* restart output according to FL_ORCVXOF flag */
1219            if ((tty->flow_ctrl & (FL_ORCVXOF | FL_OSTOP)) == FL_OSTOP) {
1220              /* disable interrupts    */
1221              rtems_interrupt_disable(level);
1222              tty->flow_ctrl &= ~FL_OSTOP;
1223              /* check for chars in output buffer (or rob_state?) */
1224              if (tty->rawOutBufState != rob_idle) {
1225              /* if chars available, call write function... */
1226                (*tty->device.write)(tty->minor,
1227                     (char *)&tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail], 1);
1228              }
1229              /* reenable interrupts */
1230              rtems_interrupt_enable(level);
1231            }
1232          }     
1233          else {
1234                newTail = (tty->rawInBuf.Tail + 1) % tty->rawInBuf.Size;
1235                /* if chars_in_buffer > highwater                */
1236                rtems_interrupt_disable(level);
1237                if ((((newTail - tty->rawInBuf.Head + tty->rawInBuf.Size)
1238                      % tty->rawInBuf.Size)
1239                     > tty->highwater) &&
1240                    !(tty->flow_ctrl & FL_IREQXOF)) {
1241                  /* incoming data stream should be stopped */
1242                  tty->flow_ctrl |= FL_IREQXOF;
1243                  if ((tty->flow_ctrl & (FL_MDXOF | FL_ISNTXOF))
1244                      ==                (FL_MDXOF             ) ){
1245                    if ((tty->flow_ctrl & FL_OSTOP) ||
1246                        (tty->rawOutBufState == rob_idle)) {
1247                      /* if tx is stopped due to XOFF or out of data */
1248                      /*    call write function here                 */
1249                      tty->flow_ctrl |= FL_ISNTXOF;
1250                      (*tty->device.write)(tty->minor,
1251                                           &(tty->termios.c_cc[VSTOP]),
1252                                           1);
1253                    }
1254                  }
1255                  else if ((tty->flow_ctrl & (FL_MDRTS | FL_IRTSOFF))
1256                           ==                (FL_MDRTS             ) ) {
1257                    tty->flow_ctrl |= FL_IRTSOFF;               
1258                    /* deactivate RTS line */
1259                    if (tty->device.stopRemoteTx != NULL) {
1260                      tty->device.stopRemoteTx(tty->minor);
1261                    }
1262                  }
1263                }
1264                /* reenable interrupts */
1265                rtems_interrupt_enable(level);
1266
1267                if (newTail == tty->rawInBuf.Head) {
1268                        dropped++;
1269                }
1270                else {
1271                        tty->rawInBuf.theBuf[newTail] = c;
1272                        tty->rawInBuf.Tail = newTail;
1273           
1274                        /*
1275                         * check to see if rcv wakeup callback was set
1276                         */
1277                        if (( !tty->tty_rcvwakeup ) && ( tty->tty_rcv.sw_pfn != NULL )) {
1278                          (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
1279                          tty->tty_rcvwakeup = 1;
1280                        }
1281                }               
1282          }
1283        }
1284        tty->rawInBufDropped += dropped;
1285        rtems_semaphore_release (tty->rawInBuf.Semaphore);
1286        return dropped;
1287}
1288
1289/*
1290 * in task-driven mode, this function is called in Tx task context
1291 * in interrupt-driven mode, this function is called in TxIRQ context
1292 */
1293int
1294rtems_termios_refill_transmitter (struct rtems_termios_tty *tty)
1295{
1296        unsigned int newTail;
1297        int nToSend;
1298        rtems_interrupt_level level;
1299        int len;
1300
1301        /* check for XOF/XON to send */
1302        if ((tty->flow_ctrl & (FL_MDXOF | FL_IREQXOF | FL_ISNTXOF))
1303            == (FL_MDXOF | FL_IREQXOF)) {
1304          /* XOFF should be sent now... */
1305          (*tty->device.write)(tty->minor,
1306                               &(tty->termios.c_cc[VSTOP]), 1);
1307
1308          rtems_interrupt_disable(level);
1309          tty->t_dqlen--;
1310          tty->flow_ctrl |= FL_ISNTXOF;
1311          rtems_interrupt_enable(level);
1312
1313          nToSend = 1;
1314        }
1315        else if ((tty->flow_ctrl & (FL_IREQXOF | FL_ISNTXOF))
1316                 == FL_ISNTXOF) {
1317          /* NOTE: send XON even, if no longer in XON/XOFF mode... */
1318          /* XON should be sent now... */
1319                /*
1320                 * FIXME: this .write call will generate another
1321                 * dequeue callback. This will advance the "Tail" in the data
1322                 * buffer, although the corresponding data is not yet out!
1323                 * Therefore the dequeue "length" should be reduced by 1
1324                 */
1325          (*tty->device.write)(tty->minor,
1326                               &(tty->termios.c_cc[VSTART]), 1);
1327
1328          rtems_interrupt_disable(level);
1329          tty->t_dqlen--;
1330          tty->flow_ctrl &= ~FL_ISNTXOF;
1331          rtems_interrupt_enable(level);
1332
1333          nToSend = 1;
1334        }
1335        else {
1336          if ( tty->rawOutBuf.Head == tty->rawOutBuf.Tail ) {
1337            /*
1338             * buffer was empty
1339             */
1340            if (tty->rawOutBufState == rob_wait) {
1341              /*
1342               * this should never happen...
1343               */
1344              rtems_semaphore_release (tty->rawOutBuf.Semaphore);
1345            }
1346            return 0;   
1347          }
1348
1349          rtems_interrupt_disable(level);
1350          len = tty->t_dqlen;
1351          tty->t_dqlen = 0;
1352          rtems_interrupt_enable(level);
1353
1354          newTail = (tty->rawOutBuf.Tail + len) % tty->rawOutBuf.Size;
1355          tty->rawOutBuf.Tail = newTail;
1356          if (tty->rawOutBufState == rob_wait) {
1357            /*
1358             * wake up any pending writer task
1359             */
1360            rtems_semaphore_release (tty->rawOutBuf.Semaphore);
1361          }
1362          if (newTail == tty->rawOutBuf.Head) {
1363            /*
1364             * Buffer has become empty
1365             */
1366            tty->rawOutBufState = rob_idle;
1367            nToSend = 0;
1368           
1369            /*
1370             * check to see if snd wakeup callback was set
1371             */
1372            if ( tty->tty_snd.sw_pfn != NULL) {
1373              (*tty->tty_snd.sw_pfn)(&tty->termios, tty->tty_snd.sw_arg);
1374            }
1375          }
1376          /* check, whether output should stop due to received XOFF */
1377          else if ((tty->flow_ctrl & (FL_MDXON | FL_ORCVXOF))
1378                   ==                (FL_MDXON | FL_ORCVXOF)) {
1379                  /* Buffer not empty, but output stops due to XOFF */
1380                  /* set flag, that output has been stopped */
1381                  rtems_interrupt_disable(level);
1382                  tty->flow_ctrl |= FL_OSTOP;
1383                  tty->rawOutBufState = rob_busy; /*apm*/
1384                  rtems_interrupt_enable(level);
1385                  nToSend = 0;
1386          }
1387          else {
1388            /*
1389             * Buffer not empty, start tranmitter
1390             */
1391            if (newTail > tty->rawOutBuf.Head)
1392                    nToSend = tty->rawOutBuf.Size - newTail;
1393            else
1394                    nToSend = tty->rawOutBuf.Head - newTail;
1395            /* when flow control XON or XOF, don't send blocks of data     */
1396            /* to allow fast reaction on incoming flow ctrl and low latency*/
1397            /* for outgoing flow control                                   */
1398            if (tty->flow_ctrl & (FL_MDXON | FL_MDXOF)) {
1399                    nToSend = 1;
1400            }
1401            tty->rawOutBufState = rob_busy; /*apm*/
1402            (*tty->device.write)(tty->minor,
1403                                 (char *)&tty->rawOutBuf.theBuf[newTail],
1404                                 nToSend);
1405          }
1406          tty->rawOutBuf.Tail = newTail; /*apm*/
1407        }
1408        return nToSend;
1409}
1410
1411/*
1412 * Characters have been transmitted
1413 * NOTE: This routine runs in the context of the
1414 *       device transmit interrupt handler.
1415 * The second argument is the number of characters transmitted so far.
1416 * This value will always be 1 for devices which generate an interrupt
1417 * for each transmitted character.
1418 * It returns number of characters left to transmit
1419 */
1420int
1421rtems_termios_dequeue_characters (void *ttyp, int len)
1422{
1423        struct rtems_termios_tty *tty = ttyp;
1424        rtems_status_code sc;
1425
1426        /*
1427         * sum up character count already sent
1428         */
1429        tty->t_dqlen += len;
1430
1431        if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
1432                /*
1433                 * send wake up to transmitter task
1434                 */
1435                sc = rtems_event_send(tty->txTaskId,
1436                                      TERMIOS_TX_START_EVENT);
1437                if (sc != RTEMS_SUCCESSFUL)
1438                        rtems_fatal_error_occurred (sc);
1439                return 0; /* nothing to output in IRQ... */
1440        }
1441        else if (tty->t_line == PPPDISC ) {
1442                /*
1443                 * call any line discipline start function
1444                 */
1445                if (linesw[tty->t_line].l_start != NULL) {
1446                        linesw[tty->t_line].l_start(tty);
1447                }
1448                return 0; /* nothing to output in IRQ... */
1449        }
1450        else {
1451                return rtems_termios_refill_transmitter(tty);
1452        }
1453}
1454
1455/*
1456 * this task actually processes any transmit events
1457 */
1458static rtems_task rtems_termios_txdaemon(rtems_task_argument argument)
1459{
1460        struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
1461        rtems_event_set the_event;
1462
1463        while (1) {
1464                /*
1465                 * wait for rtems event
1466                 */
1467                rtems_event_receive((TERMIOS_TX_START_EVENT |
1468                                     TERMIOS_TX_TERMINATE_EVENT),
1469                                    RTEMS_EVENT_ANY | RTEMS_WAIT,
1470                                    RTEMS_NO_TIMEOUT,
1471                                    &the_event);
1472                if ((the_event & TERMIOS_TX_TERMINATE_EVENT) != 0) {
1473                        tty->txTaskId = 0;
1474                        rtems_task_delete(RTEMS_SELF);
1475                }
1476                else {
1477                        /*
1478                         * call any line discipline start function
1479                         */
1480                        if (linesw[tty->t_line].l_start != NULL) {
1481                                linesw[tty->t_line].l_start(tty);
1482                        }
1483                        /*
1484                         * try to push further characters to device
1485                         */
1486                        rtems_termios_refill_transmitter(tty);
1487                }
1488        }
1489}
1490
1491/*
1492 * this task actually processes any receive events
1493 */
1494static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument)
1495{
1496        struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
1497        rtems_event_set the_event;
1498        int c;
1499        char c_buf;
1500        while (1) {
1501                /*
1502                 * wait for rtems event
1503                 */
1504                rtems_event_receive((TERMIOS_RX_PROC_EVENT |
1505                                     TERMIOS_RX_TERMINATE_EVENT),
1506                                    RTEMS_EVENT_ANY | RTEMS_WAIT,
1507                                    RTEMS_NO_TIMEOUT,
1508                                    &the_event);
1509                if ((the_event & TERMIOS_RX_TERMINATE_EVENT) != 0) {
1510                        tty->rxTaskId = 0;
1511                        rtems_task_delete(RTEMS_SELF);
1512                }
1513                else {
1514                        /*
1515                         * do something
1516                         */
1517                        c = tty->device.pollRead(tty->minor);
1518                        if (c != EOF) {
1519                                /*
1520                                 * pollRead did call enqueue on its own
1521                                 */
1522                                c_buf = c;
1523                                rtems_termios_enqueue_raw_characters (
1524                                      tty,&c_buf,1);
1525                        }
1526                }
1527        }
1528}
Note: See TracBrowser for help on using the repository browser.