source: rtems/c/src/exec/libcsupport/src/termios.c @ e6890ba

4.104.114.84.95
Last change on this file since e6890ba was e6890ba, checked in by Joel Sherrill <joel.sherrill@…>, on Aug 1, 2000 at 3:23:59 PM

Patch from Eric Norum <eric@…> to avoid lockup under
the correct circumstances of DMA buffer size, serial
line interrupts, and ethernet interrupts the termios osend routine would
lock up waiting for the raw output buffer semaphore.

  • Property mode set to 100644
File size: 29.0 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#include <rtems.h>
19#include <rtems/libio.h>
20#include <ctype.h>
21#include <errno.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <termios.h>
25#include <unistd.h>
26
27/*
28 *  FreeBSD does not support a full POSIX termios so we have to help it out
29 */
30
31#if defined(__FreeBSD__)
32#define XTABS   0
33#define ONLRET  0
34#define ONOCR   0
35#define TABDLY  0
36#define OLCUC   0
37#define ILCUC   0
38#define OCRNL   0
39#define IUCLC   0
40#endif
41
42/*
43 *  Cygwin does not define these
44 */
45
46#if defined(__CYGWIN__)
47#define ECHOPRT 0
48#endif
49
50/*
51 * The size of the cooked buffer
52 */
53#define CBUFSIZE        256
54
55/*
56 * The sizes of the raw message buffers.
57 * On most architectures it is quite a bit more
58 * efficient if these are powers of two.
59 */
60#define RAW_INPUT_BUFFER_SIZE   128
61#define RAW_OUTPUT_BUFFER_SIZE  64
62
63/*
64 * Variables associated with each termios instance.
65 * One structure for each hardware I/O device.
66 */
67struct rtems_termios_tty {
68        /*
69         * Linked-list of active TERMIOS devices
70         */
71        struct rtems_termios_tty        *forw;
72        struct rtems_termios_tty        *back;
73
74        /*
75         * How many times has this device been opened
76         */
77        int             refcount;
78
79        /*
80         * This device
81         */
82        rtems_device_major_number       major;
83        rtems_device_major_number       minor;
84
85        /*
86         * Mutual-exclusion semaphores
87         */
88        rtems_id        isem;
89        rtems_id        osem;
90
91        /*
92         * The canonical (cooked) character buffer
93         */
94        char            cbuf[CBUFSIZE];
95        int             ccount;
96        int             cindex;
97
98        /*
99         * Keep track of cursor (printhead) position
100         */
101        int             column;
102        int             read_start_column;
103
104        /*
105         * The ioctl settings
106         */
107        struct termios  termios;
108        rtems_interval  vtimeTicks;
109
110        /*
111         * Raw input character buffer
112         */
113        volatile char           rawInBuf[RAW_INPUT_BUFFER_SIZE];
114        volatile unsigned int   rawInBufHead;
115        volatile unsigned int   rawInBufTail;
116        rtems_id                rawInBufSemaphore;
117        rtems_unsigned32        rawInBufSemaphoreOptions;
118        rtems_interval          rawInBufSemaphoreTimeout;
119        rtems_interval          rawInBufSemaphoreFirstTimeout;
120        unsigned int            rawInBufDropped;        /* Statistics */
121
122        /*
123         * Raw output character buffer
124         */
125        volatile char           rawOutBuf[RAW_OUTPUT_BUFFER_SIZE];
126        volatile unsigned int   rawOutBufHead;
127        volatile unsigned int   rawOutBufTail;
128        rtems_id                rawOutBufSemaphore;
129        volatile enum {rob_idle, rob_busy, rob_wait }   rawOutBufState;
130
131        /*
132         * Callbacks to device-specific routines
133         */
134        rtems_termios_callbacks device;
135        volatile unsigned int   flow_ctrl;
136        unsigned int            lowwater,highwater;
137};
138
139/* fields for "flow_ctrl" status */
140#define FL_IREQXOF 1        /* input queue requests stop of incoming data */
141#define FL_ISNTXOF 2        /* XOFF has been sent to other side of line   */
142#define FL_IRTSOFF 4        /* RTS has been turned off for other side..   */
143
144#define FL_ORCVXOF 0x10     /* XOFF has been received                     */
145#define FL_OSTOP   0x20     /* output has been stopped due to XOFF        */
146
147#define FL_MDRTS   0x100    /* input controlled with RTS/CTS handshake    */
148#define FL_MDXON   0x200    /* input controlled with XON/XOFF protocol    */
149#define FL_MDXOF   0x400    /* output controlled with XON/XOFF protocol   */
150
151extern struct rtems_termios_tty *rtems_termios_ttyHead;
152extern struct rtems_termios_tty *rtems_termios_ttyTail;
153extern rtems_id rtems_termios_ttyMutex;
154
155/*
156 * Open a termios device
157 */
158rtems_status_code
159rtems_termios_open (
160  rtems_device_major_number      major,
161  rtems_device_minor_number      minor,
162  void                          *arg,
163  const rtems_termios_callbacks *callbacks
164  )
165{
166        rtems_status_code sc;
167        rtems_libio_open_close_args_t *args = arg;
168        struct rtems_termios_tty *tty;
169
170        /*
171         * See if the device has already been opened
172         */
173        sc = rtems_semaphore_obtain (rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
174        if (sc != RTEMS_SUCCESSFUL)
175                return sc;
176        for (tty = rtems_termios_ttyHead ; tty != NULL ; tty = tty->forw) {
177                if ((tty->major == major) && (tty->minor == minor))
178                        break;
179        }
180        if (tty == NULL) {
181                static char c = 'a';
182
183                /*
184                 * Create a new device
185                 */
186                tty = calloc (1, sizeof (struct rtems_termios_tty));
187                if (tty == NULL) {
188                        rtems_semaphore_release (rtems_termios_ttyMutex);
189                        return RTEMS_NO_MEMORY;
190                }
191                if (rtems_termios_ttyHead)
192                        rtems_termios_ttyHead->back = tty;
193                tty->forw = rtems_termios_ttyHead;
194                rtems_termios_ttyHead = tty;
195                if (rtems_termios_ttyTail == NULL)
196                        rtems_termios_ttyTail = tty;
197                tty->minor = minor;
198                tty->major = major;
199
200                /*
201                 * Set up mutex semaphores
202                 */
203                sc = rtems_semaphore_create (
204                        rtems_build_name ('T', 'R', 'i', c),
205                        1,
206                        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
207                        RTEMS_NO_PRIORITY,
208                        &tty->isem);
209                if (sc != RTEMS_SUCCESSFUL)
210                        rtems_fatal_error_occurred (sc);
211                sc = rtems_semaphore_create (
212                        rtems_build_name ('T', 'R', 'o', c),
213                        1,
214                        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
215                        RTEMS_NO_PRIORITY,
216                        &tty->osem);
217                if (sc != RTEMS_SUCCESSFUL)
218                        rtems_fatal_error_occurred (sc);
219                sc = rtems_semaphore_create (
220                        rtems_build_name ('T', 'R', 'x', c),
221                        0,
222                        RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_FIFO,
223                        RTEMS_NO_PRIORITY,
224                        &tty->rawOutBufSemaphore);
225                if (sc != RTEMS_SUCCESSFUL)
226                        rtems_fatal_error_occurred (sc);
227                tty->rawOutBufState = rob_idle;
228
229                /*
230                 * Set callbacks
231                 */
232                tty->device = *callbacks;
233                if (!tty->device.pollRead) {
234                        sc = rtems_semaphore_create (
235                                rtems_build_name ('T', 'R', 'r', c),
236                                0,
237                                RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY,
238                                RTEMS_NO_PRIORITY,
239                                &tty->rawInBufSemaphore);
240                        if (sc != RTEMS_SUCCESSFUL)
241                                rtems_fatal_error_occurred (sc);
242                }
243
244                /*
245                 * Set default parameters
246                 */
247                tty->termios.c_iflag = BRKINT | ICRNL | IXON | IMAXBEL;
248                tty->termios.c_oflag = OPOST | ONLCR | XTABS;
249                tty->termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
250                tty->termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOK | ECHOE | ECHOCTL;
251
252                tty->termios.c_cc[VINTR] = '\003';
253                tty->termios.c_cc[VQUIT] = '\034';
254                tty->termios.c_cc[VERASE] = '\177';
255                tty->termios.c_cc[VKILL] = '\025';
256                tty->termios.c_cc[VEOF] = '\004';
257                tty->termios.c_cc[VEOL] = '\000';
258                tty->termios.c_cc[VEOL2] = '\000';
259                tty->termios.c_cc[VSTART] = '\021';
260                tty->termios.c_cc[VSTOP] = '\023';
261                tty->termios.c_cc[VSUSP] = '\032';
262                tty->termios.c_cc[VREPRINT] = '\022';
263                tty->termios.c_cc[VDISCARD] = '\017';
264                tty->termios.c_cc[VWERASE] = '\027';
265                tty->termios.c_cc[VLNEXT] = '\026';
266
267                /* start with no flow control, clear flow control flags */
268                tty->flow_ctrl = 0;
269                /*
270                 * set low/highwater mark for XON/XOFF support
271                 */
272                tty->lowwater  = RAW_INPUT_BUFFER_SIZE * 1/2;
273                tty->highwater = RAW_INPUT_BUFFER_SIZE * 3/4;
274                /*
275                 * Bump name characer
276                 */
277                if (c++ == 'z')
278                        c = 'a';
279
280        }
281        args->iop->data1 = tty;
282        if (!tty->refcount++ && tty->device.firstOpen)
283                (*tty->device.firstOpen)(major, minor, arg);
284        rtems_semaphore_release (rtems_termios_ttyMutex);
285        return RTEMS_SUCCESSFUL;
286}
287
288/*
289 * Drain output queue
290 */
291static void
292drainOutput (struct rtems_termios_tty *tty)
293{
294        rtems_interrupt_level level;
295        rtems_status_code sc;
296
297        if (tty->device.outputUsesInterrupts) {
298                rtems_interrupt_disable (level);
299                while (tty->rawOutBufTail != tty->rawOutBufHead) {
300                        tty->rawOutBufState = rob_wait;
301                        rtems_interrupt_enable (level);
302                        sc = rtems_semaphore_obtain (tty->rawOutBufSemaphore,
303                                                        RTEMS_WAIT,
304                                                        RTEMS_NO_TIMEOUT);
305                        if (sc != RTEMS_SUCCESSFUL)
306                                rtems_fatal_error_occurred (sc);
307                        rtems_interrupt_disable (level);
308                }
309                rtems_interrupt_enable (level);
310        }
311}
312
313rtems_status_code
314rtems_termios_close (void *arg)
315{
316        rtems_libio_open_close_args_t *args = arg;
317        struct rtems_termios_tty *tty = args->iop->data1;
318        rtems_status_code sc;
319
320        sc = rtems_semaphore_obtain (rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
321        if (sc != RTEMS_SUCCESSFUL)
322                rtems_fatal_error_occurred (sc);
323        if (--tty->refcount == 0) {
324                drainOutput (tty);
325                if (tty->device.lastClose)
326                         (*tty->device.lastClose)(tty->major, tty->minor, arg);
327                if (tty->forw == NULL)
328                        rtems_termios_ttyTail = tty->back;
329                else
330                        tty->forw->back = tty->back;
331                if (tty->back == NULL)
332                        rtems_termios_ttyHead = tty->forw;
333                else
334                        tty->back->forw = tty->forw;
335                rtems_semaphore_delete (tty->isem);
336                rtems_semaphore_delete (tty->osem);
337                rtems_semaphore_delete (tty->rawOutBufSemaphore);
338                if (!tty->device.pollRead)
339                        rtems_semaphore_delete (tty->rawInBufSemaphore);
340                free (tty);
341        }
342        rtems_semaphore_release (rtems_termios_ttyMutex);
343        return RTEMS_SUCCESSFUL;
344}
345
346static void 
347termios_set_flowctrl(struct rtems_termios_tty *tty)
348{
349  rtems_interrupt_level level;
350  /*
351   * check for flow control options to be switched off
352   */
353
354  /* check for outgoing XON/XOFF flow control switched off */
355  if (( tty->flow_ctrl & FL_MDXON) &&
356      !(tty->termios.c_iflag & IXON)) {
357    /* clear related flags in flow_ctrl */
358    tty->flow_ctrl &= ~(FL_MDXON | FL_ORCVXOF);
359
360    /* has output been stopped due to received XOFF? */
361    if (tty->flow_ctrl & FL_OSTOP) {
362      /* disable interrupts    */
363      rtems_interrupt_disable(level);
364      tty->flow_ctrl &= ~FL_OSTOP;
365      /* check for chars in output buffer (or rob_state?) */
366      if (tty->rawOutBufState != rob_idle) {
367        /* if chars available, call write function... */
368        (*tty->device.write)(tty->minor,
369                             (char *)&tty->rawOutBuf[tty->rawOutBufTail], 1);
370      }
371      /* reenable interrupts */
372      rtems_interrupt_enable(level);
373    }
374  }
375  /* check for incoming XON/XOFF flow control switched off */
376  if (( tty->flow_ctrl & FL_MDXOF) &&
377      !(tty->termios.c_iflag & IXOFF)) {
378    /* clear related flags in flow_ctrl */
379    tty->flow_ctrl &= ~(FL_MDXOF);
380    /* FIXME: what happens, if we had sent XOFF but not yet XON? */
381    tty->flow_ctrl &= ~(FL_ISNTXOF);
382  }
383
384  /* check for incoming RTS/CTS flow control switched off */
385  if (( tty->flow_ctrl & FL_MDRTS) &&
386      !(tty->termios.c_cflag & CRTSCTS)) {
387    /* clear related flags in flow_ctrl */
388    tty->flow_ctrl &= ~(FL_MDRTS);
389   
390    /* restart remote Tx, if it was stopped */
391    if ((tty->flow_ctrl & FL_IRTSOFF) &&
392        (tty->device.startRemoteTx != NULL)) {
393      tty->device.startRemoteTx(tty->minor);
394    }
395    tty->flow_ctrl &= ~(FL_IRTSOFF);
396  }   
397 
398  /*
399   * check for flow control options to be switched on 
400   */
401  /* check for incoming RTS/CTS flow control switched on */
402  if (tty->termios.c_cflag & CRTSCTS) {
403    tty->flow_ctrl |= FL_MDRTS;
404  }
405  /* check for incoming XON/XOF flow control switched on */
406  if (tty->termios.c_iflag & IXOFF) {
407    tty->flow_ctrl |= FL_MDXOF;
408  }
409  /* check for outgoing XON/XOF flow control switched on */
410  if (tty->termios.c_iflag & IXON) {
411    tty->flow_ctrl |= FL_MDXON;
412  }
413}
414
415rtems_status_code
416rtems_termios_ioctl (void *arg)
417{
418        rtems_libio_ioctl_args_t *args = arg;
419        struct rtems_termios_tty *tty = args->iop->data1;
420        rtems_status_code sc;
421
422        args->ioctl_return = 0;
423        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
424        if (sc != RTEMS_SUCCESSFUL) {
425                args->ioctl_return = sc;
426                return sc;
427        }
428        switch (args->command) {
429        default:
430                sc = RTEMS_INVALID_NUMBER;
431                break;
432
433        case RTEMS_IO_GET_ATTRIBUTES:
434                *(struct termios *)args->buffer = tty->termios;
435                break;
436
437        case RTEMS_IO_SET_ATTRIBUTES:
438                tty->termios = *(struct termios *)args->buffer;
439
440                /* check for and process change in flow control options */
441                termios_set_flowctrl(tty);
442
443                if (tty->termios.c_lflag & ICANON) {
444                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
445                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
446                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
447                }
448                else {
449                        rtems_interval ticksPerSecond;
450                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
451                        tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10;
452                        if (tty->termios.c_cc[VTIME]) {
453                                tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
454                                tty->rawInBufSemaphoreTimeout = tty->vtimeTicks;
455                                if (tty->termios.c_cc[VMIN])
456                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
457                                else
458                                        tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks;
459                        }
460                        else {
461                                if (tty->termios.c_cc[VMIN]) {
462                                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
463                                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
464                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
465                                }
466                                else {
467                                        tty->rawInBufSemaphoreOptions = RTEMS_NO_WAIT;
468                                }
469                        }
470                }
471                if (tty->device.setAttributes)
472                        (*tty->device.setAttributes)(tty->minor, &tty->termios);
473                break;
474
475        case RTEMS_IO_TCDRAIN:
476                drainOutput (tty);
477                break;
478
479        case FIONREAD:
480                /* Half guess that this is the right operation */
481                *(int *)args->buffer = tty->ccount - tty->cindex;
482                break;
483        }
484        rtems_semaphore_release (tty->osem);
485        args->ioctl_return = sc;
486        return sc;
487}
488
489/*
490 * Send characters to device-specific code
491 */
492static void
493osend (const char *buf, int len, struct rtems_termios_tty *tty)
494{
495        unsigned int newHead;
496        rtems_interrupt_level level;
497        rtems_status_code sc;
498
499        if (!tty->device.outputUsesInterrupts) {
500                (*tty->device.write)(tty->minor, buf, len);
501                return;
502        }
503        newHead = tty->rawOutBufHead;
504        while (len) {
505                /*
506                 * Performance improvement could be made here.
507                 * Copy multiple bytes to raw buffer:
508                 * if (len > 1) && (space to buffer end, or tail > 1)
509                 *      ncopy = MIN (len, space to buffer end or tail)
510                 *      memcpy (raw buffer, buf, ncopy)
511                 *      buf += ncopy
512                 *      len -= ncopy
513                 *
514                 * To minimize latency, the memcpy should be done
515                 * with interrupts enabled.
516                 */
517                newHead = (newHead + 1) % RAW_OUTPUT_BUFFER_SIZE;
518                rtems_interrupt_disable (level);
519                while (newHead == tty->rawOutBufTail) {
520                        tty->rawOutBufState = rob_wait;
521                        rtems_interrupt_enable (level);
522                        sc = rtems_semaphore_obtain (tty->rawOutBufSemaphore,
523                                                        RTEMS_WAIT,
524                                                        RTEMS_NO_TIMEOUT);
525                        if (sc != RTEMS_SUCCESSFUL)
526                                rtems_fatal_error_occurred (sc);
527                        rtems_interrupt_disable (level);
528                }
529                tty->rawOutBuf[tty->rawOutBufHead] = *buf++;
530                tty->rawOutBufHead = newHead;
531                if (tty->rawOutBufState == rob_idle) {
532                  /* check, whether XOFF has been received */
533                  if (!(tty->flow_ctrl & FL_ORCVXOF)) {
534                    (*tty->device.write)(tty->minor,
535                                (char *)&tty->rawOutBuf[tty->rawOutBufTail],1);
536                  }
537                  else {
538                    /* remember that output has been stopped due to flow ctrl*/
539                    tty->flow_ctrl |= FL_OSTOP; 
540                  }
541                  tty->rawOutBufState = rob_busy;
542                }
543                rtems_interrupt_enable (level);
544                len--;
545        }
546}
547
548/*
549 * Handle output processing
550 */
551static void
552oproc (unsigned char c, struct rtems_termios_tty *tty)
553{
554        int     i;
555
556        if (tty->termios.c_oflag & OPOST) {
557                switch (c) {
558                case '\n':
559                        if (tty->termios.c_oflag & ONLRET)
560                                tty->column = 0;
561                        if (tty->termios.c_oflag & ONLCR) {
562                                osend ("\r", 1, tty);
563                                tty->column = 0;
564                        }
565                        break;
566
567                case '\r':
568                        if ((tty->termios.c_oflag & ONOCR) && (tty->column == 0))
569                                return;
570                        if (tty->termios.c_oflag & OCRNL) {
571                                c = '\n';
572                                if (tty->termios.c_oflag & ONLRET)
573                                        tty->column = 0;
574                                break;
575                        }
576                        tty->column = 0;
577                        break;
578
579                case '\t':
580                        i = 8 - (tty->column & 7);
581                        if ((tty->termios.c_oflag & TABDLY) == XTABS) {
582                                tty->column += i;
583                                osend ( "        ",  i, tty);
584                                return;
585                        }
586                        tty->column += i;
587                        break;
588
589                case '\b':
590                        if (tty->column > 0)
591                                tty->column--;
592                        break;
593
594                default:
595                        if (tty->termios.c_oflag & OLCUC)
596                                c = toupper(c);
597                        if (!iscntrl(c))
598                                tty->column++;
599                        break;
600                }
601        }
602        osend (&c, 1, tty);
603}
604
605rtems_status_code
606rtems_termios_write (void *arg)
607{
608        rtems_libio_rw_args_t *args = arg;
609        struct rtems_termios_tty *tty = args->iop->data1;
610        rtems_status_code sc;
611
612        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
613        if (sc != RTEMS_SUCCESSFUL)
614                return sc;
615        if (tty->termios.c_oflag & OPOST) {
616                unsigned32 count = args->count;
617                unsigned8 *buffer = args->buffer;
618                while (count--)
619                        oproc (*buffer++, tty);
620                args->bytes_moved = args->count;
621        }
622        else {
623                osend (args->buffer, args->count, tty);
624                args->bytes_moved = args->count;
625        }
626        rtems_semaphore_release (tty->osem);
627        return sc;
628}
629
630/*
631 * Echo a typed character
632 */
633static void
634echo (unsigned char c, struct rtems_termios_tty *tty)
635{
636        if ((tty->termios.c_lflag & ECHOCTL) && iscntrl(c) && (c != '\t') && (c != '\n')) {
637                char echobuf[2];
638
639                echobuf[0] = '^';
640                echobuf[1] = c ^ 0x40;
641                osend (echobuf, 2, tty);
642                tty->column += 2;
643        }
644        else {
645                oproc (c, tty);
646        }
647}
648
649/*
650 * Erase a character or line
651 * FIXME: Needs support for WERASE and ECHOPRT.
652 * FIXME: Some of the tests should check for IEXTEN, too.
653 */
654static void
655erase (struct rtems_termios_tty *tty, int lineFlag)
656{
657        if (tty->ccount == 0)
658                return;
659        if (lineFlag) {
660                if (!(tty->termios.c_lflag & ECHO)) {
661                        tty->ccount = 0;
662                        return;
663                }
664                if (!(tty->termios.c_lflag & ECHOE)) {
665                        tty->ccount = 0;
666                        echo (tty->termios.c_cc[VKILL], tty);
667                        if (tty->termios.c_lflag & ECHOK)
668                                echo ('\n', tty);
669                        return;
670                }
671        }
672        while (tty->ccount) {
673                unsigned char c = tty->cbuf[--tty->ccount];
674
675                if (tty->termios.c_lflag & ECHO) {
676                        if (!lineFlag && !(tty->termios.c_lflag & ECHOE)) {
677                                echo (tty->termios.c_cc[VERASE], tty);
678                        }
679                        else if (c == '\t') {
680                                int col = tty->read_start_column;
681                                int i = 0;
682
683                                /*
684                                 * Find the character before the tab
685                                 */
686                                while (i != tty->ccount) {
687                                        c = tty->cbuf[i++];
688                                        if (c == '\t') {
689                                                col = (col | 7) + 1;
690                                        }
691                                        else if (iscntrl (c)) {
692                                                if (tty->termios.c_lflag & ECHOCTL)
693                                                        col += 2;
694                                        }
695                                        else {
696                                                col++;
697                                        }
698                                }
699
700                                /*
701                                 * Back up over the tab
702                                 */
703                                while (tty->column > col) {
704                                        osend ("\b", 1, tty);
705                                        tty->column--;
706                                }
707                        }
708                        else {
709                                if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
710                                        osend ("\b \b", 3, tty);
711                                        if (tty->column)
712                                                tty->column--;
713                                }
714                                if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
715                                        osend ("\b \b", 3, tty);
716                                        if (tty->column)
717                                                tty->column--;
718                                }
719                        }
720                }
721                if (!lineFlag)
722                        break;
723        }
724}
725
726/*
727 * Process a single input character
728 */
729static int
730iproc (unsigned char c, struct rtems_termios_tty *tty)
731{
732        if (tty->termios.c_iflag & ISTRIP)
733                c &= 0x7f;
734        if (tty->termios.c_iflag & IUCLC)
735                c = tolower (c);
736        if (c == '\r') {
737                if (tty->termios.c_iflag & IGNCR)
738                        return 0;
739                if (tty->termios.c_iflag & ICRNL)
740                        c = '\n';
741        }
742        else if ((c == '\n') && (tty->termios.c_iflag & INLCR)) {
743                c = '\r';
744        }
745        if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
746                if (c == tty->termios.c_cc[VERASE]) {
747                        erase (tty, 0);
748                        return 0;
749                }
750                else if (c == tty->termios.c_cc[VKILL]) {
751                        erase (tty, 1);
752                        return 0;
753                }
754                else if (c == tty->termios.c_cc[VEOF]) {
755                        return 1;
756                }
757                else if (c == '\n') {
758                        if (tty->termios.c_lflag & (ECHO | ECHONL))
759                                echo (c, tty);
760                        tty->cbuf[tty->ccount++] = c;
761                        return 1;
762                }
763                else if ((c == tty->termios.c_cc[VEOL])
764                      || (c == tty->termios.c_cc[VEOL2])) {
765                        if (tty->termios.c_lflag & ECHO)
766                                echo (c, tty);
767                        tty->cbuf[tty->ccount++] = c;
768                        return 1;
769                }
770        }
771
772        /*
773         * FIXME: Should do IMAXBEL handling somehow
774         */
775        if (tty->ccount < (CBUFSIZE-1)) {
776                if (tty->termios.c_lflag & ECHO)
777                        echo (c, tty);
778                tty->cbuf[tty->ccount++] = c;
779        }
780        return 0;
781}
782
783/*
784 * Process input character, with semaphore.
785 */
786static int
787siproc (unsigned char c, struct rtems_termios_tty *tty)
788{
789        int i;
790
791        /*
792         * Obtain output semaphore if character will be echoed
793         */
794        if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) {
795                rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
796                i = iproc (c, tty);
797                rtems_semaphore_release (tty->osem);
798        }
799        else {
800                i = iproc (c, tty);
801        }
802        return i;
803}
804
805/*
806 * Fill the input buffer by polling the device
807 */
808static rtems_status_code
809fillBufferPoll (struct rtems_termios_tty *tty)
810{
811        int n;
812
813        if (tty->termios.c_lflag & ICANON) {
814                for (;;) {
815                        n = (*tty->device.pollRead)(tty->minor);
816                        if (n < 0) {
817                                rtems_task_wake_after (1);
818                        }
819                        else {
820                                if  (siproc (n, tty))
821                                        break;
822                        }
823                }
824        }
825        else {
826                rtems_interval then, now;
827                if (!tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
828                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
829                for (;;) {
830                        n = (*tty->device.pollRead)(tty->minor);
831                        if (n < 0) {
832                                if (tty->termios.c_cc[VMIN]) {
833                                        if (tty->termios.c_cc[VTIME] && tty->ccount) {
834                                                rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
835                                                if ((now - then) > tty->vtimeTicks) {
836                                                        break;
837                                                }
838                                        }
839                                }
840                                else {
841                                        if (!tty->termios.c_cc[VTIME])
842                                                break;
843                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
844                                        if ((now - then) > tty->vtimeTicks) {
845                                                break;
846                                        }
847                                }
848                                rtems_task_wake_after (1);
849                        }
850                        else {
851                                siproc (n, tty);
852                                if (tty->ccount >= tty->termios.c_cc[VMIN])
853                                        break;
854                                if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
855                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
856                        }
857                }
858        }
859        return RTEMS_SUCCESSFUL;
860}
861
862/*
863 * Fill the input buffer from the raw input queue
864 */
865static rtems_status_code
866fillBufferQueue (struct rtems_termios_tty *tty)
867{
868        rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout;
869        rtems_status_code sc;
870
871        for (;;) {
872                /*
873                 * Process characters read from raw queue
874                 */
875                while (tty->rawInBufHead != tty->rawInBufTail) {
876                        unsigned char c;
877                        unsigned int newHead;
878
879                        newHead = (tty->rawInBufHead + 1) % RAW_INPUT_BUFFER_SIZE;
880                        c = tty->rawInBuf[newHead];
881                        tty->rawInBufHead = newHead;
882                        if(((tty->rawInBufTail-newHead+RAW_INPUT_BUFFER_SIZE)
883                            % RAW_INPUT_BUFFER_SIZE) 
884                           < tty->lowwater) {
885                          tty->flow_ctrl &= ~FL_IREQXOF;
886                          /* if tx stopped and XON should be sent... */
887                          if (((tty->flow_ctrl & (FL_MDXON | FL_ISNTXOF))
888                               ==                (FL_MDXON | FL_ISNTXOF))
889                              && ((tty->rawOutBufState == rob_idle)
890                                  || (tty->flow_ctrl & FL_OSTOP))) {
891                            /* XON should be sent now... */
892                            (*tty->device.write)(tty->minor, 
893                                                 &(tty->termios.c_cc[VSTART]),
894                                                 1);
895                          }
896                          else if (tty->flow_ctrl & FL_MDRTS) {             
897                            tty->flow_ctrl &= ~FL_IRTSOFF;             
898                            /* activate RTS line */
899                            if (tty->device.startRemoteTx != NULL) {
900                              tty->device.startRemoteTx(tty->minor);
901                            }
902                          }
903                        }
904
905                        /* continue processing new character */
906                        if (tty->termios.c_lflag & ICANON) {
907                                if  (siproc (c, tty))
908                                        return RTEMS_SUCCESSFUL;
909                        }
910                        else {
911                                siproc (c, tty);
912                                if (tty->ccount >= tty->termios.c_cc[VMIN])
913                                        return RTEMS_SUCCESSFUL;
914                        }
915                        timeout = tty->rawInBufSemaphoreTimeout;
916                }
917
918                /*
919                 * Wait for characters
920                 */
921                sc = rtems_semaphore_obtain (tty->rawInBufSemaphore,
922                                                tty->rawInBufSemaphoreOptions,
923                                                timeout);
924                if (sc != RTEMS_SUCCESSFUL)
925                        break;
926        }
927        return RTEMS_SUCCESSFUL;
928}
929
930rtems_status_code
931rtems_termios_read (void *arg)
932{
933        rtems_libio_rw_args_t *args = arg;
934        struct rtems_termios_tty *tty = args->iop->data1;
935        unsigned32 count = args->count;
936        unsigned8 *buffer = args->buffer;
937        rtems_status_code sc;
938
939        sc = rtems_semaphore_obtain (tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
940        if (sc != RTEMS_SUCCESSFUL)
941                return sc;
942        if (tty->cindex == tty->ccount) {
943                tty->cindex = tty->ccount = 0;
944                tty->read_start_column = tty->column;
945                if (tty->device.pollRead)
946                        sc = fillBufferPoll (tty);
947                else
948                        sc = fillBufferQueue (tty);
949                if (sc != RTEMS_SUCCESSFUL)
950                        tty->cindex = tty->ccount = 0;
951        }
952        while (count && (tty->cindex < tty->ccount)) {
953                *buffer++ = tty->cbuf[tty->cindex++];
954                count--;
955        }
956        args->bytes_moved = args->count - count;
957        rtems_semaphore_release (tty->isem);
958        return sc;
959}
960
961/*
962 * Place characters on raw queue.
963 * NOTE: This routine runs in the context of the
964 *       device receive interrupt handler.
965 * Returns the number of characters dropped because of overflow.
966 */
967int
968rtems_termios_enqueue_raw_characters (void *ttyp, char *buf, int len)
969{
970        struct rtems_termios_tty *tty = ttyp;
971        unsigned int newTail;
972        char c;
973        int dropped = 0;
974        boolean flow_rcv = FALSE; /* TRUE, if flow control char received */
975        rtems_interrupt_level level;
976
977        while (len--) {
978          c = *buf++;
979          /* FIXME: implement IXANY: any character restarts output */
980          /* if incoming XON/XOFF controls outgoing stream: */
981          if (tty->flow_ctrl & FL_MDXON) {         
982            /* if received char is V_STOP and V_START (both are equal value) */
983            if (c == tty->termios.c_cc[VSTOP]) {
984              if (c == tty->termios.c_cc[VSTART]) {
985                /* received VSTOP and VSTART==VSTOP? */
986                /* then toggle "stop output" status  */
987                tty->flow_ctrl = tty->flow_ctrl ^ FL_ORCVXOF;
988              }
989              else {
990                /* VSTOP received (other code than VSTART) */
991                /* stop output                             */
992                tty->flow_ctrl |= FL_ORCVXOF;
993              }
994              flow_rcv = TRUE;
995            }
996            else if (c == tty->termios.c_cc[VSTART]) {
997              /* VSTART received */
998              /* restart output  */
999              tty->flow_ctrl &= ~FL_ORCVXOF;
1000              flow_rcv = TRUE;
1001            }
1002          }
1003          if (flow_rcv) {
1004            /* restart output according to FL_ORCVXOF flag */
1005            if ((tty->flow_ctrl & (FL_ORCVXOF | FL_OSTOP)) == FL_OSTOP) {
1006              /* disable interrupts    */
1007              rtems_interrupt_disable(level);
1008              tty->flow_ctrl &= ~FL_OSTOP;
1009              /* check for chars in output buffer (or rob_state?) */
1010              if (tty->rawOutBufState != rob_idle) {
1011              /* if chars available, call write function... */
1012                (*tty->device.write)(tty->minor,
1013                                     (char *)&tty->rawOutBuf[tty->rawOutBufTail], 1);
1014              }
1015              /* reenable interrupts */
1016              rtems_interrupt_enable(level);
1017            }
1018          }     
1019          else {
1020                newTail = (tty->rawInBufTail + 1) % RAW_INPUT_BUFFER_SIZE;
1021                /* if chars_in_buffer > highwater                */
1022                rtems_interrupt_disable(level);
1023                if ((((newTail - tty->rawInBufHead + RAW_INPUT_BUFFER_SIZE) 
1024                      % RAW_INPUT_BUFFER_SIZE)
1025                     > tty->highwater) && 
1026                    !(tty->flow_ctrl & FL_IREQXOF)) {
1027                  /* incoming data stream should be stopped */
1028                  tty->flow_ctrl |= FL_IREQXOF; 
1029                  if ((tty->flow_ctrl & (FL_MDXOF | FL_ISNTXOF)) 
1030                      ==                (FL_MDXOF             ) ){
1031                    if ((tty->flow_ctrl & FL_OSTOP) || 
1032                        (tty->rawOutBufState == rob_idle)) {
1033                      /* if tx is stopped due to XOFF or out of data */
1034                      /*    call write function here                 */
1035                      tty->flow_ctrl |= FL_ISNTXOF;
1036                      (*tty->device.write)(tty->minor,
1037                                           &(tty->termios.c_cc[VSTOP]),
1038                                           1);
1039                    }
1040                  }
1041                  else if ((tty->flow_ctrl & (FL_MDRTS | FL_IRTSOFF)) 
1042                           ==                (FL_MDRTS             ) ) {
1043                    tty->flow_ctrl |= FL_IRTSOFF;               
1044                    /* deactivate RTS line */
1045                    if (tty->device.stopRemoteTx != NULL) {
1046                      tty->device.stopRemoteTx(tty->minor);
1047                    }
1048                  }
1049                }
1050                /* reenable interrupts */
1051                rtems_interrupt_enable(level);
1052
1053                if (newTail == tty->rawInBufHead) {
1054                        dropped++;
1055                }
1056                else {
1057                        tty->rawInBuf[newTail] = c;
1058                        tty->rawInBufTail = newTail;
1059                }               
1060          }
1061        }
1062        tty->rawInBufDropped += dropped;
1063        rtems_semaphore_release (tty->rawInBufSemaphore);
1064        return dropped;
1065}
1066
1067/*
1068 * Characters have been transmitted
1069 * NOTE: This routine runs in the context of the
1070 *       device transmit interrupt handler.
1071 * The second argument is the number of characters transmitted so far.
1072 * This value will always be 1 for devices which generate an interrupt
1073 * for each transmitted character.
1074 * It returns number of characters left to transmit
1075 */
1076int
1077rtems_termios_dequeue_characters (void *ttyp, int len)
1078{
1079        struct rtems_termios_tty *tty = ttyp;
1080        unsigned int newTail;
1081        int nToSend;
1082
1083        /* check for XOF/XON to send */
1084        if ((tty->flow_ctrl & (FL_MDXOF | FL_IREQXOF | FL_ISNTXOF))
1085            == (FL_MDXOF | FL_IREQXOF)) {
1086          /* XOFF should be sent now... */
1087          (*tty->device.write)(tty->minor, 
1088                               &(tty->termios.c_cc[VSTOP]), 1);
1089          tty->flow_ctrl |= FL_ISNTXOF;
1090          nToSend = 1;
1091        }
1092        else if ((tty->flow_ctrl & (FL_IREQXOF | FL_ISNTXOF))
1093                 == FL_ISNTXOF) {
1094          /* NOTE: send XON even, if no longer in XON/XOFF mode... */
1095          /* XON should be sent now... */
1096          (*tty->device.write)(tty->minor, 
1097                               &(tty->termios.c_cc[VSTART]), 1);
1098          tty->flow_ctrl &= ~FL_ISNTXOF;
1099          nToSend = 1;
1100        }
1101        else {
1102          if (tty->rawOutBufState == rob_wait)
1103            rtems_semaphore_release (tty->rawOutBufSemaphore);
1104          if ( tty->rawOutBufHead == tty->rawOutBufTail ) {
1105            tty->rawOutBufState = rob_idle;
1106            return 0;   
1107          }
1108          newTail = (tty->rawOutBufTail + len) % RAW_OUTPUT_BUFFER_SIZE;
1109          if (newTail == tty->rawOutBufHead) {
1110            /*
1111             * Buffer empty
1112             */
1113            tty->rawOutBufState = rob_idle;
1114            nToSend = 0;
1115          }
1116          /* check, whether output should stop due to received XOFF */
1117          else if ((tty->flow_ctrl & (FL_MDXON | FL_ORCVXOF)) 
1118                   ==                (FL_MDXON | FL_ORCVXOF)) {
1119            /* Buffer not empty, but output stops due to XOFF */
1120            /* set flag, that output has been stopped */
1121            tty->flow_ctrl |= FL_OSTOP;
1122            tty->rawOutBufState = rob_busy;
1123            nToSend = 0;
1124          }
1125          else {
1126            /*
1127             * Buffer not empty, start tranmitter
1128             */
1129            if (newTail > tty->rawOutBufHead)
1130              nToSend = RAW_OUTPUT_BUFFER_SIZE - newTail;
1131            else
1132              nToSend = tty->rawOutBufHead - newTail;
1133            /* when flow control XON or XOF, don't send blocks of data     */
1134            /* to allow fast reaction on incoming flow ctrl and low latency*/
1135            /* for outgoing flow control                                   */
1136            if (tty->flow_ctrl & (FL_MDXON | FL_MDXOF))
1137              nToSend = 1;
1138            tty->rawOutBufState = rob_busy;
1139            (*tty->device.write)(tty->minor, (char *)&tty->rawOutBuf[newTail], nToSend);
1140          }
1141          tty->rawOutBufTail = newTail;
1142        }
1143        return nToSend;
1144}
Note: See TracBrowser for help on using the repository browser.