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

4.104.114.84.95
Last change on this file since 77a0067 was 18040d3, checked in by Joel Sherrill <joel.sherrill@…>, on 03/31/99 at 23:35:22

Patch from Thomas Doerfler <td@…> to add flow control:

Some lines for "documentation":
======================================
One thing should be noted: when XON/XOFF is enabled, the serial
device will always work with one-character buffers, so the interrupt
load for the CPU might get higer, especially on devices like MC68360
and MPC860, where the serial channels are capable of using big
buffers. But, once again, this only happens when XON/XOFF is actually
selected.

Please note that the flag IXON is set by default, so outgoing
XON/XOFF flow control is enabled by default.

XON/XOFF is controlled using the "standard" fields IXON/IXOFF in the
termios structure. The termios flag IXANY is not (yet) supported.

Hardware handshake for the incoming data stream is controlled using
the standard flag CRTSCTS. If this flag is set, whenever the receive
buffer is almost full, the driver function "device.stopRemoteTx()" is
called, when the receive buffer has more space available,
"device.startRemoteTx()" is called again. If the driver does not
provide these interface functions (entries in device structure are
NULL pointers), then these calls are suppressed.

Changes of the flow control options during operation should work at
any time, but this has not been extensively tested.

No changes to the device driver interface are needed.
================================================

One critical point when using this patch might be, that any BSP using
this version of termios will now have outgoing flow control enabled
by default, so the behaviour of these BSPs will change here. The
option IXON has already been set in older termios by default, but it
did not work until this patch. Maybe this option should be switched
off by default, what do you think?

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