]> git.saurik.com Git - apple/xnu.git/blob - bsd/netat/adsp_Close.c
xnu-1228.3.13.tar.gz
[apple/xnu.git] / bsd / netat / adsp_Close.c
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
66 static 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 */
79 int 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 */
121 int 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 */
165 void RemoveCCB(CCBPtr, struct adspcmd *);
166
167 void 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
216 int AbortIO(CCBPtr, short);
217
218 int 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 */
260 void 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 */
356 int 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
471 static 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
486 int 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
509 if ((sp->userFlags & eClosed) == 0)
510 DoClose(sp, errAborted, -1); /* abort send requests and timers */
511
512 return 0;
513 }