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