1/*
2 * Copyright (c) 2000 Apple Computer, 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 *	Copyright (c) 1990, 1995-1998 Apple Computer, Inc.
30 *	All Rights Reserved.
31 */
32
33/* dspClose.c
34 * From Mike Shoemaker v01.16 06/29/90 mbs
35 */
36/*
37 * Change log:
38 *   06/29/95 - Modified to handle flow control for writing (Tuyen Nguyen)
39 *    Modified for MP, 1996 by Tuyen Nguyen
40 *    Modified, April 9, 1997 by Tuyen Nguyen for MacOSX.
41 */
42
43#include <sys/errno.h>
44#include <sys/types.h>
45#include <sys/param.h>
46#include <machine/spl.h>
47#include <sys/systm.h>
48#include <sys/kernel.h>
49#include <sys/proc.h>
50#include <sys/filedesc.h>
51#include <sys/fcntl.h>
52#include <sys/mbuf.h>
53#include <sys/socket.h>
54#include <sys/socketvar.h>
55#include <sys/time.h>
56
57#include <netat/sysglue.h>
58#include <netat/appletalk.h>
59#include <netat/ddp.h>
60#include <netat/at_pcb.h>
61#include <netat/debug.h>
62#include <netat/adsp.h>
63#include <netat/adsp_internal.h>
64
65
66static void qRemove(CCBPtr, CCBPtr);
67
68
69/*
70 * CheckOkToClose
71 *
72 * Check to see if it is OK to close this connection cleanly.
73 *
74 * INPUTS:
75 * 		Stream pointer
76 * OUTPUTS:
77 * 		True if no outstanding transactions and we can close cleanly
78 */
79int CheckOkToClose(sp)		/* (CCBPtr sp) */
80    CCBPtr sp;
81{
82
83    if (sp->sData)		/* Outstanding data ? */
84	return 0;
85
86    if (sp->sapb)		/* Outstanding send attention ? */
87	return 0;
88
89    if (sp->frpb)		/* Outstanding forward reset ? */
90	return 0;
91
92    if (sp->sendAttnAck)
93	return 0;
94
95    if (sp->sendDataAck)
96	return 0;
97
98    /*
99     * Must be OK to close
100     */
101    sp->sendCtl	|= B_CTL_CLOSE;	/* So, need to send close advice */
102    sp->callSend = 1;
103
104    return 1;			/* It's OK to close */
105}
106
107
108/*
109 * CompleteQueue
110 *
111 * Given the address of the head of a queue of DSP parameter blocks, zero
112 * the queue, and complete each item on the queue with the given result
113 * code.
114 *
115 * INPUTS:
116 *		qhead		Address of ptr to first queue element
117 *		code		The result code
118 * OUTPUTS:
119 * 		none
120 */
121int  CompleteQueue(qhead, code)	/* (DSPPBPtr FPTR qhead, OSErr code) */
122    struct adspcmd **qhead;
123    int code;
124{
125    register struct adspcmd *p;
126    register struct adspcmd *n;
127    register gref_t *gref;
128    register int    _total = 0;
129    CCBPtr sp = 0;
130
131    n = *qhead;			/* Get first item */
132    *qhead = 0;			/* Zero out the queue */
133    if (n) {
134	gref = n->gref;
135	if (gref->info) {
136	    sp = (CCBPtr)gbuf_rptr(((gbuf_t *)gref->info));
137	    atalk_flush(sp->gref);
138	    }
139    }
140
141    while ((p = n)) {		/* while items left */
142	n = (struct adspcmd *)(p->qLink); /* Save next guy */
143	p->ioResult = code;
144	if (sp) {
145	    completepb(sp, p); 	/* complete the copy of the request */
146	    _total++;
147	} else
148	    gbuf_freem(p->mp);
149    }				/* while */
150    return(_total);
151}
152
153/*
154 * RemoveCCB
155 *
156 * Called from do close to free up the user's CCB.  So, we remove the
157 * CCB from the list of CCB's.
158 *
159 * INPUTS:
160 * 		sp	pointer to ccb
161 *		pb	a remove param block to complete when done
162 * OUTPUTS:
163 * 		none
164 */
165void RemoveCCB(CCBPtr, struct adspcmd *);
166
167void RemoveCCB(sp, pb)		/* (CCBPtr sp, DSPPBPtr pb) */
168    CCBPtr sp;
169    struct adspcmd *pb;
170{
171	gref_t *gref;
172
173	if (sp->gref == 0)
174		return;
175    /*
176     * Unlink CCB from list
177     */
178    qRemove((CCB *)AT_ADSP_STREAMS, sp); /* remove sp from active streams queue */
179
180    if (pb) {
181	pb->ioResult = 0;
182	if (pb->ioc)		/* is this a current or queued request */
183	    adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref);	/* current */
184	else {
185	    completepb(sp, pb);	/* queued */
186	}
187
188	if (sp->opb && (pb != sp->opb)) { /* if the pb requested is not the */
189	    pb = sp->opb;		/* waiting open pb, complete it too */
190	    sp->opb = 0;
191	    pb->ioResult = 0;
192	    completepb(sp, pb);
193	} else {
194	    sp->opb = 0;
195        }
196    }
197    gref = sp->gref;
198    sp->gref = 0;
199    if (gref->info == (char *)sp->sp_mp) { /* queue head is still valid */
200	    unsigned char skt;
201
202	    if ((skt = sp->localSocket) != 0) {
203	      if (adspDeassignSocket(sp) == 0)
204		  ddp_notify_nbp(skt, sp->pid, DDP_ADSP);
205	    }
206
207	  if (gref->info) {
208	    gbuf_freem((gbuf_t *)gref->info);	/* free the CCB */
209	    gref->info = 0;
210	  }
211    } else
212	gbuf_freem(sp->sp_mp);	/* our head is already gone, be sure
213				 * to release our resources too */
214}
215
216int  AbortIO(CCBPtr, short);
217
218int  AbortIO(sp, err)
219    CCBPtr sp;
220    short err;
221{
222    register int    _total;
223
224	if (sp->gref == 0)
225		return 0;
226    /*
227     * Complete all outstanding transactions.
228     */
229    _total = CompleteQueue(&sp->sapb, err); /* Abort outstanding send attentions */
230    CompleteQueue(&sp->frpb, err); /* Abort outstanding forward resets */
231
232    if (sp->sbuf_mb) { /* clear the send queue */
233	gbuf_freel(sp->sbuf_mb);
234	sp->sbuf_mb = 0;
235    }
236
237    if (sp->csbuf_mb) {
238	gbuf_freem(sp->csbuf_mb);
239	sp->csbuf_mb = 0;
240    }
241    sp->sData = 0;
242
243    return(_total);
244}
245
246/*
247 * DoClose
248 *
249 * Called from several places (probe timeout, recv close advice,
250 * dspRemove, etc.) to change state of connection to closed and
251 * complete all outstanding I/O.
252 *
253 * Will also remove the CCB if there is a dsp remove pending.
254 *
255 * INPUTS:
256 *		sp		An ADSP stream
257 * OUTPUTS:
258 * 		none
259 */
260void DoClose(sp, err, force_abort)	/* (CCBPtr sp, OSErr err) */
261    register CCBPtr sp;
262    int err;
263	int force_abort;
264{
265    register struct adspcmd *pb, *np;
266    register gbuf_t *mp;
267    int      aborted_count;
268
269    dPrintf(D_M_ADSP, D_L_TRACE, ("DoClose: pid=%d,e=%d,a=%d,s=%d,r=%d\n",
270		sp->pid, err, force_abort, sp->localSocket, sp->removing));
271    sp->userFlags |= eClosed; /* Set flag */
272    sp->state = sClosed;
273    sp->openState = O_STATE_NOTHING;
274
275    /*
276     * Clean up any timer elements
277     */
278    RemoveTimerElem(&adspGlobal.slowTimers, &sp->ProbeTimer);
279    RemoveTimerElem(&adspGlobal.fastTimers, &sp->FlushTimer);
280    RemoveTimerElem(&adspGlobal.fastTimers, &sp->RetryTimer);
281    RemoveTimerElem(&adspGlobal.fastTimers, &sp->AttnTimer);
282    RemoveTimerElem(&adspGlobal.fastTimers, &sp->ResetTimer);
283
284    aborted_count = AbortIO(sp, err);
285    np = sp->opb;		/* Get list of close/removes to complete */
286    sp->opb = 0;		/* set this list null */
287
288    while ((pb = np)) {		/* Handle all of the close/remove param blks */
289	np = (struct adspcmd *)pb->qLink; /* Get next guy (if any) */
290	pb->qLink = 0;
291	pb->ioResult = err;
292	completepb(sp, pb);
293    }
294    if (sp->removing && (force_abort >= 0)) {        /* Abort outstanding receives */
295	aborted_count += CompleteQueue(&sp->rpb, err);
296
297	if (sp->deferred_mb) {
298		gbuf_freel(sp->deferred_mb);
299		sp->deferred_mb = 0;
300	}
301	if (sp->attn_mb) {
302		gbuf_freem(sp->attn_mb);
303		sp->attn_mb = 0;
304	}
305	if (sp->rbuf_mb) { /* clear the rcv queue */
306		gbuf_freem(sp->rbuf_mb);
307		sp->rbuf_mb = 0;
308	}
309	if (sp->crbuf_mb) {
310		gbuf_freem(sp->crbuf_mb);
311		sp->crbuf_mb = 0;
312	}
313	sp->rData = 0;
314
315	/* if our connection has been timed out */
316	/* and the user wasn't notified of the TearDown */
317	/* because of pending requests on this socket */
318	/* then fake a read completion to force the notification */
319
320	if (force_abort && aborted_count == 0) {
321	    if ((mp = gbuf_alloc(sizeof(struct adspcmd), PRI_HI))) {
322	        pb = (struct adspcmd *)gbuf_rptr(mp);
323		gbuf_wset(mp,sizeof(struct adspcmd));
324
325		bzero((caddr_t) pb, sizeof(struct adspcmd));
326		pb->mp = mp;
327		pb->csCode = dspRead;
328		pb->ioResult = errAborted;
329		completepb(sp, pb);		/* send fake read completion */
330	    }
331	}
332	sp->removing = 0;
333	RemoveCCB(sp, 0); /* Will call completion routine */
334    }
335    sp->userFlags &= ~eClosed;
336}
337
338
339/*
340 * dspClose
341 *
342 * Also called for dspRemove and dspCLRemove.
343 * Must handle case of multiple close calls being issued (without
344 * abort bit set) Can only allow one pending remove though.
345 *
346 * INPUTS:
347 * 	-->	ccbRefNum		refnum of connection end
348 *	-->	abort			abort the connection
349 *
350 * OUTPUTS:
351 *	none
352 *
353 * ERRORS:
354 *		errRefNum		Bad connection Refnum
355 */
356int adspClose(sp, pb)		/* (DSPPBPtr pb) */
357    register CCBPtr sp;
358    register struct adspcmd *pb;
359{
360    register gbuf_t *mp;
361
362    /* Must execute nearly all of this with ints off because user could
363     * be issuing a second dspRemove while the first is pending.  Until
364     * we can detect this, we must not allow interrupts.
365     * Also, we can't handle the case where a close was issued earlier,
366     * and now this is the remove.  If the write completion for the
367     * close advice packet occurs in the middle of this, we might
368     * foul up.
369     */
370
371    if (sp == 0) {
372	pb->ioResult = errRefNum;
373	return EINVAL;
374    }
375
376    /*
377     * Handle dspCLRemove
378     */
379    if (pb->csCode == (short)dspCLRemove) { /* Remove connection listener */
380	if (sp->state != (short)sListening) { /* But it's not a listener! */
381	    pb->ioResult = errState;
382	    return EINVAL;
383	}
384	CompleteQueue(&sp->opb, errAborted); /* Complete all dspListens */
385	RemoveCCB(sp, pb);	/* Will call completion routine */
386	return 0;
387    }
388
389
390    /*
391     * Either dspClose or dspRemove
392     */
393
394    if (sp->removing) {		/* Don't allow dspRemove or dspClose */
395				/* after one dspRemove has been issued. */
396	pb->ioResult = errState;
397	return EINVAL;
398    }
399
400
401    /*
402     * The previous Macintosh ADSP allowed you to call close on a
403     * connection that was in the process of opening or passively
404     * waiting for an open request. It is also legal to close a
405     * connection that is already closed.  No error will be generated.
406     *
407     * It is also legal to issue a second close call while the first
408     * is still pending.
409     */
410    if (pb->csCode == (short)dspClose) {
411	if ((sp->state == (short)sPassive) || (sp->state == (short)sOpening)) {
412	    sp->state = sClosed;
413	    DoClose(sp, errAborted, 0);
414	    pb->ioResult = 0;
415	    adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref);
416	    return 0;
417	}
418
419	if (sp->state == (word)sClosed)	{ /* Ok to close a closed connection */
420	    pb->ioResult = 0;
421	    adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref);
422	    return 0;
423	}
424	if ((sp->state != (word)sOpen) && (sp->state != (word)sClosing)) {
425	    pb->ioResult = errState;
426	    return EINVAL;
427	}
428
429	sp->state = sClosing;	/* No matter what, we're closing */
430    } 				/* dspClose */
431
432    else {			/* dspRemove */
433	sp->removing = 1;	/* Prevent allowing another dspClose. */
434				/* Tells completion routine of close */
435				/* packet to remove us. */
436
437	if (sp->state == sPassive || sp->state == sClosed ||
438	    sp->state == sOpening) {
439	    sp->state = sClosed;
440	    DoClose(sp, errAborted, 0); /* Will remove CCB! */
441	    return 0;
442	} else			/* sClosing & sOpen */
443	    sp->state = sClosing;
444
445    }				/* dspRemove */
446
447    if (pb->u.closeParams.abort || CheckOkToClose(sp)) /* going to close */
448    {
449	AbortIO(sp, errAborted);
450	sp->sendCtl = B_CTL_CLOSE; /* Send close advice */
451    }
452
453    pb->ioResult = 1;
454    if ( (mp = gbuf_copym(pb->mp)) ) {	/* duplicate user request */
455	    adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref); /* release user */
456	    pb = (struct adspcmd *)gbuf_rptr(mp); /* get new parameter block */
457	    pb->ioc = 0;
458	    pb->mp = mp;
459	    qAddToEnd((struct qlink **)&sp->opb, (struct qlink *)pb);	/* and save it */
460    } else {
461	    pb->ioResult = 0;
462	    adspioc_ack(0, (gbuf_t *)pb->ioc, pb->gref); /* release user, and keep no copy
463					     * for kernel bookkeeping, yetch!
464					     */
465    }
466    CheckSend(sp);
467
468    return 0;
469}
470
471static void qRemove(qptr, elem)
472    register CCBPtr qptr;
473    register CCBPtr elem;
474{
475
476    while(qptr->ccbLink) {
477	if ((DSPPBPtr)(qptr->ccbLink) == (DSPPBPtr)elem) {
478	    qptr->ccbLink = elem->ccbLink;
479	    elem->ccbLink = 0;
480	    return;
481	}
482	qptr = qptr->ccbLink;
483    }
484}
485
486int RxClose(sp)
487    register CCBPtr sp;
488{
489    register gbuf_t *mp;
490    register struct adspcmd *pb;
491
492	if ((sp->state == sClosing) || (sp->state == sClosed))
493		return 0;
494
495    sp->state = sClosed;
496    CheckReadQueue(sp);		/* try to deliver all remaining data */
497
498    if ( (mp = gbuf_alloc(sizeof(struct adspcmd), PRI_HI)) ) {
499        pb = (struct adspcmd *)gbuf_rptr(mp);
500	gbuf_wset(mp,sizeof(struct adspcmd));
501	pb->ioc = 0;
502	pb->mp = mp;
503
504	pb->csCode = dspClose;
505	pb->ioResult = 0;
506	completepb(sp, pb);		/* send close completion */
507    }
508
509if ((sp->userFlags & eClosed) == 0)
510    DoClose(sp, errAborted, -1);	/* abort send requests and timers */
511
512    return 0;
513}
514