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