source: rtems/cpukit/libcsupport/src/termios.c @ b295019

Last change on this file since b295019 was b295019, checked in by Joel Sherrill <joel.sherrill@…>, on 10/25/06 at 11:27:28

2006-10-25 Jennifer Averett <jennifer@…>

  • libcsupport/src/termios.c: Change attribute of semaphore. It was counting interrupts not acting as a condition synchronization mutex. Since the caller did not always need to obtain the semaphore, it was not being decremented until there was no data. This could occur after hours of running a system and thousands of interrupts. The code in fillBufferQueue would spin until it had consumed all of those semaphore counts.
  • 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_SIMPLE_BINARY_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.