Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1488 lines
31 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. aspc.c
  5. Abstract:
  6. This module implements the ASP client protocol.
  7. Author:
  8. Jameel Hyder (jameelh@microsoft.com)
  9. Nikhil Kamkolkar (nikhilk@microsoft.com)
  10. Revision History:
  11. 30 Mar 1993 Initial Version
  12. Notes: Tab stop: 4
  13. --*/
  14. #include <atalk.h>
  15. #pragma hdrstop
  16. #define FILENUM ASPC
  17. #ifdef ALLOC_PRAGMA
  18. #pragma alloc_text(INIT, AtalkInitAspCInitialize)
  19. #pragma alloc_text(PAGE, AtalkAspCCreateAddress)
  20. #pragma alloc_text(PAGEASPC, AtalkAspCCleanupAddress)
  21. #pragma alloc_text(PAGEASPC, AtalkAspCCloseAddress)
  22. #pragma alloc_text(PAGEASPC, AtalkAspCCreateConnection)
  23. #pragma alloc_text(PAGEASPC, AtalkAspCCleanupConnection)
  24. #pragma alloc_text(PAGEASPC, AtalkAspCCloseConnection)
  25. #pragma alloc_text(PAGEASPC, AtalkAspCAssociateAddress)
  26. #pragma alloc_text(PAGEASPC, AtalkAspCDissociateAddress)
  27. #pragma alloc_text(PAGEASPC, AtalkAspCPostConnect)
  28. #pragma alloc_text(PAGEASPC, AtalkAspCDisconnect)
  29. #pragma alloc_text(PAGEASPC, AtalkAspCGetStatus)
  30. #pragma alloc_text(PAGEASPC, AtalkAspCGetAttn)
  31. #pragma alloc_text(PAGEASPC, AtalkAspCCmdOrWrite)
  32. #pragma alloc_text(PAGEASPC, atalkAspCIncomingOpenReply)
  33. #pragma alloc_text(PAGEASPC, atalkAspCIncomingStatus)
  34. #pragma alloc_text(PAGEASPC, atalkAspCIncomingCmdReply)
  35. #pragma alloc_text(PAGEASPC, atalkAspCHandler)
  36. #pragma alloc_text(PAGEASPC, AtalkAspCAddrDereference)
  37. #pragma alloc_text(PAGEASPC, AtalkAspCConnDereference)
  38. #pragma alloc_text(PAGEASPC, atalkAspCSessionMaintenanceTimer)
  39. #pragma alloc_text(PAGEASPC, atalkAspCQueueAddrGlobalList)
  40. #pragma alloc_text(PAGEASPC, atalkAspCDeQueueAddrGlobalList)
  41. #pragma alloc_text(PAGEASPC, atalkAspCQueueConnGlobalList)
  42. #pragma alloc_text(PAGEASPC, atalkAspCDeQueueConnGlobalList)
  43. #endif
  44. VOID
  45. AtalkInitAspCInitialize(
  46. VOID
  47. )
  48. /*++
  49. Routine Description:
  50. Arguments:
  51. Return Value:
  52. --*/
  53. {
  54. AtalkTimerInitialize(&atalkAspCConnMaint.ascm_SMTTimer,
  55. atalkAspCSessionMaintenanceTimer,
  56. ASP_SESSION_MAINTENANCE_TIMER);
  57. INITIALIZE_SPIN_LOCK(&atalkAspCLock);
  58. }
  59. ATALK_ERROR
  60. AtalkAspCCreateAddress(
  61. IN PATALK_DEV_CTX pDevCtx OPTIONAL,
  62. OUT PASPC_ADDROBJ * ppAspAddr
  63. )
  64. /*++
  65. Routine Description:
  66. Create an ASP address object.
  67. Arguments:
  68. Return Value:
  69. --*/
  70. {
  71. ATALK_ERROR Status;
  72. PASPC_ADDROBJ pAspAddr;
  73. int i;
  74. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
  75. ("AtalkAspCCreateAddr: Entered\n"));
  76. // Allocate memory for the Asp address object
  77. *ppAspAddr = NULL;
  78. if ((pAspAddr = AtalkAllocZeroedMemory(sizeof(ASPC_ADDROBJ))) == NULL)
  79. {
  80. return ATALK_RESR_MEM;
  81. }
  82. // Create an Atp Socket on the port for the Sls
  83. Status = AtalkAtpOpenAddress(AtalkDefaultPort,
  84. 0,
  85. NULL,
  86. ATP_DEF_MAX_SINGLE_PKT_SIZE,
  87. ATP_DEF_SEND_USER_BYTES_ALL,
  88. NULL,
  89. FALSE,
  90. &pAspAddr->aspcao_pAtpAddr);
  91. if (!ATALK_SUCCESS(Status))
  92. {
  93. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
  94. ("AtalkAspCCreateAddress: AtalkAtpOpenAddress %ld\n", Status));
  95. AtalkFreeMemory(pAspAddr);
  96. return Status;
  97. }
  98. // Initialize the Asp address object
  99. #if DBG
  100. pAspAddr->aspcao_Signature = ASPCAO_SIGNATURE;
  101. #endif
  102. INITIALIZE_SPIN_LOCK(&pAspAddr->aspcao_Lock);
  103. atalkAspCQueueAddrGlobalList(pAspAddr);
  104. // Refcount for creation and atp address. This goes away when atp address is closed
  105. // pAspAddr->aspcao_Flags = 0;
  106. pAspAddr->aspcao_RefCount = 1 + 1;
  107. *ppAspAddr = pAspAddr;
  108. return ATALK_NO_ERROR;
  109. }
  110. ATALK_ERROR
  111. AtalkAspCCleanupAddress(
  112. IN PASPC_ADDROBJ pAspAddr
  113. )
  114. /*++
  115. Routine Description:
  116. Arguments:
  117. Return Value:
  118. --*/
  119. {
  120. return(ATALK_NO_ERROR);
  121. }
  122. ATALK_ERROR
  123. AtalkAspCCloseAddress(
  124. IN PASPC_ADDROBJ pAspAddr,
  125. IN GENERIC_COMPLETION CompletionRoutine,
  126. IN PVOID CloseContext
  127. )
  128. /*++
  129. Routine Description:
  130. Arguments:
  131. Return Value:
  132. --*/
  133. {
  134. return(ATALK_NO_ERROR);
  135. }
  136. ATALK_ERROR
  137. AtalkAspCCreateConnection(
  138. IN PVOID pConnCtx, // Context to associate with the session
  139. IN PATALK_DEV_CTX pDevCtx OPTIONAL,
  140. OUT PASPC_CONNOBJ * ppAspConn
  141. )
  142. /*++
  143. Routine Description:
  144. Create an ASP session. The created session starts off being an orphan, i.e.
  145. it has no parent address object. It gets one when it is associated.
  146. Arguments:
  147. Return Value:
  148. --*/
  149. {
  150. PASPC_CONNOBJ pAspConn;
  151. // Allocate memory for a connection object
  152. if ((pAspConn = AtalkAllocZeroedMemory(sizeof(ASPC_CONNOBJ))) == NULL)
  153. {
  154. return ATALK_RESR_MEM;
  155. }
  156. #if DBG
  157. pAspConn->aspcco_Signature = ASPCCO_SIGNATURE;
  158. #endif
  159. INITIALIZE_SPIN_LOCK(&pAspConn->aspcco_Lock);
  160. pAspConn->aspcco_ConnCtx = pConnCtx;
  161. // pAspConn->aspcco_Flags = 0;
  162. pAspConn->aspcco_RefCount = 1; // Creation reference
  163. pAspConn->aspcco_NextSeqNum = 1; // Set to 1, not 0.
  164. AtalkInitializeRT(&pAspConn->aspcco_RT,
  165. ASP_INIT_REQ_INTERVAL,
  166. ASP_MIN_REQ_INTERVAL,
  167. ASP_MAX_REQ_INTERVAL);
  168. *ppAspConn = pAspConn;
  169. // Insert into the global connection list.
  170. atalkAspCQueueConnGlobalList(pAspConn);
  171. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
  172. ("AtalkAspCreateConnection: %lx\n", pAspConn));
  173. return ATALK_NO_ERROR;
  174. }
  175. ATALK_ERROR
  176. AtalkAspCCleanupConnection(
  177. IN PASPC_CONNOBJ pAspConn
  178. )
  179. /*++
  180. Routine Description:
  181. Arguments:
  182. Return Value:
  183. --*/
  184. {
  185. return ATALK_NO_ERROR;
  186. }
  187. ATALK_ERROR
  188. AtalkAspCCloseConnection(
  189. IN PASPC_CONNOBJ pAspConn,
  190. IN GENERIC_COMPLETION CompletionRoutine,
  191. IN PVOID CloseContext
  192. )
  193. /*++
  194. Routine Description:
  195. Arguments:
  196. Return Value:
  197. --*/
  198. {
  199. return ATALK_NO_ERROR;
  200. }
  201. ATALK_ERROR
  202. AtalkAspCAssociateAddress(
  203. IN PASPC_ADDROBJ pAspAddr,
  204. IN PASPC_CONNOBJ pAspConn
  205. )
  206. /*++
  207. Routine Description:
  208. Arguments:
  209. Return Value:
  210. --*/
  211. {
  212. ATALK_ERROR error;
  213. KIRQL OldIrql;
  214. ASSERT(VALID_ASPCAO(pAspAddr));
  215. ASSERT(VALID_ASPCCO(pAspConn));
  216. ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);
  217. error = ATALK_ALREADY_ASSOCIATED;
  218. if ((pAspConn->aspcco_Flags & ASPCCO_ASSOCIATED) == 0)
  219. {
  220. error = ATALK_NO_ERROR;
  221. pAspConn->aspcco_Flags |= ASPCCO_ASSOCIATED;
  222. pAspConn->aspcco_pAspCAddr = pAspAddr;
  223. }
  224. RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);
  225. return error;
  226. }
  227. ATALK_ERROR
  228. AtalkAspCDissociateAddress(
  229. IN PASPC_CONNOBJ pAspConn
  230. )
  231. /*++
  232. Routine Description:
  233. Arguments:
  234. Return Value:
  235. --*/
  236. {
  237. PASPC_ADDROBJ pAspAddr;
  238. KIRQL OldIrql;
  239. ATALK_ERROR error = ATALK_NO_ERROR;
  240. ASSERT(VALID_ASPCCO(pAspConn));
  241. ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);
  242. if ((pAspConn->aspcco_Flags & (ASPCCO_CONNECTING |
  243. ASPCCO_ACTIVE |
  244. ASPCCO_ASSOCIATED)) != ASPCCO_ASSOCIATED)
  245. {
  246. error = ATALK_INVALID_CONNECTION;
  247. }
  248. else
  249. {
  250. pAspAddr = pAspConn->aspcco_pAspCAddr ;
  251. ASSERT(VALID_ASPCAO(pAspAddr));
  252. // Clear associated flag.
  253. pAspConn->aspcco_Flags &= ~ASPCCO_ASSOCIATED;
  254. pAspConn->aspcco_pAspCAddr = NULL;
  255. }
  256. RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);
  257. return error;
  258. }
  259. ATALK_ERROR
  260. AtalkAspCPostConnect(
  261. IN PASPC_CONNOBJ pAspConn,
  262. IN PATALK_ADDR pRemoteAddr,
  263. IN PVOID pConnectCtx,
  264. IN GENERIC_COMPLETION CompletionRoutine
  265. )
  266. /*++
  267. Routine Description:
  268. Arguments:
  269. Return Value:
  270. --*/
  271. {
  272. ATALK_ERROR error = ATALK_NO_ERROR;
  273. BOOLEAN DerefConn = FALSE;
  274. KIRQL OldIrql;
  275. BYTE UserBytes[ATP_USERBYTES_SIZE];
  276. PBYTE pOpenPkt = NULL, pRespPkt = NULL;
  277. PAMDL pOpenAmdl = NULL, pRespAmdl = NULL;
  278. PASPC_ADDROBJ pAspAddr = pAspConn->aspcco_pAspCAddr;
  279. ASSERT(VALID_ASPCAO(pAspAddr));
  280. ASSERT(VALID_ASPCCO(pAspConn));
  281. ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);
  282. do
  283. {
  284. if ((pAspConn->aspcco_Flags & (ASPCCO_CONNECTING |
  285. ASPCCO_ACTIVE |
  286. ASPCCO_ASSOCIATED)) != ASPCCO_ASSOCIATED)
  287. {
  288. error = ATALK_INVALID_CONNECTION;
  289. break;
  290. }
  291. // Reference the connection for the request we will be posting
  292. AtalkAspCConnReferenceByPtrNonInterlock(pAspConn, &error);
  293. if (ATALK_SUCCESS(error))
  294. {
  295. DerefConn = TRUE;
  296. // Make sure flags are clean.
  297. pAspConn->aspcco_Flags |= ASPCCO_CONNECTING;
  298. pAspConn->aspcco_ConnectCtx = pConnectCtx;
  299. pAspConn->aspcco_ConnectCompletion = CompletionRoutine;
  300. pAspConn->aspcco_ServerSlsAddr = *pRemoteAddr;
  301. // Copy the atp address object for efficiency
  302. pAspConn->aspcco_pAtpAddr = pAspAddr->aspcao_pAtpAddr;
  303. }
  304. else
  305. {
  306. ASSERTMSG("AtalkAspCPostConnect: Connection ref failed\n", 0);
  307. }
  308. } while (FALSE);
  309. RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);
  310. if (ATALK_SUCCESS(error))
  311. {
  312. UserBytes[ASP_CMD_OFF] = ASP_OPEN_SESSION;
  313. UserBytes[ASP_WSS_OFF] = pAspAddr->aspcao_pAtpAddr->atpao_DdpAddr->ddpao_Addr.ata_Socket;
  314. UserBytes[ASP_VERSION_OFF] = ASP_VERSION[0];
  315. UserBytes[ASP_VERSION_OFF+1] = ASP_VERSION[1];
  316. // Post the open session request.
  317. error = AtalkAtpPostReq(pAspConn->aspcco_pAtpAddr,
  318. &pAspConn->aspcco_ServerSlsAddr,
  319. &pAspConn->aspcco_OpenSessTid,
  320. ATP_REQ_EXACTLY_ONCE, // ExactlyOnce request
  321. NULL,
  322. 0,
  323. UserBytes,
  324. NULL,
  325. 0,
  326. ATP_RETRIES_FOR_ASP,
  327. ATP_MAX_INTERVAL_FOR_ASP,
  328. THIRTY_SEC_TIMER,
  329. atalkAspCIncomingOpenReply,
  330. pAspConn);
  331. if (ATALK_SUCCESS(error))
  332. {
  333. error = ATALK_PENDING;
  334. DerefConn = FALSE;
  335. }
  336. else
  337. {
  338. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
  339. ("AtalkAspCPostConnect: AtalkAtpPostReq: failed %ld\n", error));
  340. // Remove connection from the connect list and reset states.
  341. ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);
  342. pAspConn->aspcco_Flags &= ~ASPCCO_CONNECTING;
  343. pAspConn->aspcco_ConnectCtx = NULL;
  344. pAspConn->aspcco_ConnectCompletion = NULL;
  345. pAspConn->aspcco_pAtpAddr = NULL;
  346. RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);
  347. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
  348. ("AtalkAspCPostConnect: failed %ld\n", error));
  349. }
  350. }
  351. if (DerefConn)
  352. {
  353. AtalkAspCConnDereference(pAspConn);
  354. }
  355. return error;
  356. }
  357. ATALK_ERROR
  358. AtalkAspCDisconnect(
  359. IN PASPC_CONNOBJ pAspConn,
  360. IN ATALK_DISCONNECT_TYPE DisconnectType,
  361. IN PVOID pDisconnectCtx,
  362. IN GENERIC_COMPLETION CompletionRoutine
  363. )
  364. /*++
  365. Routine Description:
  366. Arguments:
  367. Return Value:
  368. --*/
  369. {
  370. PASPC_REQUEST pAspReq, pAspReqNext;
  371. KIRQL OldIrql;
  372. ATALK_ERROR Error;
  373. // Abort all pending requests.
  374. ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);
  375. pAspConn->aspcco_Flags |= ASPCCO_DISCONNECTING;
  376. for (pAspReq = pAspConn->aspcco_pActiveReqs;
  377. pAspReq = pAspReq->aspcrq_Next;
  378. pAspReq = pAspReqNext)
  379. {
  380. pAspReqNext = pAspReq->aspcrq_Next;
  381. }
  382. RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);
  383. // Send a close session request to the other end
  384. // Error = AtalKAtpPostReq(pAspConn->aspcco_ServerSlsAddr);
  385. return ATALK_NO_ERROR;
  386. }
  387. ATALK_ERROR
  388. AtalkAspCGetStatus(
  389. IN PASPC_ADDROBJ pAspAddr,
  390. IN PATALK_ADDR pRemoteAddr,
  391. IN PAMDL pStatusAmdl,
  392. IN USHORT AmdlSize,
  393. IN PACTREQ pActReq
  394. )
  395. /*++
  396. Routine Description:
  397. Arguments:
  398. Return Value:
  399. --*/
  400. {
  401. ATALK_ERROR error;
  402. BYTE UserBytes[ATP_USERBYTES_SIZE];
  403. USHORT tid;
  404. if ((pRemoteAddr->ata_Network == 0) ||
  405. (pRemoteAddr->ata_Node == 0) ||
  406. (pRemoteAddr->ata_Socket == 0))
  407. {
  408. return ATALK_SOCKET_INVALID;
  409. }
  410. *(DWORD *)UserBytes = 0;
  411. UserBytes[ASP_CMD_OFF] = ASP_GET_STATUS;
  412. error = AtalkAtpPostReq(pAspAddr->aspcao_pAtpAddr,
  413. pRemoteAddr,
  414. &tid,
  415. 0, // ExactlyOnce request
  416. NULL,
  417. 0,
  418. UserBytes,
  419. pStatusAmdl,
  420. AmdlSize,
  421. ATP_RETRIES_FOR_ASP,
  422. ATP_MAX_INTERVAL_FOR_ASP,
  423. THIRTY_SEC_TIMER,
  424. atalkAspCIncomingStatus,
  425. (PVOID)pActReq);
  426. if (ATALK_SUCCESS(error))
  427. {
  428. error = ATALK_PENDING;
  429. }
  430. return error;
  431. }
  432. ATALK_ERROR
  433. AtalkAspCGetAttn(
  434. IN PASPC_CONNOBJ pAspConn,
  435. IN PAMDL pReadBuf,
  436. IN USHORT ReadBufLen,
  437. IN ULONG ReadFlags,
  438. IN PVOID pReadCtx,
  439. IN GENERIC_READ_COMPLETION CompletionRoutine
  440. )
  441. /*++
  442. Routine Description:
  443. Arguments:
  444. Return Value:
  445. --*/
  446. {
  447. ATALK_ERROR error = ATALK_NO_ERROR;
  448. KIRQL OldIrql;
  449. ASSERT(VALID_ASPCCO(pAspConn));
  450. ASSERT(*CompletionRoutine != NULL);
  451. ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);
  452. do
  453. {
  454. if ((pAspConn->aspcco_Flags & ASPCCO_ACTIVE) == 0)
  455. {
  456. error = ATALK_ASPC_CONN_NOT_ACTIVE;
  457. break;
  458. }
  459. if ((ReadFlags & TDI_RECEIVE_EXPEDITED) == 0)
  460. {
  461. error = ATALK_INVALID_PARAMETER;
  462. break;
  463. }
  464. if (pAspConn->aspcco_Flags & ASPCCO_ATTN_PENDING)
  465. {
  466. error = ATALK_ASPC_TOO_MANY_READS;
  467. break;
  468. }
  469. // PEEK not supported for ASPC
  470. if (ReadFlags & TDI_RECEIVE_PEEK)
  471. {
  472. error = ATALK_INVALID_REQUEST;
  473. break;
  474. }
  475. // We should have space for atleast one attention word
  476. if (ReadBufLen < sizeof(USHORT))
  477. {
  478. error = ATALK_BUFFER_TOO_SMALL;
  479. break;
  480. }
  481. // Check if we have any outstanding attention words
  482. if (pAspConn->aspcco_AttnOutPtr < pAspConn->aspcco_AttnInPtr)
  483. {
  484. PUSHORT AttnBuf;
  485. USHORT BufSize = 0;
  486. AttnBuf = AtalkGetAddressFromMdlSafe(
  487. pReadBuf,
  488. NormalPagePriority);
  489. if (AttnBuf == NULL) {
  490. error = ATALK_FAILURE;
  491. break;
  492. }
  493. while (pAspConn->aspcco_AttnOutPtr < pAspConn->aspcco_AttnInPtr)
  494. {
  495. *AttnBuf++ = pAspConn->aspcco_AttnBuf[pAspConn->aspcco_AttnOutPtr % MAX_ASPC_ATTNS];
  496. pAspConn->aspcco_AttnOutPtr++;
  497. BufSize += sizeof(USHORT);
  498. }
  499. (*CompletionRoutine)(error,
  500. pReadBuf,
  501. BufSize,
  502. ReadFlags,
  503. pReadCtx);
  504. error = ATALK_PENDING;
  505. break;
  506. }
  507. error = ATALK_INVALID_CONNECTION;
  508. if ((pAspConn->aspcco_Flags & (ASPCCO_CLOSING | ASPCCO_DISCONNECTING)) == 0)
  509. {
  510. AtalkAspCConnReferenceByPtrNonInterlock(pAspConn, &error);
  511. }
  512. if (!ATALK_SUCCESS(error))
  513. {
  514. break;
  515. }
  516. pAspConn->aspcco_Flags |= ASPCCO_ATTN_PENDING;
  517. // Remember read information in the connection object.
  518. pAspConn->aspcco_ReadCompletion = CompletionRoutine;
  519. pAspConn->aspcco_ReadCtx = pReadCtx;
  520. } while (FALSE);
  521. RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);
  522. return error;
  523. }
  524. ATALK_ERROR
  525. AtalkAspCCmdOrWrite(
  526. IN PASPC_CONNOBJ pAspConn,
  527. IN PAMDL pCmdMdl,
  528. IN USHORT CmdSize,
  529. IN PAMDL pReplyMdl,
  530. IN USHORT ReplySize,
  531. IN BOOLEAN fWrite, // If TRUE, its a write else command
  532. IN PACTREQ pActReq
  533. )
  534. /*++
  535. Routine Description:
  536. Arguments:
  537. Reply and Write buffers are overlaid.
  538. Return Value:
  539. --*/
  540. {
  541. ATALK_ERROR Error;
  542. KIRQL OldIrql;
  543. PASPC_REQUEST pAspReq;
  544. BYTE UserBytes[ATP_USERBYTES_SIZE];
  545. do
  546. {
  547. if (((pAspConn->aspcco_Flags & (ASPCCO_ACTIVE |
  548. ASPCCO_CONNECTING |
  549. ASPCCO_LOCAL_CLOSE |
  550. ASPCCO_REMOTE_CLOSE |
  551. ASPCCO_CLOSING)) != ASPCCO_ACTIVE))
  552. {
  553. Error = ATALK_INVALID_REQUEST;
  554. break;
  555. }
  556. AtalkAspCConnReference(pAspConn, &Error);
  557. if (!ATALK_SUCCESS(Error))
  558. {
  559. break;
  560. }
  561. if ((pAspReq = (PASPC_REQUEST)AtalkAllocZeroedMemory(sizeof(ASPC_REQUEST))) == NULL)
  562. {
  563. Error = ATALK_RESR_MEM;
  564. break;
  565. }
  566. #if DBG
  567. pAspReq->aspcrq_Signature = ASPCRQ_SIGNATURE;
  568. #endif
  569. pAspReq->aspcrq_Flags = fWrite ? ASPCRQ_WRITE : ASPCRQ_COMMAND;
  570. pAspReq->aspcrq_pReplyMdl = pReplyMdl;
  571. pAspReq->aspcrq_ReplySize = ReplySize;
  572. pAspReq->aspcrq_RefCount = 2; // Creation+incoming reply handler
  573. ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);
  574. pAspReq->aspcrq_SeqNum = pAspConn->aspcco_NextSeqNum ++;
  575. pAspReq->aspcrq_Next = pAspConn->aspcco_pActiveReqs;
  576. pAspConn->aspcco_pActiveReqs = pAspReq;
  577. pAspReq->aspcrq_pAspConn = pAspConn;
  578. RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);
  579. // Build user bytes and send our request over
  580. UserBytes[ASP_CMD_OFF] = fWrite ? ASP_CMD : ASP_WRITE;
  581. UserBytes[ASP_SESSIONID_OFF] = pAspConn->aspcco_SessionId;
  582. PUTSHORT2SHORT(&UserBytes[ASP_SEQUENCE_NUM_OFF], pAspReq->aspcrq_SeqNum);
  583. Error = AtalkAtpPostReq(pAspConn->aspcco_pAtpAddr,
  584. &pAspConn->aspcco_ServerSssAddr,
  585. &pAspReq->aspcrq_ReqXactId,
  586. ATP_REQ_EXACTLY_ONCE, // XO request
  587. pCmdMdl,
  588. CmdSize,
  589. UserBytes,
  590. pReplyMdl,
  591. ReplySize,
  592. ATP_RETRIES_FOR_ASP, // Retry count
  593. pAspConn->aspcco_RT.rt_Base,// Retry interval
  594. THIRTY_SEC_TIMER,
  595. atalkAspCIncomingCmdReply,
  596. pAspReq);
  597. if (!ATALK_SUCCESS(Error))
  598. {
  599. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
  600. ("AtalkAspCCmdOrWrite: AtalkAtpPostReq failed %lx\n", Error));
  601. atalkAspCIncomingCmdReply(Error,
  602. pAspReq,
  603. pCmdMdl,
  604. pReplyMdl,
  605. ReplySize,
  606. UserBytes);
  607. }
  608. } while (FALSE);
  609. return Error;
  610. }
  611. BOOLEAN
  612. AtalkAspCConnectionIsValid(
  613. IN PASPC_CONNOBJ pAspConn
  614. )
  615. /*++
  616. Routine Description:
  617. Arguments:
  618. Return Value:
  619. --*/
  620. {
  621. KIRQL OldIrql;
  622. PASPC_CONNOBJ pTmpConn;
  623. BOOLEAN fConnIsValid=FALSE;
  624. ACQUIRE_SPIN_LOCK(&atalkAspCLock, &OldIrql);
  625. pTmpConn = atalkAspCConnList;
  626. while (pTmpConn)
  627. {
  628. if (pTmpConn == pAspConn)
  629. {
  630. fConnIsValid = TRUE;
  631. break;
  632. }
  633. pTmpConn = pTmpConn->aspcco_Next;
  634. }
  635. RELEASE_SPIN_LOCK(&atalkAspCLock, OldIrql);
  636. return(fConnIsValid);
  637. }
  638. LOCAL VOID
  639. atalkAspCCloseSession(
  640. IN PASPC_CONNOBJ pAspConn
  641. )
  642. /*++
  643. Routine Description:
  644. Arguments:
  645. Return Value:
  646. --*/
  647. {
  648. }
  649. LOCAL VOID
  650. atalkAspCIncomingOpenReply(
  651. IN ATALK_ERROR ErrorCode,
  652. IN PASPC_CONNOBJ pAspConn, // Our context
  653. IN PAMDL pReqAmdl,
  654. IN PAMDL pReadAmdl,
  655. IN USHORT ReadLen,
  656. IN PBYTE ReadUserBytes
  657. )
  658. /*++
  659. Routine Description:
  660. Arguments:
  661. Return Value:
  662. --*/
  663. {
  664. ATALK_ERROR error;
  665. USHORT OpenStatus;
  666. BYTE UserBytes[ATP_USERBYTES_SIZE];
  667. BOOLEAN DerefConn = FALSE;
  668. PASPC_ADDROBJ pAspAddr = pAspConn->aspcco_pAspCAddr;
  669. ASSERT(VALID_ASPCCO(pAspConn));
  670. if (ATALK_SUCCESS(ErrorCode))
  671. do
  672. {
  673. // Check for open reply code in packet.
  674. GETSHORT2SHORT(&OpenStatus, &ReadUserBytes[ASP_ERRORCODE_OFF]);
  675. if (OpenStatus != 0)
  676. {
  677. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
  678. ("atalkAspCIncomingOpenReply: Failed %ld, %lx\n", OpenStatus, pAspConn));
  679. DerefConn = TRUE; // Since we are not queuing a request handler
  680. ErrorCode = ATALK_REMOTE_CLOSE;
  681. break;
  682. }
  683. ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
  684. // Save the socket the server's SSS
  685. pAspConn->aspcco_ServerSssAddr = pAspConn->aspcco_ServerSlsAddr;
  686. pAspConn->aspcco_ServerSssAddr.ata_Socket = ReadUserBytes[ASP_SSS_OFF];
  687. pAspConn->aspcco_SessionId = ReadUserBytes[ASP_SESSIONID_OFF];
  688. pAspConn->aspcco_Flags &= ~ASPCCO_CONNECTING;
  689. pAspConn->aspcco_Flags |= ASPCCO_ACTIVE;
  690. pAspConn->aspcco_LastContactTime = AtalkGetCurrentTick();
  691. // Reference for the request handler
  692. AtalkAspCConnReferenceByPtrNonInterlock(pAspConn, &error);
  693. // Build up userBytes to start tickling the other end.
  694. UserBytes[ASP_CMD_OFF] = ASP_TICKLE;
  695. UserBytes[ASP_SESSIONID_OFF] = pAspConn->aspcco_SessionId;
  696. PUTSHORT2SHORT(UserBytes + ASP_ERRORCODE_OFF, 0);
  697. RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
  698. // Set the request handler on this connection.
  699. // It will handle tickle's, close's and write-continue
  700. AtalkAtpSetReqHandler(pAspAddr->aspcao_pAtpAddr,
  701. atalkAspCHandler,
  702. pAspConn);
  703. error = AtalkAtpPostReq(pAspConn->aspcco_pAtpAddr,
  704. &pAspConn->aspcco_ServerSlsAddr,
  705. &pAspConn->aspcco_TickleTid,
  706. 0, // ALO transaction
  707. NULL,
  708. 0,
  709. UserBytes,
  710. NULL,
  711. 0,
  712. ATP_INFINITE_RETRIES,
  713. ASP_TICKLE_INTERVAL,
  714. THIRTY_SEC_TIMER,
  715. NULL,
  716. NULL);
  717. if (ATALK_SUCCESS(error))
  718. {
  719. ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
  720. pAspConn->aspcco_Flags |= ASPCCO_TICKLING;
  721. RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
  722. }
  723. else
  724. {
  725. DerefConn = TRUE; // Since we are not queuing a request handler
  726. }
  727. } while (FALSE);
  728. if (!ATALK_SUCCESS(ErrorCode))
  729. {
  730. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
  731. ("atalkAspCIncomingOpenReply: Incoming connect fail %lx\n", ErrorCode));
  732. AtalkAspCConnDereference(pAspConn);
  733. // Mark it as inactive
  734. ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
  735. pAspConn->aspcco_Flags &= ~ASPCCO_ACTIVE;
  736. RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
  737. }
  738. // Call the completion routine.
  739. (*pAspConn->aspcco_ConnectCompletion)(ErrorCode, pAspConn->aspcco_ConnectCtx);
  740. }
  741. LOCAL VOID
  742. atalkAspCIncomingStatus(
  743. IN ATALK_ERROR ErrorCode,
  744. IN PACTREQ pActReq, // Our Ctx
  745. IN PAMDL pReqAmdl,
  746. IN PAMDL pStatusAmdl,
  747. IN USHORT StatusLen,
  748. IN PBYTE ReadUserBytes
  749. )
  750. /*++
  751. Routine Description:
  752. Arguments:
  753. Return Value:
  754. --*/
  755. {
  756. // Call the action completion routine
  757. (*pActReq->ar_Completion)(ErrorCode, pActReq);
  758. }
  759. LOCAL VOID
  760. atalkAspCIncomingCmdReply(
  761. IN ATALK_ERROR Error,
  762. IN PASPC_REQUEST pAspReq,
  763. IN PAMDL pReqAMdl,
  764. IN PAMDL pRespAMdl,
  765. IN USHORT RespSize,
  766. IN PBYTE RespUserBytes
  767. )
  768. /*++
  769. Routine Description:
  770. Arguments:
  771. Return Value:
  772. --*/
  773. {
  774. PASPC_CONNOBJ pAspConn;
  775. PASPC_REQUEST * ppAspReq;
  776. KIRQL OldIrql;
  777. pAspConn = pAspReq->aspcrq_pAspConn;
  778. ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);
  779. // Unlink the request from the active list
  780. for (ppAspReq = &pAspConn->aspcco_pActiveReqs;
  781. *ppAspReq != NULL;
  782. ppAspReq = &((*ppAspReq)->aspcrq_Next))
  783. {
  784. if (pAspReq == *ppAspReq)
  785. {
  786. *ppAspReq = pAspReq->aspcrq_Next;
  787. break;
  788. }
  789. }
  790. ASSERT(*ppAspReq == pAspReq->aspcrq_Next);
  791. RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);
  792. // Complete the request
  793. (*pAspReq->aspcrq_pActReq->ar_Completion)(Error, pAspReq->aspcrq_pActReq);
  794. // and dereference the connection
  795. AtalkAspCConnDereference(pAspConn);
  796. // and finally free the request
  797. AtalkFreeMemory(pAspReq);
  798. }
  799. LOCAL VOID
  800. atalkAspCHandler(
  801. IN ATALK_ERROR ErrorCode,
  802. IN PASPC_CONNOBJ pAspConn,
  803. IN PATP_RESP pAtpResp, // Used by PostResp/CancelResp
  804. IN PATALK_ADDR pSrcAddr, // Address of requestor
  805. IN USHORT PktLen,
  806. IN PBYTE pPkt,
  807. IN PBYTE pUserBytes
  808. )
  809. /*++
  810. Routine Description:
  811. Handle tickle, write-continue requests, attentions and close from the server.
  812. Arguments:
  813. Return Value:
  814. --*/
  815. {
  816. USHORT SequenceNum; // From the incoming packet
  817. BYTE SessionId; // -- ditto --
  818. BYTE RequestType; // -- ditto --
  819. BOOLEAN CancelTickle, ReleaseLock = TRUE, CancelResp = FALSE, Deref = FALSE;
  820. PIRP exRecvIrp;
  821. PTDI_IND_RECEIVE_EXPEDITED exRecvHandler;
  822. PVOID exRecvHandlerCtx;
  823. ULONG exIndicateFlags;
  824. PASPC_REQUEST pAspReq;
  825. ATALK_ERROR Error;
  826. do
  827. {
  828. if (!ATALK_SUCCESS(ErrorCode))
  829. {
  830. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
  831. ("atalkAspCHandler: Error %ld\n", ErrorCode));
  832. // Take away the reference on the Conn now that the atp address is closing
  833. if (ErrorCode == ATALK_ATP_CLOSING)
  834. AtalkAspCConnDereference(pAspConn);
  835. break;
  836. }
  837. ASSERT(VALID_ASPCCO(pAspConn));
  838. ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
  839. SessionId = pUserBytes[ASP_SESSIONID_OFF];
  840. RequestType = pUserBytes[ASP_CMD_OFF];
  841. GETSHORT2SHORT(&SequenceNum, pUserBytes+ASP_SEQUENCE_NUM_OFF);
  842. AtalkAspCConnReferenceByPtrNonInterlock(pAspConn, &Error);
  843. if (ATALK_SUCCESS(Error) && (pAspConn->aspcco_SessionId == SessionId))
  844. {
  845. pAspConn->aspcco_LastContactTime = AtalkGetCurrentTick();
  846. switch (RequestType)
  847. {
  848. case ASP_CLOSE_SESSION:
  849. // Cancel all outstanding requests (and any posted replies to write continue)
  850. // and shut down the session. Start off by sending a close response.
  851. CancelTickle = ((pAspConn->aspcco_Flags &ASPCO_TICKLING) != 0);
  852. pAspConn->aspcco_Flags &= ~(ASPCCO_ACTIVE | ASPCCO_TICKLING);
  853. pAspConn->aspcco_Flags |= ASPCO_REMOTE_CLOSE;
  854. RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
  855. ReleaseLock = FALSE;
  856. // Send a CloseSession reply and close the session
  857. Error = AtalkAtpPostResp(pAtpResp,
  858. pSrcAddr,
  859. NULL,
  860. 0,
  861. NULL,
  862. AtalkAtpGenericRespComplete,
  863. pAtpResp);
  864. if (!ATALK_SUCCESS(Error))
  865. {
  866. AtalkAtpGenericRespComplete(Error, pAtpResp);
  867. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
  868. ("atalkAspSssXHandler: AtalkAtpPostResp failed %ld\n", Error));
  869. }
  870. // Cancel the tickle requests for this session
  871. if (CancelTickle)
  872. {
  873. Error = AtalkAtpCancelReq(pAspConn->aspcco_pAtpAddr,
  874. pAspConn->aspcco_TickleXactId,
  875. &pAspConn->aspcco_ServerSssAddr);
  876. if (!ATALK_SUCCESS(Error))
  877. {
  878. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
  879. ("atalkAspSssXHandler: AtalkAtpCancelReq %ld\n", Error));
  880. }
  881. }
  882. // Shut down this session, well almost ... Note that we have a reference
  883. // to this connection which will be Dereferenced by atalkAspSessionClose.
  884. atalkAspCCloseSession(pAspConn);
  885. break;
  886. case ASP_ATTENTION:
  887. // Server is sending us an attention. If we already have a getattn posted
  888. // complete that. If not, just save the attention word and indicate to AFD
  889. // that we have recvd. expedited data
  890. if ((pAspConn->aspcco_AttnInPtr - pAspConn->aspcco_AttnOutPtr) < MAX_ASPC_ATTNS)
  891. {
  892. pAspConn->aspcco_AttnBuf[pAspConn->aspcco_AttnInPtr % MAX_ASPC_ATTNS] = SequenceNum;
  893. pAspConn->aspcco_AttnInPtr++;
  894. RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
  895. ReleaseLock = FALSE;
  896. }
  897. break;
  898. case ASP_WRITE_DATA:
  899. // We need to find the request for which we sent a Write command. The
  900. // server now needs the data. Post a response for this.
  901. for (pAspReq = pAspConn->aspcco_pActiveReqs;
  902. pAspReq != NULL;
  903. pAspReq = pAspReq->aspcrq_Next)
  904. {
  905. if (pAspReq->aspcrq_SeqNum == SequenceNum)
  906. {
  907. RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
  908. ReleaseLock = FALSE;
  909. Error = AtalkAtpPostResp(pAtpResp,
  910. pSrcAddr,
  911. pAspReq->aspcrq_pWriteMdl,
  912. pAspReq->aspcrq_WriteSize,
  913. NULL,
  914. AtalkAtpGenericRespComplete,
  915. pAtpResp);
  916. Deref = TRUE;
  917. break;
  918. }
  919. }
  920. break;
  921. case ASP_TICKLE:
  922. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
  923. ("atalkAspCHandler: Received tickle from %x.%x Session %d\n",
  924. pSrcAddr->ata_Network, pSrcAddr->ata_Node, SessionId));
  925. CancelResp = TRUE;
  926. Deref = TRUE;
  927. break;
  928. default:
  929. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
  930. ("atalkAspCHandler: Invalid commnd %d from %x.%x Session %d\n",
  931. RequestType, pSrcAddr->ata_Network, pSrcAddr->ata_Node, SessionId));
  932. CancelResp = TRUE;
  933. Deref = TRUE;
  934. break;
  935. }
  936. }
  937. else
  938. {
  939. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
  940. ("atalkAspCHandler: Mismatched session id from %d.%d, expected %d, recvd. %d\n",
  941. pSrcAddr->ata_Network, pSrcAddr->ata_Node,
  942. pAspConn->aspcco_SessionId, SessionId));
  943. }
  944. if (ReleaseLock)
  945. {
  946. RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
  947. }
  948. if (CancelResp)
  949. {
  950. AtalkAtpCancelResp(pAtpResp);
  951. }
  952. if (Deref)
  953. {
  954. AtalkAspCConnDereference(pAspConn);
  955. }
  956. } while (FALSE);
  957. }
  958. LOCAL LONG FASTCALL
  959. atalkAspCSessionMaintenanceTimer(
  960. IN PTIMERLIST pTimer,
  961. IN BOOLEAN TimerShuttingDown
  962. )
  963. /*++
  964. Routine Description:
  965. Arguments:
  966. Return Value:
  967. --*/
  968. {
  969. return ATALK_TIMER_REQUEUE;
  970. }
  971. VOID FASTCALL
  972. AtalkAspCAddrDereference(
  973. IN PASPC_ADDROBJ pAspAddr
  974. )
  975. /*++
  976. Routine Description:
  977. Arguments:
  978. Return Value:
  979. --*/
  980. {
  981. BOOLEAN Close = FALSE;
  982. KIRQL OldIrql;
  983. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_REFASPADDR,
  984. ("AtalkAspCAddrDereference: %lx, %d\n",
  985. pAspAddr, pAspAddr->aspcao_RefCount-1));
  986. ASSERT (VALID_ASPCAO(pAspAddr));
  987. ACQUIRE_SPIN_LOCK(&pAspAddr->aspcao_Lock, &OldIrql);
  988. if (--(pAspAddr->aspcao_RefCount) == 0)
  989. {
  990. ASSERT(pAspAddr->aspcao_Flags & ASPCAO_CLOSING);
  991. Close = TRUE;
  992. }
  993. RELEASE_SPIN_LOCK(&pAspAddr->aspcao_Lock, OldIrql);
  994. if (Close)
  995. {
  996. if (pAspAddr->aspcao_CloseCompletion != NULL)
  997. (*pAspAddr->aspcao_CloseCompletion)(ATALK_NO_ERROR,
  998. pAspAddr->aspcao_CloseContext);
  999. // Finally free the memory
  1000. AtalkFreeMemory(pAspAddr);
  1001. AtalkUnlockAspCIfNecessary();
  1002. }
  1003. }
  1004. VOID FASTCALL
  1005. AtalkAspCConnDereference(
  1006. IN PASPC_CONNOBJ pAspConn
  1007. )
  1008. /*++
  1009. Routine Description:
  1010. Arguments:
  1011. Return Value:
  1012. --*/
  1013. {
  1014. BOOLEAN Close = FALSE;
  1015. KIRQL OldIrql;
  1016. ASSERT (VALID_ASPCCO(pAspConn));
  1017. DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_REFASPADDR,
  1018. ("AtalkAspCConnDereference: %lx, %d\n",
  1019. pAspConn, pAspConn->aspcco_RefCount-1));
  1020. ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);
  1021. if (--(pAspConn->aspcco_RefCount) == 0)
  1022. {
  1023. ASSERT(pAspConn->aspcco_Flags & ASPCCO_CLOSING);
  1024. Close = TRUE;
  1025. }
  1026. RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);
  1027. if (Close)
  1028. {
  1029. if (pAspConn->aspcco_CloseComp != NULL)
  1030. (*pAspConn->aspcco_CloseComp)(ATALK_NO_ERROR,
  1031. pAspConn->aspcco_CloseCtx);
  1032. atalkAspCDeQueueConnGlobalList(pAspConn);
  1033. // Finally free the memory
  1034. AtalkFreeMemory(pAspConn);
  1035. AtalkUnlockAspCIfNecessary();
  1036. }
  1037. }
  1038. LOCAL VOID
  1039. atalkAspCQueueAddrGlobalList(
  1040. IN PASPC_ADDROBJ pAspAddr
  1041. )
  1042. /*++
  1043. Routine Description:
  1044. Arguments:
  1045. Return Value:
  1046. --*/
  1047. {
  1048. KIRQL OldIrql;
  1049. ACQUIRE_SPIN_LOCK(&atalkAspCLock, &OldIrql);
  1050. AtalkLinkDoubleAtHead(atalkAspCAddrList, pAspAddr, aspcao_Next, aspcao_Prev);
  1051. RELEASE_SPIN_LOCK(&atalkAspCLock, OldIrql);
  1052. }
  1053. LOCAL VOID
  1054. atalkAspCDeQueueAddrGlobalList(
  1055. IN PASPC_ADDROBJ pAspAddr
  1056. )
  1057. /*++
  1058. Routine Description:
  1059. Arguments:
  1060. Return Value:
  1061. --*/
  1062. {
  1063. KIRQL OldIrql;
  1064. ACQUIRE_SPIN_LOCK(&atalkAspCLock, &OldIrql);
  1065. AtalkUnlinkDouble(pAspAddr, aspcao_Next, aspcao_Prev);
  1066. RELEASE_SPIN_LOCK(&atalkAspCLock, OldIrql);
  1067. }
  1068. LOCAL VOID
  1069. atalkAspCQueueConnGlobalList(
  1070. IN PASPC_CONNOBJ pAspConn
  1071. )
  1072. /*++
  1073. Routine Description:
  1074. Arguments:
  1075. Return Value:
  1076. --*/
  1077. {
  1078. KIRQL OldIrql;
  1079. ACQUIRE_SPIN_LOCK(&atalkAspCLock, &OldIrql);
  1080. AtalkLinkDoubleAtHead(atalkAspCConnList, pAspConn, aspcco_Next, aspcco_Prev);
  1081. RELEASE_SPIN_LOCK(&atalkAspCLock, OldIrql);
  1082. }
  1083. LOCAL VOID
  1084. atalkAspCDeQueueConnGlobalList(
  1085. IN PASPC_CONNOBJ pAspCConn
  1086. )
  1087. /*++
  1088. Routine Description:
  1089. Arguments:
  1090. Return Value:
  1091. --*/
  1092. {
  1093. KIRQL OldIrql;
  1094. ACQUIRE_SPIN_LOCK(&atalkAspCLock, &OldIrql);
  1095. AtalkUnlinkDouble(pAspCConn, aspcco_Next, aspcco_Prev);
  1096. RELEASE_SPIN_LOCK(&atalkAspCLock, OldIrql);
  1097. }