|
|
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: ¤tTime ! 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.