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

Last change on this file since bce9f3b1 was bce9f3b1, checked in by Joel Sherrill <joel.sherrill@…>, on 02/20/03 at 20:45:40

2003-02-20 Wolfram Wadepohl <W.Wadepohl@…>

PR 357/rtems_misc

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