/* Copyright (c) 2016, Texas Instruments Incorporated - http://www.ti.com/ All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* * ======== sio2.c ======== * Stream I/O Manager */ #include #include #include #include #include #include #include #include #include #include "sio2.h" #define _BOTH_ extern const ti_sysbios_heaps_HeapMem_Handle heapMemDdr3; #define HEAPDDR3 (IHeap_Handle)heapMemDdr3 __FAR__ SIO2_Attrs SIO2_ATTRS = { 2, /* nbufs */ 0, /* bufSeg, not segid */ 0, /* align */ FALSE, /* flush */ SIO2_STANDARD, /* model */ BIOS_WAIT_FOREVER, /* timeout */ NULL /* callback */ }; /* * ======== SIO2_create ======== */ SIO2_Handle SIO2_create(String name, Int mode, size_t bufsize, SIO2_Attrs *attrs) { Int i; Int errno = SIO2_ENOMEM; /* for use after "goto delete" */ DEV2_Device *entry; DEV2_Handle device; /* == stream */ DEV2_Frame *frame; SIO2_Handle stream; Queue_Handle queue; Error_Block eb; if (attrs == NULL) { attrs = &SIO2_ATTRS; } name = DEV2_match(name, &entry); if (entry == NULL) { /* * We print the passed name instead of the module name ("SIO2") * for a more informative error message. * * If DEV2_match() matches, name no longer points to the original * device name, but if entry is NULL, name is unchanged. */ Log_error1("Device %s not found", (IArg)name); return (NULL); } if (entry->type != DEV2_SIOTYPE) { Log_error2("SIO2_create: Device %s invalid type: %d", (IArg)name, entry->type); return (NULL); } Error_init(&eb); stream = (SIO2_Handle)Memory_calloc(NULL /* attrs->bufSeg? */, sizeof(SIO2_Obj), 0, &eb); if (stream == NULL) { Log_error1("SIO2_create: Memory_calloc() failed from bufSeg:%d!", (IArg)attrs->bufSeg); return (NULL); } stream->flush = attrs->flush; /* save flush flag for closing */ stream->model = attrs->model; stream->pfxns = NULL; /* NULL for all dynamic SIO2 */ /* * Initialize now since SIO2_delete() will check if the * framelist is empty, so the queue must be initialized. */ Queue_construct(&stream->framelist, NULL); /* first field in SIO2_Obj is a DEV2_Obj */ device = (DEV2_Handle)stream; /* fill in device object fields */ device->mode = mode; /* DEV2_INPUT or DEV2_OUTPUT */ device->devid = entry->devid; /* meaning depends on device */ device->bufsize = bufsize; /* size to be allocated per buffer */ device->align = attrs->align; /* buffer alignment */ device->nbufs = attrs->nbufs; /* max # of buffers allowed */ device->bufSeg = attrs->bufSeg; /* allocation memory segment */ device->params = entry->params; /* device parameters handle */ device->timeout = attrs->timeout; /* timeout used by DEV2_reclaim() */ device->callback = attrs->callback; /* callback parameters */ /* assign pointer to driver function table */ device->fxns = *(DEV2_Fxns *)(entry->fxns); /* * create todevice and fromdevice queues; */ if ((queue = Queue_create(NULL, &eb)) == NULL) { goto delete; }; device->todevice = queue; if ((queue = Queue_create(NULL, &eb)) == NULL) { goto delete; }; device->fromdevice = queue; /* * Do not allocate buffers in SIO2_ISSUERECLAIM model since the application * will provide the buffers. */ if (attrs->model == SIO2_ISSUERECLAIM) { bufsize = 0; } /* allocate the frame objects, put on framelist queue */ for (i = device->nbufs; i > 0; i--) { frame = DEV2_mkframe(attrs->bufSeg, bufsize, attrs->align); if (frame == NULL) { goto delete; } Queue_put(Queue_handle(&stream->framelist), (Queue_Elem *)frame); } /* Call the device-specific open function using DEV2_open macro */ if ((errno = DEV2_open(device, name)) != SIO2_OK) { /* open function returned an error */ delete: /* * Since SIO2_delete() calls idle and close before freeing * the device frames and buffers, we bind SYS_zero() to * make these effectively nops. This allows us to save space * by not having a special _SIO2_rmstream() function. */ device->fxns.idle = DEV2_IDLE; device->fxns.close = DEV2_CLOSE; /* * WARNING! This is used by SIO2_delete() to make sure that * it doesn't traverse the 'todevice' queue. There's a strong * dependancy between SIO2_create() and SIO2_delete(). */ device->nbufs = 0; SIO2_delete(stream); /* * errno could be SIO2_ENOMEM here, so we need to cover the most * restrictive case for the SYS_errors[] array. Although 0s are * the wrong parameters for a SIO2_ENOMEM message, they're better * than random garbage, and it's OK to pass too many for others. */ Log_error1("SIO2_create: DEV2_open() error: %d", errno); return (NULL); } /* device-specific open function was successful */ return (stream); } /* * ======== SIO2_delete ======== */ Int SIO2_delete(SIO2_Handle stream) { DEV2_Handle device = (DEV2_Handle)stream; Queue_Handle queue; DEV2_Frame *frame; Int status; #if defined(_BOTH_) Int nbufs; #endif status = DEV2_idle(device, stream->flush); /* idle device(s) */ if (status != SIO2_OK) { return (status); /* idle failed */ } #if defined(_BOTH_) /* * Pull all frames back from the device level and free them. * In SIO2_ISSUERECLAIM mode, the client must call SIO2_reclaim() * to get back all frames before calling SIO2_delete(). */ if (stream->model == SIO2_STANDARD) { nbufs = device->nbufs - 1; /* * Handle the boundary case, where SIO2_delete() is called * before nbufs I/O operations have occurred. */ queue = Queue_handle(&(stream->framelist)); while ((frame = Queue_get(queue)) != (DEV2_Frame *)queue) { DEV2_rmframe(frame, device->bufSeg, device->bufsize); nbufs--; } /* * Get any outstanding frames back and free them. */ for (; nbufs > 0; nbufs--) { status = DEV2_reclaim(device); if (status != SIO2_OK) { return (status); } frame = Queue_get(device->fromdevice); DEV2_rmframe(frame, device->bufSeg, device->bufsize); } /* * Free the standard frame object, but not the buffer. The * application has the buffer. */ if (stream->standardFrame) { DEV2_rmframe(stream->standardFrame, 0, 0); } } else { queue = Queue_handle(&stream->framelist); while ((frame = Queue_get(queue)) != (DEV2_Frame *)queue) { DEV2_rmframe(frame, device->bufSeg, 0); } } #else queue = Queue_handle(&stream->framelist); while ((frame = Queue_get(queue)) != (DEV2_Frame *)queue) { DEV2_rmframe(frame, device->bufSeg, 0); } #endif status = DEV2_close(device); /* close device(s) */ if (status != SIO2_OK) { return (status); } if (device->todevice) { Queue_delete(&device->todevice); } if (device->fromdevice) { Queue_delete(&device->fromdevice); } /* free stream object */ Memory_free(0, stream, sizeof(SIO2_Obj)); /* return status from idle and close (above) */ return (status); } /* * ======== SIO2_get ======== * * Exchange an empty buffer with a non-empty buffer from "stream". * Before: "ppbuf" points to empty buffer; * After: "ppbuf" points to non-empty buffer. */ Int SIO2_get(SIO2_Handle stream, Ptr *ppbuf) { DEV2_Handle dev; DEV2_Frame *frame; Int status; dev = (DEV2_Handle)stream; /* * This will only execute the first time SIO2_get() is called. * Copy all frames from framelist to the device's todevice queue. * The device is issued all empty frames the first time SIO2_get() * is called. */ while ((frame = (DEV2_Frame *)Queue_get(Queue_handle(&stream->framelist))) != (DEV2_Frame *)&stream->framelist ) { Queue_put(dev->todevice, (Queue_Elem *)frame); status = DEV2_issue(dev); if (status != SIO2_OK) { return (status * -1); } } frame = stream->standardFrame; /* load the frame with the buffer pointed to by ppbuf */ frame->addr = *ppbuf; frame->size = dev->bufsize; /* put the frame on the todevice queue */ Queue_put(dev->todevice, (Queue_Elem *)frame); /* send the frame to the device */ status = DEV2_issue(dev); if (status != SIO2_OK) { return (status * -1); } /* wait for an input frame to be available */ status = DEV2_reclaim(dev); if (status != SIO2_OK) { return (status * -1); } frame = (DEV2_Frame *)Queue_get(dev->fromdevice); stream->standardFrame = frame; *ppbuf = frame->addr; return (frame->size); } /* * ======== _SIO2_idle ======== * _SIO2_idle is called by SIO2_idle() and SIO2_flush() macros */ Int _SIO2_idle(SIO2_Handle stream, Bool flush) { DEV2_Handle device = (DEV2_Handle)stream; Int status; #if defined(_BOTH_) Queue_Handle queue; DEV2_Frame *frame; Int nbufs; #endif status = DEV2_idle(device, flush); /* idle device(s) */ if (status != SIO2_OK) { return (status); } #if defined(_BOTH_) /* * For standard model, pull all frames back from the device level and * put them on the stream's framelist. */ if (stream->model == SIO2_STANDARD) { nbufs = device->nbufs - 1; /* * Handle the boundary case, where SIO2_idle() is called * before nbufs I/O operations have occurred. Count the * number of frames on the framelist. */ queue = Queue_handle(&stream->framelist); frame = Queue_head(queue); while (frame != (DEV2_Frame *)queue) { frame = Queue_next((Queue_Elem *)frame); nbufs--; } /* * Get any outstanding frames back from the device and * put them onto the stream's framelist. */ for (; nbufs > 0; nbufs--) { if ((status = DEV2_reclaim(device)) != SIO2_OK) { return (status); } frame = Queue_get(device->fromdevice); Queue_put(Queue_handle(&stream->framelist), (Queue_Elem *)frame); } } #endif return (SIO2_OK); } /* * ======== SIO2_issue ======== * * Send buffer "pbuf" of size "nbytes" to "stream" with arguments "arg". * Used for both input and output. Returns to caller without blocking. * * "stream" must have been created using attrs.mode = SIO2_ISSUERECLAIM. */ Int SIO2_issue(SIO2_Handle stream, Ptr pbuf, size_t nbytes, Arg arg) { DEV2_Handle dev; DEV2_Frame *frame; Int status; dev = (DEV2_Handle)stream; /* get a valid frame from the stream's framelist queue or return error */ frame = (DEV2_Frame *)Queue_get(Queue_handle(&stream->framelist)); if (frame == (DEV2_Frame *)&stream->framelist) { return (DEV2_EBADIO); } /* load the frame with the buffer pointed to by pbuf */ frame->addr = pbuf; frame->size = nbytes; frame->arg = arg; /* put the frame on the todevice queue */ Queue_put(dev->todevice, (Queue_Elem *)frame); /* call the device's issue function to send the buffer to the stream */ status = DEV2_issue(dev); return (status); } /* * ======== SIO2_put ======== * * Exchange a non-empty buffer "buf" of size "nbytes" with an empty buffer * from "stream". * Before: "ppbuf" points to non-empty buffer; * After: "ppbuf" points to empty buffer. * */ Int SIO2_put(SIO2_Handle stream, Ptr *ppbuf, size_t nbytes) { DEV2_Handle dev; DEV2_Frame *frame; Int status; dev = (DEV2_Handle)stream; frame = stream->standardFrame; frame->size = nbytes; frame->addr = *ppbuf; Queue_put(dev->todevice, (Queue_Elem *)frame); DEV2_issue(dev); if ( (frame = (DEV2_Frame *)Queue_get(Queue_handle(&stream->framelist))) != (DEV2_Frame *)&stream->framelist ) { /* free frame is available -- no need to call DEV2_reclaim() */ stream->standardFrame = frame; } else { /* reclaim frame from device */ status = DEV2_reclaim(dev); if (status != SIO2_OK) { return (status * -1); } frame = (DEV2_Frame *)Queue_get(dev->fromdevice); stream->standardFrame = frame; } *ppbuf = frame->addr; return (frame->size); } /* * ======== SIO2_reclaimx ======== * * Extended version of SIO2_reclaim(). Identical to SIO2_reclaim() but adds * new parameter that will return the frame status field. This allows the * IOM driver to pass frame-specific status information up to the application. * * Applications still check return value of SIO2_reclaimx() (< 0 for error), * and >= 0 for success (frame size). * * See SDSsq41555 for more. */ Int SIO2_reclaimx(SIO2_Handle stream, Ptr *ppbuf, Arg *parg, Int *pfstatus) { DEV2_Handle dev; DEV2_Frame *frame; size_t size; Int status; dev = (DEV2_Handle)stream; status = DEV2_reclaim(dev); if (status == SIO2_OK) { frame = (DEV2_Frame *)Queue_get(dev->fromdevice); *ppbuf = frame->addr; if (parg != NULL) { *parg = frame->arg; } if (pfstatus != NULL) { *pfstatus = frame->status; } size = frame->size; /* * Return frame to free list (size saved above for return). */ frame->size = 0; Queue_put(Queue_handle(&stream->framelist), (Queue_Elem *)frame); return (size); } else { return (status * -1); } } /* * ======== SIO2_reclaim ======== */ Int SIO2_reclaim(SIO2_Handle stream, Ptr *ppbuf, Arg *parg) { DEV2_Handle dev; DEV2_Frame *frame; size_t size; Int status; dev = (DEV2_Handle)stream; status = DEV2_reclaim(dev); if (status == SIO2_OK) { frame = (DEV2_Frame *)Queue_get(dev->fromdevice); *ppbuf = frame->addr; if (parg != NULL) { *parg = frame->arg; } size = frame->size; /* * Return frame to free list (size saved above for return). */ frame->size = 0; Queue_put(Queue_handle(&stream->framelist), (Queue_Elem *)frame); return (size); } else { return (status * -1); } } /* * ======== SIO2_select ======== */ Uns SIO2_select(SIO2_Handle streamtab[], Int n, Uns timeout) { Int i; Uns mask = 1; Uns ready = 0; Semaphore_Struct sem; /* local variable on stack! */ Semaphore_Params semParams; SIO2_Handle *stream; #if defined(_BOTH_) DEV2_Frame *frame; #endif Semaphore_Params_init(&semParams); semParams.mode = Semaphore_Mode_BINARY; Semaphore_construct(&sem, 0, &semParams); stream = streamtab; for (i = n; i > 0; i--, stream++) { #if defined(_BOTH_) if ( (*stream)->model == SIO2_STANDARD ) { if ((*stream)->dobj.mode == DEV2_INPUT) { /* * Make sure input device has been primed and started. This * is typically done by SIO2_get() but it is possible for * SIO2_select() to be called before SIO2_get(). This code is * identical to the code in SIO2_get() -- it may make sense to * make this loop into a separate function (_SIO2_primeget()?) * to save code space. */ while ( (frame = (DEV2_Frame *)Queue_get(Queue_handle( &(*stream)->framelist))) != (DEV2_Frame *)&(*stream)->framelist ) { Queue_put((*stream)->dobj.todevice, (Queue_Elem *)frame); DEV2_issue((DEV2_Handle)*stream); /* what can we do if DEV2_issue fails? */ } } else { /* * Initial calls to SIO2_put() pull frames from the stream's * framelist instead of calling DEV2_reclaim(). Frames are * never put back onto framelist in SIO2_STANDARD model. */ if (!Queue_empty(Queue_handle(&(*stream)->framelist))) { ready = 1; } } } #endif /* see if device is ready and register call back */ if (DEV2_ready((DEV2_Handle)*stream, Semaphore_handle(&sem))) { ready = 1; } } if (!ready) { /* wait until device is ready */ Semaphore_pend(Semaphore_handle(&sem), timeout); } ready = 0; stream = streamtab; for (i = n; i > 0; i--, stream++) { /* see if device is ready and un-register call back */ if (DEV2_ready((DEV2_Handle)*stream, NULL)) { ready |= mask; } #if defined(_BOTH_) else { /* * This will only be true for DEV2_OUTPUT mode. * We don't need to check if mode == DEV2_OUTPUT here since * framelist will have been emptied for DEV2_INPUT mode (above). */ if ( ((*stream)->model == SIO2_STANDARD) && (!Queue_empty(Queue_handle(&(*stream)->framelist))) ) { ready |= mask; } } #endif mask = mask << 1; } return (ready); } /* * ======== SIO2_staticbuf ======== * Return static buffer created for gconf streams. * * The SIO2_Obj macro puts all static frames on 'stream->framelist'. * * Returns 0 if no static buffer exists. * SIO2_staticbuf can only be called once for each static buffer. */ Int SIO2_staticbuf(SIO2_Handle stream, Ptr *ppbuf) { DEV2_Frame *frame; Int size; #if defined(_BOTH_) if (stream->model == SIO2_STANDARD) { frame = stream->standardFrame; size = frame->size; frame->size = 0; *ppbuf = frame->addr; } else { frame = Queue_get(Queue_handle(&stream->framelist)); size = frame->size; frame->size = 0; *ppbuf = frame->addr; Queue_put(Queue_handle(&stream->framelist), (Queue_Elem *)frame); } #else frame = Queue_get(Queue_handle(&stream->framelist)); size = frame->size; frame->size = 0; *ppbuf = frame->addr; Queue_put(Queue_handle(&stream->framelist), frame); #endif return (size); }