X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=UDI%2Fdrivers%2Fuart_16c550%2Fuart16c550.c;h=cfe77707be24bda6c93a43646e41133b2f2e060e;hb=818e35a1e64d6761ae5684a3a4b4958566a71017;hp=d4637ffa83109cc894cb8fb4261d60982bbadabe;hpb=ad8ed64b7916f20a97d573b29642f567f9e43331;p=tpg%2Facess2.git diff --git a/UDI/drivers/uart_16c550/uart16c550.c b/UDI/drivers/uart_16c550/uart16c550.c index d4637ffa..cfe77707 100644 --- a/UDI/drivers/uart_16c550/uart16c550.c +++ b/UDI/drivers/uart_16c550/uart16c550.c @@ -9,20 +9,28 @@ #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 CONTIN(suffix, call, params, args...) extern void _EXPJOIN(suffix##__,__LINE__) params;\ - call(_EXPJOIN(suffix##__,__LINE__), gcb,## args); } \ - void _EXPJOIN(suffix##__,__LINE__) params { \ +#define _EXPLODE(params...) params +#define _ADDGCB(params...) (udi_cb_t *gcb, params) +#define CONTIN(suffix, call, args, params) extern void _EXPJOIN(suffix##__,__LINE__) _ADDGCB params;\ + call( _EXPJOIN(suffix##__,__LINE__), gcb, _EXPLODE args); } \ + void _EXPJOIN(suffix##__,__LINE__) _ADDGCB params { \ rdata_t *rdata = gcb->context; // --- Management Metalang 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 @@ -38,7 +46,7 @@ void uart_enumerate_req(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level) { case UDI_ENUMERATE_START: case UDI_ENUMERATE_START_RESCAN: - DPT_SET_ATTR_STRING(attr_list, "if_num", "uart", 4); + DPT_SET_ATTR_STRING(attr_list, "gio_type", "uart", 4); attr_list ++; cb->attr_valid_length = attr_list - cb->attr_list; udi_enumerate_ack(cb, UDI_ENUMERATE_OK, UART_OPS_GIO); @@ -109,22 +117,27 @@ void uart_bus_dev_bind__pio_map(udi_cb_t *gcb, udi_pio_handle_t new_pio_handle) return ; } - CONTIN( uart_bus_dev_bind, udi_channel_spawn, (udi_cb_t *gcb, udi_channel_t new_channel), gcb->channel, 0, UART_OPS_IRQ, rdata) -// udi_channel_spawn(uart_bus_dev_bind__intr_chanel, gcb, gcb->channel, 0, UART_OPS_IRQ, rdata); -//} -//void uart_bus_dev_bind__intr_chanel(udi_cb_t *gcb, udi_channel_t new_channel) -//{ -// rdata_t *rdata = gcb->context; + // Spawn the interrupt channel + CONTIN( uart_bus_dev_bind, udi_channel_spawn, (gcb->channel, 0, UART_OPS_IRQ, rdata), + (udi_channel_t new_channel)) rdata->interrupt_channel = new_channel; - - CONTIN( uart_bus_dev_bind, udi_cb_alloc, (udi_cb_t *gcb, udi_cb_t *new_cb), UART_CB_INTR, gcb->channel) -// udi_cb_alloc(uart_bus_dev_bind__intr_attach, gcb, NE2K_CB_INTR, gcb->channel); -// // V V V V -//} -//void uart_bus_dev_bind__intr_attach(udi_cb_t *gcb, udi_cb_t *new_cb) -//{ -// rdata_t *rdata = gcb->context; + + // 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)) if( !new_cb ) { // Oh... @@ -134,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 } @@ -144,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) { @@ -154,29 +190,185 @@ 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 ) + { + // An IRQ from something other than RX + udi_intr_event_rdy(cb); + return ; + } + + 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 { - //ne2k_intr__rx_ok( UDI_GCB(cb) ); + DEBUG_OUT("event_cb=%p, event_cb_used=%i - Queueing", + rdata->event_cb, rdata->event_cb_used); + rdata->event_pending = TRUE; } - // TODO: TX IRQs + 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); + 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) { + udi_cb_t *gcb = UDI_GCB(cb); + rdata_t *rdata = gcb->context; + switch(cb->op) + { + 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 ) { + len = rdata->rx_buffer->buf_size; + callback = uart_gio_read_complete_short; + } + else { + len = cb->data_buf->buf_size; + callback = uart_gio_read_complete; + } + 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, &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), + (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) +{ + udi_gio_xfer_cb_t *cb = UDI_MCB(gcb, udi_gio_xfer_cb_t); + cb->data_buf = new_buf; + udi_gio_xfer_ack(cb); } 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; + } } // ==================================================================== @@ -247,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 = {