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.

942 lines
21 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. send.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. VOID
  16. RemoveReferenceForBuffers(
  17. PSEND_TRACKER SendTracker
  18. );
  19. VOID
  20. ProcessSend(
  21. PTDI_CONNECTION Connection
  22. );
  23. VOID
  24. ProcessSendAtPassive(
  25. PTDI_CONNECTION Connection
  26. );
  27. VOID
  28. SendBufferToTdi(
  29. PFILE_OBJECT FileObject,
  30. PIRCOMM_BUFFER Buffer
  31. );
  32. NTSTATUS
  33. SendCompletion(
  34. PDEVICE_OBJECT DeviceObject,
  35. PIRP Irp,
  36. PVOID Context
  37. );
  38. VOID
  39. TryToCompleteCurrentIrp(
  40. PSEND_TRACKER SendTracker
  41. );
  42. VOID
  43. RemoveReferenceOnTracker(
  44. PSEND_TRACKER SendTracker
  45. )
  46. {
  47. LONG Count;
  48. Count=InterlockedDecrement(&SendTracker->ReferenceCount);
  49. if (Count == 0) {
  50. REMOVE_REFERENCE_TO_CONNECTION(SendTracker->Connection);
  51. FREE_POOL(SendTracker);
  52. }
  53. return;
  54. }
  55. PIRP
  56. GetCurrentIrpAndAddReference(
  57. PSEND_TRACKER SendTracker
  58. )
  59. {
  60. KIRQL OldIrql;
  61. PIRP Irp;
  62. KeAcquireSpinLock(
  63. &SendTracker->Connection->Send.ControlLock,
  64. &OldIrql
  65. );
  66. Irp=SendTracker->CurrentWriteIrp;
  67. if (Irp != NULL) {
  68. //
  69. // irp is still around, add a ref coun to keep it around for a while.
  70. //
  71. SendTracker->IrpReferenceCount++;
  72. }
  73. KeReleaseSpinLock(
  74. &SendTracker->Connection->Send.ControlLock,
  75. OldIrql
  76. );
  77. return Irp;
  78. }
  79. VOID
  80. ReleaseIrpReference(
  81. PSEND_TRACKER SendTracker
  82. )
  83. {
  84. KIRQL OldIrql;
  85. PIRP Irp=NULL;
  86. CONNECTION_CALLBACK Callback=NULL;
  87. PVOID Context;
  88. KeAcquireSpinLock(
  89. &SendTracker->Connection->Send.ControlLock,
  90. &OldIrql
  91. );
  92. SendTracker->IrpReferenceCount--;
  93. if (SendTracker->IrpReferenceCount==0) {
  94. //
  95. // done, with irp complete it now with the current status
  96. //
  97. Irp=SendTracker->CurrentWriteIrp;
  98. SendTracker->CurrentWriteIrp=NULL;
  99. Callback=SendTracker->CompletionRoutine;
  100. Context=SendTracker->CompletionContext;
  101. #if DBG
  102. SendTracker->CompletionRoutine=NULL;
  103. #endif
  104. SendTracker->Connection->Send.CurrentSendTracker=NULL;
  105. }
  106. KeReleaseSpinLock(
  107. &SendTracker->Connection->Send.ControlLock,
  108. OldIrql
  109. );
  110. if (Irp != NULL) {
  111. //
  112. // The ref count has gone to zero, complete the irp
  113. //
  114. (Callback)(
  115. Context,
  116. Irp
  117. );
  118. //
  119. // release the reference for the irp
  120. //
  121. RemoveReferenceOnTracker(SendTracker);
  122. }
  123. return;
  124. }
  125. VOID
  126. SetIrpAndRefcounts(
  127. PSEND_TRACKER SendTracker,
  128. PIRP Irp
  129. )
  130. {
  131. //
  132. // set the tracker refcount to 2, one for the irp, and one for the rountine that called this
  133. //
  134. SendTracker->ReferenceCount=2;
  135. //
  136. // Set the irp count to one for the rountine that called this, it will release when it done
  137. // setting up the tracker block
  138. //
  139. SendTracker->IrpReferenceCount=1;
  140. //
  141. // save the irp
  142. //
  143. SendTracker->CurrentWriteIrp=Irp;
  144. return;
  145. }
  146. VOID
  147. SendTimerProc(
  148. PKDPC Dpc,
  149. PVOID Context,
  150. PVOID SystemParam1,
  151. PVOID SystemParam2
  152. )
  153. {
  154. PSEND_TRACKER SendTracker=Context;
  155. PIRP Irp;
  156. KIRQL OldIrql;
  157. D_ERROR(DbgPrint("IRCOMM: SendTimerProc\n");)
  158. ASSERT(SendTracker->TimerSet);
  159. #if DBG
  160. SendTracker->TimerExpired=TRUE;
  161. #endif
  162. SendTracker->TimerSet=FALSE;
  163. //
  164. // try to get a hold of the irp so we can set the status
  165. //
  166. Irp=GetCurrentIrpAndAddReference(SendTracker);
  167. Irp->IoStatus.Status=STATUS_TIMEOUT;
  168. //
  169. // release on reference for the one we just added
  170. //
  171. ReleaseIrpReference(SendTracker);
  172. TryToCompleteCurrentIrp(
  173. SendTracker
  174. );
  175. //
  176. // release the second reference for the timer being set in the first place
  177. //
  178. ReleaseIrpReference(SendTracker);
  179. return;
  180. }
  181. VOID
  182. SendCancelRoutine(
  183. PDEVICE_OBJECT DeviceObject,
  184. PIRP Irp
  185. )
  186. {
  187. PSEND_TRACKER SendTracker=Irp->Tail.Overlay.DriverContext[0];
  188. KIRQL OldIrql;
  189. D_ERROR(DbgPrint("IRCOMM: SendCancelRoutine\n");)
  190. #if DBG
  191. SendTracker->IrpCanceled=TRUE;
  192. #endif
  193. IoReleaseCancelSpinLock(Irp->CancelIrql);
  194. Irp->IoStatus.Status=STATUS_CANCELLED;
  195. //
  196. // clean up the timer
  197. //
  198. TryToCompleteCurrentIrp(SendTracker);
  199. //
  200. // release the reference held for the cancel routine
  201. //
  202. ReleaseIrpReference(SendTracker);
  203. //
  204. // send tracker maybe freed at this point
  205. //
  206. return;
  207. }
  208. VOID
  209. TryToCompleteCurrentIrp(
  210. PSEND_TRACKER SendTracker
  211. )
  212. {
  213. KIRQL OldIrql;
  214. PVOID OldCancelRoutine=NULL;
  215. PIRP Irp;
  216. BOOLEAN TimerCanceled=FALSE;
  217. Irp=GetCurrentIrpAndAddReference(SendTracker);
  218. KeAcquireSpinLock(
  219. &SendTracker->Connection->Send.ControlLock,
  220. &OldIrql
  221. );
  222. if (SendTracker->TimerSet) {
  223. TimerCanceled=KeCancelTimer(
  224. &SendTracker->Timer
  225. );
  226. if (TimerCanceled) {
  227. //
  228. // We ended up canceling the timer
  229. //
  230. SendTracker->TimerSet=FALSE;
  231. } else {
  232. //
  233. // The timer is already running, we will just let it complete
  234. // and do the clean up.
  235. //
  236. }
  237. }
  238. if (Irp != NULL) {
  239. //
  240. // the irp has not already been completed
  241. //
  242. OldCancelRoutine=IoSetCancelRoutine(
  243. Irp,
  244. NULL
  245. );
  246. }
  247. KeReleaseSpinLock(
  248. &SendTracker->Connection->Send.ControlLock,
  249. OldIrql
  250. );
  251. if (TimerCanceled) {
  252. //
  253. // we canceled the timer before it could run, remove the reference for it
  254. //
  255. ReleaseIrpReference(SendTracker);
  256. }
  257. if (Irp != NULL) {
  258. if (OldCancelRoutine != NULL) {
  259. //
  260. // since the cancel rountine had not run, release its reference to the irp
  261. //
  262. ReleaseIrpReference(SendTracker);
  263. }
  264. //
  265. // if this routine got the irp, release the reference to it
  266. //
  267. ReleaseIrpReference(SendTracker);
  268. }
  269. return;
  270. }
  271. VOID
  272. SendOnConnection(
  273. IRDA_HANDLE Handle,
  274. PIRP Irp,
  275. CONNECTION_CALLBACK Callback,
  276. PVOID Context,
  277. ULONG Timeout
  278. )
  279. {
  280. PTDI_CONNECTION Connection=Handle;
  281. PIO_STACK_LOCATION IrpSp=IoGetCurrentIrpStackLocation(Irp);
  282. PSEND_TRACKER SendTracker;
  283. BOOLEAN AlreadyCanceled;
  284. KIRQL OldIrql;
  285. KIRQL OldCancelIrql;
  286. if (Connection->Send.CurrentSendTracker != NULL) {
  287. //
  288. // called when we already have an irp
  289. //
  290. (Callback)(
  291. Context,
  292. Irp
  293. );
  294. return;
  295. }
  296. SendTracker=ALLOCATE_NONPAGED_POOL(sizeof(*SendTracker));
  297. if (SendTracker == NULL) {
  298. Irp->IoStatus.Status=STATUS_INSUFFICIENT_RESOURCES;
  299. (Callback)(
  300. Context,
  301. Irp
  302. );
  303. return;
  304. }
  305. RtlZeroMemory(SendTracker,sizeof(*SendTracker));
  306. KeInitializeTimer(
  307. &SendTracker->Timer
  308. );
  309. KeInitializeDpc(
  310. &SendTracker->TimerDpc,
  311. SendTimerProc,
  312. SendTracker
  313. );
  314. //
  315. // set the irp and initialize the refcounts
  316. //
  317. SetIrpAndRefcounts(SendTracker,Irp);
  318. ADD_REFERENCE_TO_CONNECTION(Connection);
  319. //
  320. // initialize these values
  321. //
  322. SendTracker->Connection=Connection;
  323. SendTracker->BuffersOutstanding=0;
  324. SendTracker->CompletionContext = Context;
  325. SendTracker->CompletionRoutine = Callback;
  326. SendTracker->BytesRemainingInIrp = IrpSp->Parameters.Write.Length;
  327. if (Timeout > 0) {
  328. //
  329. // add a reference for the timer
  330. //
  331. GetCurrentIrpAndAddReference(SendTracker);
  332. }
  333. //
  334. // add a reference to the irp for the cancel rountine
  335. //
  336. GetCurrentIrpAndAddReference(SendTracker);
  337. KeAcquireSpinLock(
  338. &Connection->Send.ControlLock,
  339. &OldIrql
  340. );
  341. Connection->Send.CurrentSendTracker=SendTracker;
  342. if (Timeout > 0) {
  343. //
  344. // need to set a timer
  345. //
  346. LARGE_INTEGER DueTime;
  347. DueTime.QuadPart= (LONGLONG)(Timeout+100) * -10000;
  348. SendTracker->TimerSet=TRUE;
  349. KeSetTimer(
  350. &SendTracker->Timer,
  351. DueTime,
  352. &SendTracker->TimerDpc
  353. );
  354. }
  355. Irp->Tail.Overlay.DriverContext[0]=SendTracker;
  356. IoAcquireCancelSpinLock(&OldCancelIrql);
  357. AlreadyCanceled=Irp->Cancel;
  358. if (!AlreadyCanceled) {
  359. PIRCOMM_BUFFER Buffer;
  360. //
  361. // the irp has not been canceled already, set the cancel routine
  362. //
  363. IoSetCancelRoutine(
  364. Irp,
  365. SendCancelRoutine
  366. );
  367. } else {
  368. //
  369. // it was canceled when we got it
  370. //
  371. Irp->IoStatus.Status=STATUS_CANCELLED;
  372. }
  373. IoReleaseCancelSpinLock(OldCancelIrql);
  374. KeReleaseSpinLock(
  375. &Connection->Send.ControlLock,
  376. OldIrql
  377. );
  378. if (AlreadyCanceled) {
  379. //
  380. // The irp has already been canceled, just call the cancel rountine so the normal code runs
  381. //
  382. D_ERROR(DbgPrint("IRCOMM: SendOnConnection: irp already canceled\n");)
  383. IoAcquireCancelSpinLock(&Irp->CancelIrql);
  384. SendCancelRoutine(
  385. NULL,
  386. Irp
  387. );
  388. //
  389. // the cancel rountine will release the cancel spinlock
  390. //
  391. }
  392. //
  393. // release the reference for this routine
  394. //
  395. ReleaseIrpReference(SendTracker);
  396. ProcessSendAtPassive(Connection);
  397. RemoveReferenceOnTracker(SendTracker);
  398. return;
  399. }
  400. VOID
  401. ProcessSendAtPassive(
  402. PTDI_CONNECTION Connection
  403. )
  404. {
  405. if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
  406. //
  407. // less than dispatch, just call directly
  408. //
  409. ProcessSend(Connection);
  410. } else {
  411. //
  412. // Called at dispatch, queue the work item
  413. //
  414. LONG Count=InterlockedIncrement(&Connection->Send.WorkItemCount);
  415. if (Count == 1) {
  416. ExQueueWorkItem(&Connection->Send.WorkItem,CriticalWorkQueue);
  417. }
  418. }
  419. return;
  420. }
  421. VOID
  422. SendWorkItemRountine(
  423. PVOID Context
  424. )
  425. {
  426. PTDI_CONNECTION Connection=Context;
  427. //
  428. // the work item has run set the count to zero
  429. //
  430. InterlockedExchange(&Connection->Send.WorkItemCount,0);
  431. ProcessSend(Connection);
  432. }
  433. VOID
  434. ProcessSend(
  435. PTDI_CONNECTION Connection
  436. )
  437. {
  438. PSEND_TRACKER SendTracker;
  439. PIRP Irp;
  440. PIO_STACK_LOCATION IrpSp;
  441. PLIST_ENTRY ListEntry;
  442. ULONG BytesUsedInBuffer;
  443. PIRCOMM_BUFFER Buffer;
  444. BOOLEAN ExitLoop;
  445. PFILE_OBJECT FileObject;
  446. CONNECTION_HANDLE ConnectionHandle;
  447. KIRQL OldIrql;
  448. KeAcquireSpinLock(
  449. &Connection->Send.ControlLock,
  450. &OldIrql
  451. );
  452. if (Connection->Send.ProcessSendEntryCount == 0) {
  453. Connection->Send.ProcessSendEntryCount++;
  454. while ((Connection->Send.CurrentSendTracker != NULL)
  455. &&
  456. (!Connection->Send.OutOfBuffers)
  457. &&
  458. (Connection->LinkUp)
  459. &&
  460. (Connection->Send.CurrentSendTracker->BytesRemainingInIrp > 0)) {
  461. SendTracker=Connection->Send.CurrentSendTracker;
  462. InterlockedIncrement(&SendTracker->ReferenceCount);
  463. KeReleaseSpinLock(
  464. &Connection->Send.ControlLock,
  465. OldIrql
  466. );
  467. Irp=GetCurrentIrpAndAddReference(SendTracker);
  468. if (Irp != NULL) {
  469. //
  470. // got the current irp
  471. //
  472. IrpSp=IoGetCurrentIrpStackLocation(Irp);
  473. ConnectionHandle=GetCurrentConnection(Connection->LinkHandle);
  474. if (ConnectionHandle != NULL) {
  475. //
  476. // we have a good connection
  477. //
  478. FileObject=ConnectionGetFileObject(ConnectionHandle);
  479. Buffer=ConnectionGetBuffer(ConnectionHandle,BUFFER_TYPE_SEND);
  480. if (Buffer != NULL) {
  481. LONG BytesToCopy=min(SendTracker->BytesRemainingInIrp, (LONG)Buffer->BufferLength - 1);
  482. //
  483. // this buffer is going to be outstanding, set this before the bytes
  484. // remaining count goes to zero
  485. //
  486. InterlockedIncrement(&SendTracker->BuffersOutstanding);
  487. //
  488. // start with a zero length of control bytes
  489. //
  490. Buffer->Data[0]=0;
  491. //
  492. // actual data starts one byte in, after the length byte
  493. //
  494. // move the data
  495. //
  496. RtlCopyMemory(
  497. &Buffer->Data[1],
  498. (PUCHAR)Irp->AssociatedIrp.SystemBuffer+(IrpSp->Parameters.Write.Length - SendTracker->BytesRemainingInIrp),
  499. BytesToCopy
  500. );
  501. //
  502. // the count has to include the control byte
  503. //
  504. Buffer->Mdl->ByteCount= 1 + BytesToCopy;
  505. #if DBG
  506. RtlFillMemory(
  507. &Buffer->Data[Buffer->Mdl->ByteCount],
  508. Buffer->BufferLength-Buffer->Mdl->ByteCount,
  509. 0xfb
  510. );
  511. #endif
  512. InterlockedExchangeAdd(&SendTracker->BytesRemainingInIrp, -BytesToCopy);
  513. Buffer->Mdl->Next=NULL;
  514. Buffer->Context=SendTracker;
  515. InterlockedIncrement(&SendTracker->ReferenceCount);
  516. ASSERT(SendTracker->CurrentWriteIrp != NULL);
  517. SendBufferToTdi(
  518. FileObject,
  519. Buffer
  520. );
  521. #if DBG
  522. Buffer=NULL;
  523. #endif
  524. } else {
  525. //
  526. // No more buffers availible
  527. //
  528. Connection->Send.OutOfBuffers=TRUE;
  529. }
  530. ConnectionReleaseFileObject(ConnectionHandle,FileObject);
  531. ReleaseConnection(ConnectionHandle);
  532. } else {
  533. //
  534. // The connection, must be down
  535. //
  536. D_ERROR(DbgPrint("IRCOMM: ProcessSend: Link down\n");)
  537. Connection->LinkUp=FALSE;
  538. }
  539. ReleaseIrpReference(SendTracker);
  540. } else {
  541. //
  542. // the irp has already been completed from this tracking block
  543. //
  544. D_ERROR(DbgPrint("IRCOMM: ProcessSend: no irp\n");)
  545. ASSERT(SendTracker->TimerExpired || SendTracker->IrpCanceled || SendTracker->SendAborted);
  546. }
  547. RemoveReferenceOnTracker(SendTracker);
  548. KeAcquireSpinLock(
  549. &Connection->Send.ControlLock,
  550. &OldIrql
  551. );
  552. } // while
  553. Connection->Send.ProcessSendEntryCount--;
  554. }
  555. KeReleaseSpinLock(
  556. &Connection->Send.ControlLock,
  557. OldIrql
  558. );
  559. return;
  560. }
  561. VOID
  562. SendBufferToTdi(
  563. PFILE_OBJECT FileObject,
  564. PIRCOMM_BUFFER Buffer
  565. )
  566. {
  567. PDEVICE_OBJECT IrdaDeviceObject=IoGetRelatedDeviceObject(FileObject);
  568. ULONG SendLength;
  569. IoReuseIrp(Buffer->Irp,STATUS_SUCCESS);
  570. Buffer->Irp->Tail.Overlay.OriginalFileObject = FileObject;
  571. SendLength = MmGetMdlByteCount(Buffer->Mdl);
  572. TdiBuildSend(
  573. Buffer->Irp,
  574. IrdaDeviceObject,
  575. FileObject,
  576. SendCompletion,
  577. Buffer,
  578. Buffer->Mdl,
  579. 0, // send flags
  580. SendLength
  581. );
  582. IoCallDriver(IrdaDeviceObject, Buffer->Irp);
  583. return;
  584. }
  585. NTSTATUS
  586. SendCompletion(
  587. PDEVICE_OBJECT DeviceObject,
  588. PIRP BufferIrp,
  589. PVOID Context
  590. )
  591. {
  592. PIRCOMM_BUFFER Buffer=Context;
  593. PSEND_TRACKER SendTracker=Buffer->Context;
  594. PTDI_CONNECTION Connection=SendTracker->Connection;
  595. LONG BuffersOutstanding;
  596. //
  597. // save off the status for the sub transerfer
  598. //
  599. SendTracker->LastStatus=BufferIrp->IoStatus.Status;
  600. D_TRACE(DbgPrint("IRCOMM: SendComplete: Status=%08lx, len=%d\n",BufferIrp->IoStatus.Status,BufferIrp->IoStatus.Information);)
  601. #if DBG
  602. RtlFillMemory(
  603. &Buffer->Data[0],
  604. Buffer->BufferLength,
  605. 0xfe
  606. );
  607. #endif
  608. //
  609. // return the buffer
  610. //
  611. Buffer->FreeBuffer(Buffer);
  612. Connection->Send.OutOfBuffers=FALSE;
  613. #if DBG
  614. Buffer=NULL;
  615. BufferIrp=NULL;
  616. #endif
  617. BuffersOutstanding=InterlockedDecrement(&SendTracker->BuffersOutstanding);
  618. if ((BuffersOutstanding == 0) && (SendTracker->BytesRemainingInIrp == 0)) {
  619. //
  620. // All of the data in the irp has been sent and all of the irps send to
  621. // the irda stack have completed
  622. //
  623. // Done with the irp in this tracker
  624. //
  625. PIRP Irp;
  626. PIO_STACK_LOCATION IrpSp;
  627. Irp=GetCurrentIrpAndAddReference(SendTracker);
  628. if (Irp != NULL) {
  629. IrpSp=IoGetCurrentIrpStackLocation(Irp);
  630. Irp->IoStatus.Information=IrpSp->Parameters.Write.Length;
  631. Irp->IoStatus.Status=SendTracker->LastStatus;
  632. ReleaseIrpReference(SendTracker);
  633. TryToCompleteCurrentIrp(SendTracker);
  634. } else {
  635. //
  636. // the irp has already been completed from this tracking block
  637. //
  638. D_ERROR(DbgPrint("IRCOMM: SendCompletion: no irp\n");)
  639. //
  640. // this should only happen if the timer expired or the irp was canceled
  641. //
  642. ASSERT(SendTracker->TimerExpired || SendTracker->IrpCanceled || SendTracker->SendAborted);
  643. }
  644. }
  645. RemoveReferenceOnTracker(SendTracker);
  646. ProcessSendAtPassive(Connection);
  647. return STATUS_MORE_PROCESSING_REQUIRED;
  648. }
  649. VOID
  650. AbortSend(
  651. IRDA_HANDLE Handle
  652. )
  653. {
  654. PTDI_CONNECTION Connection=Handle;
  655. PSEND_TRACKER SendTracker=NULL;
  656. KIRQL OldIrql;
  657. KeAcquireSpinLock(
  658. &Connection->Send.ControlLock,
  659. &OldIrql
  660. );
  661. SendTracker=Connection->Send.CurrentSendTracker;
  662. if (SendTracker != NULL) {
  663. InterlockedIncrement(&SendTracker->ReferenceCount);
  664. }
  665. KeReleaseSpinLock(
  666. &Connection->Send.ControlLock,
  667. OldIrql
  668. );
  669. if (SendTracker != NULL) {
  670. #if DBG
  671. SendTracker->SendAborted=TRUE;
  672. #endif
  673. TryToCompleteCurrentIrp(SendTracker);
  674. RemoveReferenceOnTracker(SendTracker);
  675. }
  676. return;
  677. }