X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=UDI%2Fdrivers%2Fuart_16c550%2Fuart16c550.c;h=cfe77707be24bda6c93a43646e41133b2f2e060e;hb=82be808714198ab884748f14f06abf458a3c5d59;hp=b9d6e08134733189f1f752468649273460ebf43b;hpb=c050d8159389ab625fe1448e2cc3b47e07c999b8;p=tpg%2Facess2.git diff --git a/UDI/drivers/uart_16c550/uart16c550.c b/UDI/drivers/uart_16c550/uart16c550.c index b9d6e081..cfe77707 100644 --- a/UDI/drivers/uart_16c550/uart16c550.c +++ b/UDI/drivers/uart_16c550/uart16c550.c @@ -9,9 +9,14 @@ #include #include "uart16c550_common.h" - #include "uart16c550_pio.h" +#if 0 +# define DEBUG_OUT(fmt, v...) udi_debug_printf("%s: "fmt"\n", __func__ ,## v) +#else +# define DEBUG_OUT(...) do{}while(0) +#endif + #define __EXPJOIN(a,b) a##b #define _EXPJOIN(a,b) __EXPJOIN(a,b) #define _EXPLODE(params...) params @@ -25,6 +30,7 @@ void uart_usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level) { rdata_t *rdata = UDI_GCB(cb)->context; + //udi_trace_write(rdata->init_context, UDI_TREVENT_LOCAL_PROC_ENTRY, 0, ); // TODO: Set up region data @@ -111,15 +117,27 @@ void uart_bus_dev_bind__pio_map(udi_cb_t *gcb, udi_pio_handle_t new_pio_handle) return ; } + // Spawn the interrupt channel CONTIN( uart_bus_dev_bind, udi_channel_spawn, (gcb->channel, 0, UART_OPS_IRQ, rdata), (udi_channel_t new_channel)) - // new function rdata->interrupt_channel = new_channel; - + + // Allocate an RX buffer + CONTIN( uart_bus_dev_bind, udi_buf_write, (NULL, 0, NULL, 0, 0, UDI_NULL_BUF_PATH), + (udi_buf_t *new_buf)); + if( !new_buf ) + { + // Oh... + udi_channel_event_complete( UDI_MCB(rdata->active_cb, udi_channel_event_cb_t), + UDI_STAT_RESOURCE_UNAVAIL ); + return ; + } + rdata->rx_buffer = new_buf; + + // Create interrupt CB CONTIN( uart_bus_dev_bind, udi_cb_alloc, (UART_CB_INTR, gcb->channel), (udi_cb_t *new_cb)) - // new function if( !new_cb ) { // Oh... @@ -129,8 +147,10 @@ void uart_bus_dev_bind__pio_map(udi_cb_t *gcb, udi_pio_handle_t new_pio_handle) } udi_intr_attach_cb_t *intr_cb = UDI_MCB(new_cb, udi_intr_attach_cb_t); intr_cb->interrupt_idx = 0; - intr_cb->min_event_pend = 2; - intr_cb->preprocessing_handle = rdata->pio_handles[PIO_RX]; + intr_cb->min_event_pend = 1; + intr_cb->preprocessing_handle = rdata->pio_handles[PIO_INTR]; + + // Attach interrupt udi_intr_attach_req(intr_cb); // continued in uart_bus_dev_intr_attach_ack } @@ -139,6 +159,27 @@ void uart_bus_dev_bus_unbind_ack(udi_bus_bind_cb_t *cb) } void uart_bus_dev_intr_attach_ack(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status) { + udi_cb_t *gcb = UDI_GCB(intr_attach_cb); + rdata_t *rdata = gcb->context; + + // Allocate interrupt cbs + CONTIN(uart_bus_dev_intr_attach_ack, udi_cb_alloc_batch, + (UART_CB_INTR_EVENT, NUM_INTR_CBS, TRUE, INTR_CB_BUF_SIZE, UDI_NULL_BUF_PATH), + (udi_cb_t *first_new_cb)) + + while( first_new_cb ) + { + udi_intr_event_cb_t *cb = UDI_MCB(first_new_cb, udi_intr_event_cb_t); + first_new_cb = first_new_cb->initiator_context; + // - Set channel (udi_cb_alloc_batch sets the channel to gcb->channel) + cb->gcb.channel = rdata->interrupt_channel; + udi_intr_event_rdy(cb); + } + + udi_channel_event_cb_t *channel_cb = UDI_MCB(rdata->active_cb, udi_channel_event_cb_t); + + udi_channel_event_complete(channel_cb, UDI_OK); + // = = = = = } void uart_bus_dev_intr_detach_ack(udi_intr_detach_cb_t *intr_detach_cb) { @@ -149,18 +190,59 @@ void uart_bus_irq_channel_event_ind(udi_channel_event_cb_t *cb) } void uart_bus_irq_intr_event_ind(udi_intr_event_cb_t *cb, udi_ubit8_t flags) { - udi_debug_printf("%s: flags=%x, intr_result=%x\n", __func__, flags, cb->intr_result); - if( cb->intr_result & 0x01 ) + udi_cb_t *gcb = UDI_GCB(cb); + rdata_t *rdata = gcb->context; + + DEBUG_OUT("flags=%x, intr_result=%x", flags, cb->intr_result); + if( cb->intr_result == 0 ) { - //ne2k_intr__rx_ok( UDI_GCB(cb) ); + // An IRQ from something other than RX + udi_intr_event_rdy(cb); + return ; } - // TODO: TX IRQs + + if( rdata->rx_buffer && rdata->rx_buffer->buf_size + cb->intr_result > MAX_RX_BUFFER_SIZE ) + { + // Drop, buffer is full + DEBUG_OUT("dropping %i bytes, full rx buffer", cb->intr_result); + udi_intr_event_rdy(cb); + return ; + } + + // Copy cb->intr_result bytes into the RX buffer + CONTIN(uart_bus_irq_intr_event_ind, udi_buf_copy, + (cb->event_buf, 0, cb->intr_result, + rdata->rx_buffer, rdata->rx_buffer->buf_size, 0, + UDI_NULL_BUF_PATH), + (udi_buf_t *new_buf)); + + udi_intr_event_cb_t *cb = UDI_MCB(gcb, udi_intr_event_cb_t); + + rdata->rx_buffer = new_buf; + + DEBUG_OUT("copied %i bytes", cb->intr_result); + + // Emit a signal to GIO client + if( rdata->event_cb && !rdata->event_cb_used ) + { + rdata->event_pending = FALSE; + rdata->event_cb_used = TRUE; + udi_gio_event_ind(rdata->event_cb); + } + else + { + DEBUG_OUT("event_cb=%p, event_cb_used=%i - Queueing", + rdata->event_cb, rdata->event_cb_used); + rdata->event_pending = TRUE; + } + udi_intr_event_rdy(cb); } // --- void uart_gio_prov_channel_event_ind(udi_channel_event_cb_t *cb); void uart_gio_prov_bind_req(udi_gio_bind_cb_t *cb); void uart_gio_prov_xfer_req(udi_gio_xfer_cb_t *cb); +void uart_gio_read_complete_short(udi_cb_t *gcb, udi_buf_t *new_buf); void uart_gio_read_complete(udi_cb_t *gcb, udi_buf_t *new_buf); void uart_gio_write_complete(udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t status, udi_ubit16_t res); void uart_gio_prov_event_res(udi_gio_event_cb_t *cb); @@ -170,10 +252,33 @@ void uart_gio_prov_channel_event_ind(udi_channel_event_cb_t *cb) } void uart_gio_prov_bind_req(udi_gio_bind_cb_t *cb) { + udi_cb_t *gcb = UDI_GCB(cb); + rdata_t *rdata = gcb->context; + + // TODO: Should this device allow multiple children? + if( rdata->event_cb ) { + DEBUG_OUT("only one client allowed"); + udi_gio_bind_ack(cb, 0, 0, UDI_STAT_CANNOT_BIND_EXCL); + return; + } + + // Allocate the event CB + CONTIN( uart_gio_prov_bind_req, udi_cb_alloc, (UART_CB_GIO_EVENT, gcb->channel), + (udi_cb_t *new_cb)); + udi_gio_bind_cb_t *cb = UDI_MCB(gcb, udi_gio_bind_cb_t); + + rdata->event_cb = UDI_MCB(new_cb, udi_gio_event_cb_t); + rdata->event_cb_used = FALSE; + udi_gio_bind_ack(cb, 0, 0, UDI_OK); } void uart_gio_prov_unbind_req(udi_gio_bind_cb_t *cb) { + udi_cb_t *gcb = UDI_GCB(cb); + rdata_t *rdata = gcb->context; + + rdata->event_cb = NULL; + rdata->event_cb_used = FALSE; udi_gio_unbind_ack(cb); } void uart_gio_prov_xfer_req(udi_gio_xfer_cb_t *cb) @@ -182,41 +287,64 @@ void uart_gio_prov_xfer_req(udi_gio_xfer_cb_t *cb) rdata_t *rdata = gcb->context; switch(cb->op) { - case UDI_GIO_OP_READ: + case UDI_GIO_OP_READ: { + udi_buf_copy_call_t *callback; + udi_size_t len; // Read from local FIFO if( rdata->rx_buffer->buf_size < cb->data_buf->buf_size ) { - // Local FIFO was empty - udi_gio_xfer_nak(cb, UDI_STAT_DATA_UNDERRUN); + len = rdata->rx_buffer->buf_size; + callback = uart_gio_read_complete_short; } else { - udi_buf_copy(uart_gio_read_complete, gcb, - rdata->rx_buffer, - 0, cb->data_buf->buf_size, - cb->data_buf, - 0, cb->data_buf->buf_size, - UDI_NULL_BUF_PATH); + len = cb->data_buf->buf_size; + callback = uart_gio_read_complete; } - break; + DEBUG_OUT("Read %i/%i bytes", len, rdata->rx_buffer->buf_size); + // Replace entire destination buffer with `len` bytes from source + udi_buf_copy(callback, gcb, + rdata->rx_buffer, 0, len, + cb->data_buf, 0, cb->data_buf->buf_size, + UDI_NULL_BUF_PATH); + break; } case UDI_GIO_OP_WRITE: // Send to device udi_pio_trans(uart_gio_write_complete, gcb, - rdata->pio_handles[PIO_TX], 0, cb->data_buf, NULL); + rdata->pio_handles[PIO_TX], 0, cb->data_buf, &cb->data_buf->buf_size); break; default: udi_gio_xfer_nak(cb, UDI_STAT_NOT_SUPPORTED); break; } } +void uart_gio_read_complete_short(udi_cb_t *gcb, udi_buf_t *new_buf) +{ + rdata_t *rdata = gcb->context; + udi_gio_xfer_cb_t *cb = UDI_MCB(gcb, udi_gio_xfer_cb_t); + cb->data_buf = new_buf; + + // Delete read bytes from the RX buffer + CONTIN(uart_gio_read_complete, udi_buf_write, + (NULL, 0, rdata->rx_buffer, 0, cb->data_buf->buf_size, UDI_NULL_BUF_PATH), + (udi_buf_t *new_buf)) + udi_gio_xfer_cb_t *cb = UDI_MCB(gcb, udi_gio_xfer_cb_t); + rdata->rx_buffer = new_buf; + + // Return underrun + udi_gio_xfer_nak(cb, UDI_STAT_DATA_UNDERRUN); +} void uart_gio_read_complete(udi_cb_t *gcb, udi_buf_t *new_buf) { rdata_t *rdata = gcb->context; udi_gio_xfer_cb_t *cb = UDI_MCB(gcb, udi_gio_xfer_cb_t); cb->data_buf = new_buf; // Delete read bytes from the RX buffer - CONTIN(uart_gio_read_complete, udi_buf_write, (NULL, 0, rdata->rx_buffer, 0, cb->data_buf->buf_size, UDI_NULL_BUF_PATH), + CONTIN(uart_gio_read_complete, udi_buf_write, + (NULL, 0, rdata->rx_buffer, 0, cb->data_buf->buf_size, UDI_NULL_BUF_PATH), (udi_buf_t *new_buf)) udi_gio_xfer_cb_t *cb = UDI_MCB(gcb, udi_gio_xfer_cb_t); rdata->rx_buffer = new_buf; + + // Return completed udi_gio_xfer_ack(cb); } void uart_gio_write_complete(udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t status, udi_ubit16_t res) @@ -227,6 +355,20 @@ void uart_gio_write_complete(udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t sta } void uart_gio_prov_event_res(udi_gio_event_cb_t *cb) { + udi_cb_t *gcb = UDI_GCB(cb); + rdata_t *rdata = gcb->context; + + // If there was an event during dispatch, re-send + if( rdata->event_pending ) + { + udi_gio_event_ind(cb); + rdata->event_pending = FALSE; + } + // otherwise, clear 'Event CB used' flag + else + { + rdata->event_cb_used = FALSE; + } } // ==================================================================== @@ -297,6 +439,7 @@ udi_cb_init_t uart_cb_init_list[] = { {UART_CB_BUS_BIND, UART_META_BUS, UDI_BUS_BIND_CB_NUM, 0, 0,NULL}, {UART_CB_INTR, UART_META_BUS, UDI_BUS_INTR_ATTACH_CB_NUM, 0, 0,NULL}, {UART_CB_INTR_EVENT, UART_META_BUS, UDI_BUS_INTR_EVENT_CB_NUM, 0, 0,NULL}, + {UART_CB_GIO_EVENT, UART_META_GIO, UDI_GIO_EVENT_CB_NUM, 0, 0,NULL}, {0} }; const udi_init_t udi_init_info = {