source: rtems/cpukit/libcsupport/src/termios.c @ 360d9da

4.104.114.84.9
Last change on this file since 360d9da was 360d9da, checked in by Joel Sherrill <joel.sherrill@…>, on Mar 7, 2006 at 10:23:15 PM

2006-03-07 Till Strauman <strauman@…>

PR 830/filesystem

  • libcsupport/src/termios.c: termios ioctl(FIONREAD) reported wrong number of characters. So add chars in low-level/raw buffer to total count.
  • Property mode set to 100644
File size: 38.3 KB
Line 
1/*
2 * TERMIOS serial line support
3 *
4 *  Author:
5 *    W. Eric Norum
6 *    Saskatchewan Accelerator Laboratory
7 *    University of Saskatchewan
8 *    Saskatoon, Saskatchewan, CANADA
9 *    eric@skatter.usask.ca
10 *
11 *  The license and distribution terms for this file may be
12 *  found in the file LICENSE in this distribution or at
13 *  http://www.rtems.com/license/LICENSE.
14 *
15 *  $Id$
16 */
17
18#if HAVE_CONFIG_H
19#include "config.h"
20#endif
21
22#include <rtems.h>
23#include <rtems/libio.h>
24#include <ctype.h>
25#include <errno.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <termios.h>
29#include <unistd.h>
30#include <sys/ttycom.h>
31
32#include <rtems/termiostypes.h>
33
34/*
35 * The size of the cooked buffer
36 */
37#define CBUFSIZE        (rtems_termios_cbufsize)
38
39/*
40 * The sizes of the raw message buffers.
41 * On most architectures it is quite a bit more
42 * efficient if these are powers of two.
43 */
44#define RAW_INPUT_BUFFER_SIZE   (rtems_termios_raw_input_size)
45#define RAW_OUTPUT_BUFFER_SIZE  (rtems_termios_raw_output_size)
46
47/* fields for "flow_ctrl" status */
48#define FL_IREQXOF 1        /* input queue requests stop of incoming data */
49#define FL_ISNTXOF 2        /* XOFF has been sent to other side of line   */
50#define FL_IRTSOFF 4        /* RTS has been turned off for other side..   */
51
52#define FL_ORCVXOF 0x10     /* XOFF has been received                     */
53#define FL_OSTOP   0x20     /* output has been stopped due to XOFF        */
54
55#define FL_MDRTS   0x100    /* input controlled with RTS/CTS handshake    */
56#define FL_MDXON   0x200    /* input controlled with XON/XOFF protocol    */
57#define FL_MDXOF   0x400    /* output controlled with XON/XOFF protocol   */
58
59#define NODISC(n) \
60        { NULL, NULL,   NULL,   NULL, \
61          NULL, NULL,   NULL,   NULL }
62/*
63 * FIXME: change linesw entries consistant with linesw entry usage...
64 */
65struct  linesw linesw[MAXLDISC] =
66{
67        NODISC(0),              /* 0- termios-built-in */
68        NODISC(1),              /* 1- defunct */
69        NODISC(2),              /* 2- NTTYDISC */
70        NODISC(3),              /* TABLDISC */
71        NODISC(4),              /* SLIPDISC */
72        NODISC(5),              /* PPPDISC */
73        NODISC(6),              /* loadable */
74        NODISC(7),              /* loadable */
75};
76
77int     nlinesw = sizeof (linesw) / sizeof (linesw[0]);
78
79extern struct rtems_termios_tty *rtems_termios_ttyHead;
80extern struct rtems_termios_tty *rtems_termios_ttyTail;
81extern rtems_id rtems_termios_ttyMutex;
82
83static int rtems_termios_cbufsize = 256;
84static int rtems_termios_raw_input_size = 128;
85static int rtems_termios_raw_output_size = 64;
86
87static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument);
88static rtems_task rtems_termios_txdaemon(rtems_task_argument argument);
89/*
90 * some constants for I/O daemon task creation
91 */
92#define TERMIOS_TXTASK_PRIO 10
93#define TERMIOS_RXTASK_PRIO 9
94#define TERMIOS_TXTASK_STACKSIZE 1024
95#define TERMIOS_RXTASK_STACKSIZE 1024
96/*
97 * some events to be sent to the I/O tasks
98 */
99#define TERMIOS_TX_START_EVENT     RTEMS_EVENT_1
100#define TERMIOS_TX_TERMINATE_EVENT RTEMS_EVENT_0
101
102#define TERMIOS_RX_PROC_EVENT      RTEMS_EVENT_1
103#define TERMIOS_RX_TERMINATE_EVENT RTEMS_EVENT_0
104
105/*
106 * Open a termios device
107 */
108rtems_status_code
109rtems_termios_open (
110  rtems_device_major_number      major,
111  rtems_device_minor_number      minor,
112  void                          *arg,
113  const rtems_termios_callbacks *callbacks
114  )
115{
116        rtems_status_code sc;
117        rtems_libio_open_close_args_t *args = arg;
118        struct rtems_termios_tty *tty;
119
120        /*
121         * See if the device has already been opened
122         */
123        sc = rtems_semaphore_obtain (rtems_termios_ttyMutex,
124                                     RTEMS_WAIT, RTEMS_NO_TIMEOUT);
125        if (sc != RTEMS_SUCCESSFUL)
126                return sc;
127        for (tty = rtems_termios_ttyHead ; tty != NULL ; tty = tty->forw) {
128                if ((tty->major == major) && (tty->minor == minor))
129                        break;
130        }
131        if (tty == NULL) {
132                static char c = 'a';
133
134                /*
135                 * Create a new device
136                 */
137                tty = calloc (1, sizeof (struct rtems_termios_tty));
138                if (tty == NULL) {
139                        rtems_semaphore_release (rtems_termios_ttyMutex);
140                        return RTEMS_NO_MEMORY;
141                }
142                /*
143                 * allocate raw input buffer
144                 */
145                tty->rawInBuf.Size = RAW_INPUT_BUFFER_SIZE;
146                tty->rawInBuf.theBuf = malloc (tty->rawInBuf.Size);
147                if (tty->rawInBuf.theBuf == NULL) {
148                        free(tty);
149                        rtems_semaphore_release (rtems_termios_ttyMutex);
150                        return RTEMS_NO_MEMORY;
151                }
152                /*
153                 * allocate raw output buffer
154                 */
155                tty->rawOutBuf.Size = RAW_OUTPUT_BUFFER_SIZE;
156                tty->rawOutBuf.theBuf = malloc (tty->rawOutBuf.Size);
157                if (tty->rawOutBuf.theBuf == NULL) {
158                        free((void *)(tty->rawInBuf.theBuf));
159                        free(tty);
160                        rtems_semaphore_release (rtems_termios_ttyMutex);
161                        return RTEMS_NO_MEMORY;
162                }
163                /*
164                 * allocate cooked buffer
165                 */
166                tty->cbuf  = malloc (CBUFSIZE);
167                if (tty->cbuf == NULL) {
168                        free((void *)(tty->rawOutBuf.theBuf));
169                        free((void *)(tty->rawInBuf.theBuf));
170                        free(tty);
171                        rtems_semaphore_release (rtems_termios_ttyMutex);
172                        return RTEMS_NO_MEMORY;
173                }
174                /*
175                 * Initialize wakeup callbacks
176                 */
177                tty->tty_snd.sw_pfn = NULL;
178                tty->tty_snd.sw_arg = NULL;
179                tty->tty_rcv.sw_pfn = NULL;
180                tty->tty_rcv.sw_arg = NULL;
181                tty->tty_rcvwakeup  = 0;
182
183                /*
184                 * link tty
185                 */
186                tty->forw = rtems_termios_ttyHead;
187                tty->back = NULL;
188                if (rtems_termios_ttyHead != NULL)
189                        rtems_termios_ttyHead->back = tty;
190                rtems_termios_ttyHead = tty;
191                if (rtems_termios_ttyTail == NULL)
192                        rtems_termios_ttyTail = tty;
193
194                tty->minor = minor;
195                tty->major = major;
196
197                /*
198                 * Set up mutex semaphores
199                 */
200                sc = rtems_semaphore_create (
201                        rtems_build_name ('T', 'R', 'i', c),
202                        1,
203                        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
204                        RTEMS_NO_PRIORITY,
205                        &tty->isem);
206                if (sc != RTEMS_SUCCESSFUL)
207                        rtems_fatal_error_occurred (sc);
208                sc = rtems_semaphore_create (
209                        rtems_build_name ('T', 'R', 'o', c),
210                        1,
211                        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
212                        RTEMS_NO_PRIORITY,
213                        &tty->osem);
214                if (sc != RTEMS_SUCCESSFUL)
215                        rtems_fatal_error_occurred (sc);
216                sc = rtems_semaphore_create (
217                        rtems_build_name ('T', 'R', 'x', c),
218                        0,
219                        RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_FIFO,
220                        RTEMS_NO_PRIORITY,
221                        &tty->rawOutBuf.Semaphore);
222                if (sc != RTEMS_SUCCESSFUL)
223                        rtems_fatal_error_occurred (sc);
224                tty->rawOutBufState = rob_idle;
225
226                /*
227                 * Set callbacks
228                 */
229                tty->device = *callbacks;
230
231                /*
232                 * Create I/O tasks
233                 */
234                if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
235                        sc = rtems_task_create (
236                                   rtems_build_name ('T', 'x', 'T', c),
237                                   TERMIOS_TXTASK_PRIO,
238                                   TERMIOS_TXTASK_STACKSIZE,
239                                   RTEMS_NO_PREEMPT | RTEMS_NO_TIMESLICE |
240                                   RTEMS_NO_ASR,
241                                   RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
242                                   &tty->txTaskId);
243                        if (sc != RTEMS_SUCCESSFUL)
244                                rtems_fatal_error_occurred (sc);
245                        sc = rtems_task_create (
246                                   rtems_build_name ('R', 'x', 'T', c),
247                                   TERMIOS_RXTASK_PRIO,
248                                   TERMIOS_RXTASK_STACKSIZE,
249                                   RTEMS_NO_PREEMPT | RTEMS_NO_TIMESLICE |
250                                   RTEMS_NO_ASR,
251                                   RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
252                                   &tty->rxTaskId);
253                        if (sc != RTEMS_SUCCESSFUL)
254                                rtems_fatal_error_occurred (sc);
255
256                }
257                if ((tty->device.pollRead == NULL) ||
258                    (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN)){
259                        sc = rtems_semaphore_create (
260                                rtems_build_name ('T', 'R', 'r', c),
261                                0,
262                                RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY,
263                                RTEMS_NO_PRIORITY,
264                                &tty->rawInBuf.Semaphore);
265                        if (sc != RTEMS_SUCCESSFUL)
266                                rtems_fatal_error_occurred (sc);
267                }
268
269                /*
270                 * Set default parameters
271                 */
272                tty->termios.c_iflag = BRKINT | ICRNL | IXON | IMAXBEL;
273                tty->termios.c_oflag = OPOST | ONLCR | XTABS;
274                tty->termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
275                tty->termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOK | ECHOE | ECHOCTL;
276
277                tty->termios.c_cc[VINTR] = '\003';
278                tty->termios.c_cc[VQUIT] = '\034';
279                tty->termios.c_cc[VERASE] = '\177';
280                tty->termios.c_cc[VKILL] = '\025';
281                tty->termios.c_cc[VEOF] = '\004';
282                tty->termios.c_cc[VEOL] = '\000';
283                tty->termios.c_cc[VEOL2] = '\000';
284                tty->termios.c_cc[VSTART] = '\021';
285                tty->termios.c_cc[VSTOP] = '\023';
286                tty->termios.c_cc[VSUSP] = '\032';
287                tty->termios.c_cc[VREPRINT] = '\022';
288                tty->termios.c_cc[VDISCARD] = '\017';
289                tty->termios.c_cc[VWERASE] = '\027';
290                tty->termios.c_cc[VLNEXT] = '\026';
291
292                /* start with no flow control, clear flow control flags */
293                tty->flow_ctrl = 0;
294                /*
295                 * set low/highwater mark for XON/XOFF support
296                 */
297                tty->lowwater  = tty->rawInBuf.Size * 1/2;
298                tty->highwater = tty->rawInBuf.Size * 3/4;
299                /*
300                 * Bump name characer
301                 */
302                if (c++ == 'z')
303                        c = 'a';
304
305        }
306        args->iop->data1 = tty;
307        if (!tty->refcount++) {
308          if (tty->device.firstOpen)
309                (*tty->device.firstOpen)(major, minor, arg);
310          /*
311           * start I/O tasks, if needed
312           */
313          if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
314            sc = rtems_task_start(tty->rxTaskId,
315                                  rtems_termios_rxdaemon,
316                                  (rtems_task_argument)tty);
317            if (sc != RTEMS_SUCCESSFUL)
318              rtems_fatal_error_occurred (sc);
319
320            sc = rtems_task_start(tty->txTaskId,
321                                  rtems_termios_txdaemon,
322                                  (rtems_task_argument)tty);
323            if (sc != RTEMS_SUCCESSFUL)
324              rtems_fatal_error_occurred (sc);
325          }
326        }
327        rtems_semaphore_release (rtems_termios_ttyMutex);
328        return RTEMS_SUCCESSFUL;
329}
330
331/*
332 * Drain output queue
333 */
334static void
335drainOutput (struct rtems_termios_tty *tty)
336{
337        rtems_interrupt_level level;
338        rtems_status_code sc;
339
340        if (tty->device.outputUsesInterrupts != TERMIOS_POLLED) {
341                rtems_interrupt_disable (level);
342                while (tty->rawOutBuf.Tail != tty->rawOutBuf.Head) {
343                        tty->rawOutBufState = rob_wait;
344                        rtems_interrupt_enable (level);
345                        sc = rtems_semaphore_obtain (tty->rawOutBuf.Semaphore,
346                                                        RTEMS_WAIT,
347                                                        RTEMS_NO_TIMEOUT);
348                        if (sc != RTEMS_SUCCESSFUL)
349                                rtems_fatal_error_occurred (sc);
350                        rtems_interrupt_disable (level);
351                }
352                rtems_interrupt_enable (level);
353        }
354}
355
356rtems_status_code
357rtems_termios_close (void *arg)
358{
359        rtems_libio_open_close_args_t *args = arg;
360        struct rtems_termios_tty *tty = args->iop->data1;
361        rtems_status_code sc;
362
363        sc = rtems_semaphore_obtain (rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
364        if (sc != RTEMS_SUCCESSFUL)
365                rtems_fatal_error_occurred (sc);
366        if (--tty->refcount == 0) {
367                if (linesw[tty->t_line].l_close != NULL) {
368                        /*
369                         * call discipline-specific close
370                         */
371                        sc = linesw[tty->t_line].l_close(tty);
372                }
373                else {
374                        /*
375                         * default: just flush output buffer
376                         */
377                        drainOutput (tty);
378                }
379
380                if (tty->device.outputUsesInterrupts
381                    == TERMIOS_TASK_DRIVEN) {
382                        /*
383                         * send "terminate" to I/O tasks
384                         */
385                        sc = rtems_event_send(
386                                  tty->rxTaskId,
387                                  TERMIOS_RX_TERMINATE_EVENT);
388                        if (sc != RTEMS_SUCCESSFUL)
389                                rtems_fatal_error_occurred (sc);
390                        sc = rtems_event_send(
391                                  tty->txTaskId,
392                                  TERMIOS_TX_TERMINATE_EVENT);
393                        if (sc != RTEMS_SUCCESSFUL)
394                                rtems_fatal_error_occurred (sc);
395                }
396                if (tty->device.lastClose)
397                         (*tty->device.lastClose)(tty->major, tty->minor, arg);
398                if (tty->forw == NULL) {
399                        rtems_termios_ttyTail = tty->back;
400                        if ( rtems_termios_ttyTail != NULL ) {
401                                rtems_termios_ttyTail->forw = NULL;
402                        }
403                }
404                else {
405                        tty->forw->back = tty->back;
406                }
407                if (tty->back == NULL) {
408                        rtems_termios_ttyHead = tty->forw;
409                        if ( rtems_termios_ttyHead != NULL ) {
410                                rtems_termios_ttyHead->back = NULL;
411                        }
412                }
413                else {
414                        tty->back->forw = tty->forw;
415                }
416                rtems_semaphore_delete (tty->isem);
417                rtems_semaphore_delete (tty->osem);
418                rtems_semaphore_delete (tty->rawOutBuf.Semaphore);
419                if ((tty->device.pollRead == NULL) ||
420                    (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN))
421                        rtems_semaphore_delete (tty->rawInBuf.Semaphore);
422                free (tty->rawInBuf.theBuf);
423                free (tty->rawOutBuf.theBuf);
424                free (tty->cbuf);
425                free (tty);
426        }
427        rtems_semaphore_release (rtems_termios_ttyMutex);
428        return RTEMS_SUCCESSFUL;
429}
430
431rtems_status_code rtems_termios_bufsize (
432  int cbufsize,
433  int raw_input,
434  int raw_output
435)
436{
437  rtems_termios_cbufsize        = cbufsize;
438  rtems_termios_raw_input_size  = raw_input;
439  rtems_termios_raw_output_size = raw_output;
440  return RTEMS_SUCCESSFUL;
441}
442
443static void
444termios_set_flowctrl(struct rtems_termios_tty *tty)
445{
446  rtems_interrupt_level level;
447  /*
448   * check for flow control options to be switched off
449   */
450
451  /* check for outgoing XON/XOFF flow control switched off */
452  if (( tty->flow_ctrl & FL_MDXON) &&
453      !(tty->termios.c_iflag & IXON)) {
454    /* clear related flags in flow_ctrl */
455    tty->flow_ctrl &= ~(FL_MDXON | FL_ORCVXOF);
456
457    /* has output been stopped due to received XOFF? */
458    if (tty->flow_ctrl & FL_OSTOP) {
459      /* disable interrupts    */
460      rtems_interrupt_disable(level);
461      tty->flow_ctrl &= ~FL_OSTOP;
462      /* check for chars in output buffer (or rob_state?) */
463      if (tty->rawOutBufState != rob_idle) {
464        /* if chars available, call write function... */
465        (*tty->device.write)(tty->minor,
466                     &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
467      }
468      /* reenable interrupts */
469      rtems_interrupt_enable(level);
470    }
471  }
472  /* check for incoming XON/XOFF flow control switched off */
473  if (( tty->flow_ctrl & FL_MDXOF) &&
474      !(tty->termios.c_iflag & IXOFF)) {
475    /* clear related flags in flow_ctrl */
476    tty->flow_ctrl &= ~(FL_MDXOF);
477    /* FIXME: what happens, if we had sent XOFF but not yet XON? */
478    tty->flow_ctrl &= ~(FL_ISNTXOF);
479  }
480
481  /* check for incoming RTS/CTS flow control switched off */
482  if (( tty->flow_ctrl & FL_MDRTS) &&
483      !(tty->termios.c_cflag & CRTSCTS)) {
484    /* clear related flags in flow_ctrl */
485    tty->flow_ctrl &= ~(FL_MDRTS);
486
487    /* restart remote Tx, if it was stopped */
488    if ((tty->flow_ctrl & FL_IRTSOFF) &&
489        (tty->device.startRemoteTx != NULL)) {
490      tty->device.startRemoteTx(tty->minor);
491    }
492    tty->flow_ctrl &= ~(FL_IRTSOFF);
493  }
494
495  /*
496   * check for flow control options to be switched on
497   */
498  /* check for incoming RTS/CTS flow control switched on */
499  if (tty->termios.c_cflag & CRTSCTS) {
500    tty->flow_ctrl |= FL_MDRTS;
501  }
502  /* check for incoming XON/XOF flow control switched on */
503  if (tty->termios.c_iflag & IXOFF) {
504    tty->flow_ctrl |= FL_MDXOF;
505  }
506  /* check for outgoing XON/XOF flow control switched on */
507  if (tty->termios.c_iflag & IXON) {
508    tty->flow_ctrl |= FL_MDXON;
509  }
510}
511
512rtems_status_code
513rtems_termios_ioctl (void *arg)
514{
515        rtems_libio_ioctl_args_t *args = arg;
516        struct rtems_termios_tty *tty = args->iop->data1;
517        struct ttywakeup         *wakeup = (struct ttywakeup *)args->buffer;
518        rtems_status_code sc;
519
520        args->ioctl_return = 0;
521        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
522        if (sc != RTEMS_SUCCESSFUL) {
523                args->ioctl_return = sc;
524                return sc;
525        }
526        switch (args->command) {
527        default:
528                if (linesw[tty->t_line].l_ioctl != NULL) {
529                        sc = linesw[tty->t_line].l_ioctl(tty,args);
530                }
531                else {
532                        sc = RTEMS_INVALID_NUMBER;
533                }
534                break;
535
536        case RTEMS_IO_GET_ATTRIBUTES:
537                *(struct termios *)args->buffer = tty->termios;
538                break;
539
540        case RTEMS_IO_SET_ATTRIBUTES:
541                tty->termios = *(struct termios *)args->buffer;
542
543                /* check for and process change in flow control options */
544                termios_set_flowctrl(tty);
545
546                if (tty->termios.c_lflag & ICANON) {
547                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
548                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
549                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
550                }
551                else {
552                        rtems_interval ticksPerSecond;
553                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
554                        tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10;
555                        if (tty->termios.c_cc[VTIME]) {
556                                tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
557                                tty->rawInBufSemaphoreTimeout = tty->vtimeTicks;
558                                if (tty->termios.c_cc[VMIN])
559                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
560                                else
561                                        tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks;
562                        }
563                        else {
564                                if (tty->termios.c_cc[VMIN]) {
565                                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
566                                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
567                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
568                                }
569                                else {
570                                        tty->rawInBufSemaphoreOptions = RTEMS_NO_WAIT;
571                                }
572                        }
573                }
574                if (tty->device.setAttributes)
575                        (*tty->device.setAttributes)(tty->minor, &tty->termios);
576                break;
577
578        case RTEMS_IO_TCDRAIN:
579                drainOutput (tty);
580                break;
581
582        case RTEMS_IO_SNDWAKEUP:
583                tty->tty_snd = *wakeup;
584                break;
585
586        case RTEMS_IO_RCVWAKEUP:
587                tty->tty_rcv = *wakeup;
588                break;
589
590                /*
591                 * FIXME: add various ioctl code handlers
592                 */
593
594#if 1 /* FIXME */
595        case TIOCSETD:
596                /*
597                 * close old line discipline
598                 */
599                if (linesw[tty->t_line].l_close != NULL) {
600                        sc = linesw[tty->t_line].l_close(tty);
601                }
602                tty->t_line=*(int*)(args->buffer);
603                tty->t_sc = NULL; /* ensure that no more valid data */
604                /*
605                 * open new line discipline
606                 */
607                if (linesw[tty->t_line].l_open != NULL) {
608                        sc = linesw[tty->t_line].l_open(tty);
609                }
610                break;
611        case TIOCGETD:
612                *(int*)(args->buffer)=tty->t_line;
613                break;
614#endif
615        case FIONREAD:
616                {
617                int rawnc = tty->rawInBuf.Tail - tty->rawInBuf.Head;
618                if ( rawnc < 0 )
619                        rawnc += tty->rawInBuf.Size;
620                /* Half guess that this is the right operation */
621                *(int *)args->buffer = tty->ccount - tty->cindex + rawnc;
622                }
623                break;
624        }
625        rtems_semaphore_release (tty->osem);
626        args->ioctl_return = sc;
627        return sc;
628}
629
630/*
631 * Send characters to device-specific code
632 */
633void
634rtems_termios_puts (
635  const void *_buf, int len, struct rtems_termios_tty *tty)
636{
637        const unsigned char *buf = _buf;
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, (void *)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                uint32_t   count = args->count;
765                char      *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                                (void *)&(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        uint32_t   count = args->count;
1087        char      *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                     &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                         (void *)&(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                               (void *)&(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                               (void *)&(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                                 &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 if (tty->t_line == PPPDISC ) {
1416                /*
1417                 * call any line discipline start function
1418                 */
1419                if (linesw[tty->t_line].l_start != NULL) {
1420                        linesw[tty->t_line].l_start(tty);
1421                }
1422                return 0; /* nothing to output in IRQ... */
1423        }
1424        else {
1425                return rtems_termios_refill_transmitter(tty);
1426        }
1427}
1428
1429/*
1430 * this task actually processes any transmit events
1431 */
1432static rtems_task rtems_termios_txdaemon(rtems_task_argument argument)
1433{
1434        struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
1435        rtems_event_set the_event;
1436
1437        while (1) {
1438                /*
1439                 * wait for rtems event
1440                 */
1441                rtems_event_receive((TERMIOS_TX_START_EVENT |
1442                                     TERMIOS_TX_TERMINATE_EVENT),
1443                                    RTEMS_EVENT_ANY | RTEMS_WAIT,
1444                                    RTEMS_NO_TIMEOUT,
1445                                    &the_event);
1446                if ((the_event & TERMIOS_TX_TERMINATE_EVENT) != 0) {
1447                        tty->txTaskId = 0;
1448                        rtems_task_delete(RTEMS_SELF);
1449                }
1450                else {
1451                        /*
1452                         * call any line discipline start function
1453                         */
1454                        if (linesw[tty->t_line].l_start != NULL) {
1455                                linesw[tty->t_line].l_start(tty);
1456                        }
1457                        /*
1458                         * try to push further characters to device
1459                         */
1460                        rtems_termios_refill_transmitter(tty);
1461                }
1462        }
1463}
1464
1465/*
1466 * this task actually processes any receive events
1467 */
1468static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument)
1469{
1470        struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
1471        rtems_event_set the_event;
1472        int c;
1473        char c_buf;
1474        while (1) {
1475                /*
1476                 * wait for rtems event
1477                 */
1478                rtems_event_receive((TERMIOS_RX_PROC_EVENT |
1479                                     TERMIOS_RX_TERMINATE_EVENT),
1480                                    RTEMS_EVENT_ANY | RTEMS_WAIT,
1481                                    RTEMS_NO_TIMEOUT,
1482                                    &the_event);
1483                if ((the_event & TERMIOS_RX_TERMINATE_EVENT) != 0) {
1484                        tty->rxTaskId = 0;
1485                        rtems_task_delete(RTEMS_SELF);
1486                }
1487                else {
1488                        /*
1489                         * do something
1490                         */
1491                        c = tty->device.pollRead(tty->minor);
1492                        if (c != EOF) {
1493                                /*
1494                                 * pollRead did call enqueue on its own
1495                                 */
1496                                c_buf = c;
1497                                rtems_termios_enqueue_raw_characters (
1498                                      tty,&c_buf,1);
1499                        }
1500                }
1501        }
1502}
Note: See TracBrowser for help on using the repository browser.