|
|
1.1 root 1: /*++
2:
3: Copyright (c) 1991, 1992, 1993 Microsoft Corporation
4:
5: Module Name:
6:
7: utils.c
8:
9: Abstract:
10:
11: This module contains code that perform queueing and completion
12: manipulation on requests.
13:
14: Author:
15:
16: Anthony V. Ercolano 26-Sep-1991
17:
18: Environment:
19:
20: Kernel mode
21:
22: Revision History :
23:
24: --*/
25:
26: #include <stddef.h>
27: #include "ntddk.h"
28: #include "ntddser.h"
29: #include "serial.h"
30: #include "serialp.h"
31:
32: VOID
33: SerialRundownIrpRefs(
34: IN PIRP *CurrentOpIrp,
35: IN PKTIMER IntervalTimer,
36: IN PKTIMER TotalTimer
37: );
38:
39:
40: VOID
41: SerialKillAllReadsOrWrites(
42: IN PDEVICE_OBJECT DeviceObject,
43: IN PLIST_ENTRY QueueToClean,
44: IN PIRP *CurrentOpIrp
45: )
46:
47: /*++
48:
49: Routine Description:
50:
51: This function is used to cancel all queued and the current irps
52: for reads or for writes.
53:
54: Arguments:
55:
56: DeviceObject - A pointer to the serial device object.
57:
58: QueueToClean - A pointer to the queue which we're going to clean out.
59:
60: CurrentOpIrp - Pointer to a pointer to the current irp.
61:
62: Return Value:
63:
64: None.
65:
66: --*/
67:
68: {
69:
70: KIRQL cancelIrql;
71: PDRIVER_CANCEL cancelRoutine;
72:
73: //
74: // We acquire the cancel spin lock. This will prevent the
75: // irps from moving around.
76: //
77:
78: IoAcquireCancelSpinLock(&cancelIrql);
79:
80: //
81: // Clean the list from back to front.
82: //
83:
84: while (!IsListEmpty(QueueToClean)) {
85:
86: PIRP currentLastIrp = CONTAINING_RECORD(
87: QueueToClean->Blink,
88: IRP,
89: Tail.Overlay.ListEntry
90: );
91:
92: RemoveEntryList(QueueToClean->Blink);
93:
94: cancelRoutine = currentLastIrp->CancelRoutine;
95: currentLastIrp->CancelIrql = cancelIrql;
96: currentLastIrp->CancelRoutine = NULL;
97: currentLastIrp->Cancel = TRUE;
98:
99: cancelRoutine(
100: DeviceObject,
101: currentLastIrp
102: );
103:
104: IoAcquireCancelSpinLock(&cancelIrql);
105:
106: }
107:
108: //
109: // The queue is clean. Now go after the current if
110: // it's there.
111: //
112:
113: if (*CurrentOpIrp) {
114:
115:
116: cancelRoutine = (*CurrentOpIrp)->CancelRoutine;
117: (*CurrentOpIrp)->Cancel = TRUE;
118:
119: //
120: // If the current irp is not in a cancelable state
121: // then it *will* try to enter one and the above
122: // assignment will kill it. If it already is in
123: // a cancelable state then the following will kill it.
124: //
125:
126: if (cancelRoutine) {
127:
128: (*CurrentOpIrp)->CancelRoutine = NULL;
129: (*CurrentOpIrp)->CancelIrql = cancelIrql;
130:
131: //
132: // This irp is already in a cancelable state. We simply
133: // mark it as canceled and call the cancel routine for
134: // it.
135: //
136:
137: cancelRoutine(
138: DeviceObject,
139: *CurrentOpIrp
140: );
141:
142: } else {
143:
144: IoReleaseCancelSpinLock(cancelIrql);
145:
146: }
147:
148: } else {
149:
150: IoReleaseCancelSpinLock(cancelIrql);
151:
152: }
153:
154: }
155:
156: VOID
157: SerialGetNextIrp(
158: IN PIRP *CurrentOpIrp,
159: IN PLIST_ENTRY QueueToProcess,
160: OUT PIRP *NextIrp,
161: IN BOOLEAN CompleteCurrent
162: )
163:
164: /*++
165:
166: Routine Description:
167:
168: This function is used to make the head of the particular
169: queue the current irp. It also completes the what
170: was the old current irp if desired.
171:
172: Arguments:
173:
174: CurrentOpIrp - Pointer to a pointer to the currently active
175: irp for the particular work list. Note that
176: this item is not actually part of the list.
177:
178: QueueToProcess - The list to pull the new item off of.
179:
180: NextIrp - The next Irp to process. Note that CurrentOpIrp
181: will be set to this value under protection of the
182: cancel spin lock. However, if *NextIrp is NULL when
183: this routine returns, it is not necessaryly true the
184: what is pointed to by CurrentOpIrp will also be NULL.
185: The reason for this is that if the queue is empty
186: when we hold the cancel spin lock, a new irp may come
187: in immediately after we release the lock.
188:
189: CompleteCurrent - If TRUE then this routine will complete the
190: irp pointed to by the pointer argument
191: CurrentOpIrp.
192:
193: Return Value:
194:
195: None.
196:
197: --*/
198:
199: {
200:
201: PIRP oldIrp;
202: KIRQL oldIrql;
203:
204: IoAcquireCancelSpinLock(&oldIrql);
205:
206: oldIrp = *CurrentOpIrp;
207:
208: if (CompleteCurrent) {
209:
210: ASSERT(!oldIrp->CancelRoutine);
211:
212: }
213:
214: //
215: // Check to see if there is a new irp to start up.
216: //
217:
218: if (!IsListEmpty(QueueToProcess)) {
219:
220: PLIST_ENTRY headOfList;
221:
222: headOfList = RemoveHeadList(QueueToProcess);
223:
224: *CurrentOpIrp = CONTAINING_RECORD(
225: headOfList,
226: IRP,
227: Tail.Overlay.ListEntry
228: );
229:
230: IoSetCancelRoutine(
231: *CurrentOpIrp,
232: NULL
233: );
234:
235: } else {
236:
237: *CurrentOpIrp = NULL;
238:
239: }
240:
241: *NextIrp = *CurrentOpIrp;
242: IoReleaseCancelSpinLock(oldIrql);
243:
244: if (CompleteCurrent) {
245:
246: SerialDump(
247: SERIRPPATH,
248: ("SERIAL: Complete Irp: %x\n",oldIrp)
249: );
250: IoCompleteRequest(
251: oldIrp,
252: IO_SERIAL_INCREMENT
253: );
254:
255: }
256:
257: }
258:
259: VOID
260: SerialTryToCompleteCurrent(
261: IN PSERIAL_DEVICE_EXTENSION Extension,
262: IN PKSYNCHRONIZE_ROUTINE SynchRoutine OPTIONAL,
263: IN KIRQL IrqlForRelease,
264: IN NTSTATUS StatusToUse,
265: IN PIRP *CurrentOpIrp,
266: IN PLIST_ENTRY QueueToProcess OPTIONAL,
267: IN PKTIMER IntervalTimer OPTIONAL,
268: IN PKTIMER TotalTimer OPTIONAL,
269: IN PSERIAL_START_ROUTINE Starter OPTIONAL,
270: IN PSERIAL_GET_NEXT_ROUTINE GetNextIrp OPTIONAL
271: )
272:
273: /*++
274:
275: Routine Description:
276:
277: This routine attempts to kill all of the reasons there are
278: references on the current read/write. If everything can be killed
279: it will complete this read/write and try to start another.
280:
281: NOTE: This routine assumes that it is called with the cancel
282: spinlock held.
283:
284: Arguments:
285:
286: Extension - Simply a pointer to the device extension.
287:
288: SynchRoutine - A routine that will synchronize with the isr
289: and attempt to remove the knowledge of the
290: current irp from the isr. NOTE: This pointer
291: can be null.
292:
293: IrqlForRelease - This routine is called with the cancel spinlock held.
294: This is the irql that was current when the cancel
295: spinlock was acquired.
296:
297: StatusToUse - The irp's status field will be set to this value, if
298: this routine can complete the irp.
299:
300:
301: Return Value:
302:
303: None.
304:
305: --*/
306:
307: {
308:
309: //
310: // We can decrement the reference to "remove" the fact
311: // that the caller no longer will be accessing this irp.
312: //
313:
314: SERIAL_DEC_REFERENCE(*CurrentOpIrp);
315:
316: if (SynchRoutine) {
317:
318: KeSynchronizeExecution(
319: Extension->Interrupt,
320: SynchRoutine,
321: Extension
322: );
323:
324: }
325:
326: //
327: // Try to run down all other references to this irp.
328: //
329:
330: SerialRundownIrpRefs(
331: CurrentOpIrp,
332: IntervalTimer,
333: TotalTimer
334: );
335:
336: //
337: // See if the ref count is zero after trying to kill everybody else.
338: //
339:
340: if (!SERIAL_REFERENCE_COUNT(*CurrentOpIrp)) {
341:
342: PIRP newIrp;
343:
344:
345: //
346: // The ref count was zero so we should complete this
347: // request.
348: //
349: // The following call will also cause the current irp to be
350: // completed.
351: //
352:
353: (*CurrentOpIrp)->IoStatus.Status = StatusToUse;
354:
355: if (StatusToUse == STATUS_CANCELLED) {
356:
357: (*CurrentOpIrp)->IoStatus.Information = 0;
358:
359: }
360:
361: if (GetNextIrp) {
362:
363: IoReleaseCancelSpinLock(IrqlForRelease);
364: GetNextIrp(
365: CurrentOpIrp,
366: QueueToProcess,
367: &newIrp,
368: TRUE
369: );
370:
371: if (newIrp) {
372:
373: Starter(Extension);
374:
375: }
376:
377: } else {
378:
379: PIRP oldIrp = *CurrentOpIrp;
380:
381: //
382: // There was no get next routine. We will simply complete
383: // the irp. We should make sure that we null out the
384: // pointer to the pointer to this irp.
385: //
386:
387: *CurrentOpIrp = NULL;
388:
389: IoReleaseCancelSpinLock(IrqlForRelease);
390: SerialDump(
391: SERIRPPATH,
392: ("SERIAL: Complete Irp: %x\n",oldIrp)
393: );
394: IoCompleteRequest(
395: oldIrp,
396: IO_SERIAL_INCREMENT
397: );
398:
399: }
400:
401: } else {
402:
403: IoReleaseCancelSpinLock(IrqlForRelease);
404:
405: }
406:
407: }
408:
409: VOID
410: SerialRundownIrpRefs(
411: IN PIRP *CurrentOpIrp,
412: IN PKTIMER IntervalTimer OPTIONAL,
413: IN PKTIMER TotalTimer OPTIONAL
414: )
415:
416: /*++
417:
418: Routine Description:
419:
420: This routine runs through the various items that *could*
421: have a reference to the current read/write. It try's to kill
422: the reason. If it does succeed in killing the reason it
423: will decrement the reference count on the irp.
424:
425: NOTE: This routine assumes that it is called with the cancel
426: spin lock held.
427:
428: Arguments:
429:
430: CurrentOpIrp - Pointer to a pointer to current irp for the
431: particular operation.
432:
433: IntervalTimer - Pointer to the interval timer for the operation.
434: NOTE: This could be null.
435:
436: TotalTimer - Pointer to the total timer for the operation.
437: NOTE: This could be null.
438:
439: Return Value:
440:
441: None.
442:
443: --*/
444:
445:
446: {
447:
448: //
449: // This routine is called with the cancel spin lock held
450: // so we know only one thread of execution can be in here
451: // at one time.
452: //
453:
454: //
455: // First we see if there is still a cancel routine. If
456: // so then we can decrement the count by one.
457: //
458:
459: if ((*CurrentOpIrp)->CancelRoutine) {
460:
461: SERIAL_DEC_REFERENCE(*CurrentOpIrp);
462:
463: IoSetCancelRoutine(
464: *CurrentOpIrp,
465: NULL
466: );
467:
468: }
469:
470: if (IntervalTimer) {
471:
472: //
473: // Try to cancel the operations interval timer. If the operation
474: // returns true then the timer did have a reference to the
475: // irp. Since we've canceled this timer that reference is
476: // no longer valid and we can decrement the reference count.
477: //
478: // If the cancel returns false then this means either of two things:
479: //
480: // a) The timer has already fired.
481: //
482: // b) There never was an interval timer.
483: //
484: // In the case of "b" there is no need to decrement the reference
485: // count since the "timer" never had a reference to it.
486: //
487: // In the case of "a", then the timer itself will be coming
488: // along and decrement it's reference. Note that the caller
489: // of this routine might actually be the this timer, but it
490: // has already decremented the reference.
491: //
492:
493: if (KeCancelTimer(IntervalTimer)) {
494:
495: SERIAL_DEC_REFERENCE(*CurrentOpIrp);
496:
497: }
498:
499: }
500:
501: if (TotalTimer) {
502:
503: //
504: // Try to cancel the operations total timer. If the operation
505: // returns true then the timer did have a reference to the
506: // irp. Since we've canceled this timer that reference is
507: // no longer valid and we can decrement the reference count.
508: //
509: // If the cancel returns false then this means either of two things:
510: //
511: // a) The timer has already fired.
512: //
513: // b) There never was an total timer.
514: //
515: // In the case of "b" there is no need to decrement the reference
516: // count since the "timer" never had a reference to it.
517: //
518: // In the case of "a", then the timer itself will be coming
519: // along and decrement it's reference. Note that the caller
520: // of this routine might actually be the this timer, but it
521: // has already decremented the reference.
522: //
523:
524: if (KeCancelTimer(TotalTimer)) {
525:
526: SERIAL_DEC_REFERENCE(*CurrentOpIrp);
527:
528: }
529:
530: }
531:
532: }
533:
534: NTSTATUS
535: SerialStartOrQueue(
536: IN PSERIAL_DEVICE_EXTENSION Extension,
537: IN PIRP Irp,
538: IN PLIST_ENTRY QueueToExamine,
539: IN PIRP *CurrentOpIrp,
540: IN PSERIAL_START_ROUTINE Starter
541: )
542:
543: /*++
544:
545: Routine Description:
546:
547: This routine is used to either start or queue any requst
548: that can be queued in the driver.
549:
550: Arguments:
551:
552: Extension - Points to the serial device extension.
553:
554: Irp - The irp to either queue or start. In either
555: case the irp will be marked pending.
556:
557: QueueToExamine - The queue the irp will be place on if there
558: is already an operation in progress.
559:
560: CurrentOpIrp - Pointer to a pointer to the irp the is current
561: for the queue. The pointer pointed to will be
562: set with to Irp if what CurrentOpIrp points to
563: is NULL.
564:
565: Starter - The routine to call if the queue is empty.
566:
567: Return Value:
568:
569: This routine will return STATUS_PENDING if the queue is
570: not empty. Otherwise, it will return the status returned
571: from the starter routine (or cancel, if the cancel bit is
572: on in the irp).
573:
574:
575: --*/
576:
577: {
578:
579: KIRQL oldIrql;
580:
581: IoAcquireCancelSpinLock(&oldIrql);
582:
583: //
584: // If this is a write irp then take the amount of characters
585: // to write and add it to the count of characters to write.
586: //
587:
588: if (IoGetCurrentIrpStackLocation(Irp)->MajorFunction
589: == IRP_MJ_WRITE) {
590:
591: Extension->TotalCharsQueued +=
592: IoGetCurrentIrpStackLocation(Irp)
593: ->Parameters.Write.Length;
594:
595: } else if ((IoGetCurrentIrpStackLocation(Irp)->MajorFunction
596: == IRP_MJ_DEVICE_CONTROL) &&
597: ((IoGetCurrentIrpStackLocation(Irp)
598: ->Parameters.DeviceIoControl.IoControlCode ==
599: IOCTL_SERIAL_IMMEDIATE_CHAR) ||
600: (IoGetCurrentIrpStackLocation(Irp)
601: ->Parameters.DeviceIoControl.IoControlCode ==
602: IOCTL_SERIAL_XOFF_COUNTER))) {
603:
604: Extension->TotalCharsQueued++;
605:
606: }
607:
608: if ((IsListEmpty(QueueToExamine)) &&
609: !(*CurrentOpIrp)) {
610:
611: //
612: // There were no current operation. Mark this one as
613: // current and start it up.
614: //
615:
616: *CurrentOpIrp = Irp;
617:
618: IoReleaseCancelSpinLock(oldIrql);
619:
620: return Starter(Extension);
621:
622: } else {
623:
624: //
625: // We don't know how long the irp will be in the
626: // queue. So we need to handle cancel.
627: //
628:
629: if (Irp->Cancel) {
630:
631: IoReleaseCancelSpinLock(oldIrql);
632:
633: Irp->IoStatus.Status = STATUS_CANCELLED;
634:
635: SerialDump(
636: SERIRPPATH,
637: ("SERIAL: Complete Irp: %x\n",Irp)
638: );
639: IoCompleteRequest(
640: Irp,
641: 0
642: );
643:
644: return STATUS_CANCELLED;
645:
646: } else {
647:
648:
649: Irp->IoStatus.Status = STATUS_PENDING;
650: IoMarkIrpPending(Irp);
651:
652: InsertTailList(
653: QueueToExamine,
654: &Irp->Tail.Overlay.ListEntry
655: );
656:
657: IoSetCancelRoutine(
658: Irp,
659: SerialCancelQueued
660: );
661:
662: IoReleaseCancelSpinLock(oldIrql);
663:
664: return STATUS_PENDING;
665:
666: }
667:
668: }
669:
670: }
671:
672: VOID
673: SerialCancelQueued(
674: PDEVICE_OBJECT DeviceObject,
675: PIRP Irp
676: )
677:
678: /*++
679:
680: Routine Description:
681:
682: This routine is used to cancel Irps that currently reside on
683: a queue.
684:
685: Arguments:
686:
687: DeviceObject - Pointer to the device object for this device
688:
689: Irp - Pointer to the IRP to be canceled.
690:
691: Return Value:
692:
693: None.
694:
695: --*/
696:
697: {
698:
699: PSERIAL_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
700: PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
701:
702: Irp->IoStatus.Status = STATUS_CANCELLED;
703: Irp->IoStatus.Information = 0;
704:
705: RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
706:
707: //
708: // If this is a write irp then take the amount of characters
709: // to write and subtract it from the count of characters to write.
710: //
711:
712: if (irpSp->MajorFunction == IRP_MJ_WRITE) {
713:
714: extension->TotalCharsQueued -= irpSp->Parameters.Write.Length;
715:
716: } else if (irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
717:
718: //
719: // If it's an immediate then we need to decrement the
720: // count of chars queued. If it's a resize then we
721: // need to deallocate the pool that we're passing on
722: // to the "resizing" routine.
723: //
724:
725: if ((irpSp->Parameters.DeviceIoControl.IoControlCode ==
726: IOCTL_SERIAL_IMMEDIATE_CHAR) ||
727: (irpSp->Parameters.DeviceIoControl.IoControlCode ==
728: IOCTL_SERIAL_XOFF_COUNTER)) {
729:
730: extension->TotalCharsQueued--;
731:
732: } else if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
733: IOCTL_SERIAL_SET_QUEUE_SIZE) {
734:
735: //
736: // We shoved the pointer to the memory into the
737: // the type 3 buffer pointer which we KNOW we
738: // never use.
739: //
740:
741: ASSERT(irpSp->Parameters.DeviceIoControl.Type3InputBuffer);
742:
743: ExFreePool(irpSp->Parameters.DeviceIoControl.Type3InputBuffer);
744:
745: irpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
746:
747: }
748:
749: }
750:
751: IoReleaseCancelSpinLock(Irp->CancelIrql);
752:
753: SerialDump(
754: SERIRPPATH,
755: ("SERIAL: Complete Irp: %x\n",Irp)
756: );
757: IoCompleteRequest(
758: Irp,
759: IO_SERIAL_INCREMENT
760: );
761:
762: }
763:
764: NTSTATUS
765: SerialCompleteIfError(
766: PDEVICE_OBJECT DeviceObject,
767: PIRP Irp
768: )
769:
770: /*++
771:
772: Routine Description:
773:
774: If the current irp is not an IOCTL_SERIAL_GET_COMMSTATUS request and
775: there is an error and the application requested abort on errors,
776: then cancel the irp.
777:
778: Arguments:
779:
780: DeviceObject - Pointer to the device object for this device
781:
782: Irp - Pointer to the IRP to test.
783:
784: Return Value:
785:
786: STATUS_SUCCESS or STATUS_CANCELLED.
787:
788: --*/
789:
790: {
791:
792: PSERIAL_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
793:
794: NTSTATUS status = STATUS_SUCCESS;
795:
796: if ((extension->HandFlow.ControlHandShake &
797: SERIAL_ERROR_ABORT) && extension->ErrorWord) {
798:
799: PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
800:
801: //
802: // There is a current error in the driver. No requests should
803: // come through except for the GET_COMMSTATUS.
804: //
805:
806: if ((irpSp->MajorFunction != IRP_MJ_DEVICE_CONTROL) ||
807: (irpSp->Parameters.DeviceIoControl.IoControlCode !=
808: IOCTL_SERIAL_GET_COMMSTATUS)) {
809:
810: status = STATUS_CANCELLED;
811: Irp->IoStatus.Status = STATUS_CANCELLED;
812: Irp->IoStatus.Information = 0;
813:
814: SerialDump(
815: SERIRPPATH,
816: ("SERIAL: Complete Irp: %x\n",Irp)
817: );
818: IoCompleteRequest(
819: Irp,
820: 0
821: );
822:
823: }
824:
825: }
826:
827: return status;
828:
829: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.