ec303678392d2690947bb09a1bfad1548ca3af26
[tpg/acess2.git] / KernelLand / Modules / Interfaces / UDI / trans / gio_uart.c
1 /*
2  * Acess2 UDI Layer
3  * - By John Hodge (thePowersGang)
4  *
5  * trans/gio_uart.c
6  * - GIO UART translation (presents "uart" type GIO drivers as serial ports)
7  */
8 #define DEBUG   0
9 #include <udi.h>
10 //#include <udi_gio.h>
11 #include <acess.h>
12 #include <drv_pty.h>
13 #include <trans_uart.h>
14 #include <workqueue.h>
15
16 #define NUM_TX_CBS      2
17 #define RX_SIZE 32      // Size of a read request
18
19 typedef struct {
20         udi_init_context_t      init_context;
21         udi_cb_t        *active_cb;
22         tPTY    *PTYInstance;
23         tWorkqueue      CBWorkQueue;
24 } rdata_t;
25
26 enum {
27         ACESSUART_CB_BIND = 1,
28         ACESSUART_CB_XFER
29 };
30 enum {
31         ACESSUART_OPS_GIO = 1
32 };
33 enum {
34         ACESSUART_META_GIO = 1,
35 };
36
37 // === PROTOTYPES ===
38 void    acessuart_pty_output(void *Handle, size_t Length, const void *Data);
39
40 // === CODE ===
41 void acessuart_usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level)
42 {
43         udi_cb_t        *gcb = UDI_GCB(cb);
44         rdata_t *rdata = gcb->context;
45         Workqueue_Init(&rdata->CBWorkQueue, "UDI UART TX", offsetof(udi_gio_xfer_cb_t, gcb.initiator_context));
46         udi_usage_res(cb);
47 }
48 void acessuart_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID)
49 {
50         UNIMPLEMENTED();
51 }
52 void acessuart_final_cleanup_req(udi_mgmt_cb_t *cb)
53 {
54         UNIMPLEMENTED();
55 }
56 // ----
57 void acessuart_channel_event_ind(udi_channel_event_cb_t *cb);
58 void acessuart_channel_event_ind__tx_cbs_allocated(udi_cb_t *gcb, udi_cb_t *first_cb);
59 void acessuart_bind_ack(udi_gio_bind_cb_t *cb, udi_ubit32_t device_size_lo, udi_ubit32_t device_size_hi, udi_status_t status);
60 void acessuart_event_ind__buf_allocated(udi_cb_t *gcb, udi_buf_t *buffer);
61
62 void acessuart_channel_event_ind(udi_channel_event_cb_t *cb)
63 {
64         udi_cb_t        *gcb = UDI_GCB(cb);
65         rdata_t *rdata = gcb->context;
66         ASSERT(rdata);
67         switch(cb->event)
68         {
69         case UDI_CHANNEL_CLOSED:
70                 break;
71         case UDI_CHANNEL_BOUND: {
72                 rdata->active_cb = gcb;
73                 // 
74                 udi_cb_alloc_batch(acessuart_channel_event_ind__tx_cbs_allocated, cb->params.parent_bound.bind_cb,
75                         ACESSUART_CB_XFER, NUM_TX_CBS, FALSE, 0, UDI_NULL_BUF_PATH);
76                 // V V V V
77                 break; }
78         }
79 }
80 void acessuart_channel_event_ind__tx_cbs_allocated(udi_cb_t *gcb, udi_cb_t *first_cb)
81 {
82         rdata_t *rdata = gcb->context;
83         udi_gio_bind_cb_t       *cb = UDI_MCB(gcb, udi_gio_bind_cb_t);
84         ASSERT(rdata);
85
86         while( first_cb )
87         {
88                 udi_cb_t        *next = first_cb->initiator_context;
89                 first_cb->initiator_context = NULL;
90                 Workqueue_AddWork(&rdata->CBWorkQueue, first_cb);
91                 first_cb = next;
92         }
93
94         udi_gio_bind_req(cb);
95         // continued in acessuart_bind_ack
96 }
97 void acessuart_bind_ack(udi_gio_bind_cb_t *cb, udi_ubit32_t device_size_lo, udi_ubit32_t device_size_hi, udi_status_t status)
98 {
99         udi_cb_t        *gcb = UDI_GCB(cb);
100         rdata_t *rdata = gcb->context;
101         udi_channel_event_cb_t  *channel_cb = UDI_MCB(rdata->active_cb, udi_channel_event_cb_t);
102         
103         if( device_size_lo != 0 || device_size_hi != 0 ) {
104                 // Oops... binding failed. UARTS should not have a size
105                 udi_channel_event_complete( channel_cb, UDI_STAT_NOT_UNDERSTOOD);
106                 return ;
107         }
108         
109         // bound, create PTY instance
110         rdata->PTYInstance = PTY_Create("serial#", rdata, acessuart_pty_output, NULL, NULL);
111         if( !rdata->PTYInstance ) {
112                 udi_channel_event_complete(channel_cb, UDI_STAT_RESOURCE_UNAVAIL);
113                 return ;
114         }
115         
116         struct ptymode mode = {
117                 .OutputMode = PTYBUFFMT_TEXT,
118                 .InputMode = PTYIMODE_CANON|PTYIMODE_ECHO
119         };
120         struct ptydims dims = {
121                 .W = 80, .H = 25,
122                 .PW = 0, .PH = 0
123         };
124         PTY_SetAttrib(rdata->PTYInstance, &dims, &mode, 0);
125         
126         udi_channel_event_complete(channel_cb, UDI_OK);
127 }
128 void acessuart_unbind_ack(udi_gio_bind_cb_t *cb)
129 {
130         UNIMPLEMENTED();
131 }
132 void acessuart_xfer_ack(udi_gio_xfer_cb_t *cb)
133 {
134         udi_cb_t        *gcb = UDI_GCB(cb);
135         rdata_t *rdata = gcb->context;
136         if( cb->op == UDI_GIO_OP_WRITE ) {
137                 // Write, no action required except returning the CB to the pool
138                 udi_buf_free(cb->data_buf);
139                 //cb->data_buf = NULL;
140                 Workqueue_AddWork(&rdata->CBWorkQueue, cb);
141         }
142         else if( cb->op == UDI_GIO_OP_READ ) {
143                 // Send data to PTY
144                 UNIMPLEMENTED();
145                 // TODO: Since this was a full ACK, request more?
146         }
147         else {
148                 // Well, that was unexpected
149         }
150 }
151 void acessuart_xfer_nak(udi_gio_xfer_cb_t *cb, udi_status_t status)
152 {
153         udi_cb_t        *gcb = UDI_GCB(cb);
154         rdata_t *rdata = gcb->context;
155         if( cb->op == UDI_GIO_OP_READ && status == UDI_STAT_DATA_UNDERRUN )
156         {
157                 udi_size_t      len = cb->data_buf->buf_size;
158                 if( len == 0 )
159                 {
160                         udi_debug_printf("%s: no data read, rx buffer must be empty\n", __func__, len);
161                         ASSERT(status != UDI_OK);
162                 }
163                 else
164                 {
165                         char    tmp[len];
166                         udi_buf_read(cb->data_buf, 0, len, tmp);
167                         for( int i = 0; i < len; i ++ ) {
168                                 if( tmp[i] == '\r' )
169                                         tmp[i] = '\n';
170                         }
171                         
172                         udi_debug_printf("%s: %i bytes '%.*s'\n", __func__, len, len, tmp);
173                         PTY_SendInput(rdata->PTYInstance, tmp, len);
174                 }
175                 
176                 udi_buf_free(cb->data_buf);
177                 
178                 // if status == OK, then all bytes we requested were read.
179                 // - In which case, we want to request more
180                 if( status == UDI_OK ) {
181                         UDI_BUF_ALLOC(acessuart_event_ind__buf_allocated, gcb, NULL, RX_SIZE, UDI_NULL_BUF_PATH);
182                         return ;
183                 }
184                 
185                 Workqueue_AddWork(&rdata->CBWorkQueue, cb);
186         }
187         else {
188                 UNIMPLEMENTED();
189         }
190 }
191 void acessuart_event_ind(udi_gio_event_cb_t *cb)
192 {
193         udi_cb_t        *gcb = UDI_GCB(cb);
194         rdata_t *rdata = gcb->context;
195
196         // ACK event before requesting read
197         udi_gio_event_res(cb);
198         
199         // Begin read request
200         udi_gio_xfer_cb_t       *read_cb = Workqueue_GetWork(&rdata->CBWorkQueue);
201         UDI_BUF_ALLOC(acessuart_event_ind__buf_allocated, UDI_GCB(read_cb), NULL, RX_SIZE, UDI_NULL_BUF_PATH);
202 }
203 void acessuart_event_ind__buf_allocated(udi_cb_t *gcb, udi_buf_t *buffer)
204 {
205         udi_gio_xfer_cb_t       *cb = UDI_MCB(gcb, udi_gio_xfer_cb_t);
206
207         cb->op = UDI_GIO_OP_READ;
208         cb->tr_params = NULL;
209         cb->data_buf = buffer;
210         
211         udi_gio_xfer_req(cb);
212         // Continued in acessuart_xfer_ack/nak
213 }
214
215
216 void acessuart_pty_output(void *Handle, size_t Length, const void *Data);
217 void acessuart_pty_output__buf_allocated(udi_cb_t *gcb, udi_buf_t *buffer);
218
219 void acessuart_pty_output(void *Handle, size_t Length, const void *Data)
220 {
221         LOG("Output '%.*s'", Length, Data);
222         
223         rdata_t *rdata = Handle;
224         udi_gio_xfer_cb_t       *cb = Workqueue_GetWork(&rdata->CBWorkQueue);
225         udi_cb_t        *gcb = UDI_GCB(cb);
226         
227         UDI_BUF_ALLOC(acessuart_pty_output__buf_allocated, gcb, Data, Length, UDI_NULL_BUF_PATH);
228         // don't bother waiting for tx to complete, workqueue will block when everything is in use
229         // - And once buf_alloc returns, the data is copied
230 }
231 void acessuart_pty_output__buf_allocated(udi_cb_t *gcb, udi_buf_t *buffer)
232 {
233         //rdata_t       *rdata = gcb->context;
234         LOG("buffer = %p\n", buffer);
235         udi_gio_xfer_cb_t       *cb = UDI_MCB(gcb, udi_gio_xfer_cb_t);
236
237         cb->op = UDI_GIO_OP_WRITE;
238         cb->tr_params = NULL;
239         cb->data_buf = buffer;  
240
241         udi_gio_xfer_req(cb);
242 }
243
244
245 // --------------------------------------------------------------------
246 udi_mgmt_ops_t  acessuart_mgmt_ops = {
247         acessuart_usage_ind,
248         udi_enumerate_no_children,
249         acessuart_devmgmt_req,
250         acessuart_final_cleanup_req
251 };
252 udi_ubit8_t     acessuart_mgmt_ops_flags[4] = {0,0,0,0};
253
254 udi_primary_init_t      acessuart_pri_init = {
255         .mgmt_ops = &acessuart_mgmt_ops,
256         .mgmt_op_flags = acessuart_mgmt_ops_flags,
257         .mgmt_scratch_requirement = 0,
258         .enumeration_attr_list_length = 0,
259         .rdata_size = sizeof(rdata_t),
260         .child_data_size = 0,
261         .per_parent_paths = 0
262 };
263
264 udi_gio_client_ops_t    acessuart_gio_ops = {
265         acessuart_channel_event_ind,
266         acessuart_bind_ack,
267         acessuart_unbind_ack,
268         acessuart_xfer_ack,
269         acessuart_xfer_nak,
270         acessuart_event_ind
271 };
272 udi_ubit8_t     acessuart_gio_op_flags[7] = {0};
273
274 udi_ops_init_t  acessuart_ops_list[] = {
275         {
276                 ACESSUART_OPS_GIO, ACESSUART_META_GIO, UDI_GIO_CLIENT_OPS_NUM,
277                 0, (udi_ops_vector_t*)&acessuart_gio_ops, acessuart_gio_op_flags
278         },
279         {0}
280 };
281 udi_cb_init_t   acessuart_cb_init_list[] = {
282         {ACESSUART_CB_BIND, ACESSUART_META_GIO, UDI_GIO_BIND_CB_NUM, 0, 0,NULL},
283         {ACESSUART_CB_XFER, ACESSUART_META_GIO, UDI_GIO_XFER_CB_NUM, 0, 0,NULL},
284         {0}
285 };
286 const udi_init_t        acessuart_init = {
287         .primary_init_info = &acessuart_pri_init,
288         .ops_init_list = acessuart_ops_list,
289         .cb_init_list = acessuart_cb_init_list,
290 };
291 const char      acessuart_udiprops[] = 
292         "properties_version 0x101\0"
293         "message 1 Acess2 Kernel\0"
294         "message 2 John Hodge ([email protected])\0"
295         "message 3 Acess2 UART\0"
296         "supplier 1\0"
297         "contact 2\0"
298         "name 3\0"
299         "module acess_uart\0"
300         "shortname acessuart\0"
301         "requires udi 0x101\0"
302         "requires udi_gio 0x101\0"
303         "meta 1 udi_gio\0"
304         "message 101 UART\0"
305         "device 101 1 gio_type string uart\0"
306         "parent_bind_ops 1 0 1 1\0"
307         "\0";
308 size_t  acessuart_udiprops_size = sizeof(acessuart_udiprops);

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