/* * Copyright (c) 2000-2007 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * * dspRead.c * * From v01.17 08/22/90 mbs * Modified for MP, 1996 by Tuyen Nguyen * Modified, April 9, 1997 by Tuyen Nguyen for MacOSX. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * CheckReadQueue * * Checks to see if there is any data in the receive queue. If there * is data, a pb and the data are queued to the user. * * */ extern int adsp_check; int CheckReadQueue(sp) /* (CCBPtr sp) */ register CCBPtr sp; { register struct adspcmd *pb; unsigned short cnt; char eom = 0; register gbuf_t *mp; register gbuf_t *tmp; gref_t *gref; dPrintf(D_M_ADSP, D_L_TRACE, ("CheckReadQueue: sp=0x%x\n", (unsigned)sp)); KERNEL_DEBUG(DBG_ADSP_READ, 0, sp, sp->rbuf_mb, sp->rpb, sp->delay); trace_mbufs(D_M_ADSP_LOW, " bCQR m", sp->rbuf_mb); while (sp->rData && (pb = sp->rpb)) { /* have data */ dPrintf(D_M_ADSP, D_L_TRACE, (" pb=0x%p, gref=0x%p, ioc=0x%p, reqCount=%d (have data)\n", pb, pb->gref, pb->ioc, pb->u.ioParams.reqCount)); KERNEL_DEBUG(DBG_ADSP_READ, 1, pb, pb->gref, pb->ioc, pb->u.ioParams.reqCount); if (pb->u.ioParams.reqCount == 0) { pb->ioResult = 0; sp->rpb = pb->qLink; if (pb->ioc) { KERNEL_DEBUG(DBG_ADSP_READ, 2, pb, pb->gref, pb->ioc, 0); adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref); } else { KERNEL_DEBUG(DBG_ADSP_READ, 3, pb, pb->gref, 0, 0); completepb(sp, pb); } continue; } /* take the first packet off of sp->rbuf_mb or sp->crbuf_mb */ if ((mp = sp->rbuf_mb)) { /* Get header for oldest data */ KERNEL_DEBUG(DBG_ADSP_READ, 4, pb, mp, gbuf_msgsize(mp), gbuf_next(mp)); sp->rbuf_mb = gbuf_next(mp); gbuf_next(mp) = 0; eom = 1; } else if ((mp = sp->crbuf_mb)) { KERNEL_DEBUG(DBG_ADSP_READ, 5, pb, mp, gbuf_msgsize(mp), gbuf_next(mp)); sp->crbuf_mb = 0; eom = 0; } /* Get the first (reqCount-actCount) bytes and tack them onto the end of pb->mp. If eom is set, put the remainder of the data onto the front of sp->rbuf_mb, otherwise sp->crbuf_mb. */ cnt = gbuf_msgsize(mp); /* # of data bytes in it. */ if (cnt > (unsigned short)(pb->u.ioParams.reqCount - pb->u.ioParams.actCount)) { cnt = pb->u.ioParams.reqCount - pb->u.ioParams.actCount; /* m_split returns the tail */ if (!(tmp = (gbuf_t *)m_split(mp, cnt, M_DONTWAIT))) { cnt = 0; tmp = mp; } if (eom) { gbuf_next(tmp) = sp->rbuf_mb; sp->rbuf_mb = tmp; eom = 0; } else sp->crbuf_mb = tmp; } if (cnt) { pb->u.ioParams.actCount += cnt; gbuf_linkb(pb->mp, mp); } pb->u.ioParams.eom = eom; /* * Now clean up receive buffer to remove all of the data * we just copied */ if ((sp->rbuf_mb == 0) && (sp->crbuf_mb == 0)) /* no more data blocks */ sp->rData = 0; /* * If we've filled the parameter block, unlink it from read * queue and complete it. We also need to do this if the connection * is closed && there is no more stuff to read. */ if (eom || (pb->u.ioParams.actCount >= pb->u.ioParams.reqCount) || ((sp->state == sClosed) && (!sp->rData)) ) { /* end of message, message is full, connection * is closed and all data has been delivered, * or we are not to "delay" data delivery. */ pb->ioResult = 0; sp->rpb = pb->qLink; /* dequeue request */ if (pb->ioc) { /* data to be delivered at the time of the */ mp = gbuf_cont(pb->mp); /* ioctl call */ gbuf_cont(pb->mp) = 0; gref = (gref_t *)pb->gref; adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref); dPrintf(D_M_ADSP, D_L_TRACE, (" (pb->ioc) mp=%p\n", mp)); KERNEL_DEBUG(DBG_ADSP_READ, 0x0A, pb, mp, gbuf_next(mp), gbuf_cont(mp)); SndMsgUp(gref, mp); dPrintf(D_M_ADSP, D_L_TRACE, (" (data) size req=%d\n", pb->u.ioParams.actCount)); KERNEL_DEBUG(DBG_ADSP_READ, 0x0B, pb, pb->ioc, pb->u.ioParams.reqCount, pb->u.ioParams.actCount); } else { /* complete an queued async request */ KERNEL_DEBUG(DBG_ADSP_READ, 0x0C, pb, sp, pb->u.ioParams.actCount, sp->delay); completepb(sp, pb); } } } /* while */ if ((pb = sp->rpb)) { /* if there is an outstanding request */ dPrintf(D_M_ADSP, D_L_TRACE, (" pb=0x%p, ioc=0x%p, reqCount=%d (no more data)\n", pb, pb->ioc, pb->u.ioParams.reqCount)); KERNEL_DEBUG(DBG_ADSP_READ, 0x0D, pb, pb->ioc, pb->u.ioParams.reqCount, pb->u.ioParams.actCount); if (sp->state == sClosed) { while (pb) { KERNEL_DEBUG(DBG_ADSP_READ, 0x0E, pb, sp, pb->ioc, 0); pb->ioResult = 0; pb->u.ioParams.actCount = 0; pb->u.ioParams.eom = 0; sp->rpb = pb->qLink; if (pb->ioc) { adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref); } else { completepb(sp, pb); } pb = sp->rpb; } } else if (pb->ioc) { /* if request not complete and this * is an active ioctl, release user */ sp->rpb = pb->qLink; pb->ioResult = 1; tmp = gbuf_cont(pb->mp); /* detatch perhaps delayed data */ gbuf_cont(pb->mp) = 0; if ((mp = gbuf_copym(pb->mp))) { /* otherwise, duplicate user request */ KERNEL_DEBUG(DBG_ADSP_READ, 0x0F, pb, sp, pb->mp, 0); adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref); /* release user */ pb = (struct adspcmd *)gbuf_rptr(mp); /* get new parameter block */ pb->ioc = 0; pb->mp = mp; gbuf_cont(pb->mp) = tmp; /* reattach data */ pb->qLink = sp->rpb; /* requeue the duplicate at the head */ sp->rpb = pb; } else { /* there is no data left, but no space * to duplicate the parameter block, so * put what must be a non EOM message * back on the current receive queue, and * error out the user */ KERNEL_DEBUG(DBG_ADSP_READ, 0x10, pb, sp, pb->mp, 0); if (tmp) { sp->crbuf_mb = tmp; sp->rData = 1; } pb->ioResult = errDSPQueueSize; adspioc_ack(ENOBUFS, (gbuf_t *)pb->ioc, pb->gref); } } } /* * The receive window has opened. If was previously closed, then we * need to notify the other guy that we now have room to receive more * data. But, in order to cut down on lots of small data packets, * we'll wait until the recieve buffer is /14 empy before telling * him that there's room in our receive buffer. */ if (sp->rbufFull && (CalcRecvWdw(sp) > (sp->rbuflen >> 2))) { sp->rbufFull = 0; sp->sendDataAck = 1; sp->callSend = 1; } KERNEL_DEBUG(DBG_ADSP_READ, 0x11, sp, 0, 0, 0); trace_mbufs(D_M_ADSP_LOW, " eCQR m", sp->rbuf_mb); return 0; } /* * CheckAttn * * Checks to see if there is any attention data and passes the data back * in the passed in pb. * * INPUTS: * sp * pb * * OUTPUTS: * */ int CheckAttn(CCBPtr, struct adspcmd *); int CheckAttn(sp, pb) /* (CCBPtr sp) */ register CCBPtr sp; register struct adspcmd *pb; { gbuf_t *mp; gref_t *gref = 0; dPrintf(D_M_ADSP, D_L_TRACE, ("CheckAttn: sp=0x%x, pb=0x%x\n", (unsigned)sp, (unsigned)pb)); if ((mp = sp->attn_mb)) { /* * Deliver the attention data to the user. */ gref = (gref_t *)pb->gref; pb->u.attnParams.attnSize = sp->attnSize; pb->u.attnParams.attnCode = sp->attnCode; if (!sp->attnSize) { gbuf_freem(mp); mp = 0; } sp->userFlags &= ~eAttention; /* * Now clean up receive buffer to remove all of the data * we just copied */ sp->attn_mb = 0; pb->ioResult = 0; } else { /* * No data... */ pb->u.attnParams.attnSize = 0; pb->u.attnParams.attnCode = 0; pb->ioResult = 1; /* not done */ } adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref); if (mp) { SndMsgUp(gref, mp); } return 0; } /* * adspRead * * INPUTS: * --> sp stream pointer * --> pb user request parameter block * * OUTPUTS: * <-- actCount actual number of bytes read * <-- eom one if end-of-message, zero otherwise * * ERRORS: * errRefNum bad connection refnum * errState * errFwdReset read terminated by forward reset * errAborted request aborted by Remove or Close call */ int adspRead(sp, pb) /* (DSPPBPtr pb) */ register CCBPtr sp; register struct adspcmd *pb; { register gbuf_t *mp; dPrintf(D_M_ADSP, D_L_TRACE, ("adspRead: sp=0x%x, pb=0x%x\n", (unsigned)sp, (unsigned)pb)); KERNEL_DEBUG(DBG_ADSP_READ, 0x12, sp, pb, sp->state, sp->rData); if (sp == 0) { pb->ioResult = errRefNum; return EINVAL; } /* * It's OK to read on a closed, or closing session */ if (sp->state != sOpen && sp->state != sClosing && sp->state != sClosed) { pb->ioResult = errState; return EINVAL; } if (sp->rData && (sp->rpb == 0)) { /* if data, and no queue of pbs */ qAddToEnd((struct qlink **)&sp->rpb, (struct qlink *)pb); /* deliver data to user directly */ CheckReadQueue(sp); } else if ((pb->u.ioParams.reqCount == 0) && (sp->rpb == 0)) { /* empty read */ pb->ioResult = 0; adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref); return 0; } else { pb->ioResult = 1; if ((mp = gbuf_copym(pb->mp))) { /* otherwise, duplicate user request */ adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref); /* release user */ pb = (struct adspcmd *)gbuf_rptr(mp); /* get new parameter block */ pb->ioc = 0; pb->mp = mp; qAddToEnd((struct qlink **)&sp->rpb, (struct qlink *)pb); /* and queue it for later */ } else { pb->ioResult = errDSPQueueSize; return ENOBUFS; } } if (sp->callSend) { CheckSend(sp); /* If recv window opened, we might */ /* send an unsolicited ACK. */ } return 0; } /* * dspReadAttention * * INPUTS: * --> sp stream pointer * --> pb user request parameter block * * OUTPUTS: * <-- NONE * * ERRORS: * errRefNum bad connection refnum * errState connection is not in the right state */ int adspReadAttention(sp, pb) /* (DSPPBPtr pb) */ register CCBPtr sp; register struct adspcmd *pb; { dPrintf(D_M_ADSP, D_L_TRACE, ("adspReadAttention: sp=0x%x, pb=0x%x\n", (unsigned)sp, (unsigned)pb)); if (sp == 0) { pb->ioResult = errRefNum; return EINVAL; } /* * It's OK to read on a closed, or closing session */ if (sp->state != sOpen && sp->state != sClosing && sp->state != sClosed) { pb->ioResult = errState; return EINVAL; } CheckAttn(sp, pb); /* Anything in the attention queue */ CheckReadQueue(sp); /* check to see if receive window has opened */ if (sp->callSend) { CheckSend(sp); /* If recv window opened, we might */ /* send an unsolicited ACK. */ } return 0; } /* adspReadAttention */