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