2 /*
3 Copyright (c) 2016, Texas Instruments Incorporated - http://www.ti.com/
4 All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the
16 * distribution.
17 *
18 * Neither the name of Texas Instruments Incorporated nor the names of
19 * its contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 *
34 */
36 /*
37 * ======== sio2.c ========
38 * Stream I/O Manager
39 */
41 #include <xdc/std.h>
42 #include <xdc/runtime/Error.h>
43 #include <xdc/runtime/Memory.h>
44 #include <xdc/runtime/Log.h>
45 #include <xdc/runtime/IHeap.h>
47 #include <stddef.h>
49 #include <ti/sysbios/BIOS.h>
50 #include <ti/sysbios/knl/Queue.h>
51 #include <ti/sysbios/heaps/HeapMem.h>
53 #include "sio2.h"
55 #define _BOTH_
57 extern const ti_sysbios_heaps_HeapMem_Handle heapMemDdr3;
58 #define HEAPDDR3 (IHeap_Handle)heapMemDdr3
60 __FAR__ SIO2_Attrs SIO2_ATTRS = {
61 2, /* nbufs */
62 0, /* bufSeg, not segid */
63 0, /* align */
64 FALSE, /* flush */
65 SIO2_STANDARD, /* model */
66 BIOS_WAIT_FOREVER, /* timeout */
67 NULL /* callback */
68 };
70 /*
71 * ======== SIO2_create ========
72 */
73 SIO2_Handle SIO2_create(String name, Int mode, size_t bufsize,
74 SIO2_Attrs *attrs)
75 {
76 Int i;
77 Int errno = SIO2_ENOMEM; /* for use after "goto delete" */
78 DEV2_Device *entry;
79 DEV2_Handle device; /* == stream */
80 DEV2_Frame *frame;
81 SIO2_Handle stream;
82 Queue_Handle queue;
83 Error_Block eb;
85 if (attrs == NULL) {
86 attrs = &SIO2_ATTRS;
87 }
88 name = DEV2_match(name, &entry);
90 if (entry == NULL) {
91 /*
92 * We print the passed name instead of the module name ("SIO2")
93 * for a more informative error message.
94 *
95 * If DEV2_match() matches, name no longer points to the original
96 * device name, but if entry is NULL, name is unchanged.
97 */
98 Log_error1("Device %s not found", (IArg)name);
99 return (NULL);
100 }
102 if (entry->type != DEV2_SIOTYPE) {
103 Log_error2("SIO2_create: Device %s invalid type: %d",
104 (IArg)name, entry->type);
105 return (NULL);
106 }
108 Error_init(&eb);
109 stream = (SIO2_Handle)Memory_calloc(NULL /* attrs->bufSeg? */, sizeof(SIO2_Obj), 0, &eb);
111 if (stream == NULL) {
112 Log_error1("SIO2_create: Memory_calloc() failed from bufSeg:%d!", (IArg)attrs->bufSeg);
113 return (NULL);
114 }
116 stream->flush = attrs->flush; /* save flush flag for closing */
117 stream->model = attrs->model;
118 stream->pfxns = NULL; /* NULL for all dynamic SIO2 */
120 /*
121 * Initialize now since SIO2_delete() will check if the
122 * framelist is empty, so the queue must be initialized.
123 */
124 Queue_construct(&stream->framelist, NULL);
126 /* first field in SIO2_Obj is a DEV2_Obj */
127 device = (DEV2_Handle)stream;
129 /* fill in device object fields */
130 device->mode = mode; /* DEV2_INPUT or DEV2_OUTPUT */
131 device->devid = entry->devid; /* meaning depends on device */
132 device->bufsize = bufsize; /* size to be allocated per buffer */
133 device->align = attrs->align; /* buffer alignment */
134 device->nbufs = attrs->nbufs; /* max # of buffers allowed */
135 device->bufSeg = attrs->bufSeg; /* allocation memory segment */
136 device->params = entry->params; /* device parameters handle */
137 device->timeout = attrs->timeout; /* timeout used by DEV2_reclaim() */
138 device->callback = attrs->callback; /* callback parameters */
140 /* assign pointer to driver function table */
141 device->fxns = *(DEV2_Fxns *)(entry->fxns);
143 /*
144 * create todevice and fromdevice queues;
145 */
146 if ((queue = Queue_create(NULL, &eb)) == NULL) {
147 goto delete;
148 };
149 device->todevice = queue;
151 if ((queue = Queue_create(NULL, &eb)) == NULL) {
152 goto delete;
153 };
154 device->fromdevice = queue;
156 /*
157 * Do not allocate buffers in SIO2_ISSUERECLAIM model since the application
158 * will provide the buffers.
159 */
160 if (attrs->model == SIO2_ISSUERECLAIM) {
161 bufsize = 0;
162 }
164 /* allocate the frame objects, put on framelist queue */
165 for (i = device->nbufs; i > 0; i--) {
166 frame = DEV2_mkframe(attrs->bufSeg, bufsize, attrs->align);
167 if (frame == NULL) {
168 goto delete;
169 }
171 Queue_put(Queue_handle(&stream->framelist), (Queue_Elem *)frame);
172 }
174 /* Call the device-specific open function using DEV2_open macro */
175 if ((errno = DEV2_open(device, name)) != SIO2_OK) {
177 /* open function returned an error */
178 delete:
179 /*
180 * Since SIO2_delete() calls idle and close before freeing
181 * the device frames and buffers, we bind SYS_zero() to
182 * make these effectively nops. This allows us to save space
183 * by not having a special _SIO2_rmstream() function.
184 */
185 device->fxns.idle = DEV2_IDLE;
186 device->fxns.close = DEV2_CLOSE;
188 /*
189 * WARNING! This is used by SIO2_delete() to make sure that
190 * it doesn't traverse the 'todevice' queue. There's a strong
191 * dependancy between SIO2_create() and SIO2_delete().
192 */
193 device->nbufs = 0;
195 SIO2_delete(stream);
197 /*
198 * errno could be SIO2_ENOMEM here, so we need to cover the most
199 * restrictive case for the SYS_errors[] array. Although 0s are
200 * the wrong parameters for a SIO2_ENOMEM message, they're better
201 * than random garbage, and it's OK to pass too many for others.
202 */
203 Log_error1("SIO2_create: DEV2_open() error: %d", errno);
205 return (NULL);
206 }
208 /* device-specific open function was successful */
209 return (stream);
210 }
212 /*
213 * ======== SIO2_delete ========
214 */
215 Int SIO2_delete(SIO2_Handle stream)
216 {
217 DEV2_Handle device = (DEV2_Handle)stream;
218 Queue_Handle queue;
219 DEV2_Frame *frame;
220 Int status;
221 #if defined(_BOTH_)
222 Int nbufs;
223 #endif
224 status = DEV2_idle(device, stream->flush); /* idle device(s) */
226 if (status != SIO2_OK) {
227 return (status); /* idle failed */
228 }
230 #if defined(_BOTH_)
231 /*
232 * Pull all frames back from the device level and free them.
233 * In SIO2_ISSUERECLAIM mode, the client must call SIO2_reclaim()
234 * to get back all frames before calling SIO2_delete().
235 */
236 if (stream->model == SIO2_STANDARD) {
237 nbufs = device->nbufs - 1;
239 /*
240 * Handle the boundary case, where SIO2_delete() is called
241 * before nbufs I/O operations have occurred.
242 */
243 queue = Queue_handle(&(stream->framelist));
244 while ((frame = Queue_get(queue)) != (DEV2_Frame *)queue) {
245 DEV2_rmframe(frame, device->bufSeg, device->bufsize);
246 nbufs--;
247 }
249 /*
250 * Get any outstanding frames back and free them.
251 */
252 for (; nbufs > 0; nbufs--) {
253 status = DEV2_reclaim(device);
255 if (status != SIO2_OK) {
256 return (status);
257 }
259 frame = Queue_get(device->fromdevice);
260 DEV2_rmframe(frame, device->bufSeg, device->bufsize);
261 }
263 /*
264 * Free the standard frame object, but not the buffer. The
265 * application has the buffer.
266 */
267 if (stream->standardFrame) {
268 DEV2_rmframe(stream->standardFrame, 0, 0);
269 }
270 }
271 else {
272 queue = Queue_handle(&stream->framelist);
273 while ((frame = Queue_get(queue)) != (DEV2_Frame *)queue) {
274 DEV2_rmframe(frame, device->bufSeg, 0);
275 }
276 }
277 #else
279 queue = Queue_handle(&stream->framelist);
280 while ((frame = Queue_get(queue)) != (DEV2_Frame *)queue) {
281 DEV2_rmframe(frame, device->bufSeg, 0);
282 }
283 #endif
285 status = DEV2_close(device); /* close device(s) */
286 if (status != SIO2_OK) {
287 return (status);
288 }
290 if (device->todevice) {
291 Queue_delete(&device->todevice);
292 }
293 if (device->fromdevice) {
294 Queue_delete(&device->fromdevice);
295 }
297 /* free stream object */
298 Memory_free(0, stream, sizeof(SIO2_Obj));
300 /* return status from idle and close (above) */
301 return (status);
302 }
304 /*
305 * ======== SIO2_get ========
306 *
307 * Exchange an empty buffer with a non-empty buffer from "stream".
308 * Before: "ppbuf" points to empty buffer;
309 * After: "ppbuf" points to non-empty buffer.
310 */
311 Int SIO2_get(SIO2_Handle stream, Ptr *ppbuf)
312 {
313 DEV2_Handle dev;
314 DEV2_Frame *frame;
315 Int status;
317 dev = (DEV2_Handle)stream;
319 /*
320 * This will only execute the first time SIO2_get() is called.
321 * Copy all frames from framelist to the device's todevice queue.
322 * The device is issued all empty frames the first time SIO2_get()
323 * is called.
324 */
325 while ((frame = (DEV2_Frame *)Queue_get(Queue_handle(&stream->framelist))) !=
326 (DEV2_Frame *)&stream->framelist ) {
327 Queue_put(dev->todevice, (Queue_Elem *)frame);
328 status = DEV2_issue(dev);
329 if (status != SIO2_OK) {
330 return (status * -1);
331 }
332 }
334 frame = stream->standardFrame;
336 /* load the frame with the buffer pointed to by ppbuf */
337 frame->addr = *ppbuf;
338 frame->size = dev->bufsize;
340 /* put the frame on the todevice queue */
341 Queue_put(dev->todevice, (Queue_Elem *)frame);
343 /* send the frame to the device */
344 status = DEV2_issue(dev);
345 if (status != SIO2_OK) {
346 return (status * -1);
347 }
349 /* wait for an input frame to be available */
350 status = DEV2_reclaim(dev);
351 if (status != SIO2_OK) {
352 return (status * -1);
353 }
355 frame = (DEV2_Frame *)Queue_get(dev->fromdevice);
357 stream->standardFrame = frame;
359 *ppbuf = frame->addr;
361 return (frame->size);
362 }
364 /*
365 * ======== _SIO2_idle ========
366 * _SIO2_idle is called by SIO2_idle() and SIO2_flush() macros
367 */
368 Int _SIO2_idle(SIO2_Handle stream, Bool flush)
369 {
370 DEV2_Handle device = (DEV2_Handle)stream;
371 Int status;
372 #if defined(_BOTH_)
373 Queue_Handle queue;
374 DEV2_Frame *frame;
375 Int nbufs;
376 #endif
378 status = DEV2_idle(device, flush); /* idle device(s) */
380 if (status != SIO2_OK) {
381 return (status);
382 }
384 #if defined(_BOTH_)
385 /*
386 * For standard model, pull all frames back from the device level and
387 * put them on the stream's framelist.
388 */
389 if (stream->model == SIO2_STANDARD) {
390 nbufs = device->nbufs - 1;
392 /*
393 * Handle the boundary case, where SIO2_idle() is called
394 * before nbufs I/O operations have occurred. Count the
395 * number of frames on the framelist.
396 */
397 queue = Queue_handle(&stream->framelist);
398 frame = Queue_head(queue);
399 while (frame != (DEV2_Frame *)queue) {
400 frame = Queue_next((Queue_Elem *)frame);
401 nbufs--;
402 }
404 /*
405 * Get any outstanding frames back from the device and
406 * put them onto the stream's framelist.
407 */
408 for (; nbufs > 0; nbufs--) {
409 if ((status = DEV2_reclaim(device)) != SIO2_OK) {
410 return (status);
411 }
412 frame = Queue_get(device->fromdevice);
413 Queue_put(Queue_handle(&stream->framelist), (Queue_Elem *)frame);
414 }
415 }
416 #endif
418 return (SIO2_OK);
419 }
421 /*
422 * ======== SIO2_issue ========
423 *
424 * Send buffer "pbuf" of size "nbytes" to "stream" with arguments "arg".
425 * Used for both input and output. Returns to caller without blocking.
426 *
427 * "stream" must have been created using attrs.mode = SIO2_ISSUERECLAIM.
428 */
429 Int SIO2_issue(SIO2_Handle stream, Ptr pbuf, size_t nbytes, Arg arg)
430 {
431 DEV2_Handle dev;
432 DEV2_Frame *frame;
433 Int status;
436 dev = (DEV2_Handle)stream;
438 /* get a valid frame from the stream's framelist queue or return error */
439 frame = (DEV2_Frame *)Queue_get(Queue_handle(&stream->framelist));
440 if (frame == (DEV2_Frame *)&stream->framelist) {
441 return (DEV2_EBADIO);
442 }
444 /* load the frame with the buffer pointed to by pbuf */
445 frame->addr = pbuf;
446 frame->size = nbytes;
447 frame->arg = arg;
449 /* put the frame on the todevice queue */
450 Queue_put(dev->todevice, (Queue_Elem *)frame);
452 /* call the device's issue function to send the buffer to the stream */
453 status = DEV2_issue(dev);
455 return (status);
456 }
458 /*
459 * ======== SIO2_put ========
460 *
461 * Exchange a non-empty buffer "buf" of size "nbytes" with an empty buffer
462 * from "stream".
463 * Before: "ppbuf" points to non-empty buffer;
464 * After: "ppbuf" points to empty buffer.
465 *
466 */
467 Int SIO2_put(SIO2_Handle stream, Ptr *ppbuf, size_t nbytes)
468 {
469 DEV2_Handle dev;
470 DEV2_Frame *frame;
471 Int status;
473 dev = (DEV2_Handle)stream;
474 frame = stream->standardFrame;
475 frame->size = nbytes;
476 frame->addr = *ppbuf;
478 Queue_put(dev->todevice, (Queue_Elem *)frame);
480 DEV2_issue(dev);
482 if ( (frame = (DEV2_Frame *)Queue_get(Queue_handle(&stream->framelist))) !=
483 (DEV2_Frame *)&stream->framelist ) {
485 /* free frame is available -- no need to call DEV2_reclaim() */
486 stream->standardFrame = frame;
487 }
488 else {
490 /* reclaim frame from device */
491 status = DEV2_reclaim(dev);
492 if (status != SIO2_OK) {
493 return (status * -1);
494 }
496 frame = (DEV2_Frame *)Queue_get(dev->fromdevice);
497 stream->standardFrame = frame;
498 }
500 *ppbuf = frame->addr;
502 return (frame->size);
503 }
505 /*
506 * ======== SIO2_reclaimx ========
507 *
508 * Extended version of SIO2_reclaim(). Identical to SIO2_reclaim() but adds
509 * new parameter that will return the frame status field. This allows the
510 * IOM driver to pass frame-specific status information up to the application.
511 *
512 * Applications still check return value of SIO2_reclaimx() (< 0 for error),
513 * and >= 0 for success (frame size).
514 *
515 * See SDSsq41555 for more.
516 */
517 Int SIO2_reclaimx(SIO2_Handle stream, Ptr *ppbuf, Arg *parg, Int *pfstatus)
518 {
519 DEV2_Handle dev;
520 DEV2_Frame *frame;
521 size_t size;
522 Int status;
524 dev = (DEV2_Handle)stream;
526 status = DEV2_reclaim(dev);
528 if (status == SIO2_OK) {
529 frame = (DEV2_Frame *)Queue_get(dev->fromdevice);
531 *ppbuf = frame->addr;
533 if (parg != NULL) {
534 *parg = frame->arg;
535 }
537 if (pfstatus != NULL) {
538 *pfstatus = frame->status;
539 }
541 size = frame->size;
543 /*
544 * Return frame to free list (size saved above for return).
545 */
546 frame->size = 0;
547 Queue_put(Queue_handle(&stream->framelist), (Queue_Elem *)frame);
549 return (size);
550 }
551 else {
552 return (status * -1);
553 }
554 }
556 /*
557 * ======== SIO2_reclaim ========
558 */
559 Int SIO2_reclaim(SIO2_Handle stream, Ptr *ppbuf, Arg *parg)
560 {
561 DEV2_Handle dev;
562 DEV2_Frame *frame;
563 size_t size;
564 Int status;
566 dev = (DEV2_Handle)stream;
568 status = DEV2_reclaim(dev);
570 if (status == SIO2_OK) {
571 frame = (DEV2_Frame *)Queue_get(dev->fromdevice);
573 *ppbuf = frame->addr;
574 if (parg != NULL) {
575 *parg = frame->arg;
576 }
577 size = frame->size;
579 /*
580 * Return frame to free list (size saved above for return).
581 */
582 frame->size = 0;
583 Queue_put(Queue_handle(&stream->framelist), (Queue_Elem *)frame);
585 return (size);
586 }
587 else {
588 return (status * -1);
589 }
590 }
592 /*
593 * ======== SIO2_select ========
594 */
595 Uns SIO2_select(SIO2_Handle streamtab[], Int n, Uns timeout)
596 {
597 Int i;
598 Uns mask = 1;
599 Uns ready = 0;
600 Semaphore_Struct sem; /* local variable on stack! */
601 Semaphore_Params semParams;
602 SIO2_Handle *stream;
603 #if defined(_BOTH_)
604 DEV2_Frame *frame;
605 #endif
607 Semaphore_Params_init(&semParams);
608 semParams.mode = Semaphore_Mode_BINARY;
609 Semaphore_construct(&sem, 0, &semParams);
611 stream = streamtab;
613 for (i = n; i > 0; i--, stream++) {
615 #if defined(_BOTH_)
616 if ( (*stream)->model == SIO2_STANDARD ) {
617 if ((*stream)->dobj.mode == DEV2_INPUT) {
618 /*
619 * Make sure input device has been primed and started. This
620 * is typically done by SIO2_get() but it is possible for
621 * SIO2_select() to be called before SIO2_get(). This code is
622 * identical to the code in SIO2_get() -- it may make sense to
623 * make this loop into a separate function (_SIO2_primeget()?)
624 * to save code space.
625 */
626 while ( (frame = (DEV2_Frame *)Queue_get(Queue_handle(
627 &(*stream)->framelist))) !=
628 (DEV2_Frame *)&(*stream)->framelist ) {
629 Queue_put((*stream)->dobj.todevice, (Queue_Elem *)frame);
630 DEV2_issue((DEV2_Handle)*stream);
632 /* what can we do if DEV2_issue fails? */
633 }
634 }
635 else {
636 /*
637 * Initial calls to SIO2_put() pull frames from the stream's
638 * framelist instead of calling DEV2_reclaim(). Frames are
639 * never put back onto framelist in SIO2_STANDARD model.
640 */
641 if (!Queue_empty(Queue_handle(&(*stream)->framelist))) {
642 ready = 1;
643 }
644 }
645 }
646 #endif
648 /* see if device is ready and register call back */
649 if (DEV2_ready((DEV2_Handle)*stream, Semaphore_handle(&sem))) {
650 ready = 1;
651 }
652 }
654 if (!ready) {
655 /* wait until device is ready */
656 Semaphore_pend(Semaphore_handle(&sem), timeout);
657 }
659 ready = 0;
661 stream = streamtab;
663 for (i = n; i > 0; i--, stream++) {
664 /* see if device is ready and un-register call back */
665 if (DEV2_ready((DEV2_Handle)*stream, NULL)) {
666 ready |= mask;
667 }
669 #if defined(_BOTH_)
670 else {
671 /*
672 * This will only be true for DEV2_OUTPUT mode.
673 * We don't need to check if mode == DEV2_OUTPUT here since
674 * framelist will have been emptied for DEV2_INPUT mode (above).
675 */
676 if ( ((*stream)->model == SIO2_STANDARD) &&
677 (!Queue_empty(Queue_handle(&(*stream)->framelist))) ) {
678 ready |= mask;
679 }
680 }
681 #endif
683 mask = mask << 1;
684 }
686 return (ready);
687 }
689 /*
690 * ======== SIO2_staticbuf ========
691 * Return static buffer created for gconf streams.
692 *
693 * The SIO2_Obj macro puts all static frames on 'stream->framelist'.
694 *
695 * Returns 0 if no static buffer exists.
696 * SIO2_staticbuf can only be called once for each static buffer.
697 */
698 Int SIO2_staticbuf(SIO2_Handle stream, Ptr *ppbuf)
699 {
700 DEV2_Frame *frame;
701 Int size;
702 #if defined(_BOTH_)
703 if (stream->model == SIO2_STANDARD) {
704 frame = stream->standardFrame;
706 size = frame->size;
707 frame->size = 0;
709 *ppbuf = frame->addr;
710 }
711 else {
712 frame = Queue_get(Queue_handle(&stream->framelist));
714 size = frame->size;
715 frame->size = 0;
717 *ppbuf = frame->addr;
719 Queue_put(Queue_handle(&stream->framelist), (Queue_Elem *)frame);
720 }
721 #else
723 frame = Queue_get(Queue_handle(&stream->framelist));
725 size = frame->size;
726 frame->size = 0;
728 *ppbuf = frame->addr;
730 Queue_put(Queue_handle(&stream->framelist), frame);
732 #endif
734 return (size);
735 }