/**
* @file
*
* This file contains the TTY driver for the National Semiconductor NS16550.
*
* This part is widely cloned and second sourced. It is found in a number
* of "Super IO" controllers.
*
* This driver uses the termios pseudo driver.
*/
/*
* COPYRIGHT (c) 1998 by Radstone Technology
*
* THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
* AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
*
* You are hereby granted permission to use, copy, modify, and distribute
* this file, provided that this notice, plus the above copyright notice
* and disclaimer, appears in all copies. Radstone Technology will provide
* no support for this code.
*
* COPYRIGHT (c) 1989-2012.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <stdlib.h>
#include <rtems/bspIo.h>
#include <bsp.h>
#include <libchip/ns16550.h>
#include <libchip/ns16550_p.h>
#if defined(BSP_FEATURE_IRQ_EXTENSION)
#include <bsp/irq.h>
#elif defined(BSP_FEATURE_IRQ_LEGACY)
#include <bsp/irq.h>
#elif defined(__PPC__) || defined(__i386__)
#include <bsp/irq.h>
#define BSP_FEATURE_IRQ_LEGACY
#ifdef BSP_SHARED_HANDLER_SUPPORT
#define BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
#endif
#endif
static uint32_t NS16550_GetBaudDivisor(ns16550_context *ctx, uint32_t baud)
{
uint32_t clock;
uint32_t baudDivisor;
uint32_t err;
uint32_t actual;
uint32_t newErr;
if (ctx->clock != 0) {
clock = ctx->clock;
} else {
clock = 115200;
}
baudDivisor = clock / (baud * 16);
if (ctx->has_precision_clock_synthesizer) {
uint32_t i;
err = baud;
baudDivisor = 0x0001ffff;
for (i = 2; i <= 0x10000; i *= 2) {
uint32_t fout;
uint32_t fin;
fin = i - 1;
fout = (baud * fin * 16) / clock;
actual = (clock * fout) / (16 * fin);
newErr = actual > baud ? actual - baud : baud - actual;
if (newErr < err) {
err = newErr;
baudDivisor = fin | (fout << 16);
}
}
} else if (ctx->has_fractional_divider_register) {
uint32_t fractionalDivider = 0x10;
uint32_t mulVal;
uint32_t divAddVal;
err = baud;
clock /= 16 * baudDivisor;
for (mulVal = 1; mulVal < 16; ++mulVal) {
for (divAddVal = 0; divAddVal < mulVal; ++divAddVal) {
actual = (mulVal * clock) / (mulVal + divAddVal);
newErr = actual > baud ? actual - baud : baud - actual;
if (newErr < err) {
err = newErr;
fractionalDivider = (mulVal << 4) | divAddVal;
}
}
}
(*ctx->set_reg)(
ctx->port,
NS16550_FRACTIONAL_DIVIDER,
fractionalDivider
);
} else if (ctx->calculate_baud_divisor != NULL) {
baudDivisor = ctx->calculate_baud_divisor(ctx, baud);
}
return baudDivisor;
}
/*
* ns16550_enable_interrupts
*
* This routine initializes the port to have the specified interrupts masked.
*/
static void ns16550_enable_interrupts(
ns16550_context *ctx,
int mask
)
{
(*ctx->set_reg)(ctx->port, NS16550_INTERRUPT_ENABLE, mask);
}
static void ns16550_clear_and_set_interrupts(
ns16550_context *ctx,
uint8_t clear,
uint8_t set
)
{
rtems_interrupt_lock_context lock_context;
ns16550_get_reg get_reg = ctx->get_reg;
ns16550_set_reg set_reg = ctx->set_reg;
uintptr_t port = ctx->port;
uint8_t val;
rtems_termios_device_lock_acquire(&ctx->base, &lock_context);
val = (*get_reg)(port, NS16550_INTERRUPT_ENABLE);
val &= ~clear;
val |= set;
(*set_reg)(port, NS16550_INTERRUPT_ENABLE, val);
rtems_termios_device_lock_release(&ctx->base, &lock_context);
}
/*
* ns16550_probe
*/
bool ns16550_probe(rtems_termios_device_context *base)
{
ns16550_context *ctx = (ns16550_context *) base;
uintptr_t pNS16550;
uint8_t ucDataByte;
uint32_t ulBaudDivisor;
ns16550_set_reg setReg;
ns16550_get_reg getReg;
ctx->modem_control = SP_MODEM_IRQ;
pNS16550 = ctx->port;
setReg = ctx->set_reg;
getReg = ctx->get_reg;
/* Clear the divisor latch, clear all interrupt enables,
* and reset and
* disable the FIFO's.
*/
(*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR );
/* Set the divisor latch and set the baud rate. */
ulBaudDivisor = NS16550_GetBaudDivisor(ctx, ctx->initial_baud);
ctx->baud_divisor = ulBaudDivisor;
ucDataByte = SP_LINE_DLAB;
(*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
/* XXX */
(*setReg)(pNS16550,NS16550_DIVISOR_LATCH_L,(uint8_t)(ulBaudDivisor & 0xffU));
(*setReg)(
pNS16550,NS16550_DIVISOR_LATCH_M,
(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;
ctx->line_control = ucDataByte;
if (ctx->has_precision_clock_synthesizer) {
uint8_t fcr;
/*
* Enable precision clock synthesizer. This must be done with DLAB == 1 in
* the line control register.
*/
fcr = (*getReg)(pNS16550, NS16550_FIFO_CONTROL );
fcr |= 0x10;
(*setReg)(pNS16550, NS16550_FIFO_CONTROL, fcr);
(*setReg)(pNS16550, NS16550_SCRATCH_PAD, (uint8_t)(ulBaudDivisor >> 24));
(*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte );
(*setReg)(pNS16550, NS16550_SCRATCH_PAD, (uint8_t)(ulBaudDivisor >> 16));
} else {
(*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(ctx, NS16550_DISABLE_ALL_INTR);
/* Set data terminal ready. */
/* And open interrupt tristate line */
(*setReg)(pNS16550, NS16550_MODEM_CONTROL,ctx->modem_control);
(*getReg)(pNS16550, NS16550_LINE_STATUS );
(*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
return true;
}
static size_t ns16550_write_to_fifo(
const ns16550_context *ctx,
const char *buf,
size_t len
)
{
uintptr_t port = ctx->port;
ns16550_set_reg set = ctx->set_reg;
size_t out = len > SP_FIFO_SIZE ? SP_FIFO_SIZE : len;
size_t i;
for (i = 0; i < out; ++i) {
(*set)(port, NS16550_TRANSMIT_BUFFER, buf[i]);
}
return out;
}
/**
* @brief Process interrupt.
*/
static void ns16550_isr(void *arg)
{
rtems_termios_tty *tty = arg;
ns16550_context *ctx = rtems_termios_get_device_context(tty);
uintptr_t port = ctx->port;
ns16550_get_reg get = ctx->get_reg;
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(tty, buf, i);
/* Do transmit */
if (ctx->out_total > 0
&& (get(port, NS16550_LINE_STATUS) & SP_LSR_THOLD) != 0) {
size_t current = ctx->out_current;
ctx->out_buf += current;
ctx->out_remaining -= current;
if (ctx->out_remaining > 0) {
ctx->out_current =
ns16550_write_to_fifo(ctx, ctx->out_buf, ctx->out_remaining);
} else {
rtems_termios_dequeue_characters(tty, ctx->out_total);
}
}
} while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0);
}
static void ns16550_isr_task(void *arg)
{
rtems_termios_tty *tty = arg;
ns16550_context *ctx = rtems_termios_get_device_context(tty);
uint8_t status = (*ctx->get_reg)(ctx->port, NS16550_LINE_STATUS);
if ((status & SP_LSR_RDY) != 0) {
ns16550_clear_and_set_interrupts(ctx, SP_INT_RX_ENABLE, 0);
rtems_termios_rxirq_occured(tty);
}
if (ctx->out_total > 0 && (status & SP_LSR_THOLD) != 0) {
size_t current = ctx->out_current;
ctx->out_buf += current;
ctx->out_remaining -= current;
if (ctx->out_remaining > 0) {
ctx->out_current =
ns16550_write_to_fifo(ctx, ctx->out_buf, ctx->out_remaining);
} else {
size_t done = ctx->out_total;
ctx->out_total = 0;
ns16550_clear_and_set_interrupts(ctx, SP_INT_TX_ENABLE, 0);
rtems_termios_dequeue_characters(tty, done);
}
}
}
static int ns16550_read_task(rtems_termios_device_context *base)
{
ns16550_context *ctx = (ns16550_context *) base;
uintptr_t port = ctx->port;
ns16550_get_reg get = ctx->get_reg;
char buf[SP_FIFO_SIZE];
int i;
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;
}
}
rtems_termios_enqueue_raw_characters(ctx->tty, buf, i);
ns16550_clear_and_set_interrupts(ctx, 0, SP_INT_RX_ENABLE);
return -1;
}
/*
* ns16550_initialize_interrupts
*
* This routine initializes the port to operate in interrupt driver mode.
*/
static void ns16550_initialize_interrupts(
struct rtems_termios_tty *tty,
ns16550_context *ctx,
void (*isr)(void *)
)
{
#ifdef BSP_FEATURE_IRQ_EXTENSION
{
rtems_status_code sc = RTEMS_SUCCESSFUL;
sc = rtems_interrupt_handler_install(
ctx->irq,
"NS16550",
RTEMS_INTERRUPT_SHARED,
isr,
tty
);
if (sc != RTEMS_SUCCESSFUL) {
/* FIXME */
printk( "%s: Error: Install interrupt handler\n", __func__);
rtems_fatal_error_occurred( 0xdeadbeef);
}
}
#elif defined(BSP_FEATURE_IRQ_LEGACY)
{
int rv = 0;
#ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
rtems_irq_connect_data cd = {
ctx->irq,
isr,
tty,
NULL,
NULL,
NULL,
NULL
};
rv = BSP_install_rtems_shared_irq_handler( &cd);
#else
rtems_irq_connect_data cd = {
ctx->irq,
isr,
tty,
NULL,
NULL,
NULL
};
rv = BSP_install_rtems_irq_handler( &cd);
#endif
if (rv == 0) {
/* FIXME */
printk( "%s: Error: Install interrupt handler\n", __func__);
rtems_fatal_error_occurred( 0xdeadbeef);
}
}
#endif
}
/*
* ns16550_open
*/
static bool ns16550_open(
struct rtems_termios_tty *tty,
rtems_termios_device_context *base,
struct termios *term,
rtems_libio_open_close_args_t *args
)
{
ns16550_context *ctx = (ns16550_context *) base;
ctx->tty = tty;
/* Set initial baud */
rtems_termios_set_initial_baud(tty, ctx->initial_baud);
if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
ns16550_initialize_interrupts(tty, ctx, ns16550_isr);
ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
} else if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
ns16550_initialize_interrupts(tty, ctx, ns16550_isr_task);
ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
}
return true;
}
static void ns16550_cleanup_interrupts(
struct rtems_termios_tty *tty,
ns16550_context *ctx,
void (*isr)(void *)
)
{
#if defined(BSP_FEATURE_IRQ_EXTENSION)
rtems_status_code sc = RTEMS_SUCCESSFUL;
sc = rtems_interrupt_handler_remove(
ctx->irq,
isr,
tty
);
if (sc != RTEMS_SUCCESSFUL) {
/* FIXME */
printk("%s: Error: Remove interrupt handler\n", __func__);
rtems_fatal_error_occurred(0xdeadbeef);
}
#elif defined(BSP_FEATURE_IRQ_LEGACY)
int rv = 0;
rtems_irq_connect_data cd = {
.name = ctx->irq,
.hdl = isr,
.handle = tty
};
rv = BSP_remove_rtems_irq_handler(&cd);
if (rv == 0) {
/* FIXME */
printk("%s: Error: Remove interrupt handler\n", __func__);
rtems_fatal_error_occurred(0xdeadbeef);
}
#endif
}
/*
* ns16550_close
*/
static void ns16550_close(
struct rtems_termios_tty *tty,
rtems_termios_device_context *base,
rtems_libio_open_close_args_t *args
)
{
ns16550_context *ctx = (ns16550_context *) base;
ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR);
if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
ns16550_cleanup_interrupts(tty, ctx, ns16550_isr);
} else if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
ns16550_cleanup_interrupts(tty, ctx, ns16550_isr_task);
}
}
/**
* @brief Polled write for NS16550.
*/
void ns16550_polled_putchar(rtems_termios_device_context *base, char out)
{
ns16550_context *ctx = (ns16550_context *) base;
uintptr_t port = ctx->port;
ns16550_get_reg get = ctx->get_reg;
ns16550_set_reg set = ctx->set_reg;
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(ctx, NS16550_DISABLE_ALL_INTR);
while (true) {
/* Try to transmit the character in a critical section */
rtems_termios_device_lock_acquire(&ctx->base, &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_termios_device_lock_release(&ctx->base, &lock_context);
break;
} else {
rtems_termios_device_lock_release(&ctx->base, &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);
}
/*
* These routines provide control of the RTS and DTR lines
*/
/*
* ns16550_assert_RTS
*/
static void ns16550_assert_RTS(rtems_termios_device_context *base)
{
ns16550_context *ctx = (ns16550_context *) base;
rtems_interrupt_lock_context lock_context;
/*
* Assert RTS
*/
rtems_termios_device_lock_acquire(base, &lock_context);
ctx->modem_control |= SP_MODEM_RTS;
(*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
rtems_termios_device_lock_release(base, &lock_context);
}
/*
* ns16550_negate_RTS
*/
static void ns16550_negate_RTS(rtems_termios_device_context *base)
{
ns16550_context *ctx = (ns16550_context *) base;
rtems_interrupt_lock_context lock_context;
/*
* Negate RTS
*/
rtems_termios_device_lock_acquire(base, &lock_context);
ctx->modem_control &= ~SP_MODEM_RTS;
(*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
rtems_termios_device_lock_release(base, &lock_context);
}
/*
* These flow control routines utilise a connection from the local DTR
* line to the remote CTS line
*/
/*
* ns16550_assert_DTR
*/
static void ns16550_assert_DTR(rtems_termios_device_context *base)
{
ns16550_context *ctx = (ns16550_context *) base;
rtems_interrupt_lock_context lock_context;
/*
* Assert DTR
*/
rtems_termios_device_lock_acquire(base, &lock_context);
ctx->modem_control |= SP_MODEM_DTR;
(*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
rtems_termios_device_lock_release(base, &lock_context);
}
/*
* ns16550_negate_DTR
*/
static void ns16550_negate_DTR(rtems_termios_device_context *base)
{
ns16550_context *ctx = (ns16550_context *) base;
rtems_interrupt_lock_context lock_context;
/*
* Negate DTR
*/
rtems_termios_device_lock_acquire(base, &lock_context);
ctx->modem_control &=~SP_MODEM_DTR;
(*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL,ctx->modem_control);
rtems_termios_device_lock_release(base, &lock_context);
}
/*
* ns16550_set_attributes
*
* This function sets the channel to reflect the requested termios
* port settings.
*/
static bool ns16550_set_attributes(
rtems_termios_device_context *base,
const struct termios *t
)
{
ns16550_context *ctx = (ns16550_context *) base;
uint32_t pNS16550;
uint32_t ulBaudDivisor;
uint8_t ucLineControl;
uint32_t baud_requested;
ns16550_set_reg setReg;
pNS16550 = ctx->port;
setReg = ctx->set_reg;
/*
* Calculate the baud rate divisor
*
* Assert ensures there is no division by 0.
*/
baud_requested = rtems_termios_baud_to_number(t->c_ospeed);
_Assert( baud_requested != 0 );
ulBaudDivisor = NS16550_GetBaudDivisor(ctx, baud_requested);
ucLineControl = 0;
/*
* Parity
*/
if (t->c_cflag & PARENB) {
ucLineControl |= SP_LINE_PAR;
if (!(t->c_cflag & PARODD))
ucLineControl |= SP_LINE_ODD;
}
/*
* Character Size
*/
if (t->c_cflag & CSIZE) {
switch (t->c_cflag & CSIZE) {
case CS5: ucLineControl |= FIVE_BITS; break;
case CS6: ucLineControl |= SIX_BITS; break;
case CS7: ucLineControl |= SEVEN_BITS; break;
case CS8: ucLineControl |= EIGHT_BITS; break;
}
} else {
ucLineControl |= EIGHT_BITS; /* default to 9600,8,N,1 */
}
/*
* Stop Bits
*/
if (t->c_cflag & CSTOPB) {
ucLineControl |= SP_LINE_STOP; /* 2 stop bits */
} else {
; /* 1 stop bit */
}
/*
* Now actually set the chip
*/
if (ulBaudDivisor != ctx->baud_divisor || ucLineControl != ctx->line_control) {
rtems_interrupt_lock_context lock_context;
ctx->baud_divisor = ulBaudDivisor;
ctx->line_control = ucLineControl;
rtems_termios_device_lock_acquire(base, &lock_context);
/*
* Set the baud rate
*
* NOTE: When the Divisor Latch Access Bit (DLAB) is set to 1,
* the transmit buffer and interrupt enable registers
* turn into the LSB and MSB divisor latch registers.
*/
(*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
(*setReg)(pNS16550, NS16550_DIVISOR_LATCH_L, ulBaudDivisor&0xff);
(*setReg)(pNS16550, NS16550_DIVISOR_LATCH_M, (ulBaudDivisor>>8)&0xff);
/*
* Now write the line control
*/
if (ctx->has_precision_clock_synthesizer) {
(*setReg)(pNS16550, NS16550_SCRATCH_PAD, (uint8_t)(ulBaudDivisor >> 24));
(*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
(*setReg)(pNS16550, NS16550_SCRATCH_PAD, (uint8_t)(ulBaudDivisor >> 16));
} else {
(*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
}
rtems_termios_device_lock_release(base, &lock_context);
}
return true;
}
/**
* @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.
*/
static void ns16550_write_support_int(
rtems_termios_device_context *base,
const char *buf,
size_t len
)
{
ns16550_context *ctx = (ns16550_context *) base;
ctx->out_total = len;
if (len > 0) {
ctx->out_remaining = len;
ctx->out_buf = buf;
ctx->out_current = ns16550_write_to_fifo(ctx, buf, len);
ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR);
} else {
ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
}
}
static void ns16550_write_support_task(
rtems_termios_device_context *base,
const char *buf,
size_t len
)
{
ns16550_context *ctx = (ns16550_context *) base;
ctx->out_total = len;
if (len > 0) {
ctx->out_remaining = len;
ctx->out_buf = buf;
ctx->out_current = ns16550_write_to_fifo(ctx, buf, len);
ns16550_clear_and_set_interrupts(ctx, 0, SP_INT_TX_ENABLE);
}
}
/*
* ns16550_write_support_polled
*
* Console Termios output entry point.
*
*/
static void ns16550_write_support_polled(
rtems_termios_device_context *base,
const char *buf,
size_t len
)
{
size_t nwrite = 0;
/*
* poll each byte in the string out of the port.
*/
while (nwrite < len) {
/*
* transmit character
*/
ns16550_polled_putchar(base, *buf++);
nwrite++;
}
}
/*
* Debug gets() support
*/
int ns16550_polled_getchar(rtems_termios_device_context *base)
{
ns16550_context *ctx = (ns16550_context *) base;
uint32_t pNS16550;
unsigned char ucLineStatus;
uint8_t cChar;
ns16550_get_reg getReg;
pNS16550 = ctx->port;
getReg = ctx->get_reg;
ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
if (ucLineStatus & SP_LSR_RDY) {
cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
return (int)cChar;
}
return -1;
}
/*
* Flow control is only supported when using interrupts
*/
const rtems_termios_device_flow ns16550_flow_rtscts = {
.stop_remote_tx = ns16550_negate_RTS,
.start_remote_tx = ns16550_assert_RTS
};
const rtems_termios_device_flow ns16550_flow_dtrcts = {
.stop_remote_tx = ns16550_negate_DTR,
.start_remote_tx = ns16550_assert_DTR
};
const rtems_termios_device_handler ns16550_handler_interrupt = {
.first_open = ns16550_open,
.last_close = ns16550_close,
.poll_read = NULL,
.write = ns16550_write_support_int,
.set_attributes = ns16550_set_attributes,
.mode = TERMIOS_IRQ_DRIVEN
};
const rtems_termios_device_handler ns16550_handler_polled = {
.first_open = ns16550_open,
.last_close = ns16550_close,
.poll_read = ns16550_polled_getchar,
.write = ns16550_write_support_polled,
.set_attributes = ns16550_set_attributes,
.mode = TERMIOS_POLLED
};
const rtems_termios_device_handler ns16550_handler_task = {
.first_open = ns16550_open,
.last_close = ns16550_close,
.poll_read = ns16550_read_task,
.write = ns16550_write_support_task,
.set_attributes = ns16550_set_attributes,
.mode = TERMIOS_TASK_DRIVEN
};