source: rtems/cpukit/libcsupport/src/termios.c @ 77b0a73

4.104.114.84.95
Last change on this file since 77b0a73 was abbec0a, checked in by Joel Sherrill <joel.sherrill@…>, on 05/06/05 at 15:29:57

2005-05-06 Joel Sherrill <joel@…>

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