libchip/serial: Task driven mode for NS16550

This commit is contained in:
Sebastian Huber
2014-10-07 11:37:55 +02:00
parent 2fd3e65fac
commit 8a9bdc565f
2 changed files with 120 additions and 12 deletions

View File

@@ -98,6 +98,26 @@ static void ns16550_enable_interrupts(
(*ctx->set_reg)(ctx->port, NS16550_INTERRUPT_ENABLE, 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 * ns16550_probe
*/ */
@@ -180,8 +200,6 @@ static size_t ns16550_write_to_fifo(
return out; return out;
} }
#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
/** /**
* @brief Process interrupt. * @brief Process interrupt.
*/ */
@@ -225,7 +243,58 @@ static void ns16550_isr(void *arg)
} }
} while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0); } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0);
} }
#endif
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;
uint32_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 * ns16550_initialize_interrupts
@@ -234,7 +303,8 @@ static void ns16550_isr(void *arg)
*/ */
static void ns16550_initialize_interrupts( static void ns16550_initialize_interrupts(
struct rtems_termios_tty *tty, struct rtems_termios_tty *tty,
ns16550_context *ctx ns16550_context *ctx,
void (*isr)(void *)
) )
{ {
#ifdef BSP_FEATURE_IRQ_EXTENSION #ifdef BSP_FEATURE_IRQ_EXTENSION
@@ -244,7 +314,7 @@ static void ns16550_initialize_interrupts(
ctx->irq, ctx->irq,
"NS16550", "NS16550",
RTEMS_INTERRUPT_SHARED, RTEMS_INTERRUPT_SHARED,
ns16550_isr, isr,
tty tty
); );
if (sc != RTEMS_SUCCESSFUL) { if (sc != RTEMS_SUCCESSFUL) {
@@ -259,7 +329,7 @@ static void ns16550_initialize_interrupts(
#ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT #ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
rtems_irq_connect_data cd = { rtems_irq_connect_data cd = {
ctx->irq, ctx->irq,
ns16550_isr, isr,
tty, tty,
NULL, NULL,
NULL, NULL,
@@ -270,7 +340,7 @@ static void ns16550_initialize_interrupts(
#else #else
rtems_irq_connect_data cd = { rtems_irq_connect_data cd = {
ctx->irq, ctx->irq,
ns16550_isr, isr,
tty, tty,
NULL, NULL,
NULL, NULL,
@@ -300,11 +370,16 @@ static bool ns16550_open(
{ {
ns16550_context *ctx = (ns16550_context *) base; ns16550_context *ctx = (ns16550_context *) base;
ctx->tty = tty;
/* Set initial baud */ /* Set initial baud */
rtems_termios_set_initial_baud(tty, ctx->initial_baud); rtems_termios_set_initial_baud(tty, ctx->initial_baud);
if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) { if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
ns16550_initialize_interrupts(tty, ctx); 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); ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
} }
@@ -313,14 +388,15 @@ static bool ns16550_open(
static void ns16550_cleanup_interrupts( static void ns16550_cleanup_interrupts(
struct rtems_termios_tty *tty, struct rtems_termios_tty *tty,
ns16550_context *ctx ns16550_context *ctx,
void (*isr)(void *)
) )
{ {
#if defined(BSP_FEATURE_IRQ_EXTENSION) #if defined(BSP_FEATURE_IRQ_EXTENSION)
rtems_status_code sc = RTEMS_SUCCESSFUL; rtems_status_code sc = RTEMS_SUCCESSFUL;
sc = rtems_interrupt_handler_remove( sc = rtems_interrupt_handler_remove(
ctx->irq, ctx->irq,
ns16550_isr, isr,
tty tty
); );
if (sc != RTEMS_SUCCESSFUL) { if (sc != RTEMS_SUCCESSFUL) {
@@ -332,7 +408,7 @@ static void ns16550_cleanup_interrupts(
int rv = 0; int rv = 0;
rtems_irq_connect_data cd = { rtems_irq_connect_data cd = {
.name = ctx->irq, .name = ctx->irq,
.hdl = ns16550_isr, .hdl = isr,
.handle = tty .handle = tty
}; };
rv = BSP_remove_rtems_irq_handler(&cd); rv = BSP_remove_rtems_irq_handler(&cd);
@@ -359,7 +435,9 @@ static void ns16550_close(
ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR); ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR);
if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) { if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
ns16550_cleanup_interrupts(tty, ctx); ns16550_cleanup_interrupts(tty, ctx, ns16550_isr);
} else if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
ns16550_cleanup_interrupts(tty, ctx, ns16550_isr_task);
} }
} }
@@ -614,6 +692,25 @@ static void ns16550_write_support_int(
} }
} }
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 * ns16550_write_support_polled
* *
@@ -694,3 +791,12 @@ const rtems_termios_device_handler ns16550_handler_polled = {
.set_attributes = ns16550_set_attributes, .set_attributes = ns16550_set_attributes,
.mode = TERMIOS_POLLED .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
};

View File

@@ -74,10 +74,12 @@ typedef struct {
size_t out_remaining; size_t out_remaining;
size_t out_current; size_t out_current;
const char *out_buf; const char *out_buf;
rtems_termios_tty *tty;
} ns16550_context; } ns16550_context;
extern const rtems_termios_device_handler ns16550_handler_interrupt; extern const rtems_termios_device_handler ns16550_handler_interrupt;
extern const rtems_termios_device_handler ns16550_handler_polled; extern const rtems_termios_device_handler ns16550_handler_polled;
extern const rtems_termios_device_handler ns16550_handler_task;
extern const rtems_termios_device_flow ns16550_flow_rtscts; extern const rtems_termios_device_flow ns16550_flow_rtscts;
extern const rtems_termios_device_flow ns16550_flow_dtrcts; extern const rtems_termios_device_flow ns16550_flow_dtrcts;