OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rtems-20020807/] [cpukit/] [libcsupport/] [src/] [termios.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.