|
|
/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
pppread.c
Abstract:
Author:
Thomas J. Dimitri (TommyD) 08-May-1992
Environment:
Kernel Mode - Or whatever is the equivalent on OS/2 and DOS.
Revision History:
--*/
#if DBG
#define __FILE_SIG__ 'rppP'
#endif
#include "asyncall.h"
NTSTATUS AsyncSLIPCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context);
NTSTATUS AsyncWaitMaskCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context);
NTSTATUS AsyncPPPWaitMask( IN PASYNC_INFO Info)
/*++
Assumption -- 0 length frames are not sent (this includes headers)!!! Also, this is NOT a synchronous operation. It is always asynchronous.
Routine Description:
This service writes Length bytes of data from the caller's Buffer to the "port" handle. It is assumed that the handle uses non-buffered IO.
--*/ { NTSTATUS status; PIRP irp; PASYNC_FRAME pFrame; PASYNC_ADAPTER pAdapter=Info->Adapter;
pFrame=Info->AsyncFrame;
irp = IoAllocateIrp(Info->DeviceObject->StackSize, (BOOLEAN)FALSE); if (irp == NULL) { return(NDIS_STATUS_RESOURCES); }
InitSerialIrp(irp, Info, IOCTL_SERIAL_WAIT_ON_MASK, sizeof(ULONG));
irp->AssociatedIrp.SystemBuffer=&pFrame->WaitMask;
IoSetCompletionRoutine( irp, // irp to use
AsyncWaitMaskCompletionRoutine, // routine to call when irp is done
Info, // context to pass routine
TRUE, // call on success
TRUE, // call on error
TRUE); // call on cancel
//
// Now simply invoke the driver at its dispatch entry with the IRP.
//
Info->Flags |= ASYNC_FLAG_WAIT_MASK; REF_ASYNCINFO(Info, irp);
status = IoCallDriver(Info->DeviceObject, irp);
//
// Status for a local serial driver should be
// STATUS_SUCCESS since the irp should complete
// immediately because there are no read timeouts.
//
// For a remote serial driver, it will pend.
//
return(status); }
NTSTATUS AsyncPPPCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
/*++
This is the IO Completion routine for ReadFrame.
--*/ { NTSTATUS status; PASYNC_INFO pInfo; ULONG bytesReceived;
PASYNC_FRAME pFrame; PUCHAR frameStart, frameEnd; USHORT crcData; PUCHAR frameEnd2,frameStart2; LONG bytesWanted;
DeviceObject; // prevent compiler warnings
status = Irp->IoStatus.Status; bytesReceived=(ULONG)Irp->IoStatus.Information;
IoFreeIrp(Irp);
pInfo=Context;
pFrame=pInfo->AsyncFrame;
switch (status) {
case STATUS_SUCCESS:
pFrame=pInfo->AsyncFrame;
//
// Any bytes to process? This can happen if
// the WaitMask completes late and by the time
// we process the read, another event character has come
// in.
//
if (bytesReceived==0) { break; }
//
// Update num of bytes read total for this frame
//
pInfo->BytesRead = bytesReceived = pInfo->BytesRead + bytesReceived;
//
// Set frameEnd to last byte processed. Initially,
// we have processed nothing (i.e. processed up to
// the start of the first byte).
//
frameStart=pFrame->Frame + PPP_PADDING;
PROCESS_FRAME: //
// Now we have actuallyRead bytes unused
// Also, we may have a complete frame.
//
while (*frameStart == PPP_FLAG_BYTE && --bytesReceived) { frameStart++; }
//
// If we reach here, there is only a start FLAG...
//
if (bytesReceived == 0) { break; }
//
// frameEnd is set to the first byte not yet processed.
// If we are starting out, that is the first byte!
//
frameEnd=frameStart;
//
// Assume the start of the frame has the PPP_FLAG_BYTE
// Look for the second PPP_FLAG_BYTE (end of frame)
//
while (*frameEnd != PPP_FLAG_BYTE && --bytesReceived) { frameEnd++; }
//
// At this point...
// frameStart = beginning PPP_FLAG_BYTE seen
// frameEnd = end PPP_FLAG_BYTE
// bytesReceived = bytes after frameEnd not processed
//
//
// if bytesReceived is 0, we ran out of space before hitting
// the END flag. We will have to wait for the next round
//
// NOTE: if BytesRead gets too high we trash the frame
// because we could not find the FLAG_BYTE
//
if (bytesReceived==0) { break; } if (*(pFrame->Frame+PPP_PADDING) != PPP_FLAG_BYTE) {
//
// We had garbage at the start. Remove the garbage.
//
pInfo->SerialStats.AlignmentErrors++;
//
// Tell the transport above us that we dropped a packet
// Hopefully, it will quickly resync.
//
AsyncIndicateFragment( pInfo, WAN_ERROR_ALIGNMENT);
goto NEXT_PPP_FRAME; }
//
// Length of frame is frameEnd - frameStart
//
bytesWanted = (LONG)(frameEnd - frameStart);
frameEnd2 = frameStart2 = frameStart;
//
// Replace back all control chars, ESC, and FLAG chars
//
while (bytesWanted-- > 0) { if ((*frameEnd2=*frameStart2++) == PPP_ESC_BYTE) {
//
// We have not run the CRC check yet!!
// We have be careful about sending bytesWanted
// back to -1 on corrupted data
//
bytesWanted--;
*frameEnd2 = (*frameStart2++ ^ 0x20); }
frameEnd2++; }
if (*frameStart2 != PPP_FLAG_BYTE) { DbgTracef(-2,("BAD PPP FRAME at 0x%.8x 0x%.8x\n", frameStart, frameEnd2)); }
//
// if CRC-16, get 16 bit CRC from end of frame
//
frameEnd2 -= 2;
//
// Little endian assumptions for CRC
//
crcData=(USHORT)frameEnd2[0]+(USHORT)(frameEnd2[1] << 8); crcData ^= 0xFFFF;
//
// Change the bytesWanted field to what it normally is
// without the byte stuffing (length of frame between flags)
// Note that it can be -1 if only one byte was
// found in between the flag bytes
//
bytesWanted = (LONG)(frameEnd2 - frameStart);
//
// If we get some sort of garbage inbetween
// the PPP flags, we just assume it is noise and
// discard it. We don't record a PPP CRC error just
// an alignment error.
//
if (bytesWanted < 3) { pInfo->SerialStats.AlignmentErrors++; //
// Tell the transport above us that we dropped a packet
// Hopefully, it will quickly resync.
//
AsyncIndicateFragment(pInfo, WAN_ERROR_ALIGNMENT);
goto NEXT_PPP_FRAME; }
//
// get CRC from FLAG byte to FLAG byte
//
if (crcData != CalcCRCPPP(frameStart, bytesWanted)) {
DbgTracef(0,("---CRC check failed on control char frame!\n"));
//
// Record the CRC error
//
pInfo->SerialStats.CRCErrors++;
//
// Tell the transport above us that we dropped a packet
// Hopefully, it will quickly resync.
//
AsyncIndicateFragment( pInfo, WAN_ERROR_CRC);
goto NEXT_PPP_FRAME; }
/*
for ( i = 0; (i < (ULONG)bytesWanted) && (i < 48); i++ ) { if ( (i & 15) == 0 ) DbgTracef(-1, ("\nrx:\t") ); DbgTracef(-1, ("%.2x ", frameStart[i]) ); } DbgTracef(-1, ("\n") ); */ { KIRQL irql; NDIS_STATUS Status; PASYNC_ADAPTER Adapter = pInfo->Adapter;
KeRaiseIrql( (KIRQL)DISPATCH_LEVEL, &irql ); //
// Tell the transport above (or really RasHub) that the connection
// is now up. We have a new link speed, frame size, quality of service
//
NdisMWanIndicateReceive(&Status, Adapter->MiniportHandle, pInfo->NdisLinkContext, frameStart, // ptr to start of packet
bytesWanted); // Total packet length - header
NdisMWanIndicateReceiveComplete(Adapter->MiniportHandle, pInfo->NdisLinkContext);
KeLowerIrql( irql ); } NEXT_PPP_FRAME:
//
// if bytesReceived == 0 no frame was found
// thus we must keep the current frame and continue
// processing
//
if (bytesReceived) {
//
// Calculate how much of what we received
// just got passed up as a frame and move the
// rest to the beginning.
//
frameStart=pFrame->Frame + PPP_PADDING; frameEnd2=frameStart + pInfo->BytesRead; pInfo->BytesRead = bytesReceived = (ULONG)(frameEnd2-frameEnd);
ASYNC_MOVE_MEMORY( frameStart, // dest
frameEnd, // src
bytesReceived); // length
//
// Need at least four bytes for a frame to exist
//
if (bytesReceived > 3) { goto PROCESS_FRAME; } }
break;
case STATUS_PENDING: DbgTracef(0,("---ASYNC: Status PENDING on read\n")); pInfo->Flags &= ~(ASYNC_FLAG_PPP_READ); #if DBG
pInfo->PppreadsCompleted++; #endif
DEREF_ASYNCINFO(pInfo, Irp); // return(STATUS_MORE_PROCESSING_REQUIRED);
goto done;
case STATUS_CANCELLED: // else this is an anomally!
DbgTracef(-2,("---ASYNC: Status cancelled on read for unknown reason!!\n")); pInfo->Flags &= ~(ASYNC_FLAG_PPP_READ); DEREF_ASYNCINFO(pInfo, Irp); #if DBG
pInfo->PppreadsCompleted++; #endif
//return(STATUS_MORE_PROCESSING_REQUIRED);
goto done;
default: #if DBG
DbgPrint ("AsyncPPPCompletionRoutine: status == %x, no more reads\n", status) ; #endif
pInfo->Flags &= ~(ASYNC_FLAG_PPP_READ); DEREF_ASYNCINFO(pInfo, Irp); #if DBG
pInfo->PppreadsCompleted++; #endif
return(STATUS_MORE_PROCESSING_REQUIRED); }
if(status == STATUS_SUCCESS) { //
// Here we are at the end of processing this IRP so we go
// ahead and post another read from the serial port.
//
AsyncPPPWaitMask(pInfo); }
// We return STATUS_MORE_PROCESSING_REQUIRED so that the
// IoCompletionRoutine will stop working on the IRP.
pInfo->Flags &= ~(ASYNC_FLAG_PPP_READ); DEREF_ASYNCINFO(pInfo, Irp); #if DBG
pInfo->PppreadsCompleted++; #endif
done: if(status != STATUS_SUCCESS) { if(pInfo->PortState == PORT_CLOSING || pInfo->PortState == PORT_CLOSED) { KeSetEvent(&pInfo->ClosingEvent, 1, (BOOLEAN) FALSE); } }
return(STATUS_MORE_PROCESSING_REQUIRED); }
NTSTATUS AsyncPPPRead( IN PASYNC_INFO Info)
/*++
Assumption -- 0 length frames are not sent (this includes headers)!!! Also, this is NOT a synchronous operation. It is always asynchronous.
MUST use non-paged pool to read!!!
Routine Description:
This service writes Length bytes of data from the caller's Buffer to the "port" handle. It is assumed that the handle uses non-buffered IO.
--*/ { NTSTATUS status; PIRP irp; PDEVICE_OBJECT deviceObject=Info->DeviceObject; PFILE_OBJECT fileObject=Info->FileObject; PIO_STACK_LOCATION irpSp; PASYNC_FRAME pFrame; PASYNC_ADAPTER pAdapter=Info->Adapter; PIO_COMPLETION_ROUTINE routine;
pFrame=Info->AsyncFrame;
//
// check if this port is closing down or already closed
//
if (Info->PortState == PORT_CLOSING || Info->PortState == PORT_CLOSED) {
if (Info->PortState == PORT_CLOSED) { DbgTracef(-2,("ASYNC: Port closed - but still reading on it!\n")); }
//
// Acknowledge that the port is closed
//
KeSetEvent(&Info->ClosingEvent, // Event
1, // Priority
(BOOLEAN)FALSE); // Wait (does not follow)
//
// Ok, if this happens, we are shutting down. Stop
// posting reads. Don't make it try to deallocate the irp!
//
return(STATUS_MORE_PROCESSING_REQUIRED); }
//
// Has our stack counter reached its max?
//
if ( Info->ReadStackCounter > 1 ) {
//
// Send off the worker thread to compress this frame
//
ExInitializeWorkItem(&pFrame->WorkItem, (PWORKER_THREAD_ROUTINE) AsyncPPPRead, Info);
//
// reset stack counter since we are scheduling
// a worker thread
//
Info->ReadStackCounter=0;
//
// We choose to be nice and use delayed.
//
ExQueueWorkItem(&pFrame->WorkItem, DelayedWorkQueue);
return NDIS_STATUS_PENDING; }
//
// One more stack used up.
//
Info->ReadStackCounter++;
// get irp from frame (each frame has an irp allocate with it)
irp = IoAllocateIrp(Info->DeviceObject->StackSize, (BOOLEAN)FALSE); if (irp == NULL) { return(NDIS_STATUS_RESOURCES); }
// Setup this irp with defaults
AsyncSetupIrp(pFrame, irp);
//
// If we've read all the bytes we can and we still do not
// have a frame, we trash our buffer and start over
// again.
//
if (Info->BytesRead >= (DEFAULT_EXPANDED_PPP_MAX_FRAME_SIZE - PPP_PADDING)) {
Info->SerialStats.BufferOverrunErrors++;
//
// Tell the transport above us that we dropped a packet
// Hopefully, it will quickly resync.
//
AsyncIndicateFragment(Info, WAN_ERROR_BUFFEROVERRUN);
Info->BytesRead=0; } irp->AssociatedIrp.SystemBuffer = pFrame->Frame + Info->BytesRead + PPP_PADDING;
//
// Get a pointer to the stack location for the first driver. This will be
// used to pass the original function codes and parameters.
//
irpSp = IoGetNextIrpStackLocation(irp); irpSp->MajorFunction = IRP_MJ_READ; irpSp->FileObject = fileObject; if (fileObject->Flags & FO_WRITE_THROUGH) { irpSp->Flags = SL_WRITE_THROUGH; }
//
// If this write operation is to be performed without any caching, set the
// appropriate flag in the IRP so no caching is performed.
//
irp->Flags |= IRP_READ_OPERATION;
if (fileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) { irp->Flags |= IRP_NOCACHE; }
//
// Copy the caller's parameters to the service-specific portion of the
// IRP.
//
irpSp->Parameters.Read.Length = DEFAULT_EXPANDED_PPP_MAX_FRAME_SIZE - Info->BytesRead - PPP_PADDING;
irpSp->Parameters.Read.Key = 0; // we don't use a key
irpSp->Parameters.Read.ByteOffset = fileObject->CurrentByteOffset;
if ( Info->GetLinkInfo.SendFramingBits & SLIP_FRAMING ) {
routine=AsyncSLIPCompletionRoutine; Info->Flags |= ASYNC_FLAG_SLIP_READ;
} else { Info->Flags |= ASYNC_FLAG_PPP_READ; #if DBG
Info->Pppreads++; #endif
routine=AsyncPPPCompletionRoutine; }
REF_ASYNCINFO(Info, irp); IoSetCompletionRoutine( irp, // irp to use
routine, // routine to call when irp is done
Info, // context to pass routine
TRUE, // call on success
TRUE, // call on error
TRUE); // call on cancel
//
// We DO NOT insert the packet at the head of the IRP list for the thread.
// because we do NOT really have an IoCompletionRoutine that does
// anything with the thread.
//
//
// Now simply invoke the driver at its dispatch entry with the IRP.
//
status = IoCallDriver(deviceObject, irp);
//
// unroll the stack counter
//
if ( Info->ReadStackCounter > 0 ) {
Info->ReadStackCounter--; }
//
// Status for a local serial driver should be
// STATUS_SUCCESS since the irp should complete
// immediately because there are no read timeouts.
//
// For a remote serial driver, it will pend.
//
return(status); }
NTSTATUS AsyncWaitMaskCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
/*++
This is the IO Completion routine for ReadFrame.
--*/ { NTSTATUS status; PASYNC_INFO pInfo=Context; PASYNC_FRAME pFrame; DeviceObject; // avoid compiler warnings
status = Irp->IoStatus.Status; pFrame=pInfo->AsyncFrame;
IoFreeIrp(Irp);
// check if this port is closing down or already closed
if (pInfo->PortState == PORT_CLOSING || pInfo->PortState == PORT_CLOSED) {
if (pInfo->PortState == PORT_CLOSED) { DbgTracef(-2,("ASYNC: Port closed - but still reading on it!\n")); }
//
// Acknowledge that the port is closed
//
KeSetEvent( &pInfo->ClosingEvent, // Event
1, // Priority
(BOOLEAN)FALSE); // Wait (does not follow)
DbgTracef(1,("ASYNC: PPP no longer holds the wait_on_mask\n"));
pInfo->Flags &= ~(ASYNC_FLAG_WAIT_MASK);
DEREF_ASYNCINFO(pInfo, Irp); //
// Ok, if this happens, we are shutting down. Stop
// posting reads. Don't make it try to deallocate the irp!
//
return(STATUS_MORE_PROCESSING_REQUIRED); }
// wait failed
//
if (status != STATUS_SUCCESS) {
pInfo->PortState = PORT_FRAMING; pInfo->Flags &= ~(ASYNC_FLAG_WAIT_MASK); DEREF_ASYNCINFO(pInfo, Irp); return(STATUS_MORE_PROCESSING_REQUIRED); }
//
// Send off a irp to check comm status
// of this port (because we suspect a problem).
//
if (pFrame->WaitMask & SERIAL_EV_ERR) { AsyncCheckCommStatus(pInfo); }
//
// Check if RLSD or DSR changed state.
// If so, we probably have to complete and IRP
//
if (pFrame->WaitMask & (SERIAL_EV_RLSD | SERIAL_EV_DSR)) { TryToCompleteDDCDIrp(pInfo); }
#if DBG
if (status == STATUS_INVALID_PARAMETER) {
DbgPrint("ASYNC: PPP BAD WAIT MASK! Irp is at 0x%.8x\n",Irp); DbgBreakPoint(); } #endif
//
// If we have some more bytes (specifically the event character)
// in the buffer, let's process those new bytes
//
if (pFrame->WaitMask & (SERIAL_EV_RXFLAG | SERIAL_EV_RX80FULL)) {
//
// Read current buffer and try to process a frame
//
AsyncPPPRead(pInfo);
} else { //
// Set another WaitMask call
//
AsyncPPPWaitMask(pInfo); } pInfo->Flags &= ~(ASYNC_FLAG_WAIT_MASK); DEREF_ASYNCINFO(pInfo, Irp); // We return STATUS_MORE_PROCESSING_REQUIRED so that the
// IoCompletionRoutine will stop working on the IRP.
return(STATUS_MORE_PROCESSING_REQUIRED); }
|