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