Bool bfqAllocBuffer ( BufferQueue_Handle queue, Ptr * buffer, Uns timeout ) { BufferQueue_Frame * frame; // wait until an empty buffer is available, timeout // if necessary. if (! SEM_pend(&(queue->semEmptyBuffers), timeout) ) { return FALSE; } // double check if ( QUE_empty(&(queue->queEmptyBuffers)) ) return FALSE; // Get the frame from the queue and double check it frame = QUE_get((Ptr)&(queue->queEmptyBuffers)); if ((Ptr)frame == (Ptr)&(queue->queEmptyBuffers)) return FALSE; // Copy the pointer *buffer = frame->Buffer; // put the frame to the empty frames queue QUE_put( &(queue->queEmptyFrames), frame); return TRUE; }
/* * ======== DIO_tskIssue ======== */ Int DIO_tskIssue(DEV_Handle device) { DIO_Handle dio = (DIO_Handle)device->object; DEV_Frame *frame; Int status; frame = QUE_get(device->todevice); frame->cmd = (device->mode == DEV_INPUT) ? IOM_READ : IOM_WRITE; frame->status = IOM_PENDING; status = dio->fxns->mdSubmitChan(dio->chanp, frame); if (status < 0) { return (SYS_EBADIO); } else { if (status == IOM_COMPLETED) { QUE_put(device->fromdevice, frame); SEM_post(dio->context.sems.complete); } return (SYS_OK); } }
/* * ======== DOV_close ======== */ static Int DOV_close(DEV_Handle device) { DOV_CopyObj *copy = (DOV_CopyObj *)device->object; DEV_Frame *frame; /* close underlying device(s) */ DEV_close(&(copy->dobj)); /* move frames _up_ from downstream device */ while (!QUE_empty(copy->dobj.todevice)) { frame = QUE_get(copy->dobj.todevice); frame->size = frame->size + copy->size; frame->addr = (Char *)frame->addr - copy->size; QUE_put(device->todevice, frame); } QUE_delete(copy->dobj.todevice); QUE_delete(copy->dobj.fromdevice); /* free overlap buffer */ MEM_free(0, copy->overlap, copy->size); /* recycle copy object */ MEM_free(0, copy, sizeof(DOV_CopyObj)); return (SYS_OK); }
/* * ======== DIO_cbCallback ======== */ Void DIO_cbCallback(Ptr devp, DEV_Frame *frame) { DEV_Handle device = (DEV_Handle)devp; DIO_Handle dio = (DIO_Handle)device->object; if (frame->cmd == IOM_READ || frame->cmd == IOM_WRITE) { QUE_put(device->fromdevice, frame); dio->context.cb.fxn(dio->context.cb.arg0, dio->context.cb.arg1); } }
/* * ======== unsetup_todevice ======== * Undo the setup done for the underlying device's todevice frame. * Called if the underlying device's DEV_issue() fails. */ static Void unsetup_todevice(DEV_Handle device) { DOV_CopyObj *copy = (DOV_CopyObj *)device->object; DEV_Frame *frame; /* get empty frame from downstream queue */ frame = QUE_get(copy->dobj.todevice); frame->addr = (Char *)frame->addr - copy->size; frame->size = frame->size + copy->size; QUE_put(device->todevice, frame); }
void bfqPutBuffer ( BufferQueue_Handle queue, Ptr buffer ) { BufferQueue_Frame * frame; // get a free frame (don't have to check if queue is empty). frame = QUE_get( (Ptr) &(queue->queEmptyFrames) ); // Put the buffer to the frame. frame->Buffer = buffer; // Put the frame on the _back queue. QUE_put( &(queue->queFullBuffers), frame); // ... and post the corresponding sem SEM_post( &(queue->semFullBuffers) ); }
/* * ======== DEV_init ======== * This function is called from DEV_init macro as part of BIOS_init sequence. */ Void DEV_init(Void) { DEV_TableElem *objDevHead; DEV_TableElem *objDev; DEV_Device *dptr; IOM_Fxns *fxns; Int status; Int i; /* Hook up the linked list ... */ for (i = 0; i < _DEV_numStaticDevs; i++) { QUE_put(&DEV_table, &(_DEV_staticDevTable[i])); } /* * For each device driver, call its DXX_init function *before* the * statically created streams are "opened" (calling the device's * open function for each static stream below). */ for (i = (Int) _DEV_numStaticDevs - 1; i >= 0; i--) { if (_DEV_initFxn[i] != NULL) { (_DEV_initFxn[i])(); } } /* * Call IOM bind device function (mdBindDev) if driver is of type IOM */ objDevHead = (DEV_TableElem *)&DEV_table; for(objDev = (DEV_TableElem *) QUE_next((Ptr)objDevHead); objDev != objDevHead; objDev = (DEV_TableElem *) QUE_next((Ptr)objDev)) { dptr = &objDev->device; if (dptr->type == DEV_IOMTYPE ) { fxns = (IOM_Fxns *)dptr->fxns; status = fxns->mdBindDev(&dptr->devp, dptr->devid, dptr->params); if (status != IOM_COMPLETED) { SYS_abort("ERROR - Device %s Config Failed", dptr->name); } } } }
Bool bfqCreate ( BufferQueue_Handle queue, BufferQueue_Frame * frames, Int BufCount, Int BufSize, Int SegID) { Int i; Ptr p; // Acquire the frames queue->Frames = frames; queue->bDynamicFrames = FALSE; // Init the QUE and SEM objects. SEM_new(&(queue->semFullBuffers), 0); SEM_new(&(queue->semEmptyBuffers), BufCount); QUE_new( &(queue->queFullBuffers) ); QUE_new( &(queue->queEmptyBuffers) ); QUE_new( &(queue->queEmptyFrames) ); for (i=0; i<BufCount; i++) { // TODO: handle errors. // If the _BFQ_INIT_BUFFERS is defined, fill the buffers with 0xFF, // leave them uninitialized, otherwise. #ifdef _BFQ_INIT_BUFFERS p = MEM_valloc( SegID, BufSize, 0, 0xFF ); queue->Frames[i].Buffer = p; assertLog( p != MEM_ILLEGAL); #else p = MEM_alloc( SegID, BufSize, 0 ); queue->Frames[i].Buffer = p; assertLog( p != MEM_ILLEGAL); #endif QUE_put(&(queue->queEmptyBuffers), &(queue->Frames[i])); } queue->BufSize = BufSize; queue->BufCount = BufCount; queue->SegId = SegID; return TRUE; }
/* * ======== setup_fromdevice ======== * Setup the fromdevice frame for the upper layer (probably SIO). * Common code for DOV_input() after the DEV_input() call and for * DOV_reclaim() after the DEV_reclaim() call. */ static Void setup_fromdevice(DEV_Handle device) { DOV_CopyObj *copy = (DOV_CopyObj *)device->object; DEV_Frame *frame; frame = QUE_get(copy->dobj.fromdevice); frame->addr = (Char *)frame->addr - copy->size; frame->size = frame->size + copy->size; /* * copy bytes from end of previous buffer into current buffer */ memcpy(frame->addr, copy->overlap, copy->size); /* * copy bytes at end of current buffer into overlap buffer */ memcpy(copy->overlap, (Char *)frame->addr + frame->size - copy->size, copy->size); QUE_put(device->fromdevice, frame); }
/* * ======== DIO_tskCallback ======== */ Void DIO_tskCallback(Ptr devp, DEV_Frame *frame) { DEV_Handle device = (DEV_Handle)devp; DIO_Handle dio = (DIO_Handle)device->object; if (frame->cmd == IOM_READ || frame->cmd == IOM_WRITE) { QUE_put(device->fromdevice, frame); SEM_post(dio->context.sems.complete); /* * If semaphore was registered with DIO_ready(), dio->ready will * be non-NULL. In this case, SIO_select() is probably waiting for * this semaphore to be posted by the first ready device. */ if (dio->context.sems.ready) { SEM_post(dio->context.sems.ready); } } else { SEM_post(dio->context.sems.complete); } }
Bool bfqGetBuffer ( BufferQueue_Handle queue, Ptr * buffer, Uns timeout ) { BufferQueue_Frame * frame; // wait until a full buffer is available, timeout // if necessary. if (! SEM_pend(&(queue->semFullBuffers), timeout) ) { return FALSE; } // Get the frame from the queue and double check it assertLog(! QUE_empty(&(queue->queFullBuffers))); frame = QUE_get(&(queue->queFullBuffers)); assertLog((Ptr)frame != (Ptr)&(queue->queFullBuffers)); // put the frame to the empty frames queue QUE_put( &(queue->queEmptyFrames), frame); // Copy the pointer *buffer = frame->Buffer; return TRUE; }
/* * ======== _GIO_iomCallback ======== * This function is called by the mini-driver when I/O completes. */ Void _GIO_iomCallback(Ptr cbArg, IOM_Packet *packet) { GIO_Handle gioChan = (GIO_Handle)cbArg; GIO_AppCallback *appCallback = (GIO_AppCallback *)packet->misc; Int status; Ptr addr; size_t size; if (appCallback == NULL) { /* this was a synchronous call -- post semaphore (or alternate sync) */ GIO->SEMPOST(gioChan->syncObj); } else { status = packet->status; addr = packet->addr; size = packet->size; /* recycle packet back onto free list */ QUE_put(&gioChan->freeList, packet); /* callback into application with status and size */ (*appCallback->fxn)(appCallback->arg, status, addr, size); } }
/****************************************************************************** * Fifo_put ******************************************************************************/ Int Fifo_put(Fifo_Handle hFifo, Ptr ptr) { Fifo_Elem * elem; assert(hFifo); assert(ptr); SEM_pend(&hFifo->mutex, SYS_FOREVER); hFifo->numBufs++; SEM_post(&hFifo->mutex); elem = BUF_alloc(hFifo->hBufPool); if (elem == NULL) { Dmai_err0("Failed to allocate space for Fifo Object\n"); return Dmai_EFAIL; } elem->ptr = ptr; /* Putting an element on the queue with ptr as payload */ QUE_put(&hFifo->queue, (QUE_Elem *)elem); SEM_post(&hFifo->sem); return Dmai_EOK; }
/* * ======== DPI_open ======== */ Static Int DPI_open(DEV_Handle dev, String name) { PipeObj *pipe; SPipeObj *sPipe, *tmpPipe; /* decode and validate devid */ if (dev->devid < 0) { dev->devid = atoi(name); } SEM_pend(mutex, SYS_FOREVER); /* search pipe list for previously opened pipe with same id */ sPipe = MEM_ILLEGAL; if (!QUE_empty(sPipeList)) { tmpPipe = (SPipeObj *)QUE_head(sPipeList); do { if (tmpPipe->id == dev->devid) { sPipe = tmpPipe; break; } tmpPipe = (SPipeObj *)QUE_next((&tmpPipe->link)); } while (tmpPipe != (SPipeObj *)sPipeList); } if (sPipe == MEM_ILLEGAL) { /* * Allocate and initialize SPipeObj on first open. */ sPipe = mkSPipe(dev); if (sPipe == MEM_ILLEGAL) { SEM_post(mutex); return SYS_EALLOC; } QUE_put(sPipeList, &sPipe->link); } else { /* sPipe found on list */ if (sPipe->device[dev->mode] != NULL) { /* * Only one input and one output allowed */ SEM_post(mutex); return SYS_EBUSY; } } sPipe->device[dev->mode] = dev; SEM_post(mutex); pipe = MEM_alloc(0, sizeof (PipeObj), 0); if (pipe == MEM_ILLEGAL) { /* * We need to undo work done by mkSPipe() if first open. * Also need to undo changes to sPipeList queue. */ QUE_remove(&sPipe->link); rmSPipe(sPipe); return SYS_EALLOC; } /* * Criss-cross SEM handles so both sides are referencing * the same physical objects. */ if (dev->mode == DEV_INPUT) { pipe->fromSem = sPipe->dataSem; pipe->toSem = sPipe->freeSem; } else { pipe->toSem = sPipe->dataSem; pipe->fromSem = sPipe->freeSem; } /* * Point things around. */ pipe->sPipe = sPipe; dev->object = (Ptr)pipe; return (SYS_OK); }
/* * ======== DNL_exchange ======== */ Int DNL_exchange(DEV_Handle device) { QUE_put(device->fromdevice, QUE_get(device->todevice)); return (SYS_OK); }
/* * ======== GIO_create ======== */ GIO_Handle GIO_create(String name, Int mode, Int *status, Ptr optArgs, \ GIO_Attrs *attrs) { GIO_Handle gioChan; IOM_Packet *packet; DEV_Device *entry; Int i; Int tmpStat; if (attrs == NULL) { attrs = &GIO_ATTRS; } /* * status param is used to pass additional device status back to caller. */ if (status == NULL) { status = &tmpStat; /* no longer need to check if status valid ptr */ } *status = IOM_COMPLETED; /* * Find device structure in device table for device with name 'name'. * DEV_match() returns the remaining name string for use by the * mini-driver's create() function. */ name = DEV_match(name, &entry); if (entry == NULL) { SYS_error(name, SYS_ENODEV); /* sys error - no device found */ return (NULL); } if (entry->type != DEV_IOMTYPE) { SYS_error("IOM", SYS_EINVAL); /* sys error - invalid device parameter */ return (NULL); } /* allocate and 0-fill IOM object */ gioChan = MEM_calloc(0, sizeof(GIO_Obj), 0); if (gioChan == NULL) { *status = IOM_EALLOC; return (NULL); } /* initialize queue structures */ QUE_new(&gioChan->freeList); /* * Allocate packets for asynch I/O. */ for (i=0; i < attrs->nPackets; i++) { packet = _GIO_mkPacket(); if (packet == NULL) { *status = IOM_EALLOC; GIO_delete(gioChan); return (NULL); } QUE_put(&gioChan->freeList, packet); } /* * Create semaphore or other synchronization object. 'gioChan->syncObj' is * used to wait for I/O to complete when GIO_submit() is called with * NULL *appCallback parameter. */ gioChan->syncObj = GIO->SEMCREATE(0, NULL); if (gioChan->syncObj == NULL) { *status = IOM_EALLOC; GIO_delete(gioChan); return (NULL); } gioChan->fxns = (IOM_Fxns *)entry->fxns; gioChan->mode = mode; gioChan->timeout = attrs->timeout; *status = gioChan->fxns->mdCreateChan(&gioChan->mdChan, entry->devp, name, mode, optArgs, _GIO_iomCallback, gioChan); if (gioChan->mdChan == NULL) { GIO_delete(gioChan); return (NULL); } return (gioChan); }
/* * ======== GIO_submit ======== */ Int GIO_submit(GIO_Handle gioChan, Uns cmd, Ptr bufp, \ size_t *psize, GIO_AppCallback *appCallback) { Int status; Bool semStat; IOM_Packet *packet; if (appCallback == NULL) { /* synchronous operation, use dedicated packet */ packet = &gioChan->syncPacket; } else { /* asynchronous operation, get packet from freelist */ packet = QUE_get(&gioChan->freeList); if (packet == (IOM_Packet *)(&gioChan->freeList)) { return (IOM_ENOPACKETS); } } /* initialize size variable if psize == NULL */ if (psize == NULL) { packet->size = 0; psize = &packet->size; } packet->cmd = cmd; packet->addr = bufp; packet->size = *psize; packet->status = IOM_COMPLETED; /* * 'appCallback' will be NULL for synchronous calls. * 'packet->misc' is used in callback function to call callback (async) * or post semaphore (sync). */ packet->misc = (Arg)appCallback; /* call down into mini-driver */ status = gioChan->fxns->mdSubmitChan(gioChan->mdChan, packet); if (status == IOM_COMPLETED) { *psize = packet->size; status = packet->status; /* If async then place packet back on free list */ if (appCallback != NULL) { QUE_put(&gioChan->freeList, packet); } return (status); } /* * Call SEMPEND Fxn only if synchronous i/o and no error returned * from mdSubmitChan(). */ if (appCallback == NULL) { if (status < 0) { /* error occured */ *psize = 0; return (status); } /* synchronous I/O -- call global blocking function */ semStat = GIO->SEMPEND(gioChan->syncObj, gioChan->timeout); if (semStat) { *psize = packet->size; status = packet->status; } else { /* timeout occurred */ *psize = 0; /* * NOTE: A channel timeout needs special handling. Timeouts are * usually due to some serious underlying device or system state * and may require the channel, or possibly the device,to be reset. * Because the mini-driver may still own the IOM_Packet here * driver's will need to perform timeout processing. We will call * the mini-driver's control fxn with the IOM_CHAN_TIMEDOUT command * code. */ if ((status = gioChan->fxns->mdControlChan(gioChan->mdChan, IOM_CHAN_TIMEDOUT, NULL)) != IOM_COMPLETED) { return (IOM_ETIMEOUTUNREC); /* Fatal: may have lost IOP */ } return (IOM_ETIMEOUT); } } return (status); }
/* * ======== DOV_open ======== */ static Int DOV_open(DEV_Handle device, String name) { DOV_CopyObj *copy; DEV_Device *entry; Int status = SYS_EALLOC; DEV_Frame *frame; size_t size; if (device->mode != DEV_INPUT) { return (SYS_EINVAL); } /* * If devid is nonzero, it holds the 'size' of the overlap buffer. */ if (device->devid > 0) { size = device->devid; } else { size = atoi(name); /* * Skip the numeric characters to get to the underlying * device's name. */ while (isdigit(*name)) { name++; } } if (size <= 0 || size >= device->bufsize) { return (SYS_EINVAL); } /* * find underlying device in device table */ name = DEV_match(name, &entry); if (entry == NULL) { return (SYS_ENODEV); } /* allocate copy object */ if ((copy = MEM_alloc(0, sizeof(DOV_CopyObj), 0)) == MEM_ILLEGAL) { return (SYS_EALLOC); } copy->size = size; /* allocate and initialize overlap buffer */ if ((copy->overlap = MEM_valloc(0, size, 0, DOV->INITIAL)) == MEM_ILLEGAL) { goto e1; } copy->dobj = *device; /* copy descriptor fields */ copy->dobj.fxns = *(DEV_Fxns *)(entry->fxns); copy->dobj.devid = entry->devid; copy->dobj.params = entry->params; /* size of underlying buffers */ copy->dobj.bufsize = device->bufsize - size; /* * create queues and frames for underlying device. */ if ((copy->dobj.todevice = QUE_create(NULL)) == NULL) { goto e2; } if ((copy->dobj.fromdevice = QUE_create(NULL)) == NULL) { goto e3; } /* * adjust frame size and address according to the overlap size before * copying frames to underlying device's 'todevice' queue */ while (!QUE_empty(device->todevice)) { frame = QUE_get(device->todevice); frame->size = frame->size - size; frame->addr = (Char *)frame->addr + size; QUE_put(copy->dobj.todevice, frame); } /* open underlying device */ if ((status = DEV_open((©->dobj), name)) != SYS_OK) { goto e4; } device->object = (Ptr)copy; return (SYS_OK); /* all is well */ /* free memory and return error code */ e4: QUE_delete(copy->dobj.fromdevice); e3: QUE_delete(copy->dobj.todevice); e2: MEM_free(0, copy->overlap, copy->size); e1: MEM_free(0, copy, sizeof(DOV_CopyObj)); return (status); }
/* * ======== GIO_new ======== */ GIO_Handle GIO_new(GIO_Handle gioChan, String name, Int mode, Int *status, Ptr optArgs, IOM_Packet packetBuf[], Ptr syncObject, GIO_Attrs *attrs) { DEV_Device *entry; Int i; Int tmpStat; if (attrs == NULL) { attrs = &GIO_ATTRS; } /* * status param is used to pass additional device status back to caller. */ if (status == NULL) { status = &tmpStat; /* no longer need to check if status valid ptr */ } *status = IOM_COMPLETED; /* * Find device structure in device table for device with name 'name'. * DEV_match() returns the remaining name string for use by the * mini-driver's create() function. */ name = DEV_match(name, &entry); if (entry == NULL) { SYS_error(name, SYS_ENODEV); /* sys error - no device found */ return (NULL); } if (entry->type != DEV_IOMTYPE) { SYS_error("IOM", SYS_EINVAL); /* sys error - invalid device parameter */ return (NULL); } /* initialize queue structures */ QUE_new(&gioChan->freeList); /* zero out the packet buffers */ memset(packetBuf, 0, attrs->nPackets * sizeof(IOM_Packet)); /* Put packets into freeList. */ for (i=0; i < attrs->nPackets; i++) { QUE_put(&gioChan->freeList, &packetBuf[i]); } /* * Plug semaphore or other synchronization object. 'gioChan->syncObj' is * used to wait for I/O to complete when GIO_submit() is called with * NULL *appCallback parameter. */ gioChan->syncObj = syncObject; gioChan->fxns = (IOM_Fxns *)entry->fxns; gioChan->mode = mode; gioChan->timeout = attrs->timeout; *status = gioChan->fxns->mdCreateChan(&gioChan->mdChan, entry->devp, name, mode, optArgs, _GIO_iomCallback, gioChan); if (gioChan->mdChan == NULL) { return (NULL); } return (gioChan); }
/* * ======== DPI_issue ======== */ Static Int DPI_issue(DEV_Handle dev) { PipeObj *pipe = (PipeObj *)dev->object; SPipeObj *sPipe = pipe->sPipe; DEV_Handle otherdev = sPipe->device[dev->mode ^ 0x1]; SEM_Handle otherReady = sPipe->readySem[dev->mode ^ 0x1]; DEV_Frame *otherframe; DEV_Frame *frame; #ifdef COPYBUFS DEV_Frame *srcframe; DEV_Frame *dstframe; #endif /* * Atomically check that each side has a frame so we can do an * exchange. We can't be sure that a frame is on the * dev->todevice queue (just put there by SIO) since a task * switch to the task on the other side might intervene and * take the frame from this side. */ TSK_disable(); if (otherdev != NULL && !QUE_empty(dev->todevice) && !QUE_empty(otherdev->todevice)) { otherframe = QUE_get(otherdev->todevice); frame = QUE_get(dev->todevice); /* done with atomic stuff */ TSK_enable(); /* * #define COPYBUFS to cause buffers to be copied through the pipe * instead of being exchanged. Doing so retains the semantics of * the ISSUERECLAIM model, but is slow. If COPYBUFS is *not* defined, * then one side reclaims buffers issued by the other side, thereby * not strictly retaining buffer ordering. */ #ifdef COPYBUFS if (dev->mode == DEV_INPUT) { dstframe = frame; srcframe = otherframe; } else { dstframe = otherframe; srcframe = frame; } memcpy(dstframe->addr, srcframe->addr, srcframe->size); dstframe->size = srcframe->size; dstframe->arg = srcframe->arg; QUE_put(dev->fromdevice, frame); QUE_put(otherdev->fromdevice, otherframe); /* * frames reclaimed from an output device must have size 0. */ if (dev->mode != DEV_INPUT) { frame->size = 0; } else { otherframe->size = 0; } #else QUE_put(dev->fromdevice, otherframe); QUE_put(otherdev->fromdevice, frame); /* * frames reclaimed from an output device must have size 0. */ if (dev->mode != DEV_INPUT) { otherframe->size = 0; } else { frame->size = 0; } #endif } else { /* done with atomic stuff */ TSK_enable(); } SEM_post(pipe->toSem); if (otherReady != NULL) { SEM_post(otherReady); } return SYS_OK; }