source: rtems/c/src/lib/libc/termios.c @ 38db58f

4.104.114.84.95
Last change on this file since 38db58f was 38db58f, checked in by Joel Sherrill <joel.sherrill@…>, on 10/11/01 at 21:21:43

2001-10-11 Mike Siers <mikes@…>

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