Пример #1
0
/**
 * @brief Transmits up to @a len characters from @a buf.
 *
 * This routine is invoked either from task context with disabled interrupts to
 * start a new transmission process with exactly one character in case of an
 * idle output state or from the interrupt handler to refill the transmitter.
 *
 * Returns always zero.
 */
ssize_t ns16550_write_support_int(
  int minor,
  const char *buf,
  size_t len
)
{
  console_tbl *c = Console_Port_Tbl [minor];
  console_data *d = &Console_Port_Data [minor];
  NS16550Context *ctx = d->pDeviceContext;
  uint32_t port = c->ulCtrlPort1;
  setRegister_f set = c->setRegister;
  int i = 0;
  int out = len > SP_FIFO_SIZE ? SP_FIFO_SIZE : len;

  for (i = 0; i < out; ++i) {
    set( port, NS16550_TRANSMIT_BUFFER, buf [i]);
  }

  ctx->transmitFifoChars = out;

  if (out > 0) {
    ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR);
  } else {
    ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
  }

  return 0;
}
Пример #2
0
int ns16550_open(
  int major,
  int minor,
  void *arg
)
{
  rtems_libio_open_close_args_t *oc = (rtems_libio_open_close_args_t *) arg;
  struct rtems_termios_tty *tty = (struct rtems_termios_tty *) oc->iop->data1;
  console_tbl *c = Console_Port_Tbl [minor];
  console_data *d = &Console_Port_Data [minor];

  d->termios_data = tty;

  /* Assert DTR */
  if (c->pDeviceFlow != &ns16550_flow_DTRCTS) {
    ns16550_assert_DTR( minor);
  }

  /* Set initial baud */
  rtems_termios_set_initial_baud( tty, (intptr_t) c->pDeviceParams);

  if (c->pDeviceFns->deviceOutputUsesInterrupts) {
    ns16550_initialize_interrupts( minor);
    ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
  }

  return RTEMS_SUCCESSFUL;
}
Пример #3
0
/**
 * @brief Process interrupt.
 */
NS16550_STATIC void ns16550_process( int minor)
{
  console_tbl *c = Console_Port_Tbl [minor];
  console_data *d = &Console_Port_Data [minor];
  ns16550_context *ctx = d->pDeviceContext;
  uint32_t port = c->ulCtrlPort1;
  getRegister_f get = c->getRegister;
  int i = 0;
  char buf [SP_FIFO_SIZE];

  /* Iterate until no more interrupts are pending */
  do {
    /* Fetch received characters */
    for (i = 0; i < SP_FIFO_SIZE; ++i) {
      if ((get( port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
        buf [i] = (char) get(port, NS16550_RECEIVE_BUFFER);
      } else {
        break;
      }
    }

    /* Enqueue fetched characters */
    rtems_termios_enqueue_raw_characters( d->termios_data, buf, i);

    /* Check if we can dequeue transmitted characters */
    if (ctx->transmitFifoChars > 0
        && (get( port, NS16550_LINE_STATUS) & SP_LSR_THOLD) != 0) {
      unsigned chars = ctx->transmitFifoChars;

      /*
       * We finished the transmission, so clear the number of characters in the
       * transmit FIFO.
       */
      ctx->transmitFifoChars = 0;

      /* Dequeue transmitted characters */
      if (rtems_termios_dequeue_characters( d->termios_data, chars) == 0) {
        /* Nothing to do */
        d->bActive = false;
        ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
      }
    }
  } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0);
}
Пример #4
0
/**
 * @brief Polled write for NS16550.
 */
void ns16550_outch_polled(console_tbl *c, char out)
{
  uintptr_t port = c->ulCtrlPort1;
  getRegister_f get = c->getRegister;
  setRegister_f set = c->setRegister;
  uint32_t status = 0;
  rtems_interrupt_lock_context lock_context;

  /* Save port interrupt mask */
  uint32_t interrupt_mask = get( port, NS16550_INTERRUPT_ENABLE);

  /* Disable port interrupts */
  ns16550_enable_interrupts( c, NS16550_DISABLE_ALL_INTR);

  while (true) {
    /* Try to transmit the character in a critical section */
    rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);

    /* Read the transmitter holding register and check it */
    status = get( port, NS16550_LINE_STATUS);
    if ((status & SP_LSR_THOLD) != 0) {
      /* Transmit character */
      set( port, NS16550_TRANSMIT_BUFFER, out);

      /* Finished */
      rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
      break;
    } else {
      rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
    }

    /* Wait for transmitter holding register to be empty */
    do {
      status = get( port, NS16550_LINE_STATUS);
    } while ((status & SP_LSR_THOLD) == 0);
  }

  /* Restore port interrupt mask */
  set( port, NS16550_INTERRUPT_ENABLE, interrupt_mask);
}
Пример #5
0
int ns16550_close(
  int      major,
  int      minor,
  void    * arg
)
{
  console_tbl *c = Console_Port_Tbl [minor];

  /*
   * Negate DTR
   */
  if (c->pDeviceFlow != &ns16550_flow_DTRCTS) {
    ns16550_negate_DTR(minor);
  }

  ns16550_enable_interrupts(c, NS16550_DISABLE_ALL_INTR);

  if (c->pDeviceFns->deviceOutputUsesInterrupts) {
    ns16550_cleanup_interrupts(minor);
  }

  return(RTEMS_SUCCESSFUL);
}
Пример #6
0
void ns16550_init(int minor)
{
  uintptr_t               pNS16550;
  uint8_t                 ucDataByte;
  uint32_t                ulBaudDivisor;
  NS16550Context        *pns16550Context;
  setRegister_f           setReg;
  getRegister_f           getReg;
  console_tbl             *c = Console_Port_Tbl [minor];

  pns16550Context=(NS16550Context *)malloc(sizeof(NS16550Context));

  if (pns16550Context == NULL) {
    printk( "%s: Error: Not enough memory\n", __func__);
    rtems_fatal_error_occurred( 0xdeadbeef);
  }

  Console_Port_Data[minor].pDeviceContext=(void *)pns16550Context;
  pns16550Context->ucModemCtrl=SP_MODEM_IRQ;

  pNS16550 = c->ulCtrlPort1;
  setReg   = c->setRegister;
  getReg   = c->getRegister;

  /* Clear the divisor latch, clear all interrupt enables,
   * and reset and
   * disable the FIFO's.
   */

  (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
  ns16550_enable_interrupts( c, NS16550_DISABLE_ALL_INTR );

  /* Set the divisor latch and set the baud rate. */

  ulBaudDivisor = NS16550_GetBaudDivisor(c, (uintptr_t) c->pDeviceParams);
  ucDataByte = SP_LINE_DLAB;
  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);

  /* XXX */
  (*setReg)(pNS16550,NS16550_TRANSMIT_BUFFER,(uint8_t)(ulBaudDivisor & 0xffU));
  (*setReg)(
    pNS16550,NS16550_INTERRUPT_ENABLE,
    (uint8_t)(( ulBaudDivisor >> 8 ) & 0xffU )
  );

  /* Clear the divisor latch and set the character size to eight bits */
  /* with one stop bit and no parity checking. */
  ucDataByte = EIGHT_BITS;
  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);

  /* Enable and reset transmit and receive FIFOs. TJA     */
  ucDataByte = SP_FIFO_ENABLE;
  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);

  ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);

  ns16550_enable_interrupts(c, NS16550_DISABLE_ALL_INTR);

  /* Set data terminal ready. */
  /* And open interrupt tristate line */
  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);

  (*getReg)(pNS16550, NS16550_LINE_STATUS );
  (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
}