/** * @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; }
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; }
/** * @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); }
/** * @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); }
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); }
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 ); }