1/*
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 *
30 * dspRead.c
31 *
32 * From v01.17 08/22/90 mbs
33 *    Modified for MP, 1996 by Tuyen Nguyen
34 *   Modified, April 9, 1997 by Tuyen Nguyen for MacOSX.
35 */
36
37#include <sys/errno.h>
38#include <sys/types.h>
39#include <sys/param.h>
40#include <machine/spl.h>
41#include <sys/systm.h>
42#include <sys/kernel.h>
43#include <sys/proc.h>
44#include <sys/filedesc.h>
45#include <sys/fcntl.h>
46#include <sys/mbuf.h>
47#include <sys/socket.h>
48#include <sys/socketvar.h>
49
50#include <netat/sysglue.h>
51#include <netat/appletalk.h>
52#include <netat/at_pcb.h>
53#include <netat/debug.h>
54#include <netat/adsp.h>
55#include <netat/adsp_internal.h>
56
57/*
58 * CheckReadQueue
59 *
60 * Checks to see if there is any data in the receive queue.  If there
61 * is data, a pb and the data are queued to the user.
62 *
63 *
64 */
65extern int adsp_check;
66
67int CheckReadQueue(sp)		/* (CCBPtr sp) */
68    register CCBPtr sp;
69{
70    register struct adspcmd *pb;
71    unsigned short cnt;
72    char eom = 0;
73    register gbuf_t *mp;
74    register gbuf_t *tmp;
75    gref_t *gref;
76
77    dPrintf(D_M_ADSP, D_L_TRACE, ("CheckReadQueue: sp=0x%x\n", (unsigned)sp));
78    KERNEL_DEBUG(DBG_ADSP_READ, 0, sp, sp->rbuf_mb, sp->rpb, sp->delay);
79    trace_mbufs(D_M_ADSP_LOW, "    bCQR m", sp->rbuf_mb);
80
81    while (sp->rData && (pb = sp->rpb)) {		/* have data */
82        dPrintf(D_M_ADSP, D_L_TRACE,
83		 (" pb=0x%p, gref=0x%p, ioc=0x%p, reqCount=%d (have data)\n",
84		  pb, pb->gref, pb->ioc, pb->u.ioParams.reqCount));
85    	KERNEL_DEBUG(DBG_ADSP_READ, 1, pb, pb->gref, pb->ioc, pb->u.ioParams.reqCount);
86	if (pb->u.ioParams.reqCount == 0) {
87	    pb->ioResult = 0;
88	    sp->rpb = pb->qLink;
89	    if (pb->ioc) {
90    		KERNEL_DEBUG(DBG_ADSP_READ, 2, pb, pb->gref, pb->ioc, 0);
91		adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref);
92	    } else {
93    		KERNEL_DEBUG(DBG_ADSP_READ, 3, pb, pb->gref, 0, 0);
94		completepb(sp, pb);
95	    }
96	    continue;
97	}
98
99	/* take the first packet off of sp->rbuf_mb or sp->crbuf_mb */
100	if ((mp = sp->rbuf_mb)) {	/* Get header for oldest data */
101    	    KERNEL_DEBUG(DBG_ADSP_READ, 4, pb, mp, gbuf_msgsize(mp), gbuf_next(mp));
102	    sp->rbuf_mb = gbuf_next(mp);
103	    gbuf_next(mp) = 0;
104	    eom = 1;
105	} else if ((mp = sp->crbuf_mb)) {
106    	    KERNEL_DEBUG(DBG_ADSP_READ, 5, pb, mp, gbuf_msgsize(mp), gbuf_next(mp));
107	    sp->crbuf_mb = 0;
108	    eom = 0;
109	}
110
111	/* Get the first (reqCount-actCount) bytes and tack them onto
112	   the end of pb->mp.  If eom is set, put the remainder of the
113	   data onto the front of sp->rbuf_mb, otherwise sp->crbuf_mb. */
114	cnt = gbuf_msgsize(mp);	/* # of data bytes in it. */
115	if (cnt > (unsigned short)(pb->u.ioParams.reqCount - pb->u.ioParams.actCount)) {
116	    cnt = pb->u.ioParams.reqCount - pb->u.ioParams.actCount;
117	    /* m_split returns the tail */
118	    if (!(tmp = (gbuf_t *)m_split(mp, cnt, M_DONTWAIT))) {
119	    	cnt = 0;
120		tmp = mp;
121	    }
122	    if (eom) {
123		gbuf_next(tmp) = sp->rbuf_mb;
124		sp->rbuf_mb = tmp;
125		eom = 0;
126	    } else
127	    	sp->crbuf_mb = tmp;
128	}
129	if (cnt) {
130	    pb->u.ioParams.actCount += cnt;
131	    gbuf_linkb(pb->mp, mp);
132        }
133
134	pb->u.ioParams.eom = eom;
135	/*
136	 * Now clean up receive buffer to remove all of the data
137	 * we just copied
138	 */
139	if ((sp->rbuf_mb == 0) &&
140	    (sp->crbuf_mb == 0)) /* no more data blocks */
141	    sp->rData = 0;
142	/*
143	 * If we've filled the parameter block, unlink it from read
144	 * queue and complete it. We also need to do this if the connection
145	 * is closed && there is no more stuff to read.
146	 */
147	if (eom || (pb->u.ioParams.actCount >= pb->u.ioParams.reqCount) ||
148	    ((sp->state == sClosed) && (!sp->rData)) ) {
149	      /* end of message, message is full, connection
150	       * is closed and all data has been delivered,
151	       * or we are not to "delay" data delivery.
152	       */
153	    pb->ioResult = 0;
154	    sp->rpb = pb->qLink; /* dequeue request */
155	    if (pb->ioc) {	/* data to be delivered at the time of the */
156		mp = gbuf_cont(pb->mp); /* ioctl call */
157		gbuf_cont(pb->mp) = 0;
158		gref = (gref_t *)pb->gref;
159		adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref);
160		dPrintf(D_M_ADSP, D_L_TRACE, ("    (pb->ioc) mp=%p\n", mp));
161    		KERNEL_DEBUG(DBG_ADSP_READ, 0x0A, pb,  mp,
162			     gbuf_next(mp), gbuf_cont(mp));
163		SndMsgUp(gref, mp);
164		dPrintf(D_M_ADSP, D_L_TRACE,
165			("    (data) size req=%d\n", pb->u.ioParams.actCount));
166    		KERNEL_DEBUG(DBG_ADSP_READ, 0x0B, pb, pb->ioc,
167			     pb->u.ioParams.reqCount, pb->u.ioParams.actCount);
168	    } else {		/* complete an queued async request */
169    		KERNEL_DEBUG(DBG_ADSP_READ, 0x0C, pb, sp,
170			     pb->u.ioParams.actCount, sp->delay);
171		completepb(sp, pb);
172	    }
173	}
174    }	/* while */
175
176    if ((pb = sp->rpb)) {		/* if there is an outstanding request */
177        dPrintf(D_M_ADSP, D_L_TRACE,
178		 (" pb=0x%p, ioc=0x%p, reqCount=%d (no more data)\n",
179		  pb, pb->ioc, pb->u.ioParams.reqCount));
180    	KERNEL_DEBUG(DBG_ADSP_READ, 0x0D, pb, pb->ioc,
181		     pb->u.ioParams.reqCount, pb->u.ioParams.actCount);
182
183	if (sp->state == sClosed) {
184	    while (pb) {
185    		    KERNEL_DEBUG(DBG_ADSP_READ, 0x0E, pb, sp, pb->ioc, 0);
186		    pb->ioResult = 0;
187		    pb->u.ioParams.actCount = 0;
188		    pb->u.ioParams.eom = 0;
189		    sp->rpb = pb->qLink;
190		    if (pb->ioc) {
191			    adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref);
192		    } else {
193			    completepb(sp, pb);
194		    }
195		    pb = sp->rpb;
196	    }
197	} else if (pb->ioc) {	/* if request not complete and this
198				 * is an active ioctl, release user */
199	    sp->rpb = pb->qLink;
200	    pb->ioResult = 1;
201	    tmp = gbuf_cont(pb->mp); /* detatch perhaps delayed data */
202	    gbuf_cont(pb->mp) = 0;
203	    if ((mp = gbuf_copym(pb->mp))) { /* otherwise, duplicate user request */
204    		    KERNEL_DEBUG(DBG_ADSP_READ, 0x0F, pb, sp, pb->mp, 0);
205		    adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref); 	/* release user */
206		    pb = (struct adspcmd *)gbuf_rptr(mp); /* get new parameter block */
207		    pb->ioc = 0;
208		    pb->mp = mp;
209		    gbuf_cont(pb->mp) = tmp; /* reattach data */
210		    pb->qLink = sp->rpb; /* requeue the duplicate at the head */
211		    sp->rpb = pb;
212	    } else {		/* there is no data left, but no space
213				 * to duplicate the parameter block, so
214				 * put what must be a non EOM message
215				 * back on the current receive queue, and
216				 * error out the user
217				 */
218    		    KERNEL_DEBUG(DBG_ADSP_READ, 0x10, pb, sp, pb->mp, 0);
219		    if (tmp) {
220			    sp->crbuf_mb = tmp;
221			    sp->rData = 1;
222		    }
223		    pb->ioResult = errDSPQueueSize;
224		    adspioc_ack(ENOBUFS, (gbuf_t *)pb->ioc, pb->gref);
225	    }
226	}
227    }
228    /*
229     * The receive window has opened.  If was previously closed, then we
230     * need to notify the other guy that we now have room to receive more
231     * data.  But, in order to cut down on lots of small data packets,
232     * we'll wait until the recieve buffer  is /14 empy before telling
233     * him that there's room in our receive buffer.
234     */
235    if (sp->rbufFull && (CalcRecvWdw(sp) > (sp->rbuflen >> 2))) {
236	sp->rbufFull = 0;
237	sp->sendDataAck = 1;
238	sp->callSend = 1;
239    }
240
241    KERNEL_DEBUG(DBG_ADSP_READ, 0x11, sp, 0, 0, 0);
242    trace_mbufs(D_M_ADSP_LOW, "    eCQR m", sp->rbuf_mb);
243    return 0;
244}
245
246/*
247 * CheckAttn
248 *
249 * Checks to see if there is any attention data and passes the data back
250 * in the passed in pb.
251 *
252 * INPUTS:
253 *	sp
254 *	pb
255 *
256 * OUTPUTS:
257 *
258 */
259int CheckAttn(CCBPtr, struct adspcmd *);
260
261int CheckAttn(sp, pb)		/* (CCBPtr sp) */
262    register CCBPtr sp;
263    register struct adspcmd *pb;
264{
265    gbuf_t *mp;
266    gref_t *gref = 0;
267
268    dPrintf(D_M_ADSP, D_L_TRACE,
269	    ("CheckAttn: sp=0x%x, pb=0x%x\n", (unsigned)sp, (unsigned)pb));
270
271    if ((mp = sp->attn_mb)) {
272
273	/*
274	 * Deliver the attention data to the user.
275	 */
276	gref = (gref_t *)pb->gref;
277	pb->u.attnParams.attnSize = sp->attnSize;
278	pb->u.attnParams.attnCode = sp->attnCode;
279	if (!sp->attnSize) {
280	    gbuf_freem(mp);
281	    mp = 0;
282	}
283	sp->userFlags &= ~eAttention;
284	/*
285	 * Now clean up receive buffer to remove all of the data
286	 * we just copied
287	 */
288	sp->attn_mb = 0;
289	pb->ioResult = 0;
290    } else {
291	/*
292	 * No data...
293	 */
294	pb->u.attnParams.attnSize = 0;
295	pb->u.attnParams.attnCode = 0;
296	pb->ioResult = 1;	/* not done */
297    }
298    adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref);
299    if (mp) {
300	SndMsgUp(gref, mp);
301	}
302    return 0;
303}
304
305/*
306 * adspRead
307 *
308 * INPUTS:
309 *	--> sp			stream pointer
310 *	--> pb			user request parameter block
311 *
312 * OUTPUTS:
313 *	<-- actCount		actual number of bytes read
314 *	<-- eom			one if end-of-message, zero otherwise
315 *
316 * ERRORS:
317 *	errRefNum		bad connection refnum
318 *	errState
319 *	errFwdReset		read terminated by forward reset
320 *	errAborted		request aborted by Remove or Close call
321 */
322int adspRead(sp, pb)		/* (DSPPBPtr pb) */
323    register CCBPtr sp;
324    register struct adspcmd *pb;
325{
326    register gbuf_t *mp;
327
328    dPrintf(D_M_ADSP, D_L_TRACE,
329	    ("adspRead: sp=0x%x, pb=0x%x\n", (unsigned)sp, (unsigned)pb));
330
331    KERNEL_DEBUG(DBG_ADSP_READ, 0x12, sp, pb, sp->state, sp->rData);
332
333    if (sp == 0) {
334	pb->ioResult = errRefNum;
335	return EINVAL;
336    }
337
338    /*
339     * It's OK to read on a closed, or closing session
340     */
341    if (sp->state != sOpen && sp->state != sClosing && sp->state != sClosed) {
342	pb->ioResult = errState;
343	return EINVAL;
344    }
345    if (sp->rData && (sp->rpb == 0)) { /* if data, and no queue of pbs */
346	qAddToEnd((struct qlink **)&sp->rpb, (struct qlink *)pb); /* deliver data to user directly */
347	CheckReadQueue(sp);
348    } else if ((pb->u.ioParams.reqCount == 0) && (sp->rpb == 0)) {
349	    /* empty read */
350	    pb->ioResult = 0;
351	    adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref);
352	    return 0;
353    } else {
354	pb->ioResult = 1;
355	if ((mp = gbuf_copym(pb->mp))) { /* otherwise, duplicate user request */
356		adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref); 	/* release user */
357		pb = (struct adspcmd *)gbuf_rptr(mp); 	/* get new parameter block */
358		pb->ioc = 0;
359		pb->mp = mp;
360		qAddToEnd((struct qlink **)&sp->rpb, (struct qlink *)pb); /* and queue it for later */
361	} else {
362		pb->ioResult = errDSPQueueSize;
363		return ENOBUFS;
364	}
365    }
366
367    if (sp->callSend) {
368	CheckSend(sp);		/* If recv window opened, we might */
369				/* send an unsolicited ACK. */
370    }
371    return 0;
372}
373
374/*
375 * dspReadAttention
376 *
377 * INPUTS:
378 *	--> sp			stream pointer
379 *	--> pb			user request parameter block
380 *
381 * OUTPUTS:
382 *	<-- NONE
383 *
384 * ERRORS:
385 *	errRefNum		bad connection refnum
386 *	errState		connection is not in the right state
387 */
388int adspReadAttention(sp, pb)		/* (DSPPBPtr pb) */
389    register CCBPtr sp;
390    register struct adspcmd *pb;
391{
392    dPrintf(D_M_ADSP, D_L_TRACE,
393	    ("adspReadAttention: sp=0x%x, pb=0x%x\n", (unsigned)sp, (unsigned)pb));
394    if (sp == 0) {
395	pb->ioResult = errRefNum;
396	return EINVAL;
397    }
398
399    /*
400     * It's OK to read on a closed, or closing session
401     */
402    if (sp->state != sOpen && sp->state != sClosing && sp->state != sClosed) {
403	pb->ioResult = errState;
404	return EINVAL;
405    }
406
407    CheckAttn(sp, pb);		/* Anything in the attention queue */
408    CheckReadQueue(sp);		/* check to see if receive window has opened */
409    if (sp->callSend) {
410	CheckSend(sp);		/* If recv window opened, we might */
411				/* send an unsolicited ACK. */
412	}
413    return 0;
414} /* adspReadAttention */
415