717 lines
18 KiB
C
717 lines
18 KiB
C
/*++
|
|
|
|
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:
|
|
|
|
|
|
--*/
|
|
|
|
#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);
|
|
|
|
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.
|
|
//
|
|
|
|
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;
|
|
ULONG bitMask;
|
|
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);
|
|
|
|
|
|
bitMask = pInfo->Adapter->WanInfo.DesiredACCM;
|
|
|
|
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"));
|
|
|
|
case STATUS_CANCELLED:
|
|
// else this is an anomally!
|
|
DbgTracef(-2,("---ASYNC: Status cancelled on read for unknown reason!!\n"));
|
|
|
|
default:
|
|
#if DBG
|
|
DbgPrint ("AsyncPPPCompletionRoutine: status == %x, no more reads\n", status) ;
|
|
#endif
|
|
return(STATUS_MORE_PROCESSING_REQUIRED);
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
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);
|
|
|
|
// 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;
|
|
|
|
} else {
|
|
|
|
routine=AsyncPPPCompletionRoutine;
|
|
}
|
|
|
|
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"));
|
|
|
|
//
|
|
// 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;
|
|
|
|
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);
|
|
}
|
|
|
|
// We return STATUS_MORE_PROCESSING_REQUIRED so that the
|
|
// IoCompletionRoutine will stop working on the IRP.
|
|
return(STATUS_MORE_PROCESSING_REQUIRED);
|
|
}
|