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