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.

742 lines
23 KiB

  1. /*++
  2. Copyright (c) 1989-1993 Microsoft Corporation
  3. Module Name:
  4. Tdiout.c
  5. Abstract:
  6. This file represents the TDI interface on the bottom edge of NBT.
  7. The procedures herein conform to the TDI I/F spec. and then convert
  8. the information to NT specific Irps etc. This implementation can be
  9. changed out to run on another OS.
  10. Author:
  11. Jim Stewart (Jimst) 10-2-92
  12. Revision History:
  13. --*/
  14. #include "precomp.h" // procedure headings
  15. #include "tdiout.tmh"
  16. // function prototypes for completion routines used in this file
  17. NTSTATUS
  18. TdiSendDatagramCompletion(
  19. IN PDEVICE_OBJECT DeviceObject,
  20. IN PIRP pIrp,
  21. IN PVOID pSendbufferMdl
  22. );
  23. NTSTATUS
  24. TcpConnectComplete(
  25. IN PDEVICE_OBJECT DeviceObject,
  26. IN PIRP pIrp,
  27. IN PVOID pContext
  28. );
  29. NTSTATUS
  30. TcpDisconnectComplete(
  31. IN PDEVICE_OBJECT DeviceObject,
  32. IN PIRP pIrp,
  33. IN PVOID pContext
  34. );
  35. NTSTATUS
  36. SendSessionCompletionRoutine(
  37. IN PDEVICE_OBJECT DeviceObject,
  38. IN PIRP pIrp,
  39. IN PVOID pContext
  40. );
  41. // DEBUG
  42. VOID
  43. CheckIrpList(
  44. );
  45. //----------------------------------------------------------------------------
  46. NTSTATUS
  47. TdiSendDatagram(
  48. IN PTDI_REQUEST pRequestInfo,
  49. IN PTDI_CONNECTION_INFORMATION pSendDgramInfo,
  50. IN ULONG SendLength,
  51. OUT PULONG pSentSize,
  52. IN tDGRAM_SEND_TRACKING *pDgramTracker
  53. )
  54. /*++
  55. Routine Description:
  56. This routine sends a datagram to the transport
  57. Arguments:
  58. pSendBuffer - this is really an Mdl in NT land. It must be tacked on
  59. the end of the Mdl created for the Nbt datagram header.
  60. Return Value:
  61. The function value is the status of the operation.
  62. --*/
  63. {
  64. NTSTATUS status;
  65. PIRP pRequestIrp;
  66. PMDL pMdl;
  67. PDEVICE_OBJECT pDeviceObject;
  68. PFILE_OBJECT pFileObject;
  69. PVOID pCompletionRoutine;
  70. tBUFFER *pSendBuffer = &pDgramTracker->SendBuffer;
  71. // get an Irp to send the message in
  72. pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle;
  73. pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
  74. status = GetIrp(&pRequestIrp); // get an Irp from the list
  75. if (NT_SUCCESS(status))
  76. {
  77. pRequestIrp->CancelRoutine = NULL;
  78. // set up the completion routine passed in from Udp Send using the APC
  79. // fields in the Irp that would normally be used to complete the request
  80. // back to the client - although we are really the client here so we can
  81. // use these fields our self!
  82. pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine =
  83. (PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject;
  84. pRequestIrp->Overlay.AsynchronousParameters.UserApcContext = (PVOID)pRequestInfo->RequestContext;
  85. // Allocate a MDL and set the head sizes correctly
  86. if (!(pMdl = IoAllocateMdl (pSendBuffer->pDgramHdr, pSendBuffer->HdrLength, FALSE, FALSE, NULL)))
  87. {
  88. NbtFreeIrp(pRequestIrp);
  89. status = STATUS_INSUFFICIENT_RESOURCES;
  90. }
  91. }
  92. else
  93. {
  94. IF_DBG(NBT_DEBUG_TDIOUT)
  95. KdPrint(("Nbt.TdiSendDatagram: Failed to get an Irp"));
  96. }
  97. // tack the client's send buffer (MDL) onto the end of the datagram header
  98. // Mdl, and then pass the irp on downward to the transport
  99. if (NT_SUCCESS(status) && pSendBuffer->pBuffer) {
  100. pMdl->Next = IoAllocateMdl (pSendBuffer->pBuffer, pSendBuffer->Length, FALSE, FALSE, NULL);
  101. if (pMdl->Next == NULL) {
  102. NbtFreeIrp(pRequestIrp);
  103. status = STATUS_INSUFFICIENT_RESOURCES;
  104. IoFreeMdl(pMdl);
  105. pMdl = NULL;
  106. }
  107. }
  108. if (!NT_SUCCESS(status))
  109. {
  110. if (pRequestInfo->RequestNotifyObject) // call the completion routine (if there is one)
  111. {
  112. NBT_DEREFERENCE_DEVICE (pDgramTracker->pDeviceContext, REF_DEV_UDP_SEND, FALSE);
  113. (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject))
  114. ((PVOID)pRequestInfo->RequestContext,
  115. STATUS_INSUFFICIENT_RESOURCES,
  116. 0L);
  117. }
  118. return(STATUS_PENDING); // so the Irp is not completed twice.
  119. }
  120. // Map the pages in memory...
  121. ASSERT(!pSendBuffer->pBuffer || pMdl->Next);
  122. MmBuildMdlForNonPagedPool(pMdl);
  123. if (pMdl->Next) {
  124. MmBuildMdlForNonPagedPool(pMdl->Next);
  125. }
  126. pCompletionRoutine = TdiSendDatagramCompletion;
  127. // store some context stuff in the Irp stack so we can call the completion
  128. // routine set by the Udpsend code...
  129. TdiBuildSendDatagram (pRequestIrp,
  130. pDeviceObject,
  131. pFileObject,
  132. pCompletionRoutine,
  133. (PVOID)pMdl->Next, // The completion routine will know that we have allocated an extra MDL
  134. pMdl,
  135. SendLength,
  136. pSendDgramInfo);
  137. CHECK_COMPLETION(pRequestIrp);
  138. status = IoCallDriver(pDeviceObject,pRequestIrp);
  139. *pSentSize = SendLength; // Fill in the SentSize
  140. // The transport always completes the IRP, so as long as the irp made it
  141. // to the transport it got completed. The return code from the transport
  142. // does not indicate if the irp was completed or not. The real status
  143. // of the operation is in the Irp Iostatus return code.
  144. // What we need to do is make sure NBT does not complete the irp AND the
  145. // transport complete the Irp. Therefore this routine returns
  146. // status pending if the Irp was passed to the transport, regardless of
  147. // the return code from the transport. This return code signals the caller
  148. // that the irp will be completed via the completion routine and the
  149. // actual status of the send can be found in the Irpss IoStatus.Status
  150. // variable.
  151. //
  152. // If the Caller of this routine gets a bad return code, they can assume
  153. // that this routine failed to give the Irp to the transport and it
  154. // is safe for them to complete the Irp themselves.
  155. //
  156. // If the Completion routine is set to null, then there is no danger
  157. // of the irp completing twice and this routine will return the transport
  158. // return code in that case.
  159. if (pRequestInfo->RequestNotifyObject)
  160. {
  161. return(STATUS_PENDING);
  162. }
  163. else
  164. {
  165. return(status);
  166. }
  167. }
  168. //----------------------------------------------------------------------------
  169. NTSTATUS
  170. TdiSendDatagramCompletion(
  171. IN PDEVICE_OBJECT DeviceObject,
  172. IN PIRP pIrp,
  173. IN PVOID pSendbufferMdl
  174. )
  175. /*++
  176. Routine Description:
  177. This routine handles the completion of a datagram send to the transport.
  178. It must call the client completion routine and free the Irp and Mdl.
  179. Arguments:
  180. Return Value:
  181. NTSTATUS - success or not
  182. --*/
  183. {
  184. KIRQL OldIrq;
  185. tDGRAM_SEND_TRACKING *pTracker = pIrp->Overlay.AsynchronousParameters.UserApcContext;
  186. tDEVICECONTEXT *pDeviceContext;
  187. // check for a completion routine of the clients to call...
  188. if (pIrp->Overlay.AsynchronousParameters.UserApcRoutine)
  189. {
  190. //
  191. // The Tracker can be free'ed in the routine below, so save the Device ptr
  192. //
  193. pDeviceContext = pTracker->pDeviceContext;
  194. (*((NBT_COMPLETION)pIrp->Overlay.AsynchronousParameters.UserApcRoutine))
  195. ((PVOID)pIrp->Overlay.AsynchronousParameters.UserApcContext,
  196. pIrp->IoStatus.Status,
  197. (ULONG)pIrp->IoStatus.Information); // sent length
  198. NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_UDP_SEND, FALSE);
  199. }
  200. // Don't depend on pIrp->MdlAddress->Next which may occassionally changed by others
  201. ASSERT((PMDL)pSendbufferMdl == pIrp->MdlAddress->Next);
  202. if (pSendbufferMdl) {
  203. IoFreeMdl((PMDL)pSendbufferMdl);
  204. }
  205. // deallocate the MDL.. this is done by the IO subsystem in IoCompleteRequest
  206. pIrp->MdlAddress->Next = NULL;
  207. IoFreeMdl(pIrp->MdlAddress);
  208. NbtFreeIrp(pIrp);
  209. // return this status to stop the IO subsystem from further processing the
  210. // IRP - i.e. trying to complete it back to the initiating thread! -since
  211. // there is no initiating thread - we are the initiator
  212. return(STATUS_MORE_PROCESSING_REQUIRED);
  213. }
  214. //----------------------------------------------------------------------------
  215. PIRP
  216. NTAllocateNbtIrp(
  217. IN PDEVICE_OBJECT DeviceObject
  218. )
  219. /*++
  220. Routine Description:
  221. This routine allocates an irp by calling the IO system, and then it
  222. undoes the queuing of the irp to the current thread, since these are
  223. NBTs own irps, and should not be attached to a thread.
  224. Arguments:
  225. Return Value:
  226. NTSTATUS - success or not
  227. --*/
  228. {
  229. PIRP pIrp;
  230. // call the IO subsystem to allocate the irp
  231. pIrp = IoAllocateIrp(DeviceObject->StackSize,FALSE);
  232. if (!pIrp)
  233. {
  234. return(NULL);
  235. }
  236. //
  237. // Simply return a pointer to the packet.
  238. //
  239. return pIrp;
  240. }
  241. //----------------------------------------------------------------------------
  242. NTSTATUS
  243. TdiConnect(
  244. IN PTDI_REQUEST pRequestInfo,
  245. IN ULONG_PTR lTimeout,
  246. IN PTDI_CONNECTION_INFORMATION pSendInfo,
  247. IN PIRP pClientIrp
  248. )
  249. /*++
  250. Routine Description:
  251. This routine sends a connect request to the tranport provider, to setup
  252. a connection to the other side...
  253. Arguments:
  254. Return Value:
  255. The function value is the status of the operation.
  256. --*/
  257. {
  258. NTSTATUS status;
  259. PIRP pRequestIrp;
  260. PDEVICE_OBJECT pDeviceObject;
  261. PFILE_OBJECT pFileObject;
  262. // get an Irp to send the message in
  263. pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle;
  264. pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
  265. // get an Irp from the list
  266. status = GetIrp(&pRequestIrp);
  267. if (!NT_SUCCESS(status))
  268. {
  269. IF_DBG(NBT_DEBUG_TDIOUT)
  270. KdPrint(("Nbt.TdiConnect: Failed to get an Irp"));
  271. // call the completion routine with this status
  272. (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject))
  273. ((PVOID)pRequestInfo->RequestContext,
  274. STATUS_INSUFFICIENT_RESOURCES,
  275. 0L);
  276. return(STATUS_PENDING);
  277. }
  278. pRequestIrp->CancelRoutine = NULL;
  279. // set up the completion routine passed in from Tcp SessionStart using the APC
  280. // fields in the Irp that would normally be used to complete the request
  281. // back to the client - although we are really the client here so we can
  282. // use these fields ourselves
  283. pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine =
  284. (PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject;
  285. pRequestIrp->Overlay.AsynchronousParameters.UserApcContext =
  286. (PVOID)pRequestInfo->RequestContext;
  287. // store some context stuff in the Irp stack so we can call the completion
  288. // routine set by the Udpsend code...
  289. TdiBuildConnect(
  290. pClientIrp,
  291. pDeviceObject,
  292. pFileObject,
  293. TcpConnectComplete,
  294. (PVOID)pRequestIrp, //context value passed to completion routine
  295. lTimeout, // use timeout on connect
  296. pSendInfo,
  297. NULL);
  298. NbtTrace(NBT_TRACE_LOWER_EDGE, ("TCP TDI_CONNECT pIrp %p", pClientIrp));
  299. pRequestIrp->MdlAddress = NULL;
  300. CHECK_COMPLETION(pClientIrp);
  301. status = IoCallDriver(pDeviceObject,pClientIrp);
  302. // the transport always completes the IRP, so we always return status pending
  303. return(STATUS_PENDING);
  304. }
  305. //----------------------------------------------------------------------------
  306. NTSTATUS
  307. TdiDisconnect(
  308. IN PTDI_REQUEST pRequestInfo,
  309. IN PVOID lTimeout,
  310. IN ULONG Flags,
  311. IN PTDI_CONNECTION_INFORMATION pSendInfo,
  312. IN PCTE_IRP pClientIrp,
  313. IN BOOLEAN Wait
  314. )
  315. /*++
  316. Routine Description:
  317. This routine sends a connect request to the tranport provider, to setup
  318. a connection to the other side...
  319. Arguments:
  320. pClientIrp - this is the irp that the client used when it issued an
  321. NbtDisconnect. We pass this irp to the transport so that
  322. the client can do a Ctrl C and cancel the irp if the
  323. disconnect takes too long.
  324. Return Value:
  325. The function value is the status of the operation.
  326. --*/
  327. {
  328. NTSTATUS status;
  329. PIRP pRequestIrp;
  330. PDEVICE_OBJECT pDeviceObject;
  331. PFILE_OBJECT pFileObject;
  332. // get an Irp to send the message in
  333. pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle;
  334. pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
  335. status = GetIrp(&pRequestIrp);
  336. if (!NT_SUCCESS(status))
  337. {
  338. IF_DBG(NBT_DEBUG_TDIOUT)
  339. KdPrint(("Nbt.TdiDisconnect: Failed to get an Irp"));
  340. //
  341. // When Wait is set, the caller doesn't expect the completion routine
  342. // to be called!!!
  343. //
  344. if (!Wait) {
  345. // call the completion routine with this status
  346. (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject))
  347. ((PVOID)pRequestInfo->RequestContext,
  348. STATUS_INSUFFICIENT_RESOURCES,
  349. 0L);
  350. return STATUS_PENDING;
  351. } else {
  352. return STATUS_INSUFFICIENT_RESOURCES;
  353. }
  354. }
  355. if (!pClientIrp)
  356. {
  357. // if no client irp was passed in, then just use our Irp
  358. pClientIrp = pRequestIrp;
  359. }
  360. pRequestIrp->CancelRoutine = NULL;
  361. // set up the completion routine passed in from Tcp SessionStart using the APC
  362. // fields in the Irp that would normally be used to complete the request
  363. // back to the client - although we are really the client here so we can
  364. // use these fields ourselves
  365. pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine =
  366. (PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject;
  367. pRequestIrp->Overlay.AsynchronousParameters.UserApcContext =
  368. (PVOID)pRequestInfo->RequestContext;
  369. // store some context stuff in the Irp stack so we can call the completion
  370. // routine set by the Udpsend code...
  371. // Note that pRequestIrp is passed to the completion routine as a context
  372. // value so we will know the routine to call for the client's completion.
  373. TdiBuildDisconnect(
  374. pClientIrp,
  375. pDeviceObject,
  376. pFileObject,
  377. TcpConnectComplete,
  378. (PVOID)pRequestIrp, //context value passed to completion routine
  379. lTimeout,
  380. Flags,
  381. NULL, // send connection info
  382. NULL); // return connection info
  383. NbtTrace(NBT_TRACE_LOWER_EDGE, ("TCP TDI_DISCONNECT pIrp %p", pClientIrp));
  384. pRequestIrp->MdlAddress = NULL;
  385. // if Wait is set, then this means do a synchronous disconnect and block
  386. // until the irp is returned.
  387. //
  388. if (Wait)
  389. {
  390. status = SubmitTdiRequest(pFileObject,pClientIrp);
  391. if (!NT_SUCCESS(status))
  392. {
  393. IF_DBG(NBT_DEBUG_TDIOUT)
  394. KdPrint (("Nbt.TdiDisconnect: ERROR -- SubmitTdiRequest returned <%x>\n", status));
  395. }
  396. NbtFreeIrp(pRequestIrp);
  397. return(status);
  398. }
  399. else
  400. {
  401. CHECK_COMPLETION(pClientIrp);
  402. status = IoCallDriver(pDeviceObject,pClientIrp);
  403. // the transport always completes the IRP, so we always return status pending
  404. return(STATUS_PENDING);
  405. }
  406. }
  407. //----------------------------------------------------------------------------
  408. NTSTATUS
  409. TcpConnectComplete(
  410. IN PDEVICE_OBJECT DeviceObject,
  411. IN PIRP pIrp,
  412. IN PVOID pContext
  413. )
  414. /*++
  415. Routine Description:
  416. This routine handles the completion of a TCP session setup. The TCP
  417. connection is either setup or not depending on the status returned here.
  418. It must called the clients completion routine (in udpsend.c). Which should
  419. look after sending the NetBios sesion startup pdu across the TCP connection.
  420. The pContext value is actually one of NBTs irps that is JUST used to store
  421. the calling routines completion routine. The real Irp used is the original
  422. client's irp. This is done so that IoCancelIrp will cancel the connect
  423. properly.
  424. Arguments:
  425. Return Value:
  426. NTSTATUS - success or not
  427. --*/
  428. {
  429. KIRQL OldIrq;
  430. PIRP pMyIrp;
  431. NbtTrace(NBT_TRACE_LOWER_EDGE, ("TCP TDI_CONNECT/TDI_DISCONNECT pIrp %p: %!status!",
  432. pIrp, pIrp->IoStatus.Status));
  433. pMyIrp = (PIRP)pContext;
  434. // check for a completion routine of the clients to call...
  435. if (pMyIrp->Overlay.AsynchronousParameters.UserApcRoutine)
  436. {
  437. (*((NBT_COMPLETION)pMyIrp->Overlay.AsynchronousParameters.UserApcRoutine))
  438. ((PVOID)pMyIrp->Overlay.AsynchronousParameters.UserApcContext,
  439. pIrp->IoStatus.Status,
  440. 0L);
  441. }
  442. NbtFreeIrp(pMyIrp);
  443. // return this status to stop the IO subsystem from further processing the
  444. // IRP - i.e. trying to complete it back to the initiating thread! -since
  445. // there is not initiating thread - we are the initiator
  446. return(STATUS_MORE_PROCESSING_REQUIRED);
  447. }
  448. //----------------------------------------------------------------------------
  449. NTSTATUS
  450. TdiSend(
  451. IN PTDI_REQUEST pRequestInfo,
  452. IN USHORT sFlags,
  453. IN ULONG SendLength,
  454. OUT PULONG pSentSize,
  455. IN tBUFFER *pSendBuffer,
  456. IN ULONG Flags
  457. )
  458. /*++
  459. Routine Description:
  460. This routine sends a packet to the transport on a TCP connection
  461. Arguments:
  462. pSendBuffer - this is really an Mdl in NT land. It must be tacked on
  463. the end of the Mdl created for the Nbt datagram header.
  464. Return Value:
  465. The function value is the status of the operation.
  466. --*/
  467. {
  468. NTSTATUS status;
  469. PIRP pRequestIrp;
  470. PMDL pMdl;
  471. PDEVICE_OBJECT pDeviceObject;
  472. PFILE_OBJECT pFileObject;
  473. // get an Irp to send the message in
  474. pFileObject = (PFILE_OBJECT)pRequestInfo->Handle.AddressHandle;
  475. pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
  476. // get an Irp from the list
  477. status = GetIrp(&pRequestIrp);
  478. if (!NT_SUCCESS(status))
  479. {
  480. IF_DBG(NBT_DEBUG_TDIOUT)
  481. KdPrint(("Nbt.TdiSend: Failed to get an Irp"));
  482. // call the completion routine with this status
  483. if (pRequestInfo->RequestNotifyObject)
  484. {
  485. (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject))
  486. ((PVOID)pRequestInfo->RequestContext,
  487. STATUS_INSUFFICIENT_RESOURCES,
  488. 0L);
  489. }
  490. return(STATUS_INSUFFICIENT_RESOURCES);
  491. }
  492. pRequestIrp->CancelRoutine = NULL;
  493. // set up the completion routine passed in from Udp Send using the APC
  494. // fields in the Irp that would normally be used to complete the request
  495. // back to the client - although we are really the client here so we can
  496. // use these fields our self!
  497. pRequestIrp->Overlay.AsynchronousParameters.UserApcRoutine =
  498. (PIO_APC_ROUTINE)pRequestInfo->RequestNotifyObject;
  499. pRequestIrp->Overlay.AsynchronousParameters.UserApcContext =
  500. (PVOID)pRequestInfo->RequestContext;
  501. // get the MDL that is currently linked to the IRP (i.e. created at the
  502. // same time that we created the IRP list. Set the sizes correctly in
  503. // the MDL header.
  504. pMdl = IoAllocateMdl(
  505. pSendBuffer->pDgramHdr,
  506. pSendBuffer->HdrLength,
  507. FALSE,
  508. FALSE,
  509. NULL);
  510. if (!pMdl)
  511. {
  512. NbtFreeIrp(pRequestIrp);
  513. // call the completion routine will this status
  514. if (pRequestInfo->RequestNotifyObject)
  515. {
  516. (*((NBT_COMPLETION)pRequestInfo->RequestNotifyObject))
  517. ((PVOID)pRequestInfo->RequestContext,
  518. STATUS_INSUFFICIENT_RESOURCES,
  519. 0L);
  520. }
  521. return(STATUS_INSUFFICIENT_RESOURCES);
  522. }
  523. // Map the pages in memory...
  524. MmBuildMdlForNonPagedPool(pMdl);
  525. TdiBuildSend(
  526. pRequestIrp,
  527. pDeviceObject,
  528. pFileObject,
  529. SendSessionCompletionRoutine,
  530. NULL, //context value passed to completion routine
  531. pMdl,
  532. sFlags,
  533. SendLength); // include session hdr length (ULONG)
  534. //
  535. // tack the Client's buffer on the end, if there is one
  536. //
  537. if (pSendBuffer->Length)
  538. {
  539. pMdl->Next = pSendBuffer->pBuffer;
  540. }
  541. CHECK_COMPLETION(pRequestIrp);
  542. status = IoCallDriver(pDeviceObject,pRequestIrp);
  543. *pSentSize = SendLength; // the size we attempted to send
  544. return(status);
  545. }
  546. //----------------------------------------------------------------------------
  547. NTSTATUS
  548. SendSessionCompletionRoutine(
  549. IN PDEVICE_OBJECT DeviceObject,
  550. IN PIRP pIrp,
  551. IN PVOID pContext
  552. )
  553. /*++
  554. Routine Description:
  555. This routine handles the completion of a send to the transport.
  556. It must call any client supplied completion routine and free the Irp
  557. and Mdl back to its pool.
  558. Arguments:
  559. Return Value:
  560. NTSTATUS - success or not
  561. --*/
  562. {
  563. KIRQL OldIrq;
  564. //
  565. // check for a completion routine of the clients to call...
  566. //
  567. if (pIrp->Overlay.AsynchronousParameters.UserApcRoutine)
  568. {
  569. (*((NBT_COMPLETION)pIrp->Overlay.AsynchronousParameters.UserApcRoutine))
  570. ((PVOID)pIrp->Overlay.AsynchronousParameters.UserApcContext,
  571. pIrp->IoStatus.Status,
  572. (ULONG)pIrp->IoStatus.Information); // sent length
  573. }
  574. IoFreeMdl(pIrp->MdlAddress);
  575. NbtFreeIrp(pIrp);
  576. //
  577. // return this status to stop the IO subsystem from further processing the
  578. // IRP - i.e. trying to complete it back to the initiating thread! -since
  579. // there is no initiating thread - we are the initiator
  580. //
  581. return(STATUS_MORE_PROCESSING_REQUIRED);
  582. }