Annotation of ntddk/src/comm/serial/read.c, revision 1.1.1.1

1.1       root        1: /*++
                      2: 
                      3: Copyright (c) 1991, 1992, 1993 Microsoft Corporation
                      4: 
                      5: Module Name:
                      6: 
                      7:     read.c
                      8: 
                      9: Abstract:
                     10: 
                     11:     This module contains the code that is very specific to read
                     12:     operations in the serial driver
                     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: 
                     33: VOID
                     34: SerialCancelCurrentRead(
                     35:     PDEVICE_OBJECT DeviceObject,
                     36:     PIRP Irp
                     37:     );
                     38: 
                     39: BOOLEAN
                     40: SerialGrabReadFromIsr(
                     41:     IN PVOID Context
                     42:     );
                     43: 
                     44: BOOLEAN
                     45: SerialUpdateReadByIsr(
                     46:     IN PVOID Context
                     47:     );
                     48: 
                     49: ULONG
                     50: SerialGetCharsFromIntBuffer(
                     51:     PSERIAL_DEVICE_EXTENSION Extension
                     52:     );
                     53: 
                     54: BOOLEAN
                     55: SerialUpdateInterruptBuffer(
                     56:     IN PVOID Context
                     57:     );
                     58: 
                     59: BOOLEAN
                     60: SerialUpdateAndSwitchToUser(
                     61:     IN PVOID Context
                     62:     );
                     63: 
                     64: NTSTATUS
                     65: SerialResizeBuffer(
                     66:     IN PSERIAL_DEVICE_EXTENSION Extension
                     67:     );
                     68: 
                     69: ULONG
                     70: SerialMoveToNewIntBuffer(
                     71:     PSERIAL_DEVICE_EXTENSION Extension,
                     72:     PUCHAR NewBuffer
                     73:     );
                     74: 
                     75: BOOLEAN
                     76: SerialUpdateAndSwitchToNew(
                     77:     IN PVOID Context
                     78:     );
                     79: 
                     80: 
                     81: NTSTATUS
                     82: SerialRead(
                     83:     IN PDEVICE_OBJECT DeviceObject,
                     84:     IN PIRP Irp
                     85:     )
                     86: 
                     87: /*++
                     88: 
                     89: Routine Description:
                     90: 
                     91:     This is the dispatch routine for reading.  It validates the parameters
                     92:     for the read request and if all is ok then it places the request
                     93:     on the work queue.
                     94: 
                     95: Arguments:
                     96: 
                     97:     DeviceObject - Pointer to the device object for this device
                     98: 
                     99:     Irp - Pointer to the IRP for the current request
                    100: 
                    101: Return Value:
                    102: 
                    103:     If the io is zero length then it will return STATUS_SUCCESS,
                    104:     otherwise this routine will return the status returned by
                    105:     the actual start read routine.
                    106: 
                    107: --*/
                    108: 
                    109: {
                    110: 
                    111:     PSERIAL_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
                    112: 
                    113:     SerialDump(
                    114:         SERIRPPATH,
                    115:         ("SERIAL: Dispatch entry for: %x\n",Irp)
                    116:         );
                    117:     if (SerialCompleteIfError(
                    118:             DeviceObject,
                    119:             Irp
                    120:             ) != STATUS_SUCCESS) {
                    121: 
                    122:         return STATUS_CANCELLED;
                    123: 
                    124:     }
                    125: 
                    126:     Irp->IoStatus.Information = 0L;
                    127: 
                    128:     //
                    129:     // Quick check for a zero length read.  If it is zero length
                    130:     // then we are already done!
                    131:     //
                    132: 
                    133:     if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length) {
                    134: 
                    135:         //
                    136:         // Well it looks like we actually have to do some
                    137:         // work.  Put the read on the queue so that we can
                    138:         // process it when our previous reads are done.
                    139:         //
                    140: 
                    141:         return SerialStartOrQueue(
                    142:                    extension,
                    143:                    Irp,
                    144:                    &extension->ReadQueue,
                    145:                    &extension->CurrentReadIrp,
                    146:                    SerialStartRead
                    147:                    );
                    148: 
                    149:     } else {
                    150: 
                    151:         Irp->IoStatus.Status = STATUS_SUCCESS;
                    152:         SerialDump(
                    153:             SERIRPPATH,
                    154:             ("SERIAL: Complete Irp: %x\n",Irp)
                    155:             );
                    156:         IoCompleteRequest(
                    157:             Irp,
                    158:             0
                    159:             );
                    160: 
                    161:         return STATUS_SUCCESS;
                    162: 
                    163:     }
                    164: 
                    165: }
                    166: 
                    167: NTSTATUS
                    168: SerialStartRead(
                    169:     IN PSERIAL_DEVICE_EXTENSION Extension
                    170:     )
                    171: 
                    172: /*++
                    173: 
                    174: Routine Description:
                    175: 
                    176:     This routine is used to start off any read.  It initializes
                    177:     the Iostatus fields of the irp.  It will set up any timers
                    178:     that are used to control the read.  It will attempt to complete
                    179:     the read from data already in the interrupt buffer.  If the
                    180:     read can be completed quickly it will start off another if
                    181:     necessary.
                    182: 
                    183: Arguments:
                    184: 
                    185:     Extension - Simply a pointer to the serial device extension.
                    186: 
                    187: Return Value:
                    188: 
                    189:     This routine will return the status of the first read
                    190:     irp.  This is useful in that if we have a read that can
                    191:     complete right away (AND there had been nothing in the
                    192:     queue before it) the read could return SUCCESS and the
                    193:     application won't have to do a wait.
                    194: 
                    195: --*/
                    196: 
                    197: {
                    198: 
                    199:     SERIAL_UPDATE_CHAR updateChar;
                    200: 
                    201:     PIRP newIrp;
                    202:     KIRQL oldIrql;
                    203:     KIRQL controlIrql;
                    204: 
                    205:     BOOLEAN returnWithWhatsPresent;
                    206:     BOOLEAN os2ssreturn;
                    207:     BOOLEAN crunchDownToOne;
                    208:     BOOLEAN useTotalTimer;
                    209:     BOOLEAN useIntervalTimer;
                    210: 
                    211:     ULONG multiplierVal;
                    212:     ULONG constantVal;
                    213: 
                    214:     LARGE_INTEGER totalTime;
                    215: 
                    216:     SERIAL_TIMEOUTS timeoutsForIrp;
                    217: 
                    218:     BOOLEAN setFirstStatus = FALSE;
                    219:     NTSTATUS firstStatus;
                    220: 
                    221: 
                    222:     updateChar.Extension = Extension;
                    223: 
                    224:     do {
                    225: 
                    226:         //
                    227:         // Check to see if this is a resize request.  If it is
                    228:         // then go to a routine that specializes in that.
                    229:         //
                    230: 
                    231:         if (IoGetCurrentIrpStackLocation(Extension->CurrentReadIrp)
                    232:             ->MajorFunction != IRP_MJ_READ) {
                    233: 
                    234:             NTSTATUS localStatus = SerialResizeBuffer(Extension);
                    235: 
                    236:             if (!setFirstStatus) {
                    237: 
                    238:                 firstStatus = localStatus;
                    239:                 setFirstStatus = TRUE;
                    240: 
                    241:             }
                    242: 
                    243:         } else {
                    244: 
                    245:             Extension->NumberNeededForRead =
                    246:                 IoGetCurrentIrpStackLocation(Extension->CurrentReadIrp)
                    247:                     ->Parameters.Read.Length;
                    248: 
                    249:             //
                    250:             // Calculate the timeout value needed for the
                    251:             // request.  Note that the values stored in the
                    252:             // timeout record are in milliseconds.
                    253:             //
                    254: 
                    255:             useTotalTimer = FALSE;
                    256:             returnWithWhatsPresent = FALSE;
                    257:             os2ssreturn = FALSE;
                    258:             crunchDownToOne = FALSE;
                    259:             useIntervalTimer = FALSE;
                    260: 
                    261: 
                    262:             //
                    263:             // Always initialize the timer objects so that the
                    264:             // completion code can tell when it attempts to
                    265:             // cancel the timers whether the timers had ever
                    266:             // been Set.
                    267:             //
                    268: 
                    269:             KeInitializeTimer(&Extension->ReadRequestTotalTimer);
                    270:             KeInitializeTimer(&Extension->ReadRequestIntervalTimer);
                    271: 
                    272:             //
                    273:             // We get the *current* timeout values to use for timing
                    274:             // this read.
                    275:             //
                    276: 
                    277:             KeAcquireSpinLock(
                    278:                 &Extension->ControlLock,
                    279:                 &controlIrql
                    280:                 );
                    281: 
                    282:             timeoutsForIrp = Extension->Timeouts;
                    283: 
                    284:             KeReleaseSpinLock(
                    285:                 &Extension->ControlLock,
                    286:                 controlIrql
                    287:                 );
                    288: 
                    289:             //
                    290:             // Calculate the interval timeout for the read.
                    291:             //
                    292: 
                    293:             if (timeoutsForIrp.ReadIntervalTimeout &&
                    294:                 (timeoutsForIrp.ReadIntervalTimeout !=
                    295:                  MAXULONG)) {
                    296: 
                    297:                 useIntervalTimer = TRUE;
                    298: 
                    299:                 Extension->IntervalTime = RtlEnlargedUnsignedMultiply(
                    300:                                              timeoutsForIrp.ReadIntervalTimeout,
                    301:                                              10000
                    302:                                              );
                    303: 
                    304: 
                    305:                 if (RtlLargeIntegerGreaterThanOrEqualTo(
                    306:                         Extension->IntervalTime,
                    307:                         Extension->CutOverAmount
                    308:                         )) {
                    309: 
                    310:                     Extension->IntervalTimeToUse =
                    311:                         &Extension->LongIntervalAmount;
                    312: 
                    313:                 } else {
                    314: 
                    315:                     Extension->IntervalTimeToUse =
                    316:                         &Extension->ShortIntervalAmount;
                    317: 
                    318:                 }
                    319: 
                    320:             }
                    321: 
                    322:             if (timeoutsForIrp.ReadIntervalTimeout == MAXULONG) {
                    323: 
                    324:                 //
                    325:                 // We need to do special return quickly stuff here.
                    326:                 //
                    327:                 // 1) If both constant and multiplier are
                    328:                 //    0 then we return immediately with whatever
                    329:                 //    we've got, even if it was zero.
                    330:                 //
                    331:                 // 2) If constant and multiplier are not MAXULONG
                    332:                 //    then return immediately if any characters
                    333:                 //    are present, but if nothing is there, then
                    334:                 //    use the timeouts as specified.
                    335:                 //
                    336:                 // 3) If multiplier is MAXULONG then do as in
                    337:                 //    "2" but return when the first character
                    338:                 //    arrives.
                    339:                 //
                    340: 
                    341:                 if (!timeoutsForIrp.ReadTotalTimeoutConstant &&
                    342:                     !timeoutsForIrp.ReadTotalTimeoutMultiplier) {
                    343: 
                    344:                     returnWithWhatsPresent = TRUE;
                    345: 
                    346:                 } else if ((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
                    347:                             &&
                    348:                            (timeoutsForIrp.ReadTotalTimeoutMultiplier
                    349:                             != MAXULONG)) {
                    350: 
                    351:                     useTotalTimer = TRUE;
                    352:                     os2ssreturn = TRUE;
                    353:                     multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
                    354:                     constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
                    355: 
                    356:                 } else if ((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
                    357:                             &&
                    358:                            (timeoutsForIrp.ReadTotalTimeoutMultiplier
                    359:                             == MAXULONG)) {
                    360: 
                    361:                     useTotalTimer = TRUE;
                    362:                     os2ssreturn = TRUE;
                    363:                     crunchDownToOne = TRUE;
                    364:                     multiplierVal = 0;
                    365:                     constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
                    366: 
                    367:                 }
                    368: 
                    369:             } else {
                    370: 
                    371:                 //
                    372:                 // If both the multiplier and the constant are
                    373:                 // zero then don't do any total timeout processing.
                    374:                 //
                    375: 
                    376:                 if (timeoutsForIrp.ReadTotalTimeoutMultiplier ||
                    377:                     timeoutsForIrp.ReadTotalTimeoutConstant) {
                    378: 
                    379:                     //
                    380:                     // We have some timer values to calculate.
                    381:                     //
                    382: 
                    383:                     useTotalTimer = TRUE;
                    384:                     multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
                    385:                     constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
                    386: 
                    387:                 }
                    388: 
                    389:             }
                    390: 
                    391:             if (useTotalTimer) {
                    392: 
                    393:                 totalTime = RtlEnlargedUnsignedMultiply(
                    394:                                 Extension->NumberNeededForRead,
                    395:                                 multiplierVal
                    396:                                 );
                    397: 
                    398:                 totalTime = RtlLargeIntegerAdd(
                    399:                                 totalTime,
                    400:                                 RtlConvertUlongToLargeInteger(
                    401:                                     constantVal
                    402:                                     )
                    403:                                 );
                    404: 
                    405:                 totalTime = RtlExtendedIntegerMultiply(
                    406:                                 totalTime,
                    407:                                 -10000
                    408:                                 );
                    409: 
                    410:             }
                    411: 
                    412: 
                    413:             //
                    414:             // We do this copy in the hope of getting most (if not
                    415:             // all) of the characters out of the interrupt buffer.
                    416:             //
                    417:             // Note that we need to protect this operation with a
                    418:             // spinlock since we don't want a purge to hose us.
                    419:             //
                    420: 
                    421:             KeAcquireSpinLock(
                    422:                 &Extension->ControlLock,
                    423:                 &controlIrql
                    424:                 );
                    425: 
                    426:             updateChar.CharsCopied = SerialGetCharsFromIntBuffer(Extension);
                    427: 
                    428:             //
                    429:             // See if we have any cause to return immediately.
                    430:             //
                    431: 
                    432:             if (returnWithWhatsPresent || (!Extension->NumberNeededForRead) ||
                    433:                 (os2ssreturn &&
                    434:                  Extension->CurrentReadIrp->IoStatus.Information)) {
                    435: 
                    436:                 //
                    437:                 // We got all we needed for this read.
                    438:                 // Update the number of characters in the
                    439:                 // interrupt read buffer.
                    440:                 //
                    441: 
                    442:                 KeSynchronizeExecution(
                    443:                     Extension->Interrupt,
                    444:                     SerialUpdateInterruptBuffer,
                    445:                     &updateChar
                    446:                     );
                    447: 
                    448:                 KeReleaseSpinLock(
                    449:                     &Extension->ControlLock,
                    450:                     controlIrql
                    451:                     );
                    452: 
                    453:                 Extension->CurrentReadIrp->IoStatus.Status = STATUS_SUCCESS;
                    454:                 if (!setFirstStatus) {
                    455: 
                    456:                     firstStatus = STATUS_SUCCESS;
                    457:                     setFirstStatus = TRUE;
                    458: 
                    459:                 }
                    460: 
                    461:             } else {
                    462: 
                    463:                 //
                    464:                 // The irp might go under control of the isr.  It
                    465:                 // won't hurt to initialize the reference count
                    466:                 // right now.
                    467:                 //
                    468: 
                    469:                 SERIAL_INIT_REFERENCE(Extension->CurrentReadIrp);
                    470: 
                    471:                 IoAcquireCancelSpinLock(&oldIrql);
                    472: 
                    473:                 //
                    474:                 // We need to see if this irp should be canceled.
                    475:                 //
                    476: 
                    477:                 if (Extension->CurrentReadIrp->Cancel) {
                    478: 
                    479:                     IoReleaseCancelSpinLock(oldIrql);
                    480:                     KeReleaseSpinLock(
                    481:                         &Extension->ControlLock,
                    482:                         controlIrql
                    483:                         );
                    484:                     Extension->CurrentReadIrp->IoStatus.Status =
                    485:                         STATUS_CANCELLED;
                    486:                     Extension->CurrentReadIrp->IoStatus.Information = 0;
                    487: 
                    488:                     if (!setFirstStatus) {
                    489: 
                    490:                         firstStatus = STATUS_CANCELLED;
                    491:                         setFirstStatus = TRUE;
                    492: 
                    493:                     }
                    494: 
                    495:                 } else {
                    496: 
                    497:                     //
                    498:                     // If we are supposed to crunch the read down to
                    499:                     // one character, then update the read length
                    500:                     // in the irp and truncate the number needed for
                    501:                     // read down to one. Note that if we are doing
                    502:                     // this crunching, then the information must be
                    503:                     // zero (or we would have completed above) and
                    504:                     // the number needed for the read must still be
                    505:                     // equal to the read length.
                    506:                     //
                    507: 
                    508:                     if (crunchDownToOne) {
                    509: 
                    510:                         ASSERT(
                    511:                             (!Extension->CurrentReadIrp->IoStatus.Information)
                    512:                             &&
                    513:                             (Extension->NumberNeededForRead ==
                    514:                                 IoGetCurrentIrpStackLocation(
                    515:                                     Extension->CurrentReadIrp
                    516:                                     )->Parameters.Read.Length)
                    517:                             );
                    518: 
                    519:                         Extension->NumberNeededForRead = 1;
                    520:                         IoGetCurrentIrpStackLocation(
                    521:                             Extension->CurrentReadIrp
                    522:                             )->Parameters.Read.Length = 1;
                    523: 
                    524:                     }
                    525: 
                    526:                     //
                    527:                     // We still need to get more characters for this read.
                    528:                     // synchronize with the isr so that we can update the
                    529:                     // number of characters and if necessary it will have the
                    530:                     // isr switch to copying into the users buffer.
                    531:                     //
                    532: 
                    533:                     KeSynchronizeExecution(
                    534:                         Extension->Interrupt,
                    535:                         SerialUpdateAndSwitchToUser,
                    536:                         &updateChar
                    537:                         );
                    538: 
                    539:                     if (!updateChar.Completed) {
                    540: 
                    541:                         //
                    542:                         // The irp still isn't complete.  The
                    543:                         // completion routines will end up reinvoking
                    544:                         // this routine.  So we simply leave.
                    545:                         //
                    546:                         // First thought we should start off the total
                    547:                         // timer for the read and increment the reference
                    548:                         // count that the total timer has on the current
                    549:                         // irp.  Note that this is safe, because even if
                    550:                         // the io has been satisfied by the isr it can't
                    551:                         // complete yet because we still own the cancel
                    552:                         // spinlock.
                    553:                         //
                    554: 
                    555:                         if (useTotalTimer) {
                    556: 
                    557:                             SERIAL_INC_REFERENCE(Extension->CurrentReadIrp);
                    558: 
                    559:                             KeSetTimer(
                    560:                                 &Extension->ReadRequestTotalTimer,
                    561:                                 totalTime,
                    562:                                 &Extension->TotalReadTimeoutDpc
                    563:                                 );
                    564: 
                    565:                         }
                    566: 
                    567:                         if (useIntervalTimer) {
                    568: 
                    569:                             SERIAL_INC_REFERENCE(Extension->CurrentReadIrp);
                    570: 
                    571:                             KeQuerySystemTime(
                    572:                                 &Extension->LastReadTime
                    573:                                 );
                    574:                             KeSetTimer(
                    575:                                 &Extension->ReadRequestIntervalTimer,
                    576:                                 *Extension->IntervalTimeToUse,
                    577:                                 &Extension->IntervalReadTimeoutDpc
                    578:                                 );
                    579: 
                    580:                         }
                    581: 
                    582:                         IoMarkIrpPending(Extension->CurrentReadIrp);
                    583:                         IoReleaseCancelSpinLock(oldIrql);
                    584:                         KeReleaseSpinLock(
                    585:                             &Extension->ControlLock,
                    586:                             controlIrql
                    587:                             );
                    588:                         if (!setFirstStatus) {
                    589: 
                    590:                             firstStatus = STATUS_PENDING;
                    591: 
                    592:                         }
                    593:                         return firstStatus;
                    594: 
                    595:                     } else {
                    596: 
                    597:                         IoReleaseCancelSpinLock(oldIrql);
                    598:                         KeReleaseSpinLock(
                    599:                             &Extension->ControlLock,
                    600:                             controlIrql
                    601:                             );
                    602:                         Extension->CurrentReadIrp->IoStatus.Status =
                    603:                             STATUS_SUCCESS;
                    604: 
                    605:                         if (!setFirstStatus) {
                    606: 
                    607:                             firstStatus = STATUS_SUCCESS;
                    608:                             setFirstStatus = TRUE;
                    609: 
                    610:                         }
                    611: 
                    612:                     }
                    613: 
                    614:                 }
                    615: 
                    616:             }
                    617: 
                    618:         }
                    619: 
                    620:         //
                    621:         // Well the operation is complete.
                    622:         //
                    623: 
                    624:         SerialGetNextIrp(
                    625:             &Extension->CurrentReadIrp,
                    626:             &Extension->ReadQueue,
                    627:             &newIrp,
                    628:             TRUE
                    629:             );
                    630: 
                    631:     } while (newIrp);
                    632: 
                    633:     return firstStatus;
                    634: 
                    635: }
                    636: 
                    637: VOID
                    638: SerialCompleteRead(
                    639:     IN PKDPC Dpc,
                    640:     IN PVOID DeferredContext,
                    641:     IN PVOID SystemContext1,
                    642:     IN PVOID SystemContext2
                    643:     )
                    644: 
                    645: /*++
                    646: 
                    647: Routine Description:
                    648: 
                    649:     This routine is merely used to complete any read that
                    650:     ended up being used by the Isr.  It assumes that the
                    651:     status and the information fields of the irp are already
                    652:     correctly filled in.
                    653: 
                    654: Arguments:
                    655: 
                    656:     Dpc - Not Used.
                    657: 
                    658:     DeferredContext - Really points to the device extension.
                    659: 
                    660:     SystemContext1 - Not Used.
                    661: 
                    662:     SystemContext2 - Not Used.
                    663: 
                    664: Return Value:
                    665: 
                    666:     None.
                    667: 
                    668: --*/
                    669: 
                    670: {
                    671: 
                    672:     PSERIAL_DEVICE_EXTENSION extension = DeferredContext;
                    673:     KIRQL oldIrql;
                    674: 
                    675:     UNREFERENCED_PARAMETER(Dpc);
                    676:     UNREFERENCED_PARAMETER(SystemContext1);
                    677:     UNREFERENCED_PARAMETER(SystemContext2);
                    678: 
                    679: 
                    680:     IoAcquireCancelSpinLock(&oldIrql);
                    681: 
                    682:     //
                    683:     // We set this to indicate to the interval timer
                    684:     // that the read has completed.
                    685:     //
                    686:     // Recall that the interval timer dpc can be lurking in some
                    687:     // DPC queue.
                    688:     //
                    689: 
                    690:     extension->CountOnLastRead = SERIAL_COMPLETE_READ_COMPLETE;
                    691: 
                    692:     SerialTryToCompleteCurrent(
                    693:         extension,
                    694:         NULL,
                    695:         oldIrql,
                    696:         STATUS_SUCCESS,
                    697:         &extension->CurrentReadIrp,
                    698:         &extension->ReadQueue,
                    699:         &extension->ReadRequestIntervalTimer,
                    700:         &extension->ReadRequestTotalTimer,
                    701:         SerialStartRead,
                    702:         SerialGetNextIrp
                    703:         );
                    704: 
                    705: }
                    706: 
                    707: VOID
                    708: SerialCancelCurrentRead(
                    709:     PDEVICE_OBJECT DeviceObject,
                    710:     PIRP Irp
                    711:     )
                    712: 
                    713: /*++
                    714: 
                    715: Routine Description:
                    716: 
                    717:     This routine is used to cancel the current read.
                    718: 
                    719: Arguments:
                    720: 
                    721:     DeviceObject - Pointer to the device object for this device
                    722: 
                    723:     Irp - Pointer to the IRP to be canceled.
                    724: 
                    725: Return Value:
                    726: 
                    727:     None.
                    728: 
                    729: --*/
                    730: 
                    731: {
                    732: 
                    733:     PSERIAL_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
                    734: 
                    735:     //
                    736:     // We set this to indicate to the interval timer
                    737:     // that the read has encountered a cancel.
                    738:     //
                    739:     // Recall that the interval timer dpc can be lurking in some
                    740:     // DPC queue.
                    741:     //
                    742: 
                    743:     extension->CountOnLastRead = SERIAL_COMPLETE_READ_CANCEL;
                    744: 
                    745:     SerialTryToCompleteCurrent(
                    746:         extension,
                    747:         SerialGrabReadFromIsr,
                    748:         Irp->CancelIrql,
                    749:         STATUS_CANCELLED,
                    750:         &extension->CurrentReadIrp,
                    751:         &extension->ReadQueue,
                    752:         &extension->ReadRequestIntervalTimer,
                    753:         &extension->ReadRequestTotalTimer,
                    754:         SerialStartRead,
                    755:         SerialGetNextIrp
                    756:         );
                    757: 
                    758: }
                    759: 
                    760: BOOLEAN
                    761: SerialGrabReadFromIsr(
                    762:     IN PVOID Context
                    763:     )
                    764: 
                    765: /*++
                    766: 
                    767: Routine Description:
                    768: 
                    769:     This routine is used to grab (if possible) the irp from the
                    770:     isr.  If it finds that the isr still owns the irp it grabs
                    771:     the ipr away (updating the number of characters copied into the
                    772:     users buffer).  If it grabs it away it also decrements the
                    773:     reference count on the irp since it no longer belongs to the
                    774:     isr (and the dpc that would complete it).
                    775: 
                    776:     NOTE: This routine assumes that if the current buffer that the
                    777:           ISR is copying characters into is the interrupt buffer then
                    778:           the dpc has already been queued.
                    779: 
                    780:     NOTE: This routine is being called from KeSynchronizeExecution.
                    781: 
                    782:     NOTE: This routine assumes that it is called with the cancel spin
                    783:           lock held.
                    784: 
                    785: Arguments:
                    786: 
                    787:     Context - Really a pointer to the device extension.
                    788: 
                    789: Return Value:
                    790: 
                    791:     Always false.
                    792: 
                    793: --*/
                    794: 
                    795: {
                    796: 
                    797:     PSERIAL_DEVICE_EXTENSION extension = Context;
                    798: 
                    799:     if (extension->ReadBufferBase !=
                    800:         extension->InterruptReadBuffer) {
                    801: 
                    802:         //
                    803:         // We need to set the information to the number of characters
                    804:         // that the read wanted minus the number of characters that
                    805:         // didn't get read into the interrupt buffer.
                    806:         //
                    807: 
                    808:         extension->CurrentReadIrp->IoStatus.Information =
                    809:             IoGetCurrentIrpStackLocation(
                    810:                 extension->CurrentReadIrp
                    811:                 )->Parameters.Read.Length -
                    812:             ((extension->LastCharSlot - extension->CurrentCharSlot) + 1);
                    813: 
                    814:         //
                    815:         // Switch back to the interrupt buffer.
                    816:         //
                    817: 
                    818:         extension->ReadBufferBase = extension->InterruptReadBuffer;
                    819:         extension->CurrentCharSlot = extension->InterruptReadBuffer;
                    820:         extension->FirstReadableChar = extension->InterruptReadBuffer;
                    821:         extension->LastCharSlot = extension->InterruptReadBuffer +
                    822:                                       (extension->BufferSize - 1);
                    823:         extension->CharsInInterruptBuffer = 0;
                    824: 
                    825:         SERIAL_DEC_REFERENCE(extension->CurrentReadIrp);
                    826: 
                    827:     }
                    828: 
                    829:     return FALSE;
                    830: 
                    831: }
                    832: 
                    833: VOID
                    834: SerialReadTimeout(
                    835:     IN PKDPC Dpc,
                    836:     IN PVOID DeferredContext,
                    837:     IN PVOID SystemContext1,
                    838:     IN PVOID SystemContext2
                    839:     )
                    840: 
                    841: /*++
                    842: 
                    843: Routine Description:
                    844: 
                    845:     This routine is used to complete a read because its total
                    846:     timer has expired.
                    847: 
                    848: Arguments:
                    849: 
                    850:     Dpc - Not Used.
                    851: 
                    852:     DeferredContext - Really points to the device extension.
                    853: 
                    854:     SystemContext1 - Not Used.
                    855: 
                    856:     SystemContext2 - Not Used.
                    857: 
                    858: Return Value:
                    859: 
                    860:     None.
                    861: 
                    862: --*/
                    863: 
                    864: {
                    865: 
                    866:     PSERIAL_DEVICE_EXTENSION extension = DeferredContext;
                    867:     KIRQL oldIrql;
                    868: 
                    869:     UNREFERENCED_PARAMETER(Dpc);
                    870:     UNREFERENCED_PARAMETER(SystemContext1);
                    871:     UNREFERENCED_PARAMETER(SystemContext2);
                    872: 
                    873:     IoAcquireCancelSpinLock(&oldIrql);
                    874: 
                    875:     //
                    876:     // We set this to indicate to the interval timer
                    877:     // that the read has completed due to total timeout.
                    878:     //
                    879:     // Recall that the interval timer dpc can be lurking in some
                    880:     // DPC queue.
                    881:     //
                    882: 
                    883:     extension->CountOnLastRead = SERIAL_COMPLETE_READ_TOTAL;
                    884: 
                    885:     SerialTryToCompleteCurrent(
                    886:         extension,
                    887:         SerialGrabReadFromIsr,
                    888:         oldIrql,
                    889:         STATUS_TIMEOUT,
                    890:         &extension->CurrentReadIrp,
                    891:         &extension->ReadQueue,
                    892:         &extension->ReadRequestIntervalTimer,
                    893:         &extension->ReadRequestTotalTimer,
                    894:         SerialStartRead,
                    895:         SerialGetNextIrp
                    896:         );
                    897: 
                    898: }
                    899: 
                    900: BOOLEAN
                    901: SerialUpdateReadByIsr(
                    902:     IN PVOID Context
                    903:     )
                    904: 
                    905: /*++
                    906: 
                    907: Routine Description:
                    908: 
                    909:     This routine is used to update the count of characters read
                    910:     by the isr since the last interval timer experation.
                    911: 
                    912:     NOTE: This routine is being called from KeSynchronizeExecution.
                    913: 
                    914:     NOTE: This routine assumes that it is called with the cancel spin
                    915:           lock held.
                    916: 
                    917: Arguments:
                    918: 
                    919:     Context - Really a pointer to the device extension.
                    920: 
                    921: Return Value:
                    922: 
                    923:     Always false.
                    924: 
                    925: --*/
                    926: 
                    927: {
                    928: 
                    929:     PSERIAL_DEVICE_EXTENSION extension = Context;
                    930: 
                    931:     extension->CountOnLastRead = extension->ReadByIsr;
                    932:     extension->ReadByIsr = 0;
                    933: 
                    934:     return FALSE;
                    935: 
                    936: }
                    937: 
                    938: VOID
                    939: SerialIntervalReadTimeout(
                    940:     IN PKDPC Dpc,
                    941:     IN PVOID DeferredContext,
                    942:     IN PVOID SystemContext1,
                    943:     IN PVOID SystemContext2
                    944:     )
                    945: 
                    946: /*++
                    947: 
                    948: Routine Description:
                    949: 
                    950:     This routine is used timeout the request if the time between
                    951:     characters exceed the interval time.  A global is kept in
                    952:     the device extension that records the count of characters read
                    953:     the last the last time this routine was invoked (This dpc
                    954:     will resubmit the timer if the count has changed).  If the
                    955:     count has not changed then this routine will attempt to complete
                    956:     the irp.  Note the special case of the last count being zero.
                    957:     The timer isn't really in effect until the first character is
                    958:     read.
                    959: 
                    960: Arguments:
                    961: 
                    962:     Dpc - Not Used.
                    963: 
                    964:     DeferredContext - Really points to the device extension.
                    965: 
                    966:     SystemContext1 - Not Used.
                    967: 
                    968:     SystemContext2 - Not Used.
                    969: 
                    970: Return Value:
                    971: 
                    972:     None.
                    973: 
                    974: --*/
                    975: 
                    976: {
                    977: 
                    978:     PSERIAL_DEVICE_EXTENSION extension = DeferredContext;
                    979:     KIRQL oldIrql;
                    980: 
                    981:     UNREFERENCED_PARAMETER(Dpc);
                    982:     UNREFERENCED_PARAMETER(SystemContext1);
                    983:     UNREFERENCED_PARAMETER(SystemContext2);
                    984: 
                    985:     IoAcquireCancelSpinLock(&oldIrql);
                    986: 
                    987:     if (extension->CountOnLastRead == SERIAL_COMPLETE_READ_TOTAL) {
                    988: 
                    989:         //
                    990:         // This value is only set by the total
                    991:         // timer to indicate that it has fired.
                    992:         // If so, then we should simply try to complete.
                    993:         //
                    994: 
                    995:         SerialTryToCompleteCurrent(
                    996:             extension,
                    997:             SerialGrabReadFromIsr,
                    998:             oldIrql,
                    999:             STATUS_TIMEOUT,
                   1000:             &extension->CurrentReadIrp,
                   1001:             &extension->ReadQueue,
                   1002:             &extension->ReadRequestIntervalTimer,
                   1003:             &extension->ReadRequestTotalTimer,
                   1004:             SerialStartRead,
                   1005:             SerialGetNextIrp
                   1006:             );
                   1007: 
                   1008:     } else if (extension->CountOnLastRead == SERIAL_COMPLETE_READ_COMPLETE) {
                   1009: 
                   1010:         //
                   1011:         // This value is only set by the regular
                   1012:         // completion routine.
                   1013:         //
                   1014:         // If so, then we should simply try to complete.
                   1015:         //
                   1016: 
                   1017:         SerialTryToCompleteCurrent(
                   1018:             extension,
                   1019:             SerialGrabReadFromIsr,
                   1020:             oldIrql,
                   1021:             STATUS_SUCCESS,
                   1022:             &extension->CurrentReadIrp,
                   1023:             &extension->ReadQueue,
                   1024:             &extension->ReadRequestIntervalTimer,
                   1025:             &extension->ReadRequestTotalTimer,
                   1026:             SerialStartRead,
                   1027:             SerialGetNextIrp
                   1028:             );
                   1029: 
                   1030:     } else if (extension->CountOnLastRead == SERIAL_COMPLETE_READ_CANCEL) {
                   1031: 
                   1032:         //
                   1033:         // This value is only set by the cancel
                   1034:         // read routine.
                   1035:         //
                   1036:         // If so, then we should simply try to complete.
                   1037:         //
                   1038: 
                   1039:         SerialTryToCompleteCurrent(
                   1040:             extension,
                   1041:             SerialGrabReadFromIsr,
                   1042:             oldIrql,
                   1043:             STATUS_CANCELLED,
                   1044:             &extension->CurrentReadIrp,
                   1045:             &extension->ReadQueue,
                   1046:             &extension->ReadRequestIntervalTimer,
                   1047:             &extension->ReadRequestTotalTimer,
                   1048:             SerialStartRead,
                   1049:             SerialGetNextIrp
                   1050:             );
                   1051: 
                   1052:     } else if (extension->CountOnLastRead || extension->ReadByIsr) {
                   1053: 
                   1054:         //
                   1055:         // Something has happened since we last came here.  We
                   1056:         // check to see if the ISR has read in any more characters.
                   1057:         // If it did then we should update the isr's read count
                   1058:         // and resubmit the timer.
                   1059:         //
                   1060: 
                   1061:         if (extension->ReadByIsr) {
                   1062: 
                   1063:             KeSynchronizeExecution(
                   1064:                 extension->Interrupt,
                   1065:                 SerialUpdateReadByIsr,
                   1066:                 extension
                   1067:                 );
                   1068: 
                   1069:             //
                   1070:             // Save off the "last" time something was read.
                   1071:             // As we come back to this routine we will compare
                   1072:             // the current time to the "last" time.  If the
                   1073:             // difference is ever larger then the interval
                   1074:             // requested by the user, then time out the request.
                   1075:             //
                   1076: 
                   1077:             KeQuerySystemTime(
                   1078:                 &extension->LastReadTime
                   1079:                 );
                   1080: 
                   1081:             KeSetTimer(
                   1082:                 &extension->ReadRequestIntervalTimer,
                   1083:                 *extension->IntervalTimeToUse,
                   1084:                 &extension->IntervalReadTimeoutDpc
                   1085:                 );
                   1086: 
                   1087:             IoReleaseCancelSpinLock(oldIrql);
                   1088: 
                   1089:         } else {
                   1090: 
                   1091:             //
                   1092:             // Take the difference between the current time
                   1093:             // and the last time we had characters and
                   1094:             // see if it is greater then the interval time.
                   1095:             // if it is, then time out the request.  Otherwise
                   1096:             // go away again for a while.
                   1097:             //
                   1098: 
                   1099:             //
                   1100:             // No characters read in the interval time.  Kill
                   1101:             // this read.
                   1102:             //
                   1103: 
                   1104:             LARGE_INTEGER currentTime;
                   1105: 
                   1106:             KeQuerySystemTime(
                   1107:                 &currentTime
                   1108:                 );
                   1109: 
                   1110:             if (RtlLargeIntegerGreaterThanOrEqualTo(
                   1111:                     RtlLargeIntegerSubtract(
                   1112:                         currentTime,
                   1113:                         extension->LastReadTime
                   1114:                         ),
                   1115:                     extension->IntervalTime
                   1116:                     )) {
                   1117: 
                   1118:                 SerialTryToCompleteCurrent(
                   1119:                     extension,
                   1120:                     SerialGrabReadFromIsr,
                   1121:                     oldIrql,
                   1122:                     STATUS_TIMEOUT,
                   1123:                     &extension->CurrentReadIrp,
                   1124:                     &extension->ReadQueue,
                   1125:                     &extension->ReadRequestIntervalTimer,
                   1126:                     &extension->ReadRequestTotalTimer,
                   1127:                     SerialStartRead,
                   1128:                     SerialGetNextIrp
                   1129:                     );
                   1130: 
                   1131:             } else {
                   1132: 
                   1133:                 KeSetTimer(
                   1134:                     &extension->ReadRequestIntervalTimer,
                   1135:                     *extension->IntervalTimeToUse,
                   1136:                     &extension->IntervalReadTimeoutDpc
                   1137:                     );
                   1138:                 IoReleaseCancelSpinLock(oldIrql);
                   1139: 
                   1140:             }
                   1141: 
                   1142: 
                   1143:         }
                   1144: 
                   1145:     } else {
                   1146: 
                   1147:         //
                   1148:         // Timer doesn't really start until the first character.
                   1149:         // So we should simply resubmit ourselves.
                   1150:         //
                   1151: 
                   1152:         KeSetTimer(
                   1153:             &extension->ReadRequestIntervalTimer,
                   1154:             *extension->IntervalTimeToUse,
                   1155:             &extension->IntervalReadTimeoutDpc
                   1156:             );
                   1157: 
                   1158:         IoReleaseCancelSpinLock(oldIrql);
                   1159: 
                   1160:     }
                   1161: 
                   1162: 
                   1163: }
                   1164: 
                   1165: ULONG
                   1166: SerialGetCharsFromIntBuffer(
                   1167:     PSERIAL_DEVICE_EXTENSION Extension
                   1168:     )
                   1169: 
                   1170: /*++
                   1171: 
                   1172: Routine Description:
                   1173: 
                   1174:     This routine is used to copy any characters out of the interrupt
                   1175:     buffer into the users buffer.  It will be reading values that
                   1176:     are updated with the ISR but this is safe since this value is
                   1177:     only decremented by synchronization routines.  This routine will
                   1178:     return the number of characters copied so some other routine
                   1179:     can call a synchronization routine to update what is seen at
                   1180:     interrupt level.
                   1181: 
                   1182: Arguments:
                   1183: 
                   1184:     Extension - A pointer to the device extension.
                   1185: 
                   1186: Return Value:
                   1187: 
                   1188:     The number of characters that were copied into the user
                   1189:     buffer.
                   1190: 
                   1191: --*/
                   1192: 
                   1193: {
                   1194: 
                   1195:     //
                   1196:     // This value will be the number of characters that this
                   1197:     // routine returns.  It will be the minimum of the number
                   1198:     // of characters currently in the buffer or the number of
                   1199:     // characters required for the read.
                   1200:     //
                   1201:     ULONG numberOfCharsToGet;
                   1202: 
                   1203:     //
                   1204:     // This holds the number of characters between the first
                   1205:     // readable character and - the last character we will read or
                   1206:     // the real physical end of the buffer (not the last readable
                   1207:     // character).
                   1208:     //
                   1209:     ULONG firstTryNumberToGet;
                   1210: 
                   1211: 
                   1212:     //
                   1213:     // The minimum of the number of characters we need and
                   1214:     // the number of characters available
                   1215:     //
                   1216: 
                   1217:     numberOfCharsToGet = Extension->CharsInInterruptBuffer;
                   1218: 
                   1219:     if (numberOfCharsToGet > Extension->NumberNeededForRead) {
                   1220: 
                   1221:         numberOfCharsToGet = Extension->NumberNeededForRead;
                   1222: 
                   1223:     }
                   1224: 
                   1225:     if (numberOfCharsToGet) {
                   1226: 
                   1227:         //
                   1228:         // This will hold the number of characters between the
                   1229:         // first available character and the end of the buffer.
                   1230:         // Note that the buffer could wrap around but for the
                   1231:         // purposes of the first copy we don't care about that.
                   1232:         //
                   1233: 
                   1234:         firstTryNumberToGet = (Extension->LastCharSlot -
                   1235:                                Extension->FirstReadableChar) + 1;
                   1236: 
                   1237:         if (firstTryNumberToGet > numberOfCharsToGet) {
                   1238: 
                   1239:             //
                   1240:             // The characters don't wrap. Actually they may wrap but
                   1241:             // we don't care for the purposes of this read since the
                   1242:             // characters we need are available before the wrap.
                   1243:             //
                   1244: 
                   1245:             RtlMoveMemory(
                   1246:                 ((PUCHAR)(Extension->CurrentReadIrp->AssociatedIrp.SystemBuffer))
                   1247:                     + (IoGetCurrentIrpStackLocation(
                   1248:                            Extension->CurrentReadIrp
                   1249:                            )->Parameters.Read.Length
                   1250:                        - Extension->NumberNeededForRead
                   1251:                       ),
                   1252:                 Extension->FirstReadableChar,
                   1253:                 numberOfCharsToGet
                   1254:                 );
                   1255: 
                   1256:             Extension->NumberNeededForRead -= numberOfCharsToGet;
                   1257: 
                   1258:             //
                   1259:             // We now will move the pointer to the first character after
                   1260:             // what we just copied into the users buffer.
                   1261:             //
                   1262:             // We need to check if the stream of readable characters
                   1263:             // is wrapping around to the beginning of the buffer.
                   1264:             //
                   1265:             // Note that we may have just taken the last characters
                   1266:             // at the end of the buffer.
                   1267:             //
                   1268: 
                   1269:             if ((Extension->FirstReadableChar + (numberOfCharsToGet - 1)) ==
                   1270:                 Extension->LastCharSlot) {
                   1271: 
                   1272:                 Extension->FirstReadableChar = Extension->InterruptReadBuffer;
                   1273: 
                   1274:             } else {
                   1275: 
                   1276:                 Extension->FirstReadableChar += numberOfCharsToGet;
                   1277: 
                   1278:             }
                   1279: 
                   1280:         } else {
                   1281: 
                   1282:             //
                   1283:             // The characters do wrap.  Get up until the end of the buffer.
                   1284:             //
                   1285: 
                   1286:             RtlMoveMemory(
                   1287:                 ((PUCHAR)(Extension->CurrentReadIrp->AssociatedIrp.SystemBuffer))
                   1288:                     + (IoGetCurrentIrpStackLocation(
                   1289:                            Extension->CurrentReadIrp
                   1290:                            )->Parameters.Read.Length
                   1291:                        - Extension->NumberNeededForRead
                   1292:                       ),
                   1293:                 Extension->FirstReadableChar,
                   1294:                 firstTryNumberToGet
                   1295:                 );
                   1296: 
                   1297:             Extension->NumberNeededForRead -= firstTryNumberToGet;
                   1298: 
                   1299:             //
                   1300:             // Now get the rest of the characters from the beginning of the
                   1301:             // buffer.
                   1302:             //
                   1303: 
                   1304:             RtlMoveMemory(
                   1305:                 ((PUCHAR)(Extension->CurrentReadIrp->AssociatedIrp.SystemBuffer))
                   1306:                     + (IoGetCurrentIrpStackLocation(
                   1307:                            Extension->CurrentReadIrp
                   1308:                            )->Parameters.Read.Length
                   1309:                        - Extension->NumberNeededForRead
                   1310:                       ),
                   1311:                 Extension->InterruptReadBuffer,
                   1312:                 numberOfCharsToGet - firstTryNumberToGet
                   1313:                 );
                   1314: 
                   1315:             Extension->FirstReadableChar = Extension->InterruptReadBuffer +
                   1316:                                            (numberOfCharsToGet -
                   1317:                                             firstTryNumberToGet);
                   1318: 
                   1319:             Extension->NumberNeededForRead -= (numberOfCharsToGet -
                   1320:                                                firstTryNumberToGet);
                   1321: 
                   1322:         }
                   1323: 
                   1324:     }
                   1325: 
                   1326:     Extension->CurrentReadIrp->IoStatus.Information += numberOfCharsToGet;
                   1327:     return numberOfCharsToGet;
                   1328: 
                   1329: }
                   1330: 
                   1331: BOOLEAN
                   1332: SerialUpdateInterruptBuffer(
                   1333:     IN PVOID Context
                   1334:     )
                   1335: 
                   1336: /*++
                   1337: 
                   1338: Routine Description:
                   1339: 
                   1340:     This routine is used to update the number of characters that
                   1341:     remain in the interrupt buffer.  We need to use this routine
                   1342:     since the count could be updated during the update by execution
                   1343:     of the ISR.
                   1344: 
                   1345:     NOTE: This is called by KeSynchronizeExecution.
                   1346: 
                   1347: Arguments:
                   1348: 
                   1349:     Context - Points to a structure that contains a pointer to the
                   1350:               device extension and count of the number of characters
                   1351:               that we previously copied into the users buffer.  The
                   1352:               structure actually has a third field that we don't
                   1353:               use in this routine.
                   1354: 
                   1355: Return Value:
                   1356: 
                   1357:     Always FALSE.
                   1358: 
                   1359: --*/
                   1360: 
                   1361: {
                   1362: 
                   1363:     PSERIAL_UPDATE_CHAR update = Context;
                   1364:     PSERIAL_DEVICE_EXTENSION extension = update->Extension;
                   1365: 
                   1366:     ASSERT(extension->CharsInInterruptBuffer >= update->CharsCopied);
                   1367:     extension->CharsInInterruptBuffer -= update->CharsCopied;
                   1368: 
                   1369:     //
                   1370:     // Deal with flow control if necessary.
                   1371:     //
                   1372: 
                   1373:     SerialHandleReducedIntBuffer(extension);
                   1374: 
                   1375: 
                   1376:     return FALSE;
                   1377: 
                   1378: }
                   1379: 
                   1380: BOOLEAN
                   1381: SerialUpdateAndSwitchToUser(
                   1382:     IN PVOID Context
                   1383:     )
                   1384: 
                   1385: /*++
                   1386: 
                   1387: Routine Description:
                   1388: 
                   1389:     This routine gets the (hopefully) few characters that
                   1390:     remain in the interrupt buffer after the first time we tried
                   1391:     to get them out.  If we still don't have enough characters
                   1392:     to satisfy the read it will then we set things up so that the
                   1393:     ISR uses the user buffer copy into.
                   1394: 
                   1395:     This routine is also used to update a count that is maintained
                   1396:     by the ISR to keep track of the number of characters in its buffer.
                   1397: 
                   1398:     NOTE: This is called by KeSynchronizeExecution.
                   1399: 
                   1400: Arguments:
                   1401: 
                   1402:     Context - Points to a structure that contains a pointer to the
                   1403:               device extension, a count of the number of characters
                   1404:               that we previously copied into the users buffer, and
                   1405:               a boolean that we will set that defines whether we
                   1406:               switched the ISR to copy into the users buffer.
                   1407: 
                   1408: Return Value:
                   1409: 
                   1410:     Always FALSE.
                   1411: 
                   1412: --*/
                   1413: 
                   1414: {
                   1415: 
                   1416:     PSERIAL_UPDATE_CHAR updateChar = Context;
                   1417:     PSERIAL_DEVICE_EXTENSION extension = updateChar->Extension;
                   1418: 
                   1419:     SerialUpdateInterruptBuffer(Context);
                   1420: 
                   1421:     //
                   1422:     // There are more characters to get to satisfy this read.
                   1423:     // Copy any characters that have arrived since we got
                   1424:     // the last batch.
                   1425:     //
                   1426: 
                   1427:     updateChar->CharsCopied = SerialGetCharsFromIntBuffer(extension);
                   1428: 
                   1429:     SerialUpdateInterruptBuffer(Context);
                   1430: 
                   1431:     //
                   1432:     // No more new characters will be "received" until we exit
                   1433:     // this routine.  We again check to make sure that we
                   1434:     // haven't satisfied this read, and if we haven't we set things
                   1435:     // up so that the ISR copies into the user buffer.
                   1436:     //
                   1437: 
                   1438:     if (extension->NumberNeededForRead) {
                   1439: 
                   1440:         //
                   1441:         // We shouldn't be switching unless there are no
                   1442:         // characters left.
                   1443:         //
                   1444: 
                   1445:         ASSERT(!extension->CharsInInterruptBuffer);
                   1446: 
                   1447:         //
                   1448:         // We use the following to values to do inteval timing.
                   1449:         //
                   1450:         // CountOnLastRead is mostly used to simply prevent
                   1451:         // the interval timer from timing out before any characters
                   1452:         // are read. (Interval timing should only be effective
                   1453:         // after the first character is read.)
                   1454:         //
                   1455:         // After the first time the interval timer fires and
                   1456:         // characters have be read we will simply update with
                   1457:         // the value of ReadByIsr and then set ReadByIsr to zero.
                   1458:         // (We do that in a synchronization routine.
                   1459:         //
                   1460:         // If the interval timer dpc routine ever encounters
                   1461:         // ReadByIsr == 0 when CountOnLastRead is non-zero it
                   1462:         // will timeout the read.
                   1463:         //
                   1464:         // (Note that we have a special case of CountOnLastRead
                   1465:         // < 0.  This is done by the read completion routines other
                   1466:         // than the total timeout dpc to indicate that the total
                   1467:         // timeout has expired.)
                   1468:         //
                   1469: 
                   1470:         extension->CountOnLastRead =
                   1471:             extension->CurrentReadIrp->IoStatus.Information;
                   1472: 
                   1473:         extension->ReadByIsr = 0;
                   1474: 
                   1475:         //
                   1476:         // By compareing the read buffer base address to the
                   1477:         // the base address of the interrupt buffer the ISR
                   1478:         // can determine whether we are using the interrupt
                   1479:         // buffer or the user buffer.
                   1480:         //
                   1481: 
                   1482:         extension->ReadBufferBase =
                   1483:             extension->CurrentReadIrp->AssociatedIrp.SystemBuffer;
                   1484: 
                   1485:         //
                   1486:         // The current char slot is after the last copied in
                   1487:         // character.  We know there is always room since we
                   1488:         // we wouldn't have gotten here if there wasn't.
                   1489:         //
                   1490: 
                   1491:         extension->CurrentCharSlot = extension->ReadBufferBase +
                   1492:             extension->CurrentReadIrp->IoStatus.Information;
                   1493: 
                   1494:         //
                   1495:         // The last position that a character can go is on the
                   1496:         // last byte of user buffer.  While the actual allocated
                   1497:         // buffer space may be bigger, we know that there is at
                   1498:         // least as much as the read length.
                   1499:         //
                   1500: 
                   1501:         extension->LastCharSlot = extension->ReadBufferBase +
                   1502:                                       (IoGetCurrentIrpStackLocation(
                   1503:                                           extension->CurrentReadIrp
                   1504:                                           )->Parameters.Read.Length
                   1505:                                        - 1);
                   1506: 
                   1507:         //
                   1508:         // Mark the irp as being in a cancelable state.
                   1509:         //
                   1510: 
                   1511:         IoSetCancelRoutine(
                   1512:             extension->CurrentReadIrp,
                   1513:             SerialCancelCurrentRead
                   1514:             );
                   1515: 
                   1516:         //
                   1517:         // Increment the reference count twice.
                   1518:         //
                   1519:         // Once for the Isr owning the irp and once
                   1520:         // because the cancel routine has a reference
                   1521:         // to it.
                   1522:         //
                   1523: 
                   1524:         SERIAL_INC_REFERENCE(extension->CurrentReadIrp);
                   1525:         SERIAL_INC_REFERENCE(extension->CurrentReadIrp);
                   1526: 
                   1527:         updateChar->Completed = FALSE;
                   1528: 
                   1529:     } else {
                   1530: 
                   1531:         updateChar->Completed = TRUE;
                   1532: 
                   1533:     }
                   1534: 
                   1535:     return FALSE;
                   1536: 
                   1537: }
                   1538: //
                   1539: // We use this structure only to communicate to the synchronization
                   1540: // routine when we are switching to the resized buffer.
                   1541: //
                   1542: typedef struct _SERIAL_RESIZE_PARAMS {
                   1543:     PSERIAL_DEVICE_EXTENSION Extension;
                   1544:     PUCHAR OldBuffer;
                   1545:     PUCHAR NewBuffer;
                   1546:     ULONG NewBufferSize;
                   1547:     ULONG NumberMoved;
                   1548:     } SERIAL_RESIZE_PARAMS,*PSERIAL_RESIZE_PARAMS;
                   1549: 
                   1550: NTSTATUS
                   1551: SerialResizeBuffer(
                   1552:     IN PSERIAL_DEVICE_EXTENSION Extension
                   1553:     )
                   1554: 
                   1555: /*++
                   1556: 
                   1557: Routine Description:
                   1558: 
                   1559:     This routine will process the resize buffer request.
                   1560:     If size requested for the RX buffer is smaller than
                   1561:     the current buffer then we will simply return
                   1562:     STATUS_SUCCESS.  (We don't want to make buffers smaller.
                   1563:     If we did that then we all of a sudden have "overrun"
                   1564:     problems to deal with as well as flow control to deal
                   1565:     with - very painful.)  We ignore the TX buffer size
                   1566:     request since we don't use a TX buffer.
                   1567: 
                   1568: Arguments:
                   1569: 
                   1570:     Extension - Pointer to the device extension for the port.
                   1571: 
                   1572: Return Value:
                   1573: 
                   1574:     STATUS_SUCCESS if everything worked out ok.
                   1575:     STATUS_INSUFFICIENT_RESOURCES if we couldn't allocate the
                   1576:     memory for the buffer.
                   1577: 
                   1578: --*/
                   1579: 
                   1580: {
                   1581: 
                   1582:     PSERIAL_QUEUE_SIZE rs = Extension->CurrentReadIrp->AssociatedIrp
                   1583:                                                        .SystemBuffer;
                   1584:     PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(
                   1585:                                    Extension->CurrentReadIrp
                   1586:                                    );
                   1587:     PVOID newBuffer = irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
                   1588: 
                   1589:     irpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
                   1590:     Extension->CurrentReadIrp->IoStatus.Information = 0L;
                   1591:     Extension->CurrentReadIrp->IoStatus.Status = STATUS_SUCCESS;
                   1592: 
                   1593:     if (rs->InSize <= Extension->BufferSize) {
                   1594: 
                   1595:         //
                   1596:         // Nothing to do.  We don't make buffers smaller.  Just
                   1597:         // agree with the user.  We must deallocate the memory
                   1598:         // that was already allocated in the ioctl dispatch routine.
                   1599:         //
                   1600: 
                   1601:         ExFreePool(newBuffer);
                   1602: 
                   1603:     } else {
                   1604: 
                   1605:         SERIAL_RESIZE_PARAMS rp;
                   1606:         KIRQL controlIrql;
                   1607: 
                   1608:         //
                   1609:         // Hmmm, looks like we actually have to go
                   1610:         // through with this.  We need to move all the
                   1611:         // data that is in the current buffer into this
                   1612:         // new buffer.  We'll do this in two steps.
                   1613:         //
                   1614:         // First we go up to dispatch level and try to
                   1615:         // move as much as we can without stopping the
                   1616:         // ISR from running.  We go up to dispatch level
                   1617:         // by acquiring the control lock.  We do it at
                   1618:         // dispatch using the control lock so that:
                   1619:         //
                   1620:         //    1) We can't be context switched in the middle
                   1621:         //       of the move.  Our pointers into the buffer
                   1622:         //       could be *VERY* stale by the time we got back.
                   1623:         //
                   1624:         //    2) We use the control lock since we don't want
                   1625:         //       some pesky purge irp to come along while
                   1626:         //       we are trying to move.
                   1627:         //
                   1628:         // After the move, but while we still hold the control
                   1629:         // lock, we synch with the ISR and get those last
                   1630:         // (hopefully) few characters that have come in since
                   1631:         // we started the copy.  We switch all of our pointers,
                   1632:         // counters, and such to point to this new buffer.  NOTE:
                   1633:         // we need to be careful.  If the buffer we were using
                   1634:         // was not the default one created when we initialized
                   1635:         // the device (i.e. it was created via a previous IRP of
                   1636:         // this type), we should deallocate it.
                   1637:         //
                   1638: 
                   1639:         rp.Extension = Extension;
                   1640:         rp.OldBuffer = Extension->InterruptReadBuffer;
                   1641:         rp.NewBuffer = newBuffer;
                   1642:         rp.NewBufferSize = rs->InSize;
                   1643: 
                   1644:         KeAcquireSpinLock(
                   1645:             &Extension->ControlLock,
                   1646:             &controlIrql
                   1647:             );
                   1648: 
                   1649:         rp.NumberMoved = SerialMoveToNewIntBuffer(
                   1650:                              Extension,
                   1651:                              newBuffer
                   1652:                              );
                   1653: 
                   1654:         KeSynchronizeExecution(
                   1655:             Extension->Interrupt,
                   1656:             SerialUpdateAndSwitchToNew,
                   1657:             &rp
                   1658:             );
                   1659: 
                   1660:         KeReleaseSpinLock(
                   1661:             &Extension->ControlLock,
                   1662:             controlIrql
                   1663:             );
                   1664: 
                   1665:         //
                   1666:         // Free up the memory that the old buffer consumed.
                   1667:         //
                   1668: 
                   1669:         ExFreePool(rp.OldBuffer);
                   1670: 
                   1671:     }
                   1672: 
                   1673:     return STATUS_SUCCESS;
                   1674: 
                   1675: }
                   1676: 
                   1677: ULONG
                   1678: SerialMoveToNewIntBuffer(
                   1679:     PSERIAL_DEVICE_EXTENSION Extension,
                   1680:     PUCHAR NewBuffer
                   1681:     )
                   1682: 
                   1683: /*++
                   1684: 
                   1685: Routine Description:
                   1686: 
                   1687:     This routine is used to copy any characters out of the interrupt
                   1688:     buffer into the "new" buffer.  It will be reading values that
                   1689:     are updated with the ISR but this is safe since this value is
                   1690:     only decremented by synchronization routines.  This routine will
                   1691:     return the number of characters copied so some other routine
                   1692:     can call a synchronization routine to update what is seen at
                   1693:     interrupt level.
                   1694: 
                   1695: Arguments:
                   1696: 
                   1697:     Extension - A pointer to the device extension.
                   1698:     NewBuffer - Where the characters are to be move to.
                   1699: 
                   1700: Return Value:
                   1701: 
                   1702:     The number of characters that were copied into the user
                   1703:     buffer.
                   1704: 
                   1705: --*/
                   1706: 
                   1707: {
                   1708: 
                   1709:     ULONG numberOfCharsMoved = Extension->CharsInInterruptBuffer;
                   1710: 
                   1711:     if (numberOfCharsMoved) {
                   1712: 
                   1713:         //
                   1714:         // This holds the number of characters between the first
                   1715:         // readable character and the last character we will read or
                   1716:         // the real physical end of the buffer (not the last readable
                   1717:         // character).
                   1718:         //
                   1719:         ULONG firstTryNumberToGet = (Extension->LastCharSlot -
                   1720:                                      Extension->FirstReadableChar) + 1;
                   1721: 
                   1722:         if (firstTryNumberToGet >= numberOfCharsMoved) {
                   1723: 
                   1724:             //
                   1725:             // The characters don't wrap.
                   1726:             //
                   1727: 
                   1728:             RtlMoveMemory(
                   1729:                 NewBuffer,
                   1730:                 Extension->FirstReadableChar,
                   1731:                 numberOfCharsMoved
                   1732:                 );
                   1733: 
                   1734:             if ((Extension->FirstReadableChar+(numberOfCharsMoved-1)) ==
                   1735:                 Extension->LastCharSlot) {
                   1736: 
                   1737:                 Extension->FirstReadableChar = Extension->InterruptReadBuffer;
                   1738: 
                   1739:             } else {
                   1740: 
                   1741:                 Extension->FirstReadableChar += numberOfCharsMoved;
                   1742: 
                   1743:             }
                   1744: 
                   1745:         } else {
                   1746: 
                   1747:             //
                   1748:             // The characters do wrap.  Get up until the end of the buffer.
                   1749:             //
                   1750: 
                   1751:             RtlMoveMemory(
                   1752:                 NewBuffer,
                   1753:                 Extension->FirstReadableChar,
                   1754:                 firstTryNumberToGet
                   1755:                 );
                   1756: 
                   1757:             //
                   1758:             // Now get the rest of the characters from the beginning of the
                   1759:             // buffer.
                   1760:             //
                   1761: 
                   1762:             RtlMoveMemory(
                   1763:                 NewBuffer+firstTryNumberToGet,
                   1764:                 Extension->InterruptReadBuffer,
                   1765:                 numberOfCharsMoved - firstTryNumberToGet
                   1766:                 );
                   1767: 
                   1768:             Extension->FirstReadableChar = Extension->InterruptReadBuffer +
                   1769:                                    numberOfCharsMoved - firstTryNumberToGet;
                   1770: 
                   1771:         }
                   1772: 
                   1773:     }
                   1774: 
                   1775:     return numberOfCharsMoved;
                   1776: 
                   1777: }
                   1778: 
                   1779: BOOLEAN
                   1780: SerialUpdateAndSwitchToNew(
                   1781:     IN PVOID Context
                   1782:     )
                   1783: 
                   1784: /*++
                   1785: 
                   1786: Routine Description:
                   1787: 
                   1788:     This routine gets the (hopefully) few characters that
                   1789:     remain in the interrupt buffer after the first time we tried
                   1790:     to get them out.
                   1791: 
                   1792:     NOTE: This is called by KeSynchronizeExecution.
                   1793: 
                   1794: Arguments:
                   1795: 
                   1796:     Context - Points to a structure that contains a pointer to the
                   1797:               device extension, a pointer to the buffer we are moving
                   1798:               to, and a count of the number of characters
                   1799:               that we previously copied into the new buffer, and the
                   1800:               actual size of the new buffer.
                   1801: 
                   1802: Return Value:
                   1803: 
                   1804:     Always FALSE.
                   1805: 
                   1806: --*/
                   1807: 
                   1808: {
                   1809: 
                   1810:     PSERIAL_RESIZE_PARAMS params = Context;
                   1811:     PSERIAL_DEVICE_EXTENSION extension = params->Extension;
                   1812:     ULONG tempCharsInInterruptBuffer = extension->CharsInInterruptBuffer;
                   1813: 
                   1814:     ASSERT(extension->CharsInInterruptBuffer >= params->NumberMoved);
                   1815: 
                   1816:     //
                   1817:     // We temporarily reduce the chars in interrupt buffer to
                   1818:     // "fool" the move routine.  We will restore it after the
                   1819:     // move.
                   1820:     //
                   1821: 
                   1822:     extension->CharsInInterruptBuffer -= params->NumberMoved;
                   1823: 
                   1824:     if (extension->CharsInInterruptBuffer) {
                   1825: 
                   1826:         SerialMoveToNewIntBuffer(
                   1827:             extension,
                   1828:             params->NewBuffer + params->NumberMoved
                   1829:             );
                   1830: 
                   1831:     }
                   1832: 
                   1833:     extension->CharsInInterruptBuffer = tempCharsInInterruptBuffer;
                   1834: 
                   1835: 
                   1836:     extension->LastCharSlot = params->NewBuffer + (params->NewBufferSize - 1);
                   1837:     extension->FirstReadableChar = params->NewBuffer;
                   1838:     extension->ReadBufferBase = params->NewBuffer;
                   1839:     extension->InterruptReadBuffer = params->NewBuffer;
                   1840:     extension->BufferSize = params->NewBufferSize;
                   1841: 
                   1842:     //
                   1843:     // We *KNOW* that the new interrupt buffer is larger than the
                   1844:     // old buffer.  We don't need to worry about it being full.
                   1845:     //
                   1846: 
                   1847:     extension->CurrentCharSlot = extension->InterruptReadBuffer +
                   1848:                                  extension->CharsInInterruptBuffer;
                   1849: 
                   1850:     //
                   1851:     // We set up the default xon/xoff limits.
                   1852:     //
                   1853: 
                   1854:     extension->HandFlow.XoffLimit = extension->BufferSize >> 3;
                   1855:     extension->HandFlow.XonLimit = extension->BufferSize >> 1;
                   1856: 
                   1857:     extension->BufferSizePt8 = ((3*(extension->BufferSize>>2))+
                   1858:                                    (extension->BufferSize>>4));
                   1859: 
                   1860:     //
                   1861:     // Since we (essentially) reduced the percentage of the interrupt
                   1862:     // buffer being full, we need to handle any flow control.
                   1863:     //
                   1864: 
                   1865:     SerialHandleReducedIntBuffer(extension);
                   1866: 
                   1867:     return FALSE;
                   1868: 
                   1869: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.