Source code of Windows XP (NT5)
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.

1748 lines
39 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. link.c
  5. Abstract:
  6. This module contains the code that is very specific to initialization
  7. and unload operations in the irenum driver
  8. Author:
  9. Brian Lieuallen, 7-13-2000
  10. Environment:
  11. Kernel mode
  12. Revision History :
  13. --*/
  14. //#include "internal.h"
  15. #define UNICODE 1
  16. #include <ntosp.h>
  17. #include <zwapi.h>
  18. #include <tdikrnl.h>
  19. #define UINT ULONG //tmp
  20. #include <irioctl.h>
  21. #include <ircommtdi.h>
  22. #include <ircomm.h>
  23. #include <ircommdbg.h>
  24. #include "buffer.h"
  25. #include <ntddser.h>
  26. #include "link.h"
  27. typedef enum {
  28. LINK_IDLE,
  29. LINK_PRE_CONNECT,
  30. LINK_ACCEPTED,
  31. LINK_ACCEPT_FAILED,
  32. LINK_CONNECTED,
  33. LINK_DISCONNECTING,
  34. LINK_CLOSING
  35. } LINK_STATE ;
  36. typedef struct {
  37. LONG ReferenceCount;
  38. LINK_STATE State;
  39. BUFFER_POOL_HANDLE SendBufferPool;
  40. BUFFER_POOL_HANDLE ControlBufferPool;
  41. BUFFER_POOL_HANDLE ReceiveBufferPool;
  42. } CONNECTION_OBJECT; *PCONNECTION_OBJECT;
  43. typedef struct _LINK_OBJECT {
  44. KSPIN_LOCK Lock;
  45. LONG ReferenceCount;
  46. KEVENT CloseEvent;
  47. BOOLEAN Closing;
  48. HANDLE AddressFileHandle;
  49. PFILE_OBJECT AddressFileObject;
  50. PFILE_OBJECT ConnectionFileObject;
  51. PVOID Context;
  52. PLINK_RECEIVE LinkReceiveHandler;
  53. PLINK_STATE LinkStateHandler;
  54. WORK_QUEUE_ITEM WorkItem;
  55. ULONG SendBuffers;
  56. ULONG ControlBuffers;
  57. ULONG ReceiveBuffers;
  58. CONNECTION_OBJECT Connection;
  59. } LINK_OBJECT, *PLINK_OBJECT;
  60. VOID
  61. RemoveReferenceFromConnection(
  62. PLINK_OBJECT LinkObject
  63. );
  64. NTSTATUS
  65. GetMaxSendPdu(
  66. PFILE_OBJECT FileObject,
  67. PULONG MaxPdu
  68. );
  69. NTSTATUS
  70. ClientEventReceive (
  71. IN PVOID TdiEventContext,
  72. IN CONNECTION_CONTEXT ConnectionContext,
  73. IN ULONG ReceiveFlags,
  74. IN ULONG BytesIndicated,
  75. IN ULONG BytesAvailable,
  76. OUT ULONG *BytesTaken,
  77. IN PVOID Tsdu,
  78. OUT PIRP *IoRequestPacket
  79. );
  80. NTSTATUS
  81. LinkEventDisconnect(
  82. IN PVOID TdiEventContext,
  83. IN CONNECTION_CONTEXT ConnectionContext,
  84. IN int DisconnectDataLength,
  85. IN PVOID DisconnectData,
  86. IN int DisconnectInformationLength,
  87. IN PVOID DisconnectInformation,
  88. IN ULONG DisconnectFlags
  89. );
  90. NTSTATUS
  91. LinkEventConnect(
  92. IN PVOID TdiEventContext,
  93. IN int RemoteAddressLength,
  94. IN PVOID RemoteAddress,
  95. IN int UserDataLength,
  96. IN PVOID UserData,
  97. IN int OptionsLength,
  98. IN PVOID Options,
  99. OUT CONNECTION_CONTEXT *ConnectionContext,
  100. OUT PIRP *AcceptIrp
  101. );
  102. NTSTATUS
  103. IrdaCompleteAcceptIrp(
  104. PDEVICE_OBJECT DeviceObject,
  105. PIRP Irp,
  106. PVOID Context
  107. );
  108. NTSTATUS
  109. IrdaSetEventHandler (
  110. IN PFILE_OBJECT FileObject,
  111. IN ULONG EventType,
  112. IN PVOID EventHandler,
  113. IN PVOID EventContext
  114. );
  115. VOID
  116. ConnectionPassiveWorkRoutine(
  117. PVOID Context
  118. );
  119. NTSTATUS
  120. IrdaCreateAddress(
  121. IN PTDI_ADDRESS_IRDA pRequestedIrdaAddr,
  122. OUT PHANDLE pAddrHandle
  123. )
  124. {
  125. NTSTATUS Status;
  126. UNICODE_STRING DeviceName;
  127. OBJECT_ATTRIBUTES ObjectAttributes;
  128. IO_STATUS_BLOCK Iosb;
  129. UCHAR EaBuf[sizeof(FILE_FULL_EA_INFORMATION)-1 +
  130. TDI_TRANSPORT_ADDRESS_LENGTH+1 +
  131. sizeof(TRANSPORT_ADDRESS) +
  132. sizeof(TDI_ADDRESS_IRDA)];
  133. PFILE_FULL_EA_INFORMATION pEa = (PFILE_FULL_EA_INFORMATION) EaBuf;
  134. ULONG EaBufLen = sizeof(EaBuf);
  135. TRANSPORT_ADDRESS UNALIGNED * pTranAddr = (PTRANSPORT_ADDRESS)
  136. &(pEa->EaName[TDI_TRANSPORT_ADDRESS_LENGTH + 1]);
  137. TDI_ADDRESS_IRDA UNALIGNED * pIrdaAddr = (PTDI_ADDRESS_IRDA)
  138. pTranAddr->Address[0].Address;
  139. pEa->NextEntryOffset = 0;
  140. pEa->Flags = 0;
  141. pEa->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
  142. RtlCopyMemory(pEa->EaName,
  143. TdiTransportAddress,
  144. pEa->EaNameLength + 1);
  145. pEa->EaValueLength = sizeof(TRANSPORT_ADDRESS) + sizeof(TDI_ADDRESS_IRDA);
  146. pTranAddr->TAAddressCount = 1;
  147. pTranAddr->Address[0].AddressLength = sizeof(TDI_ADDRESS_IRDA);
  148. pTranAddr->Address[0].AddressType = TDI_ADDRESS_TYPE_IRDA;
  149. RtlCopyMemory(pIrdaAddr,
  150. pRequestedIrdaAddr,
  151. sizeof(TDI_ADDRESS_IRDA));
  152. RtlInitUnicodeString(&DeviceName, IRDA_DEVICE_NAME);
  153. InitializeObjectAttributes(&ObjectAttributes, &DeviceName,
  154. OBJ_CASE_INSENSITIVE, NULL, NULL);
  155. Status = ZwCreateFile(
  156. pAddrHandle,
  157. GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
  158. &ObjectAttributes,
  159. &Iosb, // returned status information.
  160. 0, // block size (unused).
  161. 0, // file attributes.
  162. FILE_SHARE_READ | FILE_SHARE_WRITE,
  163. FILE_CREATE, // create disposition.
  164. 0, // create options.
  165. pEa,
  166. EaBufLen);
  167. return Status;
  168. }
  169. NTSTATUS
  170. IrdaCreateConnection(
  171. OUT PHANDLE pConnHandle,
  172. IN PVOID ClientContext)
  173. {
  174. NTSTATUS Status;
  175. UNICODE_STRING DeviceName;
  176. OBJECT_ATTRIBUTES ObjectAttributes;
  177. IO_STATUS_BLOCK Iosb;
  178. UCHAR EaBuf[sizeof(FILE_FULL_EA_INFORMATION)-1 +
  179. TDI_CONNECTION_CONTEXT_LENGTH + 1 +
  180. sizeof(CONNECTION_CONTEXT)];
  181. PFILE_FULL_EA_INFORMATION pEa = (PFILE_FULL_EA_INFORMATION) EaBuf;
  182. ULONG EaBufLen = sizeof(EaBuf);
  183. CONNECTION_CONTEXT UNALIGNED *ctx;
  184. pEa->NextEntryOffset = 0;
  185. pEa->Flags = 0;
  186. pEa->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH;
  187. pEa->EaValueLength = sizeof(CONNECTION_CONTEXT);
  188. RtlCopyMemory(pEa->EaName, TdiConnectionContext, pEa->EaNameLength + 1);
  189. ctx = (CONNECTION_CONTEXT UNALIGNED *)&pEa->EaName[pEa->EaNameLength + 1];
  190. *ctx = (CONNECTION_CONTEXT) ClientContext;
  191. RtlInitUnicodeString(&DeviceName, IRDA_DEVICE_NAME);
  192. InitializeObjectAttributes(&ObjectAttributes, &DeviceName,
  193. OBJ_CASE_INSENSITIVE, NULL, NULL);
  194. Status = ZwCreateFile(pConnHandle,
  195. GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
  196. &ObjectAttributes,
  197. &Iosb, // returned status information.
  198. 0, // block size (unused).
  199. 0, // file attributes.
  200. FILE_SHARE_READ | FILE_SHARE_WRITE,
  201. FILE_CREATE, // create disposition.
  202. 0, // create options.
  203. pEa,
  204. EaBufLen);
  205. return Status;
  206. }
  207. NTSTATUS
  208. IrdaDisconnect(
  209. PFILE_OBJECT ConnectionFileObject
  210. )
  211. {
  212. PIRP pIrp;
  213. KEVENT Event;
  214. IO_STATUS_BLOCK Iosb;
  215. NTSTATUS Status;
  216. KeInitializeEvent( &Event, SynchronizationEvent, FALSE );
  217. pIrp = TdiBuildInternalDeviceControlIrp(
  218. TDI_DISCONNECT,
  219. IoGetRelatedDeviceObject(ConnectionFileObject),
  220. ConnectionFileObject,
  221. &Event,
  222. &Iosb
  223. );
  224. if (pIrp == NULL) {
  225. return STATUS_INSUFFICIENT_RESOURCES;
  226. }
  227. TdiBuildDisconnect(
  228. pIrp,
  229. IoGetRelatedDeviceObject(ConnectionFileObject),
  230. ConnectionFileObject,
  231. NULL,
  232. NULL,
  233. NULL,
  234. TDI_DISCONNECT_ABORT,
  235. NULL,
  236. NULL
  237. );
  238. IoCallDriver(IoGetRelatedDeviceObject(ConnectionFileObject), pIrp);
  239. KeWaitForSingleObject((PVOID) &Event, Executive, KernelMode, FALSE, NULL);
  240. Status = Iosb.Status;
  241. return Status;
  242. }
  243. NTSTATUS
  244. IrdaAssociateAddress(
  245. PFILE_OBJECT ConnectionFileObject,
  246. HANDLE AddressHandle
  247. )
  248. {
  249. PIRP pIrp;
  250. KEVENT Event;
  251. IO_STATUS_BLOCK Iosb;
  252. NTSTATUS Status;
  253. KeInitializeEvent( &Event, SynchronizationEvent, FALSE );
  254. pIrp = TdiBuildInternalDeviceControlIrp(
  255. TDI_ASSOCIATE_ADDRESS,
  256. IoGetRelatedDeviceObject(ConnectionFileObject),
  257. ConnectionFileObject,
  258. &Event,
  259. &Iosb);
  260. if (pIrp == NULL)
  261. return STATUS_INSUFFICIENT_RESOURCES;
  262. TdiBuildAssociateAddress(
  263. pIrp,
  264. IoGetRelatedDeviceObject(ConnectionFileObject),
  265. ConnectionFileObject,
  266. NULL,
  267. NULL,
  268. AddressHandle);
  269. Status = IoCallDriver(IoGetRelatedDeviceObject(ConnectionFileObject), pIrp);
  270. if (Status == STATUS_PENDING) {
  271. KeWaitForSingleObject((PVOID) &Event, Executive, KernelMode, FALSE, NULL);
  272. }
  273. else
  274. {
  275. ASSERT(NT_ERROR(Status) || KeReadStateEvent(&Event));
  276. }
  277. if (NT_SUCCESS(Status))
  278. {
  279. Status = Iosb.Status;
  280. }
  281. return Status;
  282. }
  283. NTSTATUS
  284. IrdaCreateConnectionForAddress(
  285. HANDLE AddressFileHandle,
  286. PVOID Context,
  287. PFILE_OBJECT *ConnectionFileObject
  288. )
  289. {
  290. NTSTATUS Status;
  291. HANDLE ConnectionFileHandle;
  292. *ConnectionFileObject=NULL;
  293. Status = IrdaCreateConnection(&ConnectionFileHandle, Context);
  294. if (!NT_SUCCESS(Status)) {
  295. goto done;
  296. }
  297. Status = ObReferenceObjectByHandle(
  298. ConnectionFileHandle,
  299. 0L, // DesiredAccess
  300. NULL,
  301. KernelMode,
  302. ConnectionFileObject,
  303. NULL
  304. );
  305. ZwClose(ConnectionFileHandle);
  306. if (!NT_SUCCESS(Status)) {
  307. goto done;
  308. }
  309. Status = IrdaAssociateAddress(*ConnectionFileObject, AddressFileHandle);
  310. if (!NT_SUCCESS(Status)) {
  311. ObDereferenceObject(*ConnectionFileObject);
  312. *ConnectionFileObject=NULL;
  313. }
  314. done:
  315. return Status;
  316. }
  317. NTSTATUS
  318. InitiateConnection(
  319. PFILE_OBJECT ConnectionFileObject,
  320. ULONG DeviceAddress,
  321. PSTR ServiceName
  322. )
  323. {
  324. UCHAR AddrBuf[sizeof(TRANSPORT_ADDRESS) + sizeof(TDI_ADDRESS_IRDA)];
  325. PTRANSPORT_ADDRESS pTranAddr = (PTRANSPORT_ADDRESS) AddrBuf;
  326. PTDI_ADDRESS_IRDA pIrdaAddr = (PTDI_ADDRESS_IRDA) pTranAddr->Address[0].Address;
  327. TDI_CONNECTION_INFORMATION ConnInfo;
  328. PIRP Irp;
  329. NTSTATUS Status;
  330. KEVENT Event;
  331. IO_STATUS_BLOCK Iosb;
  332. KeInitializeEvent(
  333. &Event,
  334. NotificationEvent,
  335. FALSE
  336. );
  337. Irp = TdiBuildInternalDeviceControlIrp(
  338. TDI_CONNECT,
  339. IoGetRelatedDeviceObject(ConnectionFileObject),
  340. ConnectionFileObject,
  341. &Event,
  342. &Iosb
  343. );
  344. if (Irp == NULL) {
  345. D_ERROR(DbgPrint("IRCOMM: TdiBuildInternalDeviceControlIrp Failed\n");)
  346. Status=STATUS_INSUFFICIENT_RESOURCES;
  347. goto CleanUp;
  348. }
  349. RtlZeroMemory(pIrdaAddr,sizeof(*pIrdaAddr));
  350. RtlCopyMemory(pIrdaAddr->irdaDeviceID, &DeviceAddress, 4);
  351. strcpy(pIrdaAddr->irdaServiceName,ServiceName);
  352. pTranAddr->TAAddressCount = 1;
  353. ConnInfo.UserDataLength = 0;
  354. ConnInfo.UserData = NULL;
  355. ConnInfo.OptionsLength = 0;
  356. ConnInfo.Options = NULL;
  357. ConnInfo.RemoteAddressLength = sizeof(AddrBuf);
  358. ConnInfo.RemoteAddress = pTranAddr;
  359. TdiBuildConnect(
  360. Irp,
  361. IoGetRelatedDeviceObject(ConnectionFileObject),
  362. ConnectionFileObject,
  363. NULL, // CompRoutine
  364. NULL, // Context
  365. NULL, // Timeout
  366. &ConnInfo,
  367. NULL); // ReturnConnectionInfo
  368. Status = IoCallDriver(IoGetRelatedDeviceObject(ConnectionFileObject), Irp);
  369. //
  370. // If necessary, wait for the I/O to complete.
  371. //
  372. D_ERROR(DbgPrint("IRCOMM: status %08lx, %08lx\n",Status,Iosb.Status);)
  373. KeWaitForSingleObject(
  374. &Event,
  375. Executive,
  376. KernelMode,
  377. FALSE,
  378. NULL
  379. );
  380. Status = Iosb.Status;
  381. CleanUp:
  382. return Status;
  383. }
  384. VOID
  385. RemoveReferenceOnLink(
  386. PLINK_OBJECT LinkObject
  387. )
  388. {
  389. LONG Count=InterlockedDecrement(&LinkObject->ReferenceCount);
  390. if (Count == 0) {
  391. ASSERT(LinkObject->Closing);
  392. KeSetEvent(
  393. &LinkObject->CloseEvent,
  394. IO_NO_INCREMENT,
  395. FALSE
  396. );
  397. }
  398. return;
  399. }
  400. NTSTATUS
  401. CreateTdiLink(
  402. ULONG DeviceAddress,
  403. CHAR *ServiceName,
  404. BOOLEAN OutGoingConnection,
  405. LINK_HANDLE *LinkHandle,
  406. PVOID Context,
  407. PLINK_RECEIVE LinkReceiveHandler,
  408. PLINK_STATE LinkStateHandler,
  409. ULONG SendBuffers,
  410. ULONG ControlBuffers,
  411. ULONG ReceiveBuffers
  412. )
  413. {
  414. NTSTATUS Status;
  415. PLINK_OBJECT LinkObject;
  416. UCHAR AddrBuf[sizeof(TRANSPORT_ADDRESS) + sizeof(TDI_ADDRESS_IRDA)];
  417. PTRANSPORT_ADDRESS TranAddr = (PTRANSPORT_ADDRESS) AddrBuf;
  418. PTDI_ADDRESS_IRDA IrdaAddr = (PTDI_ADDRESS_IRDA) TranAddr->Address[0].Address;
  419. *LinkHandle=NULL;
  420. LinkObject=ALLOCATE_NONPAGED_POOL(sizeof(*LinkObject));
  421. if (LinkObject == NULL) {
  422. return STATUS_INSUFFICIENT_RESOURCES;
  423. }
  424. RtlZeroMemory(LinkObject,sizeof(*LinkObject));
  425. KeInitializeSpinLock(&LinkObject->Lock);
  426. ExInitializeWorkItem(
  427. &LinkObject->WorkItem,
  428. ConnectionPassiveWorkRoutine,
  429. LinkObject
  430. );
  431. LinkObject->ReferenceCount=1;
  432. KeInitializeEvent(
  433. &LinkObject->CloseEvent,
  434. NotificationEvent,
  435. FALSE
  436. );
  437. LinkObject->Connection.State=LINK_IDLE;
  438. LinkObject->LinkReceiveHandler=LinkReceiveHandler;
  439. LinkObject->LinkStateHandler=LinkStateHandler;
  440. LinkObject->Context=Context;
  441. LinkObject->SendBuffers=SendBuffers;
  442. LinkObject->ControlBuffers=ControlBuffers;
  443. LinkObject->ReceiveBuffers=ReceiveBuffers;
  444. if (OutGoingConnection) {
  445. IrdaAddr->irdaServiceName[0] = 0; // tells irda.sys addrObj is a client
  446. } else {
  447. strcpy(IrdaAddr->irdaServiceName,ServiceName);
  448. }
  449. //
  450. // open the tdi address and get a handle
  451. //
  452. Status=IrdaCreateAddress(
  453. IrdaAddr,
  454. &LinkObject->AddressFileHandle
  455. );
  456. if (!NT_SUCCESS(Status)) {
  457. goto CleanUp;
  458. }
  459. //
  460. // get the file object the handle refers to
  461. //
  462. Status = ObReferenceObjectByHandle(
  463. LinkObject->AddressFileHandle,
  464. 0L, // DesiredAccess
  465. NULL,
  466. KernelMode,
  467. (PVOID *)&LinkObject->AddressFileObject,
  468. NULL
  469. );
  470. if (Status != STATUS_SUCCESS) {
  471. D_ERROR(DbgPrint("IRCOMM: ObReferenceObjectByHandle Failed %08lx\n",Status);)
  472. goto CleanUp;
  473. }
  474. //
  475. // create a connection object and associate it with the address
  476. //
  477. Status=IrdaCreateConnectionForAddress(
  478. LinkObject->AddressFileHandle,
  479. LinkObject,
  480. &LinkObject->ConnectionFileObject
  481. );
  482. if (!NT_SUCCESS(Status)) {
  483. D_ERROR(DbgPrint("IRCOMM: IrdaCreateConnectionForAddress Failed %08lx\n",Status);)
  484. goto CleanUp;
  485. }
  486. IrdaSetEventHandler(
  487. LinkObject->AddressFileObject,
  488. TDI_EVENT_RECEIVE,
  489. ClientEventReceive,
  490. LinkObject
  491. );
  492. IrdaSetEventHandler(
  493. LinkObject->AddressFileObject,
  494. TDI_EVENT_DISCONNECT,
  495. LinkEventDisconnect,
  496. LinkObject
  497. );
  498. //
  499. // save this now, since we may get a callbacks for a connection already
  500. //
  501. *LinkHandle=LinkObject;
  502. if (!OutGoingConnection) {
  503. //
  504. // we are going to be waiting for an incoming connection
  505. //
  506. IrdaIASStringSet(
  507. LinkObject->AddressFileHandle,
  508. "PnP",
  509. "Name",
  510. "Windows 2000"
  511. );
  512. IrdaIASStringSet(
  513. LinkObject->AddressFileHandle,
  514. "PnP",
  515. "DeviceID",
  516. "IR_NULL_OUT"
  517. );
  518. IrdaSetEventHandler(
  519. LinkObject->AddressFileObject,
  520. TDI_EVENT_CONNECT,
  521. LinkEventConnect,
  522. LinkObject
  523. );
  524. Status=STATUS_SUCCESS;
  525. } else {
  526. //
  527. // we are creating an outgoing connection
  528. //
  529. Status=InitiateConnection(
  530. LinkObject->ConnectionFileObject,
  531. DeviceAddress,
  532. ServiceName
  533. );
  534. if (NT_SUCCESS(Status)) {
  535. KIRQL OldIrql;
  536. KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
  537. //
  538. // we a connection begining now
  539. //
  540. InterlockedIncrement(&LinkObject->Connection.ReferenceCount);
  541. //
  542. // the connection counts against the link
  543. //
  544. InterlockedIncrement(&LinkObject->ReferenceCount);
  545. LinkObject->Connection.State=LINK_PRE_CONNECT;
  546. KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
  547. ExQueueWorkItem(
  548. &LinkObject->WorkItem,
  549. DelayedWorkQueue
  550. );
  551. } else {
  552. //
  553. // could not create the connection
  554. //
  555. *LinkHandle=NULL;
  556. goto CleanUp;
  557. }
  558. }
  559. return Status;
  560. CleanUp:
  561. if (LinkObject->ConnectionFileObject != NULL) {
  562. ObDereferenceObject(LinkObject->ConnectionFileObject);
  563. }
  564. if (LinkObject->AddressFileObject != NULL) {
  565. ObDereferenceObject(LinkObject->AddressFileObject);
  566. }
  567. if (LinkObject->AddressFileHandle != NULL) {
  568. ZwClose(LinkObject->AddressFileHandle);
  569. }
  570. FREE_POOL(LinkObject);
  571. return Status;
  572. }
  573. VOID
  574. CloseTdiLink(
  575. LINK_HANDLE LinkHandle
  576. )
  577. {
  578. PLINK_OBJECT LinkObject=LinkHandle;
  579. KIRQL OldIrql;
  580. BOOLEAN Release=FALSE;
  581. LinkObject->Closing=TRUE;
  582. IrdaSetEventHandler(
  583. LinkObject->AddressFileObject,
  584. TDI_EVENT_RECEIVE,
  585. NULL,
  586. NULL
  587. );
  588. IrdaSetEventHandler(
  589. LinkObject->AddressFileObject,
  590. TDI_EVENT_DISCONNECT,
  591. NULL,
  592. NULL
  593. );
  594. IrdaSetEventHandler(
  595. LinkObject->AddressFileObject,
  596. TDI_EVENT_CONNECT,
  597. NULL,
  598. NULL
  599. );
  600. KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
  601. switch (LinkObject->Connection.State) {
  602. case LINK_IDLE:
  603. case LINK_DISCONNECTING:
  604. case LINK_ACCEPT_FAILED:
  605. break;
  606. case LINK_CONNECTED:
  607. //
  608. // it is in the connected state, we need to get it cleaned up
  609. //
  610. LinkObject->Connection.State=LINK_DISCONNECTING;
  611. Release=TRUE;
  612. default:
  613. break;
  614. }
  615. KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
  616. if (Release) {
  617. RemoveReferenceFromConnection(LinkObject);
  618. }
  619. RemoveReferenceOnLink(LinkObject);
  620. KeWaitForSingleObject(
  621. &LinkObject->CloseEvent,
  622. Executive,
  623. KernelMode,
  624. FALSE,
  625. NULL
  626. );
  627. //
  628. // the link should now be inactive
  629. //
  630. LinkObject->Connection.State=LINK_CLOSING;
  631. if (LinkObject->ConnectionFileObject != NULL) {
  632. ObDereferenceObject(LinkObject->ConnectionFileObject);
  633. }
  634. if (LinkObject->AddressFileObject != NULL) {
  635. ObDereferenceObject(LinkObject->AddressFileObject);
  636. }
  637. if (LinkObject->AddressFileHandle != NULL) {
  638. ZwClose(LinkObject->AddressFileHandle);
  639. }
  640. FREE_POOL(LinkObject);
  641. return;
  642. }
  643. CONNECTION_HANDLE
  644. GetCurrentConnection(
  645. LINK_HANDLE LinkHandle
  646. )
  647. {
  648. PLINK_OBJECT LinkObject=LinkHandle;
  649. CONNECTION_HANDLE ConnectionHandle=NULL;
  650. KIRQL OldIrql;
  651. KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
  652. if (LinkObject->Connection.State == LINK_CONNECTED) {
  653. InterlockedIncrement(&LinkObject->Connection.ReferenceCount);
  654. ConnectionHandle=LinkHandle;
  655. }
  656. KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
  657. return ConnectionHandle;
  658. }
  659. VOID
  660. ReleaseConnection(
  661. CONNECTION_HANDLE ConnectionHandle
  662. )
  663. {
  664. PLINK_OBJECT LinkObject=ConnectionHandle;
  665. RemoveReferenceFromConnection(LinkObject);
  666. return;
  667. }
  668. PFILE_OBJECT
  669. ConnectionGetFileObject(
  670. CONNECTION_HANDLE ConnectionHandle
  671. )
  672. {
  673. PLINK_OBJECT LinkObject=ConnectionHandle;
  674. PFILE_OBJECT FileObject;
  675. FileObject=LinkObject->ConnectionFileObject;
  676. ObReferenceObject(FileObject);
  677. return FileObject;
  678. }
  679. VOID
  680. ConnectionReleaseFileObject(
  681. CONNECTION_HANDLE ConnectionHandle,
  682. PFILE_OBJECT FileObject
  683. )
  684. {
  685. PLINK_OBJECT LinkObject=ConnectionHandle;
  686. ObDereferenceObject(FileObject);
  687. return;
  688. }
  689. PIRCOMM_BUFFER
  690. ConnectionGetBuffer(
  691. CONNECTION_HANDLE ConnectionHandle,
  692. BUFFER_TYPE BufferType
  693. )
  694. {
  695. PLINK_OBJECT LinkObject=ConnectionHandle;
  696. switch (BufferType) {
  697. case BUFFER_TYPE_SEND:
  698. return GetBuffer(LinkObject->Connection.SendBufferPool);
  699. case BUFFER_TYPE_CONTROL:
  700. return GetBuffer(LinkObject->Connection.ControlBufferPool);
  701. case BUFFER_TYPE_RECEIVE:
  702. return GetBuffer(LinkObject->Connection.ReceiveBufferPool);
  703. default:
  704. return NULL;
  705. }
  706. return NULL;
  707. }
  708. NTSTATUS
  709. IrdaRestartDeviceControl (
  710. IN PDEVICE_OBJECT DeviceObject,
  711. IN PIRP Irp,
  712. IN PVOID Context
  713. )
  714. {
  715. //
  716. // N.B. This routine can never be demand paged because it can be
  717. // called before any endpoints have been placed on the global
  718. // list--see IrdaAllocateEndpoint() and it's call to
  719. // IrdaGetTransportInfo().
  720. //
  721. //
  722. // If there was an MDL in the IRP, free it and reset the pointer to
  723. // NULL. The IO system can't handle a nonpaged pool MDL being freed
  724. // in an IRP, which is why we do it here.
  725. //
  726. if ( Irp->MdlAddress != NULL ) {
  727. IoFreeMdl( Irp->MdlAddress );
  728. Irp->MdlAddress = NULL;
  729. }
  730. return STATUS_SUCCESS;
  731. } // IrdaRestartDeviceControl
  732. NTSTATUS
  733. IrdaIssueDeviceControl (
  734. IN HANDLE FileHandle OPTIONAL,
  735. IN PFILE_OBJECT FileObject OPTIONAL,
  736. IN PVOID IrpParameters,
  737. IN ULONG IrpParametersLength,
  738. IN PVOID MdlBuffer,
  739. IN ULONG MdlBufferLength,
  740. IN UCHAR MinorFunction
  741. )
  742. /*++
  743. Routine Description:
  744. Issues a device control returst to a TDI provider and waits for the
  745. request to complete.
  746. Note that while FileHandle and FileObject are both marked as optional,
  747. in reality exactly one of these must be specified.
  748. Arguments:
  749. FileHandle - a TDI handle.
  750. FileObject - a pointer to the file object corresponding to a TDI
  751. handle
  752. IrpParameters - information to write to the parameters section of the
  753. stack location of the IRP.
  754. IrpParametersLength - length of the parameter information. Cannot be
  755. greater than 16.
  756. MdlBuffer - if non-NULL, a buffer of nonpaged pool to be mapped
  757. into an MDL and placed in the MdlAddress field of the IRP.
  758. MdlBufferLength - the size of the buffer pointed to by MdlBuffer.
  759. MinorFunction - the minor function code for the request.
  760. Return Value:
  761. NTSTATUS -- Indicates the status of the request.
  762. --*/
  763. {
  764. NTSTATUS status;
  765. PFILE_OBJECT fileObject;
  766. PIRP irp;
  767. PIO_STACK_LOCATION irpSp;
  768. KEVENT event;
  769. IO_STATUS_BLOCK ioStatusBlock;
  770. PDEVICE_OBJECT deviceObject;
  771. PMDL mdl;
  772. PAGED_CODE( );
  773. //
  774. // Initialize the kernel event that will signal I/O completion.
  775. //
  776. KeInitializeEvent( &event, SynchronizationEvent, FALSE );
  777. if( FileHandle != NULL ) {
  778. ASSERT( FileObject == NULL );
  779. //
  780. // Get the file object corresponding to the directory's handle.
  781. // Referencing the file object every time is necessary because the
  782. // IO completion routine dereferences it.
  783. //
  784. status = ObReferenceObjectByHandle(
  785. FileHandle,
  786. 0L, // DesiredAccess
  787. NULL, // ObjectType
  788. KernelMode,
  789. (PVOID *)&fileObject,
  790. NULL
  791. );
  792. if ( !NT_SUCCESS(status) ) {
  793. return status;
  794. }
  795. } else {
  796. ASSERT( FileObject != NULL );
  797. //
  798. // Reference the passed in file object. This is necessary because
  799. // the IO completion routine dereferences it.
  800. //
  801. ObReferenceObject( FileObject );
  802. fileObject = FileObject;
  803. }
  804. //
  805. // Set the file object event to a non-signaled state.
  806. //
  807. (VOID) KeResetEvent( &fileObject->Event );
  808. //
  809. // Attempt to allocate and initialize the I/O Request Packet (IRP)
  810. // for this operation.
  811. //
  812. deviceObject = IoGetRelatedDeviceObject ( fileObject );
  813. irp = IoAllocateIrp( (deviceObject)->StackSize, TRUE );
  814. if ( irp == NULL ) {
  815. ObDereferenceObject( fileObject );
  816. return STATUS_INSUFFICIENT_RESOURCES;
  817. }
  818. //
  819. // Fill in the service independent parameters in the IRP.
  820. //
  821. irp->Flags = (LONG)IRP_SYNCHRONOUS_API;
  822. irp->RequestorMode = KernelMode;
  823. irp->PendingReturned = FALSE;
  824. irp->UserIosb = &ioStatusBlock;
  825. irp->UserEvent = &event;
  826. irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
  827. irp->AssociatedIrp.SystemBuffer = NULL;
  828. irp->UserBuffer = NULL;
  829. irp->Tail.Overlay.Thread = PsGetCurrentThread();
  830. irp->Tail.Overlay.OriginalFileObject = fileObject;
  831. irp->Tail.Overlay.AuxiliaryBuffer = NULL;
  832. /*
  833. DEBUG ioStatusBlock.Status = STATUS_UNSUCCESSFUL;
  834. DEBUG ioStatusBlock.Information = (ULONG)-1;
  835. */
  836. //
  837. // If an MDL buffer was specified, get an MDL, map the buffer,
  838. // and place the MDL pointer in the IRP.
  839. //
  840. if ( MdlBuffer != NULL ) {
  841. mdl = IoAllocateMdl(
  842. MdlBuffer,
  843. MdlBufferLength,
  844. FALSE,
  845. FALSE,
  846. irp
  847. );
  848. if ( mdl == NULL ) {
  849. IoFreeIrp( irp );
  850. ObDereferenceObject( fileObject );
  851. return STATUS_INSUFFICIENT_RESOURCES;
  852. }
  853. MmBuildMdlForNonPagedPool( mdl );
  854. } else {
  855. irp->MdlAddress = NULL;
  856. }
  857. //
  858. // Put the file object pointer in the stack location.
  859. //
  860. irpSp = IoGetNextIrpStackLocation( irp );
  861. irpSp->FileObject = fileObject;
  862. irpSp->DeviceObject = deviceObject;
  863. //
  864. // Fill in the service-dependent parameters for the request.
  865. //
  866. ASSERT( IrpParametersLength <= sizeof(irpSp->Parameters) );
  867. RtlCopyMemory( &irpSp->Parameters, IrpParameters, IrpParametersLength );
  868. irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  869. irpSp->MinorFunction = MinorFunction;
  870. //
  871. // Set up a completion routine which we'll use to free the MDL
  872. // allocated previously.
  873. //
  874. IoSetCompletionRoutine( irp, IrdaRestartDeviceControl, NULL, TRUE, TRUE, TRUE );
  875. //
  876. // Queue the IRP to the thread and pass it to the driver.
  877. //
  878. IoEnqueueIrp( irp );
  879. status = IoCallDriver( deviceObject, irp );
  880. //
  881. // If necessary, wait for the I/O to complete.
  882. //
  883. if ( status == STATUS_PENDING ) {
  884. KeWaitForSingleObject( (PVOID)&event, UserRequest, KernelMode, FALSE, NULL );
  885. }
  886. //
  887. // If the request was successfully queued, get the final I/O status.
  888. //
  889. if ( NT_SUCCESS(status) ) {
  890. status = ioStatusBlock.Status;
  891. }
  892. return status;
  893. } // IrdaIssueDeviceControl
  894. NTSTATUS
  895. IrdaSetEventHandler (
  896. IN PFILE_OBJECT FileObject,
  897. IN ULONG EventType,
  898. IN PVOID EventHandler,
  899. IN PVOID EventContext
  900. )
  901. /*++
  902. Routine Description:
  903. Sets up a TDI indication handler on a connection or address object
  904. (depending on the file handle). This is done synchronously, which
  905. shouldn't usually be an issue since TDI providers can usually complete
  906. indication handler setups immediately.
  907. Arguments:
  908. FileObject - a pointer to the file object for an open connection or
  909. address object.
  910. EventType - the event for which the indication handler should be
  911. called.
  912. EventHandler - the routine to call when tghe specified event occurs.
  913. EventContext - context which is passed to the indication routine.
  914. Return Value:
  915. NTSTATUS -- Indicates the status of the request.
  916. --*/
  917. {
  918. TDI_REQUEST_KERNEL_SET_EVENT parameters;
  919. PAGED_CODE( );
  920. parameters.EventType = EventType;
  921. parameters.EventHandler = EventHandler;
  922. parameters.EventContext = EventContext;
  923. return IrdaIssueDeviceControl(
  924. NULL,
  925. FileObject,
  926. &parameters,
  927. sizeof(parameters),
  928. NULL,
  929. 0,
  930. TDI_SET_EVENT_HANDLER
  931. );
  932. } // IrdaSetEventHandler
  933. NTSTATUS
  934. ClientEventReceive (
  935. IN PVOID TdiEventContext,
  936. IN CONNECTION_CONTEXT ConnectionContext,
  937. IN ULONG ReceiveFlags,
  938. IN ULONG BytesIndicated,
  939. IN ULONG BytesAvailable,
  940. OUT ULONG *BytesTaken,
  941. IN PVOID Tsdu,
  942. OUT PIRP *IoRequestPacket
  943. )
  944. {
  945. PLINK_OBJECT LinkObject=(PLINK_OBJECT)ConnectionContext;
  946. NTSTATUS Status;
  947. if (!LinkObject->Closing) {
  948. InterlockedIncrement(&LinkObject->ReferenceCount);
  949. Status= (LinkObject->LinkReceiveHandler)(
  950. LinkObject->Context,
  951. ReceiveFlags,
  952. BytesIndicated,
  953. BytesAvailable,
  954. BytesTaken,
  955. Tsdu,
  956. IoRequestPacket
  957. );
  958. RemoveReferenceOnLink(LinkObject);
  959. } else {
  960. Status=STATUS_SUCCESS;
  961. *BytesTaken=BytesAvailable;
  962. }
  963. return Status;
  964. }
  965. NTSTATUS
  966. LinkEventDisconnect(
  967. IN PVOID TdiEventContext,
  968. IN CONNECTION_CONTEXT ConnectionContext,
  969. IN int DisconnectDataLength,
  970. IN PVOID DisconnectData,
  971. IN int DisconnectInformationLength,
  972. IN PVOID DisconnectInformation,
  973. IN ULONG DisconnectFlags
  974. )
  975. {
  976. PLINK_OBJECT LinkObject=(PLINK_OBJECT)ConnectionContext;
  977. KIRQL OldIrql;
  978. BOOLEAN Release=FALSE;
  979. if (!LinkObject->Closing) {
  980. KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
  981. if (LinkObject->Connection.State == LINK_CONNECTED) {
  982. LinkObject->Connection.State=LINK_DISCONNECTING;
  983. Release=TRUE;
  984. }
  985. KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
  986. if (Release) {
  987. RemoveReferenceFromConnection(LinkObject);
  988. }
  989. }
  990. return STATUS_SUCCESS;
  991. }
  992. NTSTATUS
  993. LinkEventConnect(
  994. IN PVOID TdiEventContext,
  995. IN int RemoteAddressLength,
  996. IN PVOID RemoteAddress,
  997. IN int UserDataLength,
  998. IN PVOID UserData,
  999. IN int OptionsLength,
  1000. IN PVOID Options,
  1001. OUT CONNECTION_CONTEXT *ConnectionContext,
  1002. OUT PIRP *AcceptIrp
  1003. )
  1004. {
  1005. PLINK_OBJECT LinkObject=(PLINK_OBJECT)TdiEventContext;
  1006. PIRP Irp;
  1007. PDEVICE_OBJECT DeviceObject=IoGetRelatedDeviceObject ( LinkObject->ConnectionFileObject);
  1008. KIRQL OldIrql;
  1009. Irp = IoAllocateIrp((CCHAR)(DeviceObject->StackSize), FALSE);
  1010. if ( Irp == NULL ) {
  1011. return STATUS_INSUFFICIENT_RESOURCES;
  1012. }
  1013. KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
  1014. if ((LinkObject->Connection.State != LINK_IDLE) || LinkObject->Closing) {
  1015. KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
  1016. IoFreeIrp(Irp);
  1017. return STATUS_CONNECTION_REFUSED;
  1018. }
  1019. LinkObject->Connection.State=LINK_ACCEPTED;
  1020. //
  1021. // we now have a connection starting, in the refcount
  1022. //
  1023. InterlockedIncrement(&LinkObject->Connection.ReferenceCount);
  1024. //
  1025. // the connection counts agains the link
  1026. //
  1027. InterlockedIncrement(&LinkObject->ReferenceCount);
  1028. KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
  1029. TdiBuildAccept(
  1030. Irp,
  1031. DeviceObject,
  1032. LinkObject->ConnectionFileObject,
  1033. IrdaCompleteAcceptIrp,
  1034. LinkObject,
  1035. NULL, // request connection information
  1036. NULL // return connection information
  1037. );
  1038. IoSetNextIrpStackLocation(Irp);
  1039. //
  1040. // Set the return IRP so the transport processes this accept IRP.
  1041. //
  1042. *AcceptIrp = Irp;
  1043. //
  1044. // Set up the connection context as a pointer to the connection block
  1045. // we're going to use for this connect request. This allows the
  1046. // TDI provider to which connection object to use.
  1047. //
  1048. *ConnectionContext = (CONNECTION_CONTEXT) LinkObject;
  1049. return STATUS_MORE_PROCESSING_REQUIRED;
  1050. }
  1051. NTSTATUS
  1052. IrdaCompleteAcceptIrp(
  1053. PDEVICE_OBJECT DeviceObject,
  1054. PIRP Irp,
  1055. PVOID Context
  1056. )
  1057. {
  1058. PLINK_OBJECT LinkObject=(PLINK_OBJECT)Context;
  1059. KIRQL OldIrql;
  1060. KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
  1061. if (NT_SUCCESS(Irp->IoStatus.Status)) {
  1062. LinkObject->Connection.State=LINK_PRE_CONNECT;
  1063. ExQueueWorkItem(
  1064. &LinkObject->WorkItem,
  1065. DelayedWorkQueue
  1066. );
  1067. } else {
  1068. LinkObject->Connection.State=LINK_ACCEPT_FAILED;
  1069. }
  1070. KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
  1071. if (!NT_SUCCESS(Irp->IoStatus.Status)) {
  1072. //
  1073. // no connection anymore
  1074. //
  1075. RemoveReferenceFromConnection(LinkObject);
  1076. }
  1077. IoFreeIrp(Irp);
  1078. return STATUS_MORE_PROCESSING_REQUIRED;
  1079. }
  1080. NTSTATUS
  1081. GetMaxSendPdu(
  1082. PFILE_OBJECT FileObject,
  1083. PULONG MaxPdu
  1084. )
  1085. {
  1086. PIRP Irp;
  1087. IO_STATUS_BLOCK IoStatus;
  1088. KEVENT Event;
  1089. *MaxPdu=50;
  1090. KeInitializeEvent(
  1091. &Event,
  1092. NotificationEvent,
  1093. FALSE
  1094. );
  1095. Irp=IoBuildDeviceIoControlRequest(
  1096. IOCTL_IRDA_GET_SEND_PDU_LEN,
  1097. IoGetRelatedDeviceObject(FileObject),
  1098. NULL,
  1099. 0,
  1100. MaxPdu,
  1101. sizeof(*MaxPdu),
  1102. FALSE,
  1103. &Event,
  1104. &IoStatus
  1105. );
  1106. if (Irp != NULL) {
  1107. PIO_STACK_LOCATION IrpSp=IoGetNextIrpStackLocation(Irp);
  1108. IrpSp->FileObject=FileObject;
  1109. IoCallDriver(
  1110. IoGetRelatedDeviceObject(FileObject),
  1111. Irp
  1112. );
  1113. KeWaitForSingleObject(
  1114. &Event,
  1115. Executive,
  1116. KernelMode,
  1117. FALSE,
  1118. NULL
  1119. );
  1120. DbgPrint("IRCOMM: maxsendpdu=%d\n",*MaxPdu);
  1121. return IoStatus.Status;
  1122. }
  1123. return STATUS_INSUFFICIENT_RESOURCES;
  1124. }
  1125. VOID
  1126. ConnectionPassiveWorkRoutine(
  1127. PVOID Context
  1128. )
  1129. {
  1130. PLINK_OBJECT LinkObject=Context;
  1131. KIRQL OldIrql;
  1132. ULONG MaxSendPdu=50;
  1133. BOOLEAN Connected;
  1134. KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
  1135. switch (LinkObject->Connection.State) {
  1136. case LINK_PRE_CONNECT:
  1137. KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
  1138. GetMaxSendPdu(LinkObject->ConnectionFileObject,&MaxSendPdu);
  1139. LinkObject->Connection.SendBufferPool=CreateBufferPool(
  1140. IoGetRelatedDeviceObject(LinkObject->ConnectionFileObject)->StackSize,
  1141. MaxSendPdu,
  1142. LinkObject->SendBuffers
  1143. );
  1144. LinkObject->Connection.ControlBufferPool=CreateBufferPool(
  1145. IoGetRelatedDeviceObject(LinkObject->ConnectionFileObject)->StackSize,
  1146. MaxSendPdu,
  1147. LinkObject->ControlBuffers
  1148. );
  1149. LinkObject->Connection.ReceiveBufferPool=CreateBufferPool(
  1150. IoGetRelatedDeviceObject(LinkObject->ConnectionFileObject)->StackSize,
  1151. 1,
  1152. LinkObject->ReceiveBuffers
  1153. );
  1154. LinkObject->Connection.State=LINK_CONNECTED;
  1155. Connected=TRUE;
  1156. break;
  1157. case LINK_DISCONNECTING:
  1158. Connected=FALSE;
  1159. KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
  1160. IrdaDisconnect(LinkObject->ConnectionFileObject);
  1161. if (LinkObject->Connection.SendBufferPool != NULL) {
  1162. FreeBufferPool(LinkObject->Connection.SendBufferPool);
  1163. LinkObject->Connection.SendBufferPool=NULL;
  1164. }
  1165. if (LinkObject->Connection.ControlBufferPool != NULL) {
  1166. FreeBufferPool(LinkObject->Connection.ControlBufferPool);
  1167. LinkObject->Connection.ControlBufferPool=NULL;
  1168. }
  1169. if (LinkObject->Connection.ReceiveBufferPool != NULL) {
  1170. FreeBufferPool(LinkObject->Connection.ReceiveBufferPool);
  1171. LinkObject->Connection.ReceiveBufferPool=NULL;
  1172. }
  1173. LinkObject->Connection.State=LINK_IDLE;
  1174. break;
  1175. case LINK_ACCEPT_FAILED:
  1176. Connected=FALSE;
  1177. LinkObject->Connection.State=LINK_IDLE;
  1178. KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
  1179. break;
  1180. default:
  1181. ASSERT(0);
  1182. Connected=FALSE;
  1183. KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
  1184. break;
  1185. }
  1186. if (!LinkObject->Closing) {
  1187. //
  1188. // tell the client about the state change
  1189. //
  1190. InterlockedIncrement(&LinkObject->ReferenceCount);
  1191. (LinkObject->LinkStateHandler)(
  1192. LinkObject->Context,
  1193. Connected,
  1194. MaxSendPdu
  1195. );
  1196. RemoveReferenceOnLink(LinkObject);
  1197. }
  1198. if (!Connected) {
  1199. //
  1200. // we have completed the disconnection, remove the reference the connection
  1201. // has to the link
  1202. //
  1203. RemoveReferenceOnLink(LinkObject);
  1204. }
  1205. return;
  1206. }
  1207. VOID
  1208. RemoveReferenceFromConnection(
  1209. PLINK_OBJECT LinkObject
  1210. )
  1211. {
  1212. KIRQL OldIrql;
  1213. LONG Count;
  1214. KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
  1215. Count=InterlockedDecrement(&LinkObject->Connection.ReferenceCount);
  1216. if (Count == 0) {
  1217. ExQueueWorkItem(
  1218. &LinkObject->WorkItem,
  1219. DelayedWorkQueue
  1220. );
  1221. }
  1222. KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
  1223. return;
  1224. }