source: rtems/cpukit/libcsupport/src/termios.c @ 8ebf2e4d

4.104.114.84.95
Last change on this file since 8ebf2e4d was 1f5d2ba, checked in by Chris Johns <chrisj@…>, on 07/02/03 at 14:20:36

Patch by Chris Johns <cjohns@…> adding the rtems_termios_bufsize call.

  • 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.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#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}
474
475static void
476termios_set_flowctrl(struct rtems_termios_tty *tty)
477{
478  rtems_interrupt_level level;
479  /*
480   * check for flow control options to be switched off
481   */
482
483  /* check for outgoing XON/XOFF flow control switched off */
484  if (( tty->flow_ctrl & FL_MDXON) &&
485      !(tty->termios.c_iflag & IXON)) {
486    /* clear related flags in flow_ctrl */
487    tty->flow_ctrl &= ~(FL_MDXON | FL_ORCVXOF);
488
489    /* has output been stopped due to received XOFF? */
490    if (tty->flow_ctrl & FL_OSTOP) {
491      /* disable interrupts    */
492      rtems_interrupt_disable(level);
493      tty->flow_ctrl &= ~FL_OSTOP;
494      /* check for chars in output buffer (or rob_state?) */
495      if (tty->rawOutBufState != rob_idle) {
496        /* if chars available, call write function... */
497        (*tty->device.write)(tty->minor,
498                     (char *)&tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
499      }
500      /* reenable interrupts */
501      rtems_interrupt_enable(level);
502    }
503  }
504  /* check for incoming XON/XOFF flow control switched off */
505  if (( tty->flow_ctrl & FL_MDXOF) &&
506      !(tty->termios.c_iflag & IXOFF)) {
507    /* clear related flags in flow_ctrl */
508    tty->flow_ctrl &= ~(FL_MDXOF);
509    /* FIXME: what happens, if we had sent XOFF but not yet XON? */
510    tty->flow_ctrl &= ~(FL_ISNTXOF);
511  }
512
513  /* check for incoming RTS/CTS flow control switched off */
514  if (( tty->flow_ctrl & FL_MDRTS) &&
515      !(tty->termios.c_cflag & CRTSCTS)) {
516    /* clear related flags in flow_ctrl */
517    tty->flow_ctrl &= ~(FL_MDRTS);
518   
519    /* restart remote Tx, if it was stopped */
520    if ((tty->flow_ctrl & FL_IRTSOFF) &&
521        (tty->device.startRemoteTx != NULL)) {
522      tty->device.startRemoteTx(tty->minor);
523    }
524    tty->flow_ctrl &= ~(FL_IRTSOFF);
525  }   
526 
527  /*
528   * check for flow control options to be switched on 
529   */
530  /* check for incoming RTS/CTS flow control switched on */
531  if (tty->termios.c_cflag & CRTSCTS) {
532    tty->flow_ctrl |= FL_MDRTS;
533  }
534  /* check for incoming XON/XOF flow control switched on */
535  if (tty->termios.c_iflag & IXOFF) {
536    tty->flow_ctrl |= FL_MDXOF;
537  }
538  /* check for outgoing XON/XOF flow control switched on */
539  if (tty->termios.c_iflag & IXON) {
540    tty->flow_ctrl |= FL_MDXON;
541  }
542}
543
544rtems_status_code
545rtems_termios_ioctl (void *arg)
546{
547        rtems_libio_ioctl_args_t *args = arg;
548        struct rtems_termios_tty *tty = args->iop->data1;
549        struct ttywakeup         *wakeup = (struct ttywakeup *)args->buffer;
550        rtems_status_code sc;
551
552        args->ioctl_return = 0;
553        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
554        if (sc != RTEMS_SUCCESSFUL) {
555                args->ioctl_return = sc;
556                return sc;
557        }
558        switch (args->command) {
559        default:
560                if (linesw[tty->t_line].l_ioctl != NULL) {
561                        sc = linesw[tty->t_line].l_ioctl(tty,args);
562                }
563                else {
564                        sc = RTEMS_INVALID_NUMBER;
565                }
566                break;
567
568        case RTEMS_IO_GET_ATTRIBUTES:
569                *(struct termios *)args->buffer = tty->termios;
570                break;
571
572        case RTEMS_IO_SET_ATTRIBUTES:
573                tty->termios = *(struct termios *)args->buffer;
574
575                /* check for and process change in flow control options */
576                termios_set_flowctrl(tty);
577
578                if (tty->termios.c_lflag & ICANON) {
579                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
580                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
581                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
582                }
583                else {
584                        rtems_interval ticksPerSecond;
585                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
586                        tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10;
587                        if (tty->termios.c_cc[VTIME]) {
588                                tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
589                                tty->rawInBufSemaphoreTimeout = tty->vtimeTicks;
590                                if (tty->termios.c_cc[VMIN])
591                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
592                                else
593                                        tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks;
594                        }
595                        else {
596                                if (tty->termios.c_cc[VMIN]) {
597                                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
598                                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
599                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
600                                }
601                                else {
602                                        tty->rawInBufSemaphoreOptions = RTEMS_NO_WAIT;
603                                }
604                        }
605                }
606                if (tty->device.setAttributes)
607                        (*tty->device.setAttributes)(tty->minor, &tty->termios);
608                break;
609
610        case RTEMS_IO_TCDRAIN:
611                drainOutput (tty);
612                break;
613
614        case RTEMS_IO_SNDWAKEUP:
615                tty->tty_snd = *wakeup;
616                break;
617
618        case RTEMS_IO_RCVWAKEUP:
619                tty->tty_rcv = *wakeup;
620                break;
621
622                /*
623                 * FIXME: add various ioctl code handlers
624                 */
625
626#if 1 /* FIXME */
627        case TIOCSETD:
628                /*
629                 * close old line discipline
630                 */
631                if (linesw[tty->t_line].l_close != NULL) {
632                        sc = linesw[tty->t_line].l_close(tty);
633                }
634                tty->t_line=*(int*)(args->buffer);
635                tty->t_sc = NULL; /* ensure that no more valid data */
636                /*
637                 * open new line discipline
638                 */
639                if (linesw[tty->t_line].l_open != NULL) {
640                        sc = linesw[tty->t_line].l_open(tty);
641                }
642                break;
643        case TIOCGETD: 
644                *(int*)(args->buffer)=tty->t_line;
645                break;         
646#endif
647        case FIONREAD:
648                /* Half guess that this is the right operation */
649                *(int *)args->buffer = tty->ccount - tty->cindex;
650                break;
651        }
652        rtems_semaphore_release (tty->osem);
653        args->ioctl_return = sc;
654        return sc;
655}
656
657/*
658 * Send characters to device-specific code
659 */
660void
661rtems_termios_puts (const char *buf, int len, struct rtems_termios_tty *tty)
662{
663        unsigned int newHead;
664        rtems_interrupt_level level;
665        rtems_status_code sc;
666
667        if (tty->device.outputUsesInterrupts == TERMIOS_POLLED) {
668                (*tty->device.write)(tty->minor, buf, len);
669                return;
670        }
671        newHead = tty->rawOutBuf.Head;
672        while (len) {
673                /*
674                 * Performance improvement could be made here.
675                 * Copy multiple bytes to raw buffer:
676                 * if (len > 1) && (space to buffer end, or tail > 1)
677                 *      ncopy = MIN (len, space to buffer end or tail)
678                 *      memcpy (raw buffer, buf, ncopy)
679                 *      buf += ncopy
680                 *      len -= ncopy
681                 *
682                 * To minimize latency, the memcpy should be done
683                 * with interrupts enabled.
684                 */
685                newHead = (newHead + 1) % tty->rawOutBuf.Size;
686                rtems_interrupt_disable (level);
687                while (newHead == tty->rawOutBuf.Tail) {
688                        tty->rawOutBufState = rob_wait;
689                        rtems_interrupt_enable (level);
690                        sc = rtems_semaphore_obtain (tty->rawOutBuf.Semaphore,
691                                                        RTEMS_WAIT,
692                                                        RTEMS_NO_TIMEOUT);
693                        if (sc != RTEMS_SUCCESSFUL)
694                                rtems_fatal_error_occurred (sc);
695                        rtems_interrupt_disable (level);
696                }
697                tty->rawOutBuf.theBuf[tty->rawOutBuf.Head] = *buf++;
698                tty->rawOutBuf.Head = newHead;
699                if (tty->rawOutBufState == rob_idle) {
700                  /* check, whether XOFF has been received */
701                  if (!(tty->flow_ctrl & FL_ORCVXOF)) {
702                    (*tty->device.write)(tty->minor,
703                        (char *)&tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
704                  }
705                  else {
706                    /* remember that output has been stopped due to flow ctrl*/
707                    tty->flow_ctrl |= FL_OSTOP;
708                  }
709                  tty->rawOutBufState = rob_busy;
710                }
711                rtems_interrupt_enable (level);
712                len--;
713        }
714}
715
716/*
717 * Handle output processing
718 */
719static void
720oproc (unsigned char c, struct rtems_termios_tty *tty)
721{
722        int     i;
723
724        if (tty->termios.c_oflag & OPOST) {
725                switch (c) {
726                case '\n':
727                        if (tty->termios.c_oflag & ONLRET)
728                                tty->column = 0;
729                        if (tty->termios.c_oflag & ONLCR) {
730                                rtems_termios_puts ("\r", 1, tty);
731                                tty->column = 0;
732                        }
733                        break;
734
735                case '\r':
736                        if ((tty->termios.c_oflag & ONOCR) && (tty->column == 0))
737                                return;
738                        if (tty->termios.c_oflag & OCRNL) {
739                                c = '\n';
740                                if (tty->termios.c_oflag & ONLRET)
741                                        tty->column = 0;
742                                break;
743                        }
744                        tty->column = 0;
745                        break;
746
747                case '\t':
748                        i = 8 - (tty->column & 7);
749                        if ((tty->termios.c_oflag & TABDLY) == XTABS) {
750                                tty->column += i;
751                                rtems_termios_puts ( "        ",  i, tty);
752                                return;
753                        }
754                        tty->column += i;
755                        break;
756
757                case '\b':
758                        if (tty->column > 0)
759                                tty->column--;
760                        break;
761
762                default:
763                        if (tty->termios.c_oflag & OLCUC)
764                                c = toupper(c);
765                        if (!iscntrl(c))
766                                tty->column++;
767                        break;
768                }
769        }
770        rtems_termios_puts (&c, 1, tty);
771}
772
773rtems_status_code
774rtems_termios_write (void *arg)
775{
776        rtems_libio_rw_args_t *args = arg;
777        struct rtems_termios_tty *tty = args->iop->data1;
778        rtems_status_code sc;
779
780        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
781        if (sc != RTEMS_SUCCESSFUL)
782                return sc;
783        if (linesw[tty->t_line].l_write != NULL) {
784                sc = linesw[tty->t_line].l_write(tty,args);
785                rtems_semaphore_release (tty->osem);
786                return sc;
787        }
788        if (tty->termios.c_oflag & OPOST) {
789                unsigned32 count = args->count;
790                unsigned8 *buffer = args->buffer;
791                while (count--)
792                        oproc (*buffer++, tty);
793                args->bytes_moved = args->count;
794        }
795        else {
796                rtems_termios_puts (args->buffer, args->count, tty);
797                args->bytes_moved = args->count;
798        }
799        rtems_semaphore_release (tty->osem);
800        return sc;
801}
802
803/*
804 * Echo a typed character
805 */
806static void
807echo (unsigned char c, struct rtems_termios_tty *tty)
808{
809        if ((tty->termios.c_lflag & ECHOCTL) && iscntrl(c) && (c != '\t') && (c != '\n')) {
810                char echobuf[2];
811
812                echobuf[0] = '^';
813                echobuf[1] = c ^ 0x40;
814                rtems_termios_puts (echobuf, 2, tty);
815                tty->column += 2;
816        }
817        else {
818                oproc (c, tty);
819        }
820}
821
822/*
823 * Erase a character or line
824 * FIXME: Needs support for WERASE and ECHOPRT.
825 * FIXME: Some of the tests should check for IEXTEN, too.
826 */
827static void
828erase (struct rtems_termios_tty *tty, int lineFlag)
829{
830        if (tty->ccount == 0)
831                return;
832        if (lineFlag) {
833                if (!(tty->termios.c_lflag & ECHO)) {
834                        tty->ccount = 0;
835                        return;
836                }
837                if (!(tty->termios.c_lflag & ECHOE)) {
838                        tty->ccount = 0;
839                        echo (tty->termios.c_cc[VKILL], tty);
840                        if (tty->termios.c_lflag & ECHOK)
841                                echo ('\n', tty);
842                        return;
843                }
844        }
845        while (tty->ccount) {
846                unsigned char c = tty->cbuf[--tty->ccount];
847
848                if (tty->termios.c_lflag & ECHO) {
849                        if (!lineFlag && !(tty->termios.c_lflag & ECHOE)) {
850                                echo (tty->termios.c_cc[VERASE], tty);
851                        }
852                        else if (c == '\t') {
853                                int col = tty->read_start_column;
854                                int i = 0;
855
856                                /*
857                                 * Find the character before the tab
858                                 */
859                                while (i != tty->ccount) {
860                                        c = tty->cbuf[i++];
861                                        if (c == '\t') {
862                                                col = (col | 7) + 1;
863                                        }
864                                        else if (iscntrl (c)) {
865                                                if (tty->termios.c_lflag & ECHOCTL)
866                                                        col += 2;
867                                        }
868                                        else {
869                                                col++;
870                                        }
871                                }
872
873                                /*
874                                 * Back up over the tab
875                                 */
876                                while (tty->column > col) {
877                                        rtems_termios_puts ("\b", 1, tty);
878                                        tty->column--;
879                                }
880                        }
881                        else {
882                                if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
883                                        rtems_termios_puts ("\b \b", 3, tty);
884                                        if (tty->column)
885                                                tty->column--;
886                                }
887                                if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
888                                        rtems_termios_puts ("\b \b", 3, tty);
889                                        if (tty->column)
890                                                tty->column--;
891                                }
892                        }
893                }
894                if (!lineFlag)
895                        break;
896        }
897}
898
899/*
900 * Process a single input character
901 */
902static int
903iproc (unsigned char c, struct rtems_termios_tty *tty)
904{
905        if (tty->termios.c_iflag & ISTRIP)
906                c &= 0x7f;
907        if (tty->termios.c_iflag & IUCLC)
908                c = tolower (c);
909        if (c == '\r') {
910                if (tty->termios.c_iflag & IGNCR)
911                        return 0;
912                if (tty->termios.c_iflag & ICRNL)
913                        c = '\n';
914        }
915        else if ((c == '\n') && (tty->termios.c_iflag & INLCR)) {
916                c = '\r';
917        }
918        if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
919                if (c == tty->termios.c_cc[VERASE]) {
920                        erase (tty, 0);
921                        return 0;
922                }
923                else if (c == tty->termios.c_cc[VKILL]) {
924                        erase (tty, 1);
925                        return 0;
926                }
927                else if (c == tty->termios.c_cc[VEOF]) {
928                        return 1;
929                }
930                else if (c == '\n') {
931                        if (tty->termios.c_lflag & (ECHO | ECHONL))
932                                echo (c, tty);
933                        tty->cbuf[tty->ccount++] = c;
934                        return 1;
935                }
936                else if ((c == tty->termios.c_cc[VEOL])
937                      || (c == tty->termios.c_cc[VEOL2])) {
938                        if (tty->termios.c_lflag & ECHO)
939                                echo (c, tty);
940                        tty->cbuf[tty->ccount++] = c;
941                        return 1;
942                }
943        }
944
945        /*
946         * FIXME: Should do IMAXBEL handling somehow
947         */
948        if (tty->ccount < (CBUFSIZE-1)) {
949                if (tty->termios.c_lflag & ECHO)
950                        echo (c, tty);
951                tty->cbuf[tty->ccount++] = c;
952        }
953        return 0;
954}
955
956/*
957 * Process input character, with semaphore.
958 */
959static int
960siproc (unsigned char c, struct rtems_termios_tty *tty)
961{
962        int i;
963
964        /*
965         * Obtain output semaphore if character will be echoed
966         */
967        if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) {
968                rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
969                i = iproc (c, tty);
970                rtems_semaphore_release (tty->osem);
971        }
972        else {
973                i = iproc (c, tty);
974        }
975        return i;
976}
977
978/*
979 * Fill the input buffer by polling the device
980 */
981static rtems_status_code
982fillBufferPoll (struct rtems_termios_tty *tty)
983{
984        int n;
985
986        if (tty->termios.c_lflag & ICANON) {
987                for (;;) {
988                        n = (*tty->device.pollRead)(tty->minor);
989                        if (n < 0) {
990                                rtems_task_wake_after (1);
991                        }
992                        else {
993                                if  (siproc (n, tty))
994                                        break;
995                        }
996                }
997        }
998        else {
999                rtems_interval then, now;
1000                if (!tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
1001                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
1002                for (;;) {
1003                        n = (*tty->device.pollRead)(tty->minor);
1004                        if (n < 0) {
1005                                if (tty->termios.c_cc[VMIN]) {
1006                                        if (tty->termios.c_cc[VTIME] && tty->ccount) {
1007                                                rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
1008                                                if ((now - then) > tty->vtimeTicks) {
1009                                                        break;
1010                                                }
1011                                        }
1012                                }
1013                                else {
1014                                        if (!tty->termios.c_cc[VTIME])
1015                                                break;
1016                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
1017                                        if ((now - then) > tty->vtimeTicks) {
1018                                                break;
1019                                        }
1020                                }
1021                                rtems_task_wake_after (1);
1022                        }
1023                        else {
1024                                siproc (n, tty);
1025                                if (tty->ccount >= tty->termios.c_cc[VMIN])
1026                                        break;
1027                                if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
1028                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
1029                        }
1030                }
1031        }
1032        return RTEMS_SUCCESSFUL;
1033}
1034
1035/*
1036 * Fill the input buffer from the raw input queue
1037 */
1038static rtems_status_code
1039fillBufferQueue (struct rtems_termios_tty *tty)
1040{
1041        rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout;
1042        rtems_status_code sc;
1043        int               wait = (int)1;
1044
1045        while ( wait ) {
1046                /*
1047                 * Process characters read from raw queue
1048                 */
1049                while (tty->rawInBuf.Head != tty->rawInBuf.Tail) {
1050                        unsigned char c;
1051                        unsigned int newHead;
1052
1053                        newHead = (tty->rawInBuf.Head + 1) % tty->rawInBuf.Size;
1054                        c = tty->rawInBuf.theBuf[newHead];
1055                        tty->rawInBuf.Head = newHead;
1056                        if(((tty->rawInBuf.Tail-newHead+tty->rawInBuf.Size)
1057                            % tty->rawInBuf.Size)
1058                           < tty->lowwater) {
1059                          tty->flow_ctrl &= ~FL_IREQXOF;
1060                          /* if tx stopped and XON should be sent... */
1061                          if (((tty->flow_ctrl & (FL_MDXON | FL_ISNTXOF))
1062                               ==                (FL_MDXON | FL_ISNTXOF))
1063                              && ((tty->rawOutBufState == rob_idle)
1064                                  || (tty->flow_ctrl & FL_OSTOP))) {
1065                            /* XON should be sent now... */
1066                            (*tty->device.write)(tty->minor,
1067                                                 &(tty->termios.c_cc[VSTART]),
1068                                                 1);
1069                          }
1070                          else if (tty->flow_ctrl & FL_MDRTS) {             
1071                            tty->flow_ctrl &= ~FL_IRTSOFF;             
1072                            /* activate RTS line */
1073                            if (tty->device.startRemoteTx != NULL) {
1074                              tty->device.startRemoteTx(tty->minor);
1075                            }
1076                          }
1077                        }
1078
1079                        /* continue processing new character */
1080                        if (tty->termios.c_lflag & ICANON) {
1081                                if  (siproc (c, tty))
1082                                        wait = 0;
1083                        }
1084                        else {
1085                                siproc (c, tty);
1086                                if (tty->ccount >= tty->termios.c_cc[VMIN])
1087                                        wait = 0;
1088                        }
1089                        timeout = tty->rawInBufSemaphoreTimeout;
1090                }
1091
1092                /*
1093                 * Wait for characters
1094                 */
1095                if ( wait ) {
1096                        sc = rtems_semaphore_obtain (tty->rawInBuf.Semaphore,
1097                                tty->rawInBufSemaphoreOptions,
1098                                timeout);
1099                        if (sc != RTEMS_SUCCESSFUL)
1100                                break;
1101                }
1102        }
1103        return RTEMS_SUCCESSFUL;
1104}
1105
1106rtems_status_code
1107rtems_termios_read (void *arg)
1108{
1109        rtems_libio_rw_args_t *args = arg;
1110        struct rtems_termios_tty *tty = args->iop->data1;
1111        unsigned32 count = args->count;
1112        unsigned8 *buffer = args->buffer;
1113        rtems_status_code sc;
1114
1115        sc = rtems_semaphore_obtain (tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
1116        if (sc != RTEMS_SUCCESSFUL)
1117                return sc;
1118        if (linesw[tty->t_line].l_read != NULL) {
1119                sc = linesw[tty->t_line].l_read(tty,args);
1120                tty->tty_rcvwakeup = 0;
1121                rtems_semaphore_release (tty->isem);
1122                return sc;
1123        }
1124        if (tty->cindex == tty->ccount) {
1125                tty->cindex = tty->ccount = 0;
1126                tty->read_start_column = tty->column;
1127                if (tty->device.pollRead != NULL
1128                    && tty->device.outputUsesInterrupts == TERMIOS_POLLED)
1129                        sc = fillBufferPoll (tty);
1130                else
1131                        sc = fillBufferQueue (tty);
1132                if (sc != RTEMS_SUCCESSFUL)
1133                        tty->cindex = tty->ccount = 0;
1134        }
1135        while (count && (tty->cindex < tty->ccount)) {
1136                *buffer++ = tty->cbuf[tty->cindex++];
1137                count--;
1138        }
1139        args->bytes_moved = args->count - count;
1140        tty->tty_rcvwakeup = 0;
1141        rtems_semaphore_release (tty->isem);
1142        return sc;
1143}
1144
1145/*
1146 * signal receive interrupt to rx daemon
1147 * NOTE: This routine runs in the context of the
1148 *       device receive interrupt handler.
1149 */
1150void rtems_termios_rxirq_occured(struct rtems_termios_tty *tty)
1151{
1152        /*
1153         * send event to rx daemon task
1154         */
1155        rtems_event_send(tty->rxTaskId,TERMIOS_RX_PROC_EVENT);
1156}
1157
1158/*
1159 * Place characters on raw queue.
1160 * NOTE: This routine runs in the context of the
1161 *       device receive interrupt handler.
1162 * Returns the number of characters dropped because of overflow.
1163 */
1164int
1165rtems_termios_enqueue_raw_characters (void *ttyp, char *buf, int len)
1166{
1167        struct rtems_termios_tty *tty = ttyp;
1168        unsigned int newTail;
1169        char c;
1170        int dropped = 0;
1171        boolean flow_rcv = FALSE; /* TRUE, if flow control char received */
1172        rtems_interrupt_level level;
1173
1174        if (linesw[tty->t_line].l_rint != NULL) {
1175          while (len--) {
1176            c = *buf++;
1177            linesw[tty->t_line].l_rint(c,tty);
1178          }
1179           
1180          /*
1181           * check to see if rcv wakeup callback was set
1182           */
1183          if (( !tty->tty_rcvwakeup ) && ( tty->tty_rcv.sw_pfn != NULL )) {
1184            (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
1185            tty->tty_rcvwakeup = 1;
1186          }
1187          return 0;
1188        }
1189
1190        while (len--) {
1191          c = *buf++;
1192          /* FIXME: implement IXANY: any character restarts output */
1193          /* if incoming XON/XOFF controls outgoing stream: */
1194          if (tty->flow_ctrl & FL_MDXON) {         
1195            /* if received char is V_STOP and V_START (both are equal value) */
1196            if (c == tty->termios.c_cc[VSTOP]) {
1197              if (c == tty->termios.c_cc[VSTART]) {
1198                /* received VSTOP and VSTART==VSTOP? */
1199                /* then toggle "stop output" status  */
1200                tty->flow_ctrl = tty->flow_ctrl ^ FL_ORCVXOF;
1201              }
1202              else {
1203                /* VSTOP received (other code than VSTART) */
1204                /* stop output                             */
1205                tty->flow_ctrl |= FL_ORCVXOF;
1206              }
1207              flow_rcv = TRUE;
1208            }
1209            else if (c == tty->termios.c_cc[VSTART]) {
1210              /* VSTART received */
1211              /* restart output  */
1212              tty->flow_ctrl &= ~FL_ORCVXOF;
1213              flow_rcv = TRUE;
1214            }
1215          }
1216          if (flow_rcv) {
1217            /* restart output according to FL_ORCVXOF flag */
1218            if ((tty->flow_ctrl & (FL_ORCVXOF | FL_OSTOP)) == FL_OSTOP) {
1219              /* disable interrupts    */
1220              rtems_interrupt_disable(level);
1221              tty->flow_ctrl &= ~FL_OSTOP;
1222              /* check for chars in output buffer (or rob_state?) */
1223              if (tty->rawOutBufState != rob_idle) {
1224              /* if chars available, call write function... */
1225                (*tty->device.write)(tty->minor,
1226                     (char *)&tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail], 1);
1227              }
1228              /* reenable interrupts */
1229              rtems_interrupt_enable(level);
1230            }
1231          }     
1232          else {
1233                newTail = (tty->rawInBuf.Tail + 1) % tty->rawInBuf.Size;
1234                /* if chars_in_buffer > highwater                */
1235                rtems_interrupt_disable(level);
1236                if ((((newTail - tty->rawInBuf.Head + tty->rawInBuf.Size)
1237                      % tty->rawInBuf.Size)
1238                     > tty->highwater) &&
1239                    !(tty->flow_ctrl & FL_IREQXOF)) {
1240                  /* incoming data stream should be stopped */
1241                  tty->flow_ctrl |= FL_IREQXOF;
1242                  if ((tty->flow_ctrl & (FL_MDXOF | FL_ISNTXOF))
1243                      ==                (FL_MDXOF             ) ){
1244                    if ((tty->flow_ctrl & FL_OSTOP) ||
1245                        (tty->rawOutBufState == rob_idle)) {
1246                      /* if tx is stopped due to XOFF or out of data */
1247                      /*    call write function here                 */
1248                      tty->flow_ctrl |= FL_ISNTXOF;
1249                      (*tty->device.write)(tty->minor,
1250                                           &(tty->termios.c_cc[VSTOP]),
1251                                           1);
1252                    }
1253                  }
1254                  else if ((tty->flow_ctrl & (FL_MDRTS | FL_IRTSOFF))
1255                           ==                (FL_MDRTS             ) ) {
1256                    tty->flow_ctrl |= FL_IRTSOFF;               
1257                    /* deactivate RTS line */
1258                    if (tty->device.stopRemoteTx != NULL) {
1259                      tty->device.stopRemoteTx(tty->minor);
1260                    }
1261                  }
1262                }
1263                /* reenable interrupts */
1264                rtems_interrupt_enable(level);
1265
1266                if (newTail == tty->rawInBuf.Head) {
1267                        dropped++;
1268                }
1269                else {
1270                        tty->rawInBuf.theBuf[newTail] = c;
1271                        tty->rawInBuf.Tail = newTail;
1272           
1273                        /*
1274                         * check to see if rcv wakeup callback was set
1275                         */
1276                        if (( !tty->tty_rcvwakeup ) && ( tty->tty_rcv.sw_pfn != NULL )) {
1277                          (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
1278                          tty->tty_rcvwakeup = 1;
1279                        }
1280                }               
1281          }
1282        }
1283        tty->rawInBufDropped += dropped;
1284        rtems_semaphore_release (tty->rawInBuf.Semaphore);
1285        return dropped;
1286}
1287
1288/*
1289 * in task-driven mode, this function is called in Tx task context
1290 * in interrupt-driven mode, this function is called in TxIRQ context
1291 */
1292int
1293rtems_termios_refill_transmitter (struct rtems_termios_tty *tty)
1294{
1295        unsigned int newTail;
1296        int nToSend;
1297        rtems_interrupt_level level;
1298        int len;
1299
1300        /* check for XOF/XON to send */
1301        if ((tty->flow_ctrl & (FL_MDXOF | FL_IREQXOF | FL_ISNTXOF))
1302            == (FL_MDXOF | FL_IREQXOF)) {
1303          /* XOFF should be sent now... */
1304          (*tty->device.write)(tty->minor,
1305                               &(tty->termios.c_cc[VSTOP]), 1);
1306
1307          rtems_interrupt_disable(level);
1308          tty->t_dqlen--;
1309          tty->flow_ctrl |= FL_ISNTXOF;
1310          rtems_interrupt_enable(level);
1311
1312          nToSend = 1;
1313        }
1314        else if ((tty->flow_ctrl & (FL_IREQXOF | FL_ISNTXOF))
1315                 == FL_ISNTXOF) {
1316          /* NOTE: send XON even, if no longer in XON/XOFF mode... */
1317          /* XON should be sent now... */
1318                /*
1319                 * FIXME: this .write call will generate another
1320                 * dequeue callback. This will advance the "Tail" in the data
1321                 * buffer, although the corresponding data is not yet out!
1322                 * Therefore the dequeue "length" should be reduced by 1
1323                 */
1324          (*tty->device.write)(tty->minor,
1325                               &(tty->termios.c_cc[VSTART]), 1);
1326
1327          rtems_interrupt_disable(level);
1328          tty->t_dqlen--;
1329          tty->flow_ctrl &= ~FL_ISNTXOF;
1330          rtems_interrupt_enable(level);
1331
1332          nToSend = 1;
1333        }
1334        else {
1335          if ( tty->rawOutBuf.Head == tty->rawOutBuf.Tail ) {
1336            /*
1337             * buffer was empty
1338             */
1339            if (tty->rawOutBufState == rob_wait) {
1340              /*
1341               * this should never happen...
1342               */
1343              rtems_semaphore_release (tty->rawOutBuf.Semaphore);
1344            }
1345            return 0;   
1346          }
1347
1348          rtems_interrupt_disable(level);
1349          len = tty->t_dqlen;
1350          tty->t_dqlen = 0;
1351          rtems_interrupt_enable(level);
1352
1353          newTail = (tty->rawOutBuf.Tail + len) % tty->rawOutBuf.Size;
1354          tty->rawOutBuf.Tail = newTail;
1355          if (tty->rawOutBufState == rob_wait) {
1356            /*
1357             * wake up any pending writer task
1358             */
1359            rtems_semaphore_release (tty->rawOutBuf.Semaphore);
1360          }
1361          if (newTail == tty->rawOutBuf.Head) {
1362            /*
1363             * Buffer has become empty
1364             */
1365            tty->rawOutBufState = rob_idle;
1366            nToSend = 0;
1367           
1368            /*
1369             * check to see if snd wakeup callback was set
1370             */
1371            if ( tty->tty_snd.sw_pfn != NULL) {
1372              (*tty->tty_snd.sw_pfn)(&tty->termios, tty->tty_snd.sw_arg);
1373            }
1374          }
1375          /* check, whether output should stop due to received XOFF */
1376          else if ((tty->flow_ctrl & (FL_MDXON | FL_ORCVXOF))
1377                   ==                (FL_MDXON | FL_ORCVXOF)) {
1378                  /* Buffer not empty, but output stops due to XOFF */
1379                  /* set flag, that output has been stopped */
1380                  rtems_interrupt_disable(level);
1381                  tty->flow_ctrl |= FL_OSTOP;
1382                  tty->rawOutBufState = rob_busy; /*apm*/
1383                  rtems_interrupt_enable(level);
1384                  nToSend = 0;
1385          }
1386          else {
1387            /*
1388             * Buffer not empty, start tranmitter
1389             */
1390            if (newTail > tty->rawOutBuf.Head)
1391                    nToSend = tty->rawOutBuf.Size - newTail;
1392            else
1393                    nToSend = tty->rawOutBuf.Head - newTail;
1394            /* when flow control XON or XOF, don't send blocks of data     */
1395            /* to allow fast reaction on incoming flow ctrl and low latency*/
1396            /* for outgoing flow control                                   */
1397            if (tty->flow_ctrl & (FL_MDXON | FL_MDXOF)) {
1398                    nToSend = 1;
1399            }
1400            tty->rawOutBufState = rob_busy; /*apm*/
1401            (*tty->device.write)(tty->minor,
1402                                 (char *)&tty->rawOutBuf.theBuf[newTail],
1403                                 nToSend);
1404          }
1405          tty->rawOutBuf.Tail = newTail; /*apm*/
1406        }
1407        return nToSend;
1408}
1409
1410/*
1411 * Characters have been transmitted
1412 * NOTE: This routine runs in the context of the
1413 *       device transmit interrupt handler.
1414 * The second argument is the number of characters transmitted so far.
1415 * This value will always be 1 for devices which generate an interrupt
1416 * for each transmitted character.
1417 * It returns number of characters left to transmit
1418 */
1419int
1420rtems_termios_dequeue_characters (void *ttyp, int len)
1421{
1422        struct rtems_termios_tty *tty = ttyp;
1423        rtems_status_code sc;
1424
1425        /*
1426         * sum up character count already sent
1427         */
1428        tty->t_dqlen += len;
1429
1430        if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
1431                /*
1432                 * send wake up to transmitter task
1433                 */
1434                sc = rtems_event_send(tty->txTaskId,
1435                                      TERMIOS_TX_START_EVENT);
1436                if (sc != RTEMS_SUCCESSFUL)
1437                        rtems_fatal_error_occurred (sc);
1438                return 0; /* nothing to output in IRQ... */
1439        }
1440        else if (tty->t_line == PPPDISC ) {
1441                /*
1442                 * call any line discipline start function
1443                 */
1444                if (linesw[tty->t_line].l_start != NULL) {
1445                        linesw[tty->t_line].l_start(tty);
1446                }
1447                return 0; /* nothing to output in IRQ... */
1448        }
1449        else {
1450                return rtems_termios_refill_transmitter(tty);
1451        }
1452}
1453
1454/*
1455 * this task actually processes any transmit events
1456 */
1457static rtems_task rtems_termios_txdaemon(rtems_task_argument argument)
1458{
1459        struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
1460        rtems_event_set the_event;
1461
1462        while (1) {
1463                /*
1464                 * wait for rtems event
1465                 */
1466                rtems_event_receive((TERMIOS_TX_START_EVENT |
1467                                     TERMIOS_TX_TERMINATE_EVENT),
1468                                    RTEMS_EVENT_ANY | RTEMS_WAIT,
1469                                    RTEMS_NO_TIMEOUT,
1470                                    &the_event);
1471                if ((the_event & TERMIOS_TX_TERMINATE_EVENT) != 0) {
1472                        tty->txTaskId = 0;
1473                        rtems_task_delete(RTEMS_SELF);
1474                }
1475                else {
1476                        /*
1477                         * call any line discipline start function
1478                         */
1479                        if (linesw[tty->t_line].l_start != NULL) {
1480                                linesw[tty->t_line].l_start(tty);
1481                        }
1482                        /*
1483                         * try to push further characters to device
1484                         */
1485                        rtems_termios_refill_transmitter(tty);
1486                }
1487        }
1488}
1489
1490/*
1491 * this task actually processes any receive events
1492 */
1493static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument)
1494{
1495        struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
1496        rtems_event_set the_event;
1497        int c;
1498        char c_buf;
1499        while (1) {
1500                /*
1501                 * wait for rtems event
1502                 */
1503                rtems_event_receive((TERMIOS_RX_PROC_EVENT |
1504                                     TERMIOS_RX_TERMINATE_EVENT),
1505                                    RTEMS_EVENT_ANY | RTEMS_WAIT,
1506                                    RTEMS_NO_TIMEOUT,
1507                                    &the_event);
1508                if ((the_event & TERMIOS_RX_TERMINATE_EVENT) != 0) {
1509                        tty->rxTaskId = 0;
1510                        rtems_task_delete(RTEMS_SELF);
1511                }
1512                else {
1513                        /*
1514                         * do something
1515                         */
1516                        c = tty->device.pollRead(tty->minor);
1517                        if (c != EOF) {
1518                                /*
1519                                 * pollRead did call enqueue on its own
1520                                 */
1521                                c_buf = c;
1522                                rtems_termios_enqueue_raw_characters (
1523                                      tty,&c_buf,1);
1524                        }
1525                }
1526        }
1527}
Note: See TracBrowser for help on using the repository browser.