UDI/uart_16c550 - RX support, apply erratum E20010702.1 (pci_base_class)
[tpg/acess2.git] / UDI / drivers / uart_16c550 / uart16c550.c
index d4637ff..0f7bdc9 100644 (file)
 
 #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
@@ -38,7 +40,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 +111,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 +141,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 +153,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 +184,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_cb_t        *gcb = UDI_GCB(cb);
+       rdata_t *rdata = gcb->context;
+       
        udi_debug_printf("%s: flags=%x, intr_result=%x\n", __func__, flags, cb->intr_result);
-       if( cb->intr_result & 0x01 )
+       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
+               udi_debug_printf("%s: dropping %i bytes, full rx buffer\n", __func__, 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;
+
+       udi_debug_printf("%s: copied %i bytes\n", __func__, 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
+       {
+               udi_debug_printf("%s: event_cb=%p, event_cb_used=%i - Queueing\n",
+                       __func__, 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);
+
 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 ) {
+               udi_debug_printf("%s: only one client allowed\n", __func__);
+               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;
+               }
+               udi_debug_printf("%s: Read %i/%i bytes\n", __func__, 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 +433,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 = {

UCC git Repository :: git.ucc.asn.au