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.

1261 lines
40 KiB

  1. /*-------------------------------------------------------------------
  2. | write.c -
  3. 1-22-99 - add missing IoReleaseCancelSpinLock() to CompleteWrite(),
  4. missing since 1-18-99 changes. kpb.
  5. 1-18-99 - Adjust sync lock for write packets to avoid bugchecks with
  6. wait on tx option. kpb
  7. 9-25-98 - bugfix, immediate write could drop 1 byte due to faulty
  8. txport buffer check.
  9. Copyright 1993-98 Comtrol Corporation. All rights reserved.
  10. |--------------------------------------------------------------------*/
  11. #include "precomp.h"
  12. /************************************************************************
  13. Routine Description:
  14. This is the dispatch routine for write. It validates the parameters
  15. for the write request and if all is ok then it places the request
  16. on the work queue.
  17. Arguments:
  18. DeviceObject - Pointer to the device object for this device
  19. Irp - Pointer to the IRP for the current request
  20. Return Value:
  21. If the io is zero length then it will return STATUS_SUCCESS,
  22. otherwise this routine will return STATUS_PENDING.
  23. ************************************************************************/
  24. NTSTATUS
  25. SerialWrite(
  26. IN PDEVICE_OBJECT DeviceObject,
  27. IN PIRP Irp
  28. )
  29. {
  30. PSERIAL_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
  31. NTSTATUS Status;
  32. BOOLEAN acceptingIRPs;
  33. acceptingIRPs = SerialIRPPrologue(Extension);
  34. if (acceptingIRPs == FALSE) {
  35. Irp->IoStatus.Information = 0;
  36. Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
  37. SerialCompleteRequest(Extension, Irp, IO_NO_INCREMENT);
  38. return STATUS_NO_SUCH_DEVICE;
  39. }
  40. if (Extension->DeviceType == DEV_BOARD)
  41. {
  42. Irp->IoStatus.Information = 0;
  43. Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  44. SerialCompleteRequest (Extension, Irp, IO_NO_INCREMENT);
  45. return STATUS_NOT_SUPPORTED;
  46. };
  47. #ifdef TRACE_PORT
  48. if (Extension->TraceOptions)
  49. {
  50. if (Extension->TraceOptions & 1) // trace messages
  51. {
  52. Tprintf("Write, Len:%d",
  53. (ULONG) IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length);
  54. // dump data into the trace buffer in a hex or ascii dump format
  55. TraceDump(Extension,
  56. Irp->AssociatedIrp.SystemBuffer,
  57. IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length,0);
  58. }
  59. else if (Extension->TraceOptions & 4) // trace output data
  60. {
  61. TracePut(
  62. Irp->AssociatedIrp.SystemBuffer,
  63. IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length);
  64. }
  65. }
  66. #endif
  67. if (Extension->ErrorWord)
  68. {
  69. if (SerialCompleteIfError( DeviceObject, Irp ) != STATUS_SUCCESS)
  70. {
  71. ExtTrace(Extension,D_Error, " ErrorSet!");
  72. return STATUS_CANCELLED;
  73. }
  74. }
  75. Irp->IoStatus.Information = 0L;
  76. // Quick check for a zero length write. If it is zero length
  77. // then we are already done!
  78. if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length)
  79. {
  80. //------ 10-22-96, start code addition to speed up 1-byte writes
  81. #define WRT_LEN (IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length)
  82. #ifdef S_VS
  83. // vs1000 code
  84. if (!Extension->port_config->WaitOnTx) // have to let ISR handle physical end detect
  85. {
  86. if (WRT_LEN < OUT_BUF_SIZE)
  87. {
  88. // ISR is locked out since it only handles IRP's queued up
  89. // any other contention problems?
  90. //extension->lock_out_other_tasks = 1;
  91. if ((Extension->CurrentWriteIrp == NULL) // no current write
  92. && (IsListEmpty(&Extension->WriteQueue))) // no queued up output data
  93. {
  94. // if room in hardware
  95. // bug: kpb, 9-22-98
  96. //if ( (ULONG)(OUT_BUF_SIZE-PortGetTxCnt(Extension->Port)) >= WRT_LEN)
  97. if ( (ULONG)(PortGetTxRoom(Extension->Port)) > WRT_LEN)
  98. {
  99. // Send it all ,WriteTxBlk will chk fifo
  100. q_put(&Extension->Port->QOut,
  101. (PUCHAR)(Irp->AssociatedIrp.SystemBuffer),
  102. WRT_LEN);
  103. Extension->ISR_Flags |= TX_NOT_EMPTY; // use to detect fifo empty
  104. Extension->OurStats.TransmittedCount += WRT_LEN;
  105. ++Extension->sent_packets;
  106. Irp->IoStatus.Information = WRT_LEN;
  107. ExtTrace(Extension,D_Ioctl, " ,IMMED. WRITE");
  108. Irp->IoStatus.Status = STATUS_SUCCESS;
  109. SerialCompleteRequest(Extension, Irp, 0);
  110. return STATUS_SUCCESS;
  111. }
  112. }
  113. } // if (WRT_LEN < OUT_BUF_SIZE)
  114. } // if (!Extension->port_config->WaitOnTx)
  115. #else
  116. // rocketport code
  117. if (!Extension->port_config->WaitOnTx) // have to let ISR handle physical end detect
  118. {
  119. if (WRT_LEN <= MAXTX_SIZE)
  120. {
  121. // ISR is locked out since it only handles IRP's queued up
  122. if ((Extension->CurrentWriteIrp == NULL) // no current write
  123. && (IsListEmpty(&Extension->WriteQueue))) // no queued up output data
  124. {
  125. // if room in hardware
  126. if ( (ULONG)(MAXTX_SIZE-sGetTxCnt(Extension->ChP)) >= WRT_LEN)
  127. {
  128. if (Extension->Option & OPTION_RS485_SOFTWARE_TOGGLE)
  129. {
  130. if ((Extension->DTRRTSStatus & SERIAL_RTS_STATE) == 0)
  131. {
  132. sSetRTS(Extension->ChP);
  133. Extension->DTRRTSStatus |= SERIAL_RTS_STATE;
  134. }
  135. }
  136. // Send it all ,WriteTxBlk will chk fifo
  137. sWriteTxBlk(Extension->ChP,
  138. (PUCHAR)(Irp->AssociatedIrp.SystemBuffer),
  139. WRT_LEN);
  140. Extension->ISR_Flags |= TX_NOT_EMPTY; // use to detect fifo empty
  141. Extension->OurStats.TransmittedCount += WRT_LEN;
  142. ++Extension->sent_packets;
  143. Irp->IoStatus.Information = WRT_LEN;
  144. ExtTrace(Extension,D_Ioctl, " ,IMMED. WRITE");
  145. ++Extension->sent_packets;
  146. Irp->IoStatus.Status = STATUS_SUCCESS;
  147. SerialCompleteRequest(Extension, Irp, 0);
  148. return STATUS_SUCCESS;
  149. }
  150. }
  151. } // if (WRT_LEN <= MAXTX_SIZE)
  152. } // if (!Extension->port_config->WaitOnTx)
  153. #endif // rocketport code
  154. //------ 10-22-96, end code addition to speed up 1-byte writes
  155. // Put the write on the queue so that we can
  156. // process it when our previous writes are done.
  157. ++Extension->sent_packets;
  158. Status = SerialStartOrQueue(
  159. Extension,
  160. Irp,
  161. &Extension->WriteQueue,
  162. &Extension->CurrentWriteIrp,
  163. SerialStartWrite
  164. );
  165. if (Status == STATUS_PENDING)
  166. {
  167. ExtTrace(Extension,D_Ioctl, " ,PENDING");
  168. }
  169. return Status;
  170. }
  171. else // if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length)
  172. {
  173. Irp->IoStatus.Status = STATUS_SUCCESS;
  174. SerialCompleteRequest(Extension, Irp, 0 );
  175. return STATUS_SUCCESS;
  176. }
  177. }
  178. /***************************************************************************
  179. Routine Description:
  180. This routine is used to start off any write. It initializes
  181. the Iostatus fields of the irp. It will set up any timers
  182. that are used to control the write.
  183. Arguments:
  184. Extension - Points to the serial device extension
  185. Return Value:
  186. This routine will return STATUS_PENDING for all writes
  187. other than those that we find are cancelled.
  188. ***************************************************************************/
  189. NTSTATUS
  190. SerialStartWrite(
  191. IN PSERIAL_DEVICE_EXTENSION Extension
  192. )
  193. {
  194. PIRP NewIrp;
  195. KIRQL OldIrql;
  196. LARGE_INTEGER TotalTime;
  197. BOOLEAN UseATimer;
  198. SERIAL_TIMEOUTS Timeouts;
  199. BOOLEAN SetFirstStatus = FALSE;
  200. NTSTATUS FirstStatus;
  201. do {
  202. // If there is an xoff counter then complete it.
  203. #ifdef REMOVED // Will not suppor Xoff Counter IRP
  204. IoAcquireCancelSpinLock(&OldIrql);
  205. // We see if there is a actually an Xoff counter irp.
  206. // If there is, we put the write irp back on the head
  207. // of the write list. We then kill the xoff counter.
  208. // The xoff counter killing code will actually make the
  209. // xoff counter back into the current write irp, and
  210. // in the course of completing the xoff (which is now
  211. // the current write) we will restart this irp.
  212. if(Extension->CurrentXoffIrp)
  213. {
  214. if (SERIAL_REFERENCE_COUNT(Extension->CurrentXoffIrp)) {
  215. {
  216. // The reference count is non-zero. This implies that
  217. // the xoff irp has not made it through the completion
  218. // path yet. We will increment the reference count
  219. // and attempt to complete it ourseleves.
  220. SERIAL_SET_REFERENCE(
  221. Extension->CurrentXoffIrp,
  222. SERIAL_REF_XOFF_REF
  223. );
  224. //
  225. // The following call will actually release the
  226. // cancel spin lock.
  227. //
  228. SerialTryToCompleteCurrent(
  229. Extension,
  230. SerialGrabXoffFromIsr,
  231. OldIrql,
  232. STATUS_SERIAL_MORE_WRITES,
  233. &Extension->CurrentXoffIrp,
  234. NULL,
  235. NULL,
  236. &Extension->XoffCountTimer,
  237. NULL,
  238. NULL,
  239. SERIAL_REF_XOFF_REF
  240. );
  241. } else {
  242. //
  243. // The irp is well on its way to being finished.
  244. // We can let the regular completion code do the
  245. // work. Just release the spin lock.
  246. //
  247. IoReleaseCancelSpinLock(OldIrql);
  248. }
  249. }
  250. else
  251. {
  252. IoReleaseCancelSpinLock(OldIrql);
  253. }
  254. #endif //REMOVED
  255. UseATimer = FALSE;
  256. // Calculate the timeout value needed for the
  257. // request. Note that the values stored in the
  258. // timeout record are in milliseconds. Note that
  259. // if the timeout values are zero then we won't start
  260. // the timer.
  261. KeAcquireSpinLock( &Extension->ControlLock, &OldIrql );
  262. Timeouts = Extension->Timeouts;
  263. KeReleaseSpinLock( &Extension->ControlLock, OldIrql );
  264. if (Timeouts.WriteTotalTimeoutConstant ||
  265. Timeouts.WriteTotalTimeoutMultiplier)
  266. {
  267. PIO_STACK_LOCATION IrpSp =
  268. IoGetCurrentIrpStackLocation(Extension->CurrentWriteIrp);
  269. UseATimer = TRUE;
  270. TotalTime.QuadPart =
  271. ((LONGLONG)((UInt32x32To64(
  272. (IrpSp->MajorFunction == IRP_MJ_WRITE)?
  273. (IrpSp->Parameters.Write.Length):
  274. (1),
  275. Timeouts.WriteTotalTimeoutMultiplier
  276. )
  277. + Timeouts.WriteTotalTimeoutConstant)))
  278. * -10000;
  279. }
  280. // The irp may be going to the isr shortly. Now
  281. // is a good time to initialize its reference counts.
  282. SERIAL_INIT_REFERENCE(Extension->CurrentWriteIrp);
  283. // We need to see if this irp should be canceled.
  284. IoAcquireCancelSpinLock(&OldIrql);
  285. if (Extension->CurrentWriteIrp->Cancel)
  286. {
  287. IoReleaseCancelSpinLock(OldIrql);
  288. ExtTrace(Extension,D_Ioctl, " (write canceled)");
  289. Extension->CurrentWriteIrp->IoStatus.Status = STATUS_CANCELLED;
  290. if (!SetFirstStatus)
  291. {
  292. FirstStatus = STATUS_CANCELLED;
  293. SetFirstStatus = TRUE;
  294. }
  295. }
  296. else
  297. {
  298. if(!SetFirstStatus)
  299. {
  300. // If we haven't set our first status, then
  301. // this is the only irp that could have possibly
  302. // not been on the queue. (It could have been
  303. // on the queue if this routine is being invoked
  304. // from the completion routine.) Since this
  305. // irp might never have been on the queue we
  306. // should mark it as pending.
  307. IoMarkIrpPending(Extension->CurrentWriteIrp);
  308. SetFirstStatus = TRUE;
  309. FirstStatus = STATUS_PENDING;
  310. }
  311. // We give the irp to to the isr to write out.
  312. // We set a cancel routine that knows how to
  313. // grab the current write away from the isr.
  314. // Since the cancel routine has an implicit reference
  315. // to this irp up the reference count.
  316. IoSetCancelRoutine(
  317. Extension->CurrentWriteIrp,
  318. SerialCancelCurrentWrite
  319. );
  320. SERIAL_SET_REFERENCE(
  321. Extension->CurrentWriteIrp,
  322. SERIAL_REF_CANCEL
  323. );
  324. if(UseATimer)
  325. {
  326. //ExtTrace(Extension,D_Ioctl, " (total timer used)");
  327. KeSetTimer(
  328. &Extension->WriteRequestTotalTimer,
  329. TotalTime,
  330. &Extension->TotalWriteTimeoutDpc
  331. );
  332. // This timer now has a reference to the irp.
  333. SERIAL_SET_REFERENCE(
  334. Extension->CurrentWriteIrp,
  335. SERIAL_REF_TOTAL_TIMER
  336. );
  337. }
  338. #ifdef NEW_WRITE_SYNC_LOCK
  339. // write some data now
  340. SyncUp(Driver.InterruptObject,
  341. &Driver.TimerLock,
  342. SerialGiveWriteToIsr,
  343. Extension);
  344. #else
  345. SerialGiveWriteToIsr(Extension);
  346. #endif
  347. IoReleaseCancelSpinLock(OldIrql);
  348. break;
  349. }
  350. // Well the write was canceled before we could start it up.
  351. // Try to get another.
  352. SerialGetNextWrite(
  353. &Extension->CurrentWriteIrp,
  354. &Extension->WriteQueue,
  355. &NewIrp,
  356. TRUE,
  357. Extension
  358. );
  359. } while (NewIrp);
  360. return FirstStatus;
  361. }
  362. /****************************************************************************
  363. Routine Description:
  364. This routine completes the old write as well as getting
  365. a pointer to the next write.
  366. The reason that we have have pointers to the current write
  367. queue as well as the current write irp is so that this
  368. routine may be used in the common completion code for
  369. read and write.
  370. Arguments:
  371. CurrentOpIrp - Pointer to the pointer that points to the
  372. current write irp.
  373. QueueToProcess - Pointer to the write queue.
  374. NewIrp - A pointer to a pointer to the irp that will be the
  375. current irp. Note that this could end up pointing
  376. to a null pointer. This does NOT necessaryly mean
  377. that there is no current write. What could occur
  378. is that while the cancel lock is held the write
  379. queue ended up being empty, but as soon as we release
  380. the cancel spin lock a new irp came in from
  381. SerialStartWrite.
  382. CompleteCurrent - Flag indicates whether the CurrentOpIrp should
  383. be completed.
  384. Return Value:
  385. None.
  386. ****************************************************************************/
  387. VOID
  388. SerialGetNextWrite(
  389. IN PIRP *CurrentOpIrp,
  390. IN PLIST_ENTRY QueueToProcess,
  391. IN PIRP *NewIrp,
  392. IN BOOLEAN CompleteCurrent,
  393. PSERIAL_DEVICE_EXTENSION Extension
  394. )
  395. {
  396. // LARGE_INTEGER charTime; // 100 ns ticks per char, related to baud rate
  397. // LARGE_INTEGER WaitTime; // Actual time req'd for buffer to drain
  398. // PSERIAL_DEVICE_EXTENSION Extension = CONTAINING_RECORD(
  399. // QueueToProcess,
  400. // SERIAL_DEVICE_EXTENSION,
  401. // WriteQueue
  402. // );
  403. do {
  404. // We could be completing a flush.
  405. if (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction
  406. == IRP_MJ_WRITE)
  407. { //------- normal write block
  408. KIRQL OldIrql;
  409. // assert that our TotalCharsQueued var is not screwed up.
  410. MyAssert(Extension->TotalCharsQueued >=
  411. (IoGetCurrentIrpStackLocation(*CurrentOpIrp)
  412. ->Parameters.Write.Length));
  413. IoAcquireCancelSpinLock(&OldIrql);
  414. // increment our character count
  415. Extension->TotalCharsQueued -=
  416. IoGetCurrentIrpStackLocation(*CurrentOpIrp)
  417. ->Parameters.Write.Length;
  418. IoReleaseCancelSpinLock(OldIrql);
  419. }
  420. else if (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction
  421. == IRP_MJ_DEVICE_CONTROL)
  422. { //------- xoffcounter nonsense
  423. KIRQL OldIrql;
  424. PIRP Irp;
  425. PSERIAL_XOFF_COUNTER Xc;
  426. IoAcquireCancelSpinLock(&OldIrql);
  427. Irp = *CurrentOpIrp;
  428. Xc = Irp->AssociatedIrp.SystemBuffer;
  429. //
  430. // We should never have a xoff counter when we
  431. // get to this point.
  432. //
  433. ASSERT(!Extension->CurrentXoffIrp);
  434. //
  435. // We absolutely shouldn't have a cancel routine
  436. // at this point.
  437. //
  438. ASSERT(!Irp->CancelRoutine);
  439. // If CurrentXoffIrp is not equal to null, this
  440. // implies that this is the "second" time around
  441. // for this irp, which implies that we should really
  442. // be completing it this time.
  443. //
  444. // This could only be a xoff counter masquerading as
  445. // a write irp.
  446. //
  447. Extension->TotalCharsQueued--;
  448. //
  449. // Check to see of the xoff irp has been set with success.
  450. // This means that the write completed normally. If that
  451. // is the case, and it hasn't been set to cancel in the
  452. // meanwhile, then go on and make it the CurrentXoffIrp.
  453. //
  454. if (Irp->IoStatus.Status != STATUS_SUCCESS) {
  455. //
  456. // Oh well, we can just finish it off.
  457. //
  458. NOTHING;
  459. } else if (Irp->Cancel) {
  460. Irp->IoStatus.Status = STATUS_CANCELLED;
  461. } else {
  462. //
  463. // Give it a new cancel routine, and increment the
  464. // reference count because the cancel routine has
  465. // a reference to it.
  466. //
  467. IoSetCancelRoutine(
  468. Irp,
  469. SerialCancelCurrentXoff
  470. );
  471. SERIAL_SET_REFERENCE(
  472. Irp,
  473. SERIAL_REF_CANCEL
  474. );
  475. //
  476. // We don't want to complete the current irp now. This
  477. // will now get completed by the Xoff counter code.
  478. //
  479. CompleteCurrent = FALSE;
  480. //
  481. // Give the counter to the isr.
  482. //
  483. Extension->CurrentXoffIrp = Irp;
  484. //KeSynchronizeExecution(
  485. // Driver.Interrupt,
  486. // SerialGiveXoffToIsr,
  487. // Extension
  488. // );
  489. SerialGiveXoffToIsr(Extension);
  490. //
  491. // Start the timer for the counter and increment
  492. // the reference count since the timer has a
  493. // reference to the irp.
  494. //
  495. if(Xc->Timeout)
  496. {
  497. LARGE_INTEGER delta;
  498. delta.QuadPart = -((LONGLONG)UInt32x32To64(
  499. 1000,
  500. Xc->Timeout
  501. ));
  502. KeSetTimer(
  503. &Extension->XoffCountTimer,
  504. delta,
  505. &Extension->XoffCountTimeoutDpc
  506. );
  507. SERIAL_SET_REFERENCE(Irp,SERIAL_REF_TOTAL_TIMER);
  508. } // timeout
  509. }
  510. IoReleaseCancelSpinLock(OldIrql);
  511. }
  512. //
  513. // Note that the following call will (probably) also cause
  514. // the current irp to be completed.
  515. //
  516. SerialGetNextIrp(
  517. CurrentOpIrp,
  518. QueueToProcess,
  519. NewIrp,
  520. CompleteCurrent,
  521. Extension
  522. );
  523. if (!*NewIrp) {
  524. KIRQL OldIrql;
  525. IoAcquireCancelSpinLock(&OldIrql);
  526. //KeSynchronizeExecution(
  527. // Extension->Interrupt,
  528. // SerialProcessEmptyTransmit,
  529. // Extension
  530. // );
  531. //SerialProcessEmptyTransmit();
  532. IoReleaseCancelSpinLock(OldIrql);
  533. break;
  534. }
  535. else if (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction ==
  536. IRP_MJ_FLUSH_BUFFERS )
  537. { //------ flush operation
  538. // If flush, wait for Tx FIFO to empty before completing
  539. ExtTrace(Extension,D_Ioctl, "(end flush write)");
  540. #ifdef S_RK
  541. #ifdef COMMENT_OUT
  542. (took this out 9-22-97 - kpb)
  543. // Calculate 100ns ticks to delay for each character
  544. // Negate for call to KeDelay...
  545. charTime = RtlLargeIntegerNegate(SerialGetCharTime(Extension));
  546. // While Tx FIFO and Tx Shift Register aren't empty
  547. while ( (sGetChanStatusLo(Extension->ChP) & DRAINED) != DRAINED )
  548. { WaitTime = RtlExtendedIntegerMultiply(charTime,
  549. sGetTxCnt(Extension->ChP)
  550. );
  551. KeDelayExecutionThread(KernelMode,FALSE,&WaitTime);
  552. }
  553. #endif
  554. #endif
  555. //
  556. // If we encounter a flush request we just want to get
  557. // the next irp and complete the flush.
  558. //
  559. // Note that if NewIrp is non-null then it is also
  560. // equal to CurrentWriteIrp.
  561. //
  562. ASSERT((*NewIrp) == (*CurrentOpIrp));
  563. (*NewIrp)->IoStatus.Status = STATUS_SUCCESS;
  564. }
  565. else {
  566. break;
  567. }
  568. } while (TRUE);
  569. }
  570. /********************************************************************
  571. Routine Description:
  572. This routine is merely used to complete any write. It
  573. assumes that the status and the information fields of
  574. the irp are already correctly filled in.
  575. Arguments:
  576. Dpc - Not Used.
  577. DeferredContext - Really points to the device extension.
  578. SystemContext1 - Not Used.
  579. SystemContext2 - Not Used.
  580. Return Value:
  581. None.
  582. *********************************************************************/
  583. VOID
  584. SerialCompleteWrite(
  585. IN PKDPC Dpc,
  586. IN PVOID DeferredContext,
  587. IN PVOID SystemContext1,
  588. IN PVOID SystemContext2
  589. )
  590. {
  591. PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
  592. KIRQL OldIrql;
  593. UNREFERENCED_PARAMETER(Dpc);
  594. UNREFERENCED_PARAMETER(SystemContext1);
  595. UNREFERENCED_PARAMETER(SystemContext2);
  596. IoAcquireCancelSpinLock(&OldIrql);
  597. // make sure no one else grabbed it first, since ISR will
  598. // set this flag to 2 to indicate it is ours to end.
  599. if (Extension->WriteBelongsToIsr == 2)
  600. {
  601. Extension->WriteBelongsToIsr = 0;
  602. SerialTryToCompleteCurrent(
  603. Extension,
  604. NULL,
  605. OldIrql,
  606. STATUS_SUCCESS,
  607. &Extension->CurrentWriteIrp,
  608. &Extension->WriteQueue,
  609. NULL,
  610. &Extension->WriteRequestTotalTimer,
  611. SerialStartWrite,
  612. SerialGetNextWrite,
  613. SERIAL_REF_ISR
  614. );
  615. }
  616. else
  617. {
  618. IoReleaseCancelSpinLock(OldIrql);
  619. }
  620. }
  621. /******************************************************************************
  622. Routine Description:
  623. Try to start off the write by slipping it in behind
  624. a transmit immediate char, or if that isn't available
  625. and the transmit holding register is empty, "tickle"
  626. the UART into interrupting with a transmit buffer
  627. empty.
  628. NOTE: This routine is called by KeSynchronizeExecution.
  629. NOTE: This routine assumes that it is called with the
  630. cancel spin lock held.
  631. Arguments:
  632. Context - Really a pointer to the device extension.
  633. Return Value:
  634. This routine always returns FALSE.
  635. ******************************************************************************/
  636. BOOLEAN
  637. SerialGiveWriteToIsr(
  638. IN PVOID Context
  639. )
  640. {
  641. PSERIAL_DEVICE_EXTENSION Extension = Context;
  642. ULONG wCount;
  643. ULONG room;
  644. ULONG write_cnt;
  645. ULONG OurWriteLength;
  646. // The current stack location. This contains all of the
  647. // information we need to process this particular request.
  648. PIO_STACK_LOCATION IrpSp;
  649. IrpSp = IoGetCurrentIrpStackLocation(Extension->CurrentWriteIrp);
  650. // We might have a xoff counter request masquerading as a
  651. // write. The length of these requests will always be one
  652. // and we can get a pointer to the actual character from
  653. // the data supplied by the user.
  654. if (IrpSp->MajorFunction == IRP_MJ_WRITE)
  655. {
  656. //------ start code addition to avoid tx-lag, see if we can start
  657. // sending data to hardware immediately
  658. OurWriteLength = IrpSp->Parameters.Write.Length;
  659. write_cnt = 0; // use this as a flag as well if we will write() now.
  660. if ((IsListEmpty(&Extension->WriteQueue))) // no queued up output data
  661. {
  662. // the startwrite routine added CurrentWriteIrp which is us,
  663. // so we are the only Write Irp in existence. This is important
  664. // so that isr.c is not trying to serve up off our irp que too.
  665. //// [bug, 3-28-98, kpb] room = sGetTxCnt(Extension->ChP);
  666. #ifdef S_RK
  667. room = (ULONG)(MAXTX_SIZE-sGetTxCnt(Extension->ChP));
  668. #else
  669. room = (ULONG) PortGetTxRoom(Extension->Port);
  670. #endif
  671. if (room > 10) // we have room in the hardware
  672. {
  673. // at this point we have some non-trivial amount of space in
  674. // the hardware buffer to put some data.
  675. write_cnt = IrpSp->Parameters.Write.Length;
  676. }
  677. if (write_cnt) // we are going to write() now
  678. {
  679. #ifdef S_RK
  680. // toggle rts if needed
  681. if (Extension->Option & OPTION_RS485_SOFTWARE_TOGGLE)
  682. {
  683. if ((Extension->DTRRTSStatus & SERIAL_RTS_STATE) == 0)
  684. {
  685. sSetRTS(Extension->ChP);
  686. Extension->DTRRTSStatus |= SERIAL_RTS_STATE;
  687. }
  688. }
  689. // Send as much as possible,WriteTxBlk will chk fifo
  690. wCount = sWriteTxBlk(Extension->ChP,
  691. (PUCHAR)(Extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer),
  692. write_cnt);
  693. #else
  694. // Send it all ,WriteTxBlk will chk fifo
  695. if (write_cnt > room) // limit to what we have space for
  696. wCount = room;
  697. else wCount = write_cnt;
  698. q_put(&Extension->Port->QOut,
  699. (PUCHAR)(Extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer),
  700. wCount);
  701. #endif
  702. Extension->OurStats.TransmittedCount += wCount;
  703. Extension->CurrentWriteIrp->IoStatus.Information += wCount;
  704. // following used to detect fifo empty, semaphore
  705. // which passes control to the ISR routine.
  706. OurWriteLength = (IrpSp->Parameters.Write.Length - wCount);
  707. Extension->ISR_Flags |= TX_NOT_EMPTY;
  708. // and gives this write to ISR
  709. ExtTrace(Extension,D_Ioctl, " , Immed Part Write");
  710. } // write() it out
  711. } // no queue write packets
  712. //------ 1-08-98, end code addition to avoid tx-lag
  713. //add irp to queue, give isr.c the write irp to finish
  714. Extension->WriteLength = OurWriteLength;
  715. if (Extension->port_config->WaitOnTx)
  716. {
  717. // then definitely let ISR finish it, ISR must wait for tx-fifo to drain
  718. Extension->WriteBelongsToIsr = 1;
  719. }
  720. else
  721. {
  722. if (OurWriteLength == 0)
  723. {
  724. // its done, finish it off
  725. Extension->WriteBelongsToIsr = 2;
  726. KeInsertQueueDpc( &Extension->CompleteWriteDpc, NULL, NULL );
  727. }
  728. else
  729. Extension->WriteBelongsToIsr = 1;
  730. }
  731. }
  732. else
  733. {
  734. // !!!!! WHAT is a xoff counter????
  735. // An xoff-counter is something the virtual 16450 uart driver uses
  736. // to send an xoff, it sends an xoff and also starts a timer for
  737. // what purpose I am not sure. Tried some code where it just sends
  738. // an xoff without the timer, this seemed to work ok, but the sent
  739. // xoff should be synced up with the other outgoing data packets in
  740. // the order received from the app.
  741. //It's an xoff counter......
  742. Extension->WriteLength = 1;
  743. Extension->WriteCurrentChar =
  744. ((PUCHAR)Extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer) +
  745. FIELD_OFFSET(SERIAL_XOFF_COUNTER, XoffChar);
  746. }
  747. //endwr:
  748. // The isr now has a reference to the irp.
  749. SERIAL_SET_REFERENCE(
  750. Extension->CurrentWriteIrp,
  751. SERIAL_REF_ISR
  752. );
  753. return FALSE;
  754. }
  755. /****************************************************************************
  756. Routine Description:
  757. This routine is used to cancel the current write.
  758. Arguments:
  759. DeviceObject - Pointer to the device object for this device
  760. Irp - Pointer to the IRP to be canceled.
  761. Return Value:
  762. None.
  763. *****************************************************************************/
  764. VOID
  765. SerialCancelCurrentWrite(
  766. PDEVICE_OBJECT DeviceObject,
  767. PIRP Irp
  768. )
  769. {
  770. PSERIAL_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
  771. ExtTrace(Extension,D_Ioctl, "(cancel cur-write)");
  772. SerialTryToCompleteCurrent(
  773. Extension,
  774. SerialGrabWriteFromIsr,
  775. Irp->CancelIrql,
  776. STATUS_CANCELLED,
  777. &Extension->CurrentWriteIrp,
  778. &Extension->WriteQueue,
  779. NULL,
  780. &Extension->WriteRequestTotalTimer,
  781. SerialStartWrite,
  782. SerialGetNextWrite,
  783. SERIAL_REF_CANCEL
  784. );
  785. }
  786. /***************************************************************************
  787. Routine Description:
  788. This routine will try to timeout the current write.
  789. Arguments:
  790. Dpc - Not Used.
  791. DeferredContext - Really points to the device extension.
  792. SystemContext1 - Not Used.
  793. SystemContext2 - Not Used.
  794. Return Value:
  795. None.
  796. ***************************************************************************/
  797. VOID
  798. SerialWriteTimeout(
  799. IN PKDPC Dpc,
  800. IN PVOID DeferredContext,
  801. IN PVOID SystemContext1,
  802. IN PVOID SystemContext2
  803. )
  804. {
  805. PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
  806. KIRQL OldIrql;
  807. UNREFERENCED_PARAMETER(Dpc);
  808. UNREFERENCED_PARAMETER(SystemContext1);
  809. UNREFERENCED_PARAMETER(SystemContext2);
  810. ExtTrace(Extension,D_Ioctl, "(write-timeout)");
  811. IoAcquireCancelSpinLock(&OldIrql);
  812. SerialTryToCompleteCurrent(
  813. Extension,
  814. SerialGrabWriteFromIsr,
  815. OldIrql,
  816. STATUS_TIMEOUT,
  817. &Extension->CurrentWriteIrp,
  818. &Extension->WriteQueue,
  819. NULL,
  820. &Extension->WriteRequestTotalTimer,
  821. SerialStartWrite,
  822. SerialGetNextWrite,
  823. SERIAL_REF_TOTAL_TIMER
  824. );
  825. }
  826. /***************************************************************************
  827. Routine Description:
  828. This routine is used to grab the current irp, which could be timing
  829. out or canceling, from the ISR
  830. NOTE: This routine is being called from KeSynchronizeExecution.
  831. NOTE: This routine assumes that the cancel spin lock is held
  832. when this routine is called.
  833. Arguments:
  834. Context - Really a pointer to the device extension.
  835. Return Value:
  836. Always false.
  837. ***************************************************************************/
  838. BOOLEAN
  839. SerialGrabWriteFromIsr(
  840. IN PVOID Context
  841. )
  842. {
  843. PSERIAL_DEVICE_EXTENSION Extension = Context;
  844. #ifdef NEW_WAIT
  845. ULONG in_q;
  846. #endif
  847. #ifdef NEW_WAIT
  848. if (Extension->WriteBelongsToIsr != 0)
  849. {
  850. // isr owns irp, or it is has queued dpc to complete it.
  851. // reset this flag to take back from the isr.
  852. Extension->WriteBelongsToIsr = 0;
  853. // We could have an xoff counter masquerading as a
  854. // write irp. If so, don't update the write length.
  855. if (IoGetCurrentIrpStackLocation(Extension->CurrentWriteIrp)
  856. ->MajorFunction != IRP_MJ_WRITE)
  857. {
  858. Extension->CurrentWriteIrp->IoStatus.Information = 0;
  859. }
  860. else
  861. {
  862. SERIAL_CLEAR_REFERENCE(Extension->CurrentWriteIrp,
  863. SERIAL_REF_ISR);
  864. Extension->WriteLength = 0;
  865. if (Extension->port_config->WaitOnTx)
  866. {
  867. // want to report how many characters are "stuck", or did
  868. // not really make it out the port if a timeout occurs.
  869. #ifdef S_RK
  870. in_q = sGetTxCnt(Extension->ChP);
  871. #else
  872. // may have add in box cout too?
  873. in_q = PortGetTxCnt(Extension->Port);
  874. #endif
  875. if (Extension->CurrentWriteIrp->IoStatus.Information >= in_q)
  876. Extension->CurrentWriteIrp->IoStatus.Information -= in_q;
  877. }
  878. }
  879. }
  880. #else
  881. // Check if the write length is non-zero. If it is non-zero
  882. // then the ISR still owns the irp. We calculate the the number
  883. // of characters written and update the information field of the
  884. // irp with the characters written. We then clear the write length
  885. // the isr sees.
  886. if (Extension->WriteLength)
  887. {
  888. // We could have an xoff counter masquerading as a
  889. // write irp. If so, don't update the write length.
  890. if (IoGetCurrentIrpStackLocation(Extension->CurrentWriteIrp)
  891. ->MajorFunction == IRP_MJ_WRITE)
  892. {
  893. Extension->CurrentWriteIrp->IoStatus.Information =
  894. IoGetCurrentIrpStackLocation(
  895. Extension->CurrentWriteIrp
  896. )->Parameters.Write.Length -
  897. Extension->WriteLength;
  898. }
  899. else
  900. {
  901. Extension->CurrentWriteIrp->IoStatus.Information = 0;
  902. }
  903. SERIAL_CLEAR_REFERENCE(Extension->CurrentWriteIrp,
  904. SERIAL_REF_ISR);
  905. Extension->WriteLength = 0;
  906. }
  907. #endif
  908. return FALSE;
  909. }
  910. // Xoff Counter code: UNUSED
  911. /*-----------------------------------------------------------------
  912. SerialGrabXoffFromIsr -
  913. Routine Description:
  914. This routine is used to grab an xoff counter irp from the
  915. isr when it is no longer masquerading as a write irp. This
  916. routine is called by the cancel and timeout code for the
  917. xoff counter ioctl.
  918. NOTE: This routine is being called from KeSynchronizeExecution.
  919. NOTE: This routine assumes that the cancel spin lock is held
  920. when this routine is called.
  921. Arguments:
  922. Context - Really a pointer to the device extension.
  923. Return Value:
  924. Always false.
  925. |-----------------------------------------------------------------*/
  926. BOOLEAN
  927. SerialGrabXoffFromIsr(
  928. IN PVOID Context
  929. )
  930. {
  931. PSERIAL_DEVICE_EXTENSION Extension = Context;
  932. if (Extension->CountSinceXoff) {
  933. //
  934. // This is only non-zero when there actually is a Xoff ioctl
  935. // counting down.
  936. //
  937. Extension->CountSinceXoff = 0;
  938. //
  939. // We decrement the count since the isr no longer owns
  940. // the irp.
  941. //
  942. SERIAL_CLEAR_REFERENCE(
  943. Extension->CurrentXoffIrp,
  944. SERIAL_REF_ISR
  945. );
  946. }
  947. return FALSE;
  948. }
  949. /******************************************************************************
  950. Routine Description:
  951. This routine is merely used to truely complete an xoff counter irp. It
  952. assumes that the status and the information fields of the irp are
  953. already correctly filled in.
  954. Arguments:
  955. Dpc - Not Used.
  956. DeferredContext - Really points to the device extension.
  957. SystemContext1 - Not Used.
  958. SystemContext2 - Not Used.
  959. Return Value:
  960. None.
  961. ******************************************************************************/
  962. VOID
  963. SerialCompleteXoff(
  964. IN PKDPC Dpc,
  965. IN PVOID DeferredContext,
  966. IN PVOID SystemContext1,
  967. IN PVOID SystemContext2
  968. )
  969. {
  970. PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
  971. KIRQL OldIrql;
  972. UNREFERENCED_PARAMETER(Dpc);
  973. UNREFERENCED_PARAMETER(SystemContext1);
  974. UNREFERENCED_PARAMETER(SystemContext2);
  975. IoAcquireCancelSpinLock(&OldIrql);
  976. SerialTryToCompleteCurrent(
  977. Extension,
  978. NULL,
  979. OldIrql,
  980. STATUS_SUCCESS,
  981. &Extension->CurrentXoffIrp,
  982. NULL,
  983. NULL,
  984. &Extension->XoffCountTimer,
  985. NULL,
  986. NULL,
  987. SERIAL_REF_ISR
  988. );
  989. }
  990. /*------------------------------------------------------------------
  991. Routine Description:
  992. This routine is merely used to truely complete an xoff counter irp,
  993. if its timer has run out.
  994. Arguments:
  995. Dpc - Not Used.
  996. DeferredContext - Really points to the device extension.
  997. SystemContext1 - Not Used.
  998. SystemContext2 - Not Used.
  999. Return Value:
  1000. None.
  1001. -------------------------------------------------------------------*/
  1002. VOID
  1003. SerialTimeoutXoff(
  1004. IN PKDPC Dpc,
  1005. IN PVOID DeferredContext,
  1006. IN PVOID SystemContext1,
  1007. IN PVOID SystemContext2
  1008. )
  1009. {
  1010. PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
  1011. KIRQL OldIrql;
  1012. UNREFERENCED_PARAMETER(Dpc);
  1013. UNREFERENCED_PARAMETER(SystemContext1);
  1014. UNREFERENCED_PARAMETER(SystemContext2);
  1015. IoAcquireCancelSpinLock(&OldIrql);
  1016. SerialTryToCompleteCurrent(
  1017. Extension,
  1018. SerialGrabXoffFromIsr,
  1019. OldIrql,
  1020. STATUS_SERIAL_COUNTER_TIMEOUT,
  1021. &Extension->CurrentXoffIrp,
  1022. NULL,
  1023. NULL,
  1024. NULL,
  1025. NULL,
  1026. NULL,
  1027. SERIAL_REF_TOTAL_TIMER
  1028. );
  1029. }
  1030. /*---------------------------------------------------------------
  1031. Routine Description:
  1032. This routine is used to cancel the current write.
  1033. Arguments:
  1034. DeviceObject - Pointer to the device object for this device
  1035. Irp - Pointer to the IRP to be canceled.
  1036. Return Value:
  1037. None.
  1038. ----------------------------------------------------------------*/
  1039. VOID
  1040. SerialCancelCurrentXoff(
  1041. PDEVICE_OBJECT DeviceObject,
  1042. PIRP Irp
  1043. )
  1044. {
  1045. PSERIAL_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
  1046. SerialTryToCompleteCurrent(
  1047. Extension,
  1048. SerialGrabXoffFromIsr,
  1049. Irp->CancelIrql,
  1050. STATUS_CANCELLED,
  1051. &Extension->CurrentXoffIrp,
  1052. NULL,
  1053. NULL,
  1054. &Extension->XoffCountTimer,
  1055. NULL,
  1056. NULL,
  1057. SERIAL_REF_CANCEL
  1058. );
  1059. }
  1060. /*------------------------------------------------------------------------
  1061. Routine Description:
  1062. This routine starts off the xoff counter. It merely
  1063. has to set the xoff count and increment the reference
  1064. count to denote that the isr has a reference to the irp.
  1065. NOTE: This routine is called by KeSynchronizeExecution.
  1066. NOTE: This routine assumes that it is called with the
  1067. cancel spin lock held.
  1068. Arguments:
  1069. Context - Really a pointer to the device extension.
  1070. Return Value:
  1071. This routine always returns FALSE.
  1072. ------------------------------------------------------------------------*/
  1073. BOOLEAN
  1074. SerialGiveXoffToIsr(
  1075. IN PVOID Context
  1076. )
  1077. {
  1078. PSERIAL_DEVICE_EXTENSION Extension = Context;
  1079. //
  1080. // The current stack location. This contains all of the
  1081. // information we need to process this particular request.
  1082. //
  1083. PSERIAL_XOFF_COUNTER Xc =
  1084. Extension->CurrentXoffIrp->AssociatedIrp.SystemBuffer;
  1085. Extension->CountSinceXoff = Xc->Counter;
  1086. // The isr now has a reference to the irp.
  1087. SERIAL_SET_REFERENCE(
  1088. Extension->CurrentXoffIrp,
  1089. SERIAL_REF_ISR
  1090. );
  1091. return FALSE;
  1092. }