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