source: rtems/c/src/libchip/serial/ns16550-context.c @ 02958c5e

4.115
Last change on this file since 02958c5e was 02958c5e, checked in by Josh Oguin <josh.oguin@…>, on 11/19/14 at 20:28:08

libchip/serial/ns16550* and z8530*: Assert on baud number to avoid divide by 0

This was flagged by CodeSonar?. It should be impossible to get an
incorrect baud number back but ensure this in debug mode. The _Assert()
keeps their scanner from evaluating for divide by 0 past this point.

  • Property mode set to 100644
File size: 20.4 KB
Line 
1/**
2 *  @file
3 * 
4 *  This file contains the TTY driver for the National Semiconductor NS16550.
5 *
6 *  This part is widely cloned and second sourced.  It is found in a number
7 *  of "Super IO" controllers.
8 *
9 *  This driver uses the termios pseudo driver.
10 */
11
12/*
13 *  COPYRIGHT (c) 1998 by Radstone Technology
14 *
15 *  THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
16 *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
17 *  IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
18 *  AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
19 *
20 *  You are hereby granted permission to use, copy, modify, and distribute
21 *  this file, provided that this notice, plus the above copyright notice
22 *  and disclaimer, appears in all copies. Radstone Technology will provide
23 *  no support for this code.
24 *
25 *  COPYRIGHT (c) 1989-2012.
26 *  On-Line Applications Research Corporation (OAR).
27 *
28 *  The license and distribution terms for this file may be
29 *  found in the file LICENSE in this distribution or at
30 *  http://www.rtems.org/license/LICENSE.
31 */
32
33#include <stdlib.h>
34
35#include <rtems/bspIo.h>
36
37#include <bsp.h>
38
39#include "ns16550.h"
40#include "ns16550_p.h"
41
42#if defined(BSP_FEATURE_IRQ_EXTENSION)
43  #include <bsp/irq.h>
44#elif defined(BSP_FEATURE_IRQ_LEGACY)
45  #include <bsp/irq.h>
46#elif defined(__PPC__) || defined(__i386__)
47  #include <bsp/irq.h>
48  #define BSP_FEATURE_IRQ_LEGACY
49  #ifdef BSP_SHARED_HANDLER_SUPPORT
50    #define BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
51  #endif
52#endif
53
54static uint32_t NS16550_GetBaudDivisor(ns16550_context *ctx, uint32_t baud)
55{
56  uint32_t clock = ctx->clock;
57  uint32_t baudDivisor = (clock != 0 ? clock : 115200) / (baud * 16);
58
59  if (ctx->has_fractional_divider_register) {
60    uint32_t fractionalDivider = 0x10;
61    uint32_t err = baud;
62    uint32_t mulVal;
63    uint32_t divAddVal;
64
65    clock /= 16 * baudDivisor;
66    for (mulVal = 1; mulVal < 16; ++mulVal) {
67      for (divAddVal = 0; divAddVal < mulVal; ++divAddVal) {
68        uint32_t actual = (mulVal * clock) / (mulVal + divAddVal);
69        uint32_t newErr = actual > baud ? actual - baud : baud - actual;
70
71        if (newErr < err) {
72          err = newErr;
73          fractionalDivider = (mulVal << 4) | divAddVal;
74        }
75      }
76    }
77
78    (*ctx->set_reg)(
79      ctx->port,
80      NS16550_FRACTIONAL_DIVIDER,
81      fractionalDivider
82    );
83  }
84
85  return baudDivisor;
86}
87
88/*
89 *  ns16550_enable_interrupts
90 *
91 *  This routine initializes the port to have the specified interrupts masked.
92 */
93static void ns16550_enable_interrupts(
94  ns16550_context *ctx,
95  int              mask
96)
97{
98  (*ctx->set_reg)(ctx->port, NS16550_INTERRUPT_ENABLE, mask);
99}
100
101static void ns16550_clear_and_set_interrupts(
102  ns16550_context *ctx,
103  uint8_t          clear,
104  uint8_t          set
105)
106{
107  rtems_interrupt_lock_context lock_context;
108  ns16550_get_reg get_reg = ctx->get_reg;
109  ns16550_set_reg set_reg = ctx->set_reg;
110  uintptr_t port = ctx->port;
111  uint8_t val;
112
113  rtems_termios_device_lock_acquire(&ctx->base, &lock_context);
114  val = (*get_reg)(port, NS16550_INTERRUPT_ENABLE);
115  val &= ~clear;
116  val |= set;
117  (*set_reg)(port, NS16550_INTERRUPT_ENABLE, val);
118  rtems_termios_device_lock_release(&ctx->base, &lock_context);
119}
120
121/*
122 *  ns16550_probe
123 */
124
125bool ns16550_probe(rtems_termios_device_context *base)
126{
127  ns16550_context        *ctx = (ns16550_context *) base;
128  uintptr_t               pNS16550;
129  uint8_t                 ucDataByte;
130  uint32_t                ulBaudDivisor;
131  ns16550_set_reg         setReg;
132  ns16550_get_reg         getReg;
133
134  ctx->modem_control = SP_MODEM_IRQ;
135
136  pNS16550 = ctx->port;   
137  setReg   = ctx->set_reg;
138  getReg   = ctx->get_reg;
139
140  /* Clear the divisor latch, clear all interrupt enables,
141   * and reset and
142   * disable the FIFO's.
143   */
144
145  (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
146  ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR );
147
148  /* Set the divisor latch and set the baud rate. */
149
150  ulBaudDivisor = NS16550_GetBaudDivisor(ctx, ctx->initial_baud);
151  ucDataByte = SP_LINE_DLAB;
152  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
153
154  /* XXX */
155  (*setReg)(pNS16550,NS16550_TRANSMIT_BUFFER,(uint8_t)(ulBaudDivisor & 0xffU));
156  (*setReg)(
157    pNS16550,NS16550_INTERRUPT_ENABLE,
158    (uint8_t)(( ulBaudDivisor >> 8 ) & 0xffU )
159  );
160
161  /* Clear the divisor latch and set the character size to eight bits */
162  /* with one stop bit and no parity checking. */
163  ucDataByte = EIGHT_BITS;
164  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
165
166  /* Enable and reset transmit and receive FIFOs. TJA     */
167  ucDataByte = SP_FIFO_ENABLE;
168  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
169
170  ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
171  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
172
173  ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR);
174
175  /* Set data terminal ready. */
176  /* And open interrupt tristate line */
177  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,ctx->modem_control);
178
179  (*getReg)(pNS16550, NS16550_LINE_STATUS );
180  (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
181
182  return true;
183}
184
185static size_t ns16550_write_to_fifo(
186  const ns16550_context *ctx,
187  const char *buf,
188  size_t len
189)
190{
191  uint32_t port = ctx->port;
192  ns16550_set_reg set = ctx->set_reg;
193  size_t out = len > SP_FIFO_SIZE ? SP_FIFO_SIZE : len;
194  size_t i;
195
196  for (i = 0; i < out; ++i) {
197    (*set)(port, NS16550_TRANSMIT_BUFFER, buf[i]);
198  }
199
200  return out;
201}
202
203/**
204 * @brief Process interrupt.
205 */
206static void ns16550_isr(void *arg)
207{
208  rtems_termios_tty *tty = arg;
209  ns16550_context *ctx = rtems_termios_get_device_context(tty);
210  uint32_t port = ctx->port;
211  ns16550_get_reg get = ctx->get_reg;
212  int i = 0;
213  char buf [SP_FIFO_SIZE];
214
215  /* Iterate until no more interrupts are pending */
216  do {
217    /* Fetch received characters */
218    for (i = 0; i < SP_FIFO_SIZE; ++i) {
219      if ((get( port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
220        buf [i] = (char) get(port, NS16550_RECEIVE_BUFFER);
221      } else {
222        break;
223      }
224    }
225
226    /* Enqueue fetched characters */
227    rtems_termios_enqueue_raw_characters(tty, buf, i);
228
229    /* Do transmit */
230    if (ctx->out_total > 0
231        && (get(port, NS16550_LINE_STATUS) & SP_LSR_THOLD) != 0) {
232      size_t current = ctx->out_current;
233
234      ctx->out_buf += current;
235      ctx->out_remaining -= current;
236
237      if (ctx->out_remaining > 0) {
238        ctx->out_current =
239          ns16550_write_to_fifo(ctx, ctx->out_buf, ctx->out_remaining);
240      } else {
241        rtems_termios_dequeue_characters(tty, ctx->out_total);
242      }
243    }
244  } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0);
245}
246
247static void ns16550_isr_task(void *arg)
248{
249  rtems_termios_tty *tty = arg;
250  ns16550_context *ctx = rtems_termios_get_device_context(tty);
251  uint8_t status = (*ctx->get_reg)(ctx->port, NS16550_LINE_STATUS);
252
253  if ((status & SP_LSR_RDY) != 0) {
254    ns16550_clear_and_set_interrupts(ctx, SP_INT_RX_ENABLE, 0);
255    rtems_termios_rxirq_occured(tty);
256  }
257
258  if (ctx->out_total > 0 && (status & SP_LSR_THOLD) != 0) {
259    size_t current = ctx->out_current;
260
261    ctx->out_buf += current;
262    ctx->out_remaining -= current;
263
264    if (ctx->out_remaining > 0) {
265      ctx->out_current =
266        ns16550_write_to_fifo(ctx, ctx->out_buf, ctx->out_remaining);
267    } else {
268      size_t done = ctx->out_total;
269
270      ctx->out_total = 0;
271      ns16550_clear_and_set_interrupts(ctx, SP_INT_TX_ENABLE, 0);
272      rtems_termios_dequeue_characters(tty, done);
273    }
274  }
275}
276
277static int ns16550_read_task(rtems_termios_device_context *base)
278{
279  ns16550_context *ctx = (ns16550_context *) base;
280  uint32_t port = ctx->port;
281  ns16550_get_reg get = ctx->get_reg;
282  char buf[SP_FIFO_SIZE];
283  int i;
284
285  for (i = 0; i < SP_FIFO_SIZE; ++i) {
286    if ((get(port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
287      buf[i] = (char) get(port, NS16550_RECEIVE_BUFFER);
288    } else {
289      break;
290    }
291  }
292
293  rtems_termios_enqueue_raw_characters(ctx->tty, buf, i);
294  ns16550_clear_and_set_interrupts(ctx, 0, SP_INT_RX_ENABLE);
295
296  return -1;
297}
298
299/*
300 *  ns16550_initialize_interrupts
301 *
302 *  This routine initializes the port to operate in interrupt driver mode.
303 */
304static void ns16550_initialize_interrupts(
305  struct rtems_termios_tty *tty,
306  ns16550_context          *ctx,
307  void                    (*isr)(void *)
308)
309{
310  #ifdef BSP_FEATURE_IRQ_EXTENSION
311    {
312      rtems_status_code sc = RTEMS_SUCCESSFUL;
313      sc = rtems_interrupt_handler_install(
314        ctx->irq,
315        "NS16550",
316        RTEMS_INTERRUPT_SHARED,
317        isr,
318        tty
319      );
320      if (sc != RTEMS_SUCCESSFUL) {
321        /* FIXME */
322        printk( "%s: Error: Install interrupt handler\n", __func__);
323        rtems_fatal_error_occurred( 0xdeadbeef);
324      }
325    }
326  #elif defined(BSP_FEATURE_IRQ_LEGACY)
327    {
328      int rv = 0;
329      #ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
330        rtems_irq_connect_data cd = {
331          ctx->irq,
332          isr,
333          tty,
334          NULL,
335          NULL,
336          NULL,
337          NULL
338        };
339        rv = BSP_install_rtems_shared_irq_handler( &cd);
340      #else
341        rtems_irq_connect_data cd = {
342          ctx->irq,
343          isr,
344          tty,
345          NULL,
346          NULL,
347          NULL
348        };
349        rv = BSP_install_rtems_irq_handler( &cd);
350      #endif
351      if (rv == 0) {
352        /* FIXME */
353        printk( "%s: Error: Install interrupt handler\n", __func__);
354        rtems_fatal_error_occurred( 0xdeadbeef);
355      }
356    }
357  #endif
358}
359
360/*
361 *  ns16550_open
362 */
363
364static bool ns16550_open(
365  struct rtems_termios_tty      *tty,
366  rtems_termios_device_context  *base,
367  struct termios                *term,
368  rtems_libio_open_close_args_t *args
369)
370{
371  ns16550_context *ctx = (ns16550_context *) base;
372
373  ctx->tty = tty;
374
375  /* Set initial baud */
376  rtems_termios_set_initial_baud(tty, ctx->initial_baud);
377
378  if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
379    ns16550_initialize_interrupts(tty, ctx, ns16550_isr);
380    ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
381  } else if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
382    ns16550_initialize_interrupts(tty, ctx, ns16550_isr_task);
383    ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
384  }
385
386  return true;
387}
388
389static void ns16550_cleanup_interrupts(
390  struct rtems_termios_tty *tty,
391  ns16550_context          *ctx,
392  void                    (*isr)(void *)
393)
394{
395  #if defined(BSP_FEATURE_IRQ_EXTENSION)
396    rtems_status_code sc = RTEMS_SUCCESSFUL;
397    sc = rtems_interrupt_handler_remove(
398      ctx->irq,
399      isr,
400      tty
401    );
402    if (sc != RTEMS_SUCCESSFUL) {
403      /* FIXME */
404      printk("%s: Error: Remove interrupt handler\n", __func__);
405      rtems_fatal_error_occurred(0xdeadbeef);
406    }
407  #elif defined(BSP_FEATURE_IRQ_LEGACY)
408    int rv = 0;
409    rtems_irq_connect_data cd = {
410      .name = ctx->irq,
411      .hdl = isr,
412      .handle = tty
413    };
414    rv = BSP_remove_rtems_irq_handler(&cd);
415    if (rv == 0) {
416      /* FIXME */
417      printk("%s: Error: Remove interrupt handler\n", __func__);
418      rtems_fatal_error_occurred(0xdeadbeef);
419    }
420  #endif
421}
422
423/*
424 *  ns16550_close
425 */
426
427static void ns16550_close(
428  struct rtems_termios_tty      *tty,
429  rtems_termios_device_context  *base,
430  rtems_libio_open_close_args_t *args
431)
432{
433  ns16550_context *ctx = (ns16550_context *) base;
434
435  ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR);
436
437  if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
438    ns16550_cleanup_interrupts(tty, ctx, ns16550_isr);
439  } else if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
440    ns16550_cleanup_interrupts(tty, ctx, ns16550_isr_task);
441  }
442}
443
444/**
445 * @brief Polled write for NS16550.
446 */
447void ns16550_polled_putchar(rtems_termios_device_context *base, char out)
448{
449  ns16550_context *ctx = (ns16550_context *) base;
450  uintptr_t port = ctx->port;
451  ns16550_get_reg get = ctx->get_reg;
452  ns16550_set_reg set = ctx->set_reg;
453  uint32_t status = 0;
454  rtems_interrupt_lock_context lock_context;
455
456  /* Save port interrupt mask */
457  uint32_t interrupt_mask = get( port, NS16550_INTERRUPT_ENABLE);
458
459  /* Disable port interrupts */
460  ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR);
461
462  while (true) {
463    /* Try to transmit the character in a critical section */
464    rtems_termios_device_lock_acquire(&ctx->base, &lock_context);
465
466    /* Read the transmitter holding register and check it */
467    status = get( port, NS16550_LINE_STATUS);
468    if ((status & SP_LSR_THOLD) != 0) {
469      /* Transmit character */
470      set( port, NS16550_TRANSMIT_BUFFER, out);
471
472      /* Finished */
473      rtems_termios_device_lock_release(&ctx->base, &lock_context);
474      break;
475    } else {
476      rtems_termios_device_lock_release(&ctx->base, &lock_context);
477    }
478
479    /* Wait for transmitter holding register to be empty */
480    do {
481      status = get( port, NS16550_LINE_STATUS);
482    } while ((status & SP_LSR_THOLD) == 0);
483  }
484
485  /* Restore port interrupt mask */
486  set( port, NS16550_INTERRUPT_ENABLE, interrupt_mask);
487}
488
489/*
490 * These routines provide control of the RTS and DTR lines
491 */
492
493/*
494 *  ns16550_assert_RTS
495 */
496
497static void ns16550_assert_RTS(rtems_termios_device_context *base)
498{
499  ns16550_context             *ctx = (ns16550_context *) base;
500  rtems_interrupt_lock_context lock_context;
501
502  /*
503   * Assert RTS
504   */
505  rtems_termios_device_lock_acquire(base, &lock_context);
506  ctx->modem_control |= SP_MODEM_RTS;
507  (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
508  rtems_termios_device_lock_release(base, &lock_context);
509}
510
511/*
512 *  ns16550_negate_RTS
513 */
514
515static void ns16550_negate_RTS(rtems_termios_device_context *base)
516{
517  ns16550_context             *ctx = (ns16550_context *) base;
518  rtems_interrupt_lock_context lock_context;
519
520  /*
521   * Negate RTS
522   */
523  rtems_termios_device_lock_acquire(base, &lock_context);
524  ctx->modem_control &= ~SP_MODEM_RTS;
525  (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
526  rtems_termios_device_lock_release(base, &lock_context);
527}
528
529/*
530 * These flow control routines utilise a connection from the local DTR
531 * line to the remote CTS line
532 */
533
534/*
535 *  ns16550_assert_DTR
536 */
537
538static void ns16550_assert_DTR(rtems_termios_device_context *base)
539{
540  ns16550_context             *ctx = (ns16550_context *) base;
541  rtems_interrupt_lock_context lock_context;
542
543  /*
544   * Assert DTR
545   */
546  rtems_termios_device_lock_acquire(base, &lock_context);
547  ctx->modem_control |= SP_MODEM_DTR;
548  (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
549  rtems_termios_device_lock_release(base, &lock_context);
550}
551
552/*
553 *  ns16550_negate_DTR
554 */
555
556static void ns16550_negate_DTR(rtems_termios_device_context *base)
557{
558  ns16550_context             *ctx = (ns16550_context *) base;
559  rtems_interrupt_lock_context lock_context;
560
561  /*
562   * Negate DTR
563   */
564  rtems_termios_device_lock_acquire(base, &lock_context);
565  ctx->modem_control &=~SP_MODEM_DTR;
566  (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL,ctx->modem_control);
567  rtems_termios_device_lock_release(base, &lock_context);
568}
569
570/*
571 *  ns16550_set_attributes
572 *
573 *  This function sets the channel to reflect the requested termios
574 *  port settings.
575 */
576
577static bool ns16550_set_attributes(
578  rtems_termios_device_context *base,
579  const struct termios         *t
580)
581{
582  ns16550_context        *ctx = (ns16550_context *) base;
583  uint32_t                pNS16550;
584  uint32_t                ulBaudDivisor;
585  uint8_t                 ucLineControl;
586  uint32_t                baud_requested;
587  ns16550_set_reg         setReg;
588  rtems_interrupt_lock_context lock_context;
589
590  pNS16550 = ctx->port;
591  setReg   = ctx->set_reg;
592
593  /*
594   *  Calculate the baud rate divisor
595   *
596   *  Assert ensures there is no division by 0.
597   */
598
599  baud_requested = rtems_termios_baud_to_number(t->c_cflag);
600  _Assert( baud_requested != 0 );
601
602  ulBaudDivisor = NS16550_GetBaudDivisor(ctx, baud_requested);
603
604  ucLineControl = 0;
605
606  /*
607   *  Parity
608   */
609
610  if (t->c_cflag & PARENB) {
611    ucLineControl |= SP_LINE_PAR;
612    if (!(t->c_cflag & PARODD))
613      ucLineControl |= SP_LINE_ODD;
614  }
615
616  /*
617   *  Character Size
618   */
619
620  if (t->c_cflag & CSIZE) {
621    switch (t->c_cflag & CSIZE) {
622      case CS5:  ucLineControl |= FIVE_BITS;  break;
623      case CS6:  ucLineControl |= SIX_BITS;   break;
624      case CS7:  ucLineControl |= SEVEN_BITS; break;
625      case CS8:  ucLineControl |= EIGHT_BITS; break;
626    }
627  } else {
628    ucLineControl |= EIGHT_BITS;               /* default to 9600,8,N,1 */
629  }
630
631  /*
632   *  Stop Bits
633   */
634
635  if (t->c_cflag & CSTOPB) {
636    ucLineControl |= SP_LINE_STOP;              /* 2 stop bits */
637  } else {
638    ;                                           /* 1 stop bit */
639  }
640
641  /*
642   *  Now actually set the chip
643   */
644
645  rtems_termios_device_lock_acquire(base, &lock_context);
646
647    /*
648     *  Set the baud rate
649     *
650     *  NOTE: When the Divisor Latch Access Bit (DLAB) is set to 1,
651     *        the transmit buffer and interrupt enable registers
652     *        turn into the LSB and MSB divisor latch registers.
653     */
654
655    (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
656    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
657    (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
658
659    /*
660     *  Now write the line control
661     */
662    (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
663
664  rtems_termios_device_lock_release(base, &lock_context);
665
666  return true;
667}
668
669/**
670 * @brief Transmits up to @a len characters from @a buf.
671 *
672 * This routine is invoked either from task context with disabled interrupts to
673 * start a new transmission process with exactly one character in case of an
674 * idle output state or from the interrupt handler to refill the transmitter.
675 *
676 * Returns always zero.
677 */
678static void ns16550_write_support_int(
679  rtems_termios_device_context *base,
680  const char                   *buf,
681  size_t                        len
682)
683{
684  ns16550_context *ctx = (ns16550_context *) base;
685
686  ctx->out_total = len;
687
688  if (len > 0) {
689    ctx->out_remaining = len;
690    ctx->out_buf = buf;
691    ctx->out_current = ns16550_write_to_fifo(ctx, buf, len);
692
693    ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR);
694  } else {
695    ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
696  }
697}
698
699static void ns16550_write_support_task(
700  rtems_termios_device_context *base,
701  const char                   *buf,
702  size_t                        len
703)
704{
705  ns16550_context *ctx = (ns16550_context *) base;
706
707  ctx->out_total = len;
708
709  if (len > 0) {
710    ctx->out_remaining = len;
711    ctx->out_buf = buf;
712    ctx->out_current = ns16550_write_to_fifo(ctx, buf, len);
713
714    ns16550_clear_and_set_interrupts(ctx, 0, SP_INT_TX_ENABLE);
715  }
716}
717
718/*
719 *  ns16550_write_support_polled
720 *
721 *  Console Termios output entry point.
722 *
723 */
724
725static void ns16550_write_support_polled(
726  rtems_termios_device_context *base,
727  const char                   *buf,
728  size_t                        len
729)
730{
731  size_t nwrite = 0;
732
733  /*
734   * poll each byte in the string out of the port.
735   */
736  while (nwrite < len) {
737    /*
738     * transmit character
739     */
740    ns16550_polled_putchar(base, *buf++);
741    nwrite++;
742  }
743}
744
745/*
746 *  Debug gets() support
747 */
748int ns16550_polled_getchar(rtems_termios_device_context *base)
749{
750  ns16550_context     *ctx = (ns16550_context *) base;
751  uint32_t             pNS16550;
752  unsigned char        ucLineStatus;
753  uint8_t              cChar;
754  ns16550_get_reg      getReg;
755
756  pNS16550 = ctx->port;
757  getReg   = ctx->get_reg;
758
759  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
760  if (ucLineStatus & SP_LSR_RDY) {
761    cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
762    return (int)cChar;
763  }
764  return -1;
765}
766
767/*
768 * Flow control is only supported when using interrupts
769 */
770
771const rtems_termios_device_flow ns16550_flow_rtscts = {
772  .stop_remote_tx = ns16550_negate_RTS,
773  .start_remote_tx = ns16550_assert_RTS
774};
775
776const rtems_termios_device_flow ns16550_flow_dtrcts = {
777  .stop_remote_tx = ns16550_negate_DTR,
778  .start_remote_tx = ns16550_assert_DTR
779};
780
781const rtems_termios_device_handler ns16550_handler_interrupt = {
782  .first_open = ns16550_open,
783  .last_close = ns16550_close,
784  .poll_read = NULL,
785  .write = ns16550_write_support_int,
786  .set_attributes = ns16550_set_attributes,
787  .mode = TERMIOS_IRQ_DRIVEN
788};
789
790const rtems_termios_device_handler ns16550_handler_polled = {
791  .first_open = ns16550_open,
792  .last_close = ns16550_close,
793  .poll_read = ns16550_polled_getchar,
794  .write = ns16550_write_support_polled,
795  .set_attributes = ns16550_set_attributes,
796  .mode = TERMIOS_POLLED
797};
798
799const rtems_termios_device_handler ns16550_handler_task = {
800  .first_open = ns16550_open,
801  .last_close = ns16550_close,
802  .poll_read = ns16550_read_task,
803  .write = ns16550_write_support_task,
804  .set_attributes = ns16550_set_attributes,
805  .mode = TERMIOS_TASK_DRIVEN
806};
Note: See TracBrowser for help on using the repository browser.