Beispiel #1
0
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;
}
Beispiel #2
0
/*
 *  ======== 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);
    }
}
Beispiel #3
0
/*
 *  ======== 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);
}
Beispiel #4
0
/*
 *  ======== 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);
    }
}
Beispiel #5
0
/*
 *  ======== 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);
}
Beispiel #6
0
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) );
}
Beispiel #7
0
/*
 *  ======== 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);
            }
        }
    }
}
Beispiel #8
0
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;
}
Beispiel #9
0
/*
 *  ======== 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);
}
Beispiel #10
0
/*
 *  ======== 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);
    }
}
Beispiel #11
0
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;
}
Beispiel #12
0
/*
 *  ======== _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);
    }
}
Beispiel #13
0
/******************************************************************************
 * 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;
}
Beispiel #14
0
/*
 *  ======== 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);
}
Beispiel #15
0
/*
 *  ======== DNL_exchange ========
 */
Int DNL_exchange(DEV_Handle device)
{
    QUE_put(device->fromdevice, QUE_get(device->todevice));

    return (SYS_OK);
}
Beispiel #16
0
/*
 *  ======== 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);
}
Beispiel #17
0
/*
 *  ======== 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);
}
Beispiel #18
0
/*
 *  ======== 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((&copy->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);
}
Beispiel #19
0
/*
 *  ======== 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);
}
Beispiel #20
0
/*
 *  ======== 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;
}