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

4.115
Last change on this file since 02958c5e was 02958c5e, checked in by Josh Oguin <josh.oguin@…>, on Nov 19, 2014 at 8:28:08 PM

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: 21.6 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.h>
36#include <rtems/libio.h>
37#include <rtems/ringbuf.h>
38#include <rtems/bspIo.h>
39#include <rtems/termiostypes.h>
40
41#include <libchip/serial.h>
42#include <libchip/sersupp.h>
43
44#include <bsp.h>
45
46#include "ns16550_p.h"
47#include "ns16550.h"
48
49#if defined(BSP_FEATURE_IRQ_EXTENSION)
50  #include <bsp/irq.h>
51#elif defined(BSP_FEATURE_IRQ_LEGACY)
52  #include <bsp/irq.h>
53#elif defined(__PPC__) || defined(__i386__)
54  #include <bsp/irq.h>
55  #define BSP_FEATURE_IRQ_LEGACY
56  #ifdef BSP_SHARED_HANDLER_SUPPORT
57    #define BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
58  #endif
59#endif
60
61typedef struct {
62  uint8_t ucModemCtrl;
63  int transmitFifoChars;
64} NS16550Context;
65
66/*
67 * Driver functions
68 */
69
70NS16550_STATIC void ns16550_init(int minor);
71
72NS16550_STATIC int ns16550_open(
73  int major,
74  int minor,
75  void  * arg
76);
77
78NS16550_STATIC int ns16550_close(
79  int major,
80  int minor,
81  void  * arg
82);
83
84NS16550_STATIC void ns16550_write_polled(
85  int   minor,
86  char  cChar
87);
88
89NS16550_STATIC int ns16550_assert_RTS(
90  int minor
91);
92
93NS16550_STATIC int ns16550_negate_RTS(
94  int minor
95);
96
97NS16550_STATIC int ns16550_assert_DTR(
98  int minor
99);
100
101NS16550_STATIC int ns16550_negate_DTR(
102  int minor
103);
104
105NS16550_STATIC void ns16550_initialize_interrupts(int minor);
106
107NS16550_STATIC void ns16550_cleanup_interrupts(int minor);
108
109NS16550_STATIC ssize_t ns16550_write_support_int(
110  int   minor,
111  const char *buf,
112  size_t len
113);
114
115NS16550_STATIC ssize_t ns16550_write_support_polled(
116  int   minor,
117  const char *buf,
118  size_t len
119  );
120
121int ns16550_inbyte_nonblocking_polled(
122  int minor
123);
124
125NS16550_STATIC void ns16550_enable_interrupts(
126  console_tbl *c,
127  int         mask
128);
129
130NS16550_STATIC int ns16550_set_attributes(
131  int                   minor,
132  const struct termios *t
133);
134
135#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
136  NS16550_STATIC void ns16550_isr(void *arg);
137#endif
138
139static rtems_interrupt_lock ns16550_lock =
140  RTEMS_INTERRUPT_LOCK_INITIALIZER("NS16550");
141
142/*
143 * Flow control is only supported when using interrupts
144 */
145
146const console_flow ns16550_flow_RTSCTS = {
147  ns16550_negate_RTS,             /* deviceStopRemoteTx */
148  ns16550_assert_RTS              /* deviceStartRemoteTx */
149};
150
151const console_flow ns16550_flow_DTRCTS = {
152  ns16550_negate_DTR,             /* deviceStopRemoteTx */
153  ns16550_assert_DTR              /* deviceStartRemoteTx */
154};
155
156const console_fns ns16550_fns = {
157  libchip_serial_default_probe,   /* deviceProbe */
158  ns16550_open,                   /* deviceFirstOpen */
159  ns16550_close,                  /* deviceLastClose */
160  NULL,                           /* deviceRead */
161  ns16550_write_support_int,      /* deviceWrite */
162  ns16550_init,                   /* deviceInitialize */
163  ns16550_write_polled,           /* deviceWritePolled */
164  ns16550_set_attributes,         /* deviceSetAttributes */
165  true                            /* deviceOutputUsesInterrupts */
166};
167
168const console_fns ns16550_fns_polled = {
169  libchip_serial_default_probe,        /* deviceProbe */
170  ns16550_open,                        /* deviceFirstOpen */
171  ns16550_close,                       /* deviceLastClose */
172  ns16550_inbyte_nonblocking_polled,   /* deviceRead */
173  ns16550_write_support_polled,        /* deviceWrite */
174  ns16550_init,                        /* deviceInitialize */
175  ns16550_write_polled,                /* deviceWritePolled */
176  ns16550_set_attributes,              /* deviceSetAttributes */
177  false                                /* deviceOutputUsesInterrupts */
178};
179
180static uint32_t NS16550_GetBaudDivisor(const console_tbl *c, uint32_t baud)
181{
182  uint32_t clock = c->ulClock;
183  uint32_t baudDivisor = (clock != 0 ? clock : 115200) / (baud * 16);
184
185  if (c->deviceType == SERIAL_NS16550_WITH_FDR) {
186    uint32_t fractionalDivider = 0x10;
187    uint32_t err = baud;
188    uint32_t mulVal;
189    uint32_t divAddVal;
190
191    clock /= 16 * baudDivisor;
192    for (mulVal = 1; mulVal < 16; ++mulVal) {
193      for (divAddVal = 0; divAddVal < mulVal; ++divAddVal) {
194        uint32_t actual = (mulVal * clock) / (mulVal + divAddVal);
195        uint32_t newErr = actual > baud ? actual - baud : baud - actual;
196
197        if (newErr < err) {
198          err = newErr;
199          fractionalDivider = (mulVal << 4) | divAddVal;
200        }
201      }
202    }
203
204    (*c->setRegister)(
205      c->ulCtrlPort1,
206      NS16550_FRACTIONAL_DIVIDER,
207      fractionalDivider
208    );
209  }
210
211  return baudDivisor;
212}
213
214/*
215 *  ns16550_init
216 */
217
218void ns16550_init(int minor)
219{
220  uintptr_t               pNS16550;
221  uint8_t                 ucDataByte;
222  uint32_t                ulBaudDivisor;
223  NS16550Context        *pns16550Context;
224  setRegister_f           setReg;
225  getRegister_f           getReg;
226  console_tbl             *c = Console_Port_Tbl [minor];
227
228  pns16550Context=(NS16550Context *)malloc(sizeof(NS16550Context));
229
230  if (pns16550Context == NULL) {
231    printk( "%s: Error: Not enough memory\n", __func__);
232    rtems_fatal_error_occurred( 0xdeadbeef);
233  }
234
235  Console_Port_Data[minor].pDeviceContext=(void *)pns16550Context;
236  pns16550Context->ucModemCtrl=SP_MODEM_IRQ;
237
238  pNS16550 = c->ulCtrlPort1;   
239  setReg   = c->setRegister;
240  getReg   = c->getRegister;
241
242  /* Clear the divisor latch, clear all interrupt enables,
243   * and reset and
244   * disable the FIFO's.
245   */
246
247  (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
248  ns16550_enable_interrupts( c, NS16550_DISABLE_ALL_INTR );
249
250  /* Set the divisor latch and set the baud rate. */
251
252  ulBaudDivisor = NS16550_GetBaudDivisor(c, (uintptr_t) c->pDeviceParams);
253  ucDataByte = SP_LINE_DLAB;
254  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
255
256  /* XXX */
257  (*setReg)(pNS16550,NS16550_TRANSMIT_BUFFER,(uint8_t)(ulBaudDivisor & 0xffU));
258  (*setReg)(
259    pNS16550,NS16550_INTERRUPT_ENABLE,
260    (uint8_t)(( ulBaudDivisor >> 8 ) & 0xffU )
261  );
262
263  /* Clear the divisor latch and set the character size to eight bits */
264  /* with one stop bit and no parity checking. */
265  ucDataByte = EIGHT_BITS;
266  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
267
268  /* Enable and reset transmit and receive FIFOs. TJA     */
269  ucDataByte = SP_FIFO_ENABLE;
270  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
271
272  ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
273  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
274
275  ns16550_enable_interrupts(c, NS16550_DISABLE_ALL_INTR);
276
277  /* Set data terminal ready. */
278  /* And open interrupt tristate line */
279  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
280
281  (*getReg)(pNS16550, NS16550_LINE_STATUS );
282  (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
283}
284
285/*
286 *  ns16550_open
287 */
288
289int ns16550_open(
290  int major,
291  int minor,
292  void *arg
293)
294{
295  rtems_libio_open_close_args_t *oc = (rtems_libio_open_close_args_t *) arg;
296  struct rtems_termios_tty *tty = (struct rtems_termios_tty *) oc->iop->data1;
297  console_tbl *c = Console_Port_Tbl [minor];
298  console_data *d = &Console_Port_Data [minor];
299
300  d->termios_data = tty;
301
302  /* Assert DTR */
303  if (c->pDeviceFlow != &ns16550_flow_DTRCTS) {
304    ns16550_assert_DTR( minor);
305  }
306
307  /* Set initial baud */
308  rtems_termios_set_initial_baud( tty, (intptr_t) c->pDeviceParams);
309
310  if (c->pDeviceFns->deviceOutputUsesInterrupts) {
311    ns16550_initialize_interrupts( minor);
312    ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
313  }
314
315  return RTEMS_SUCCESSFUL;
316}
317
318/*
319 *  ns16550_close
320 */
321
322int ns16550_close(
323  int      major,
324  int      minor,
325  void    * arg
326)
327{
328  console_tbl *c = Console_Port_Tbl [minor];
329
330  /*
331   * Negate DTR
332   */
333  if (c->pDeviceFlow != &ns16550_flow_DTRCTS) {
334    ns16550_negate_DTR(minor);
335  }
336
337  ns16550_enable_interrupts(c, NS16550_DISABLE_ALL_INTR);
338
339  if (c->pDeviceFns->deviceOutputUsesInterrupts) {
340    ns16550_cleanup_interrupts(minor);
341  }
342
343  return(RTEMS_SUCCESSFUL);
344}
345
346/**
347 * @brief Polled write for NS16550.
348 */
349void ns16550_outch_polled(console_tbl *c, char out)
350{
351  uintptr_t port = c->ulCtrlPort1;
352  getRegister_f get = c->getRegister;
353  setRegister_f set = c->setRegister;
354  uint32_t status = 0;
355  rtems_interrupt_lock_context lock_context;
356
357  /* Save port interrupt mask */
358  uint32_t interrupt_mask = get( port, NS16550_INTERRUPT_ENABLE);
359
360  /* Disable port interrupts */
361  ns16550_enable_interrupts( c, NS16550_DISABLE_ALL_INTR);
362
363  while (true) {
364    /* Try to transmit the character in a critical section */
365    rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
366
367    /* Read the transmitter holding register and check it */
368    status = get( port, NS16550_LINE_STATUS);
369    if ((status & SP_LSR_THOLD) != 0) {
370      /* Transmit character */
371      set( port, NS16550_TRANSMIT_BUFFER, out);
372
373      /* Finished */
374      rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
375      break;
376    } else {
377      rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
378    }
379
380    /* Wait for transmitter holding register to be empty */
381    do {
382      status = get( port, NS16550_LINE_STATUS);
383    } while ((status & SP_LSR_THOLD) == 0);
384  }
385
386  /* Restore port interrupt mask */
387  set( port, NS16550_INTERRUPT_ENABLE, interrupt_mask);
388}
389
390void ns16550_write_polled(int minor, char out)
391{
392  console_tbl *c = Console_Port_Tbl [minor];
393 
394  ns16550_outch_polled( c, out );
395}
396
397/*
398 * These routines provide control of the RTS and DTR lines
399 */
400
401/*
402 *  ns16550_assert_RTS
403 */
404
405NS16550_STATIC int ns16550_assert_RTS(int minor)
406{
407  uint32_t                pNS16550;
408  rtems_interrupt_lock_context lock_context;
409  NS16550Context        *pns16550Context;
410  setRegister_f           setReg;
411
412  pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
413
414  pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
415  setReg   = Console_Port_Tbl[minor]->setRegister;
416
417  /*
418   * Assert RTS
419   */
420  rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
421  pns16550Context->ucModemCtrl|=SP_MODEM_RTS;
422  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
423  rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
424  return 0;
425}
426
427/*
428 *  ns16550_negate_RTS
429 */
430
431NS16550_STATIC int ns16550_negate_RTS(int minor)
432{
433  uint32_t                pNS16550;
434  rtems_interrupt_lock_context lock_context;
435  NS16550Context        *pns16550Context;
436  setRegister_f           setReg;
437
438  pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
439
440  pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
441  setReg   = Console_Port_Tbl[minor]->setRegister;
442
443  /*
444   * Negate RTS
445   */
446  rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
447  pns16550Context->ucModemCtrl&=~SP_MODEM_RTS;
448  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
449  rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
450  return 0;
451}
452
453/*
454 * These flow control routines utilise a connection from the local DTR
455 * line to the remote CTS line
456 */
457
458/*
459 *  ns16550_assert_DTR
460 */
461
462NS16550_STATIC int ns16550_assert_DTR(int minor)
463{
464  uint32_t                pNS16550;
465  rtems_interrupt_lock_context lock_context;
466  NS16550Context        *pns16550Context;
467  setRegister_f           setReg;
468
469  pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
470
471  pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
472  setReg   = Console_Port_Tbl[minor]->setRegister;
473
474  /*
475   * Assert DTR
476   */
477  rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
478  pns16550Context->ucModemCtrl|=SP_MODEM_DTR;
479  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
480  rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
481  return 0;
482}
483
484/*
485 *  ns16550_negate_DTR
486 */
487
488NS16550_STATIC int ns16550_negate_DTR(int minor)
489{
490  uint32_t                pNS16550;
491  rtems_interrupt_lock_context lock_context;
492  NS16550Context        *pns16550Context;
493  setRegister_f           setReg;
494
495  pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
496
497  pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
498  setReg   = Console_Port_Tbl[minor]->setRegister;
499
500  /*
501   * Negate DTR
502   */
503  rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
504  pns16550Context->ucModemCtrl&=~SP_MODEM_DTR;
505  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
506  rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
507  return 0;
508}
509
510/*
511 *  ns16550_set_attributes
512 *
513 *  This function sets the channel to reflect the requested termios
514 *  port settings.
515 */
516
517int ns16550_set_attributes(
518  int                   minor,
519  const struct termios *t
520)
521{
522  uint32_t                pNS16550;
523  uint32_t                ulBaudDivisor;
524  uint8_t                 ucLineControl;
525  uint32_t                baud_requested;
526  setRegister_f           setReg;
527  rtems_interrupt_lock_context lock_context;
528  const console_tbl      *c = Console_Port_Tbl [minor];
529
530  pNS16550 = c->ulCtrlPort1;
531  setReg   = c->setRegister;
532
533  /*
534   *  Calculate the baud rate divisor
535   *
536   *  Assert ensures there is no division by 0.
537   */
538
539  baud_requested = rtems_termios_baud_to_number(t->c_cflag);
540  _Assert( baud_requested != 0 );
541  ulBaudDivisor = NS16550_GetBaudDivisor(c, baud_requested);
542
543  ucLineControl = 0;
544
545  /*
546   *  Parity
547   */
548
549  if (t->c_cflag & PARENB) {
550    ucLineControl |= SP_LINE_PAR;
551    if (!(t->c_cflag & PARODD))
552      ucLineControl |= SP_LINE_ODD;
553  }
554
555  /*
556   *  Character Size
557   */
558
559  if (t->c_cflag & CSIZE) {
560    switch (t->c_cflag & CSIZE) {
561      case CS5:  ucLineControl |= FIVE_BITS;  break;
562      case CS6:  ucLineControl |= SIX_BITS;   break;
563      case CS7:  ucLineControl |= SEVEN_BITS; break;
564      case CS8:  ucLineControl |= EIGHT_BITS; break;
565    }
566  } else {
567    ucLineControl |= EIGHT_BITS;               /* default to 9600,8,N,1 */
568  }
569
570  /*
571   *  Stop Bits
572   */
573
574  if (t->c_cflag & CSTOPB) {
575    ucLineControl |= SP_LINE_STOP;              /* 2 stop bits */
576  } else {
577    ;                                           /* 1 stop bit */
578  }
579
580  /*
581   *  Now actually set the chip
582   */
583
584  rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
585
586    /*
587     *  Set the baud rate
588     *
589     *  NOTE: When the Divisor Latch Access Bit (DLAB) is set to 1,
590     *        the transmit buffer and interrupt enable registers
591     *        turn into the LSB and MSB divisor latch registers.
592     */
593
594    (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
595    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
596    (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
597
598    /*
599     *  Now write the line control
600     */
601    (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
602
603  rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
604
605  return 0;
606}
607
608#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
609
610/**
611 * @brief Process interrupt.
612 */
613NS16550_STATIC void ns16550_process( int minor)
614{
615  console_tbl *c = Console_Port_Tbl [minor];
616  console_data *d = &Console_Port_Data [minor];
617  NS16550Context *ctx = d->pDeviceContext;
618  uint32_t port = c->ulCtrlPort1;
619  getRegister_f get = c->getRegister;
620  int i = 0;
621  char buf [SP_FIFO_SIZE];
622
623  /* Iterate until no more interrupts are pending */
624  do {
625    /* Fetch received characters */
626    for (i = 0; i < SP_FIFO_SIZE; ++i) {
627      if ((get( port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
628        buf [i] = (char) get(port, NS16550_RECEIVE_BUFFER);
629      } else {
630        break;
631      }
632    }
633
634    /* Enqueue fetched characters */
635    rtems_termios_enqueue_raw_characters( d->termios_data, buf, i);
636
637    /* Check if we can dequeue transmitted characters */
638    if (ctx->transmitFifoChars > 0
639        && (get( port, NS16550_LINE_STATUS) & SP_LSR_THOLD) != 0) {
640
641      /* Dequeue transmitted characters */
642      rtems_termios_dequeue_characters(
643        d->termios_data,
644        ctx->transmitFifoChars
645      );
646    }
647  } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0);
648}
649#endif
650
651/**
652 * @brief Transmits up to @a len characters from @a buf.
653 *
654 * This routine is invoked either from task context with disabled interrupts to
655 * start a new transmission process with exactly one character in case of an
656 * idle output state or from the interrupt handler to refill the transmitter.
657 *
658 * Returns always zero.
659 */
660ssize_t ns16550_write_support_int(
661  int minor,
662  const char *buf,
663  size_t len
664)
665{
666  console_tbl *c = Console_Port_Tbl [minor];
667  console_data *d = &Console_Port_Data [minor];
668  NS16550Context *ctx = d->pDeviceContext;
669  uint32_t port = c->ulCtrlPort1;
670  setRegister_f set = c->setRegister;
671  int i = 0;
672  int out = len > SP_FIFO_SIZE ? SP_FIFO_SIZE : len;
673
674  for (i = 0; i < out; ++i) {
675    set( port, NS16550_TRANSMIT_BUFFER, buf [i]);
676  }
677
678  ctx->transmitFifoChars = out;
679
680  if (out > 0) {
681    ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR);
682  } else {
683    ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
684  }
685
686  return 0;
687}
688
689/*
690 *  ns16550_enable_interrupts
691 *
692 *  This routine initializes the port to have the specified interrupts masked.
693 */
694NS16550_STATIC void ns16550_enable_interrupts(
695  console_tbl *c,
696  int         mask
697)
698{
699  uint32_t       pNS16550;
700  setRegister_f  setReg;
701
702  pNS16550 = c->ulCtrlPort1;
703  setReg   = c->setRegister;
704
705  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
706}
707
708#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
709  void ns16550_isr(void *arg)
710  {
711    int minor = (int) arg;
712
713    ns16550_process( minor);
714  }
715#endif
716
717/*
718 *  ns16550_initialize_interrupts
719 *
720 *  This routine initializes the port to operate in interrupt driver mode.
721 */
722NS16550_STATIC void ns16550_initialize_interrupts( int minor)
723{
724#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
725  console_tbl *c = Console_Port_Tbl [minor];
726#endif
727
728  #ifdef BSP_FEATURE_IRQ_EXTENSION
729    {
730      rtems_status_code sc = RTEMS_SUCCESSFUL;
731      sc = rtems_interrupt_handler_install(
732        c->ulIntVector,
733        "NS16550",
734        RTEMS_INTERRUPT_SHARED,
735        ns16550_isr,
736        (void *) minor
737      );
738      if (sc != RTEMS_SUCCESSFUL) {
739        /* FIXME */
740        printk( "%s: Error: Install interrupt handler\n", __func__);
741        rtems_fatal_error_occurred( 0xdeadbeef);
742      }
743    }
744  #elif defined(BSP_FEATURE_IRQ_LEGACY)
745    {
746      int rv = 0;
747      #ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
748        rtems_irq_connect_data cd = {
749          c->ulIntVector,
750          ns16550_isr,
751          (void *) minor,
752          NULL,
753          NULL,
754          NULL,
755          NULL
756        };
757        rv = BSP_install_rtems_shared_irq_handler( &cd);
758      #else
759        rtems_irq_connect_data cd = {
760          c->ulIntVector,
761          ns16550_isr,
762          (void *) minor,
763          NULL,
764          NULL,
765          NULL
766        };
767        rv = BSP_install_rtems_irq_handler( &cd);
768      #endif
769      if (rv == 0) {
770        /* FIXME */
771        printk( "%s: Error: Install interrupt handler\n", __func__);
772        rtems_fatal_error_occurred( 0xdeadbeef);
773      }
774    }
775  #endif
776}
777
778NS16550_STATIC void ns16550_cleanup_interrupts(int minor)
779{
780  #if defined(BSP_FEATURE_IRQ_EXTENSION)
781    rtems_status_code sc = RTEMS_SUCCESSFUL;
782    console_tbl *c = Console_Port_Tbl [minor];
783    sc = rtems_interrupt_handler_remove(
784      c->ulIntVector,
785      ns16550_isr,
786      (void *) minor
787    );
788    if (sc != RTEMS_SUCCESSFUL) {
789      /* FIXME */
790      printk("%s: Error: Remove interrupt handler\n", __func__);
791      rtems_fatal_error_occurred(0xdeadbeef);
792    }
793  #elif defined(BSP_FEATURE_IRQ_LEGACY)
794    int rv = 0;
795    console_tbl *c = Console_Port_Tbl [minor];
796    rtems_irq_connect_data cd = {
797      .name = c->ulIntVector,
798      .hdl = ns16550_isr,
799      .handle = (void *) minor
800    };
801    rv = BSP_remove_rtems_irq_handler(&cd);
802    if (rv == 0) {
803      /* FIXME */
804      printk("%s: Error: Remove interrupt handler\n", __func__);
805      rtems_fatal_error_occurred(0xdeadbeef);
806    }
807  #endif
808}
809
810/*
811 *  ns16550_write_support_polled
812 *
813 *  Console Termios output entry point.
814 *
815 */
816
817ssize_t ns16550_write_support_polled(
818  int         minor,
819  const char *buf,
820  size_t      len
821)
822{
823  int nwrite = 0;
824
825  /*
826   * poll each byte in the string out of the port.
827   */
828  while (nwrite < len) {
829    /*
830     * transmit character
831     */
832    ns16550_write_polled(minor, *buf++);
833    nwrite++;
834  }
835
836  /*
837   * return the number of bytes written.
838   */
839  return nwrite;
840}
841
842/*
843 *  Debug gets() support
844 */
845int ns16550_inch_polled(
846  console_tbl *c
847)
848{
849  uint32_t             pNS16550;
850  unsigned char        ucLineStatus;
851  uint8_t              cChar;
852  getRegister_f        getReg;
853
854  pNS16550 = c->ulCtrlPort1;
855  getReg   = c->getRegister;
856
857  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
858  if (ucLineStatus & SP_LSR_RDY) {
859    cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
860    return (int)cChar;
861  }
862  return -1;
863}
864
865/*
866 *  ns16550_inbyte_nonblocking_polled
867 *
868 *  Console Termios polling input entry point.
869 */
870int ns16550_inbyte_nonblocking_polled(int minor)
871{
872  console_tbl *c = Console_Port_Tbl [minor];
873 
874  return ns16550_inch_polled( c );
875}
Note: See TracBrowser for help on using the repository browser.