windows-nt/Source/XPSP1/NT/net/rras/ndis/asyncmac/ioctl.c
2020-09-26 16:20:57 +08:00

763 lines
24 KiB
C

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
ioctl.c
Abstract:
This is the main file for handling DevIOCtl calls for AsyncMAC.
This driver conforms to the NDIS 3.0 interface.
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"
#ifdef NDIS_NT
#include <ntiologc.h>
#endif
// asyncmac.c will define the global parameters.
VOID
AsyncSendLineUp(
PASYNC_INFO pInfo
)
{
PASYNC_ADAPTER pAdapter = pInfo->Adapter;
NDIS_MAC_LINE_UP MacLineUp;
//
// divide the baud by 100 because NDIS wants it in 100s of bits per sec
//
MacLineUp.LinkSpeed = pInfo->LinkSpeed / 100;
MacLineUp.Quality = pInfo->QualOfConnect;
MacLineUp.SendWindow = ASYNC_WINDOW_SIZE;
MacLineUp.ConnectionWrapperID = pInfo;
MacLineUp.NdisLinkHandle = pInfo;
MacLineUp.NdisLinkContext = pInfo->NdisLinkContext;
//
// Tell the transport above (or really RasHub) that the connection
// is now up. We have a new link speed, frame size, quality of service
//
NdisMIndicateStatus(pAdapter->MiniportHandle,
NDIS_STATUS_WAN_LINE_UP, // General Status.
&MacLineUp, // (baud rate in 100 bps).
sizeof(NDIS_MAC_LINE_UP));
//
// Get the next binding (in case of multiple bindings like BloodHound)
//
pInfo->NdisLinkContext = MacLineUp.NdisLinkContext;
}
NTSTATUS
AsyncIOCtlRequest(
IN PIRP pIrp,
IN PIO_STACK_LOCATION pIrpSp
)
/*++
Routine Description:
This routine takes an irp and checks to see if the IOCtl
is a valid one. If so, it performs the IOCtl and returns
any errors in the process.
Return Value:
The function value is the final status of the IOCtl.
--*/
{
NTSTATUS status;
ULONG funcCode;
PVOID pBufOut;
ULONG InBufLength, OutBufLength;
NDIS_HANDLE hNdisEndPoint;
PASYMAC_CLOSE pCloseStruct;
PASYMAC_OPEN pOpenStruct;
PASYMAC_DCDCHANGE pDCDStruct;
PASYNC_ADAPTER Adapter;
LARGE_INTEGER li ;
//
// Initialize locals.
//
status = STATUS_SUCCESS;
//
// Initialize the I/O Status block
//
InBufLength = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
OutBufLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
funcCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode;
//
// Validate the function code
//
#ifdef MY_DEVICE_OBJECT
if ( (funcCode >> 16) != FILE_DEVICE_ASYMAC ) {
return STATUS_INVALID_PARAMETER;
}
#else
if ( (funcCode >> 16) != FILE_DEVICE_NETWORK ) {
return STATUS_INVALID_PARAMETER;
}
#endif
//
// Get a quick ptr to the IN/OUT SystemBuffer
//
pBufOut = pIrp->AssociatedIrp.SystemBuffer;
switch ( funcCode ) {
case IOCTL_ASYMAC_OPEN:
DbgTracef(0,("AsyncIOCtlRequest: IOCTL_ASYMAC_OPEN.\n"));
pIrp->IoStatus.Information = sizeof(ASYMAC_OPEN);
if (InBufLength >= sizeof(ASYMAC_OPEN) &&
OutBufLength >= sizeof(ASYMAC_OPEN)) {
pOpenStruct = pBufOut;
} else {
status = STATUS_INFO_LENGTH_MISMATCH;
}
break;
case IOCTL_ASYMAC_CLOSE:
DbgTracef(0,("AsyncIOCtlRequest: IOCTL_ASYMAC_CLOSE\n"));
if ( InBufLength >= sizeof(ASYMAC_CLOSE) ) {
pCloseStruct = pBufOut;
} else {
status = STATUS_INFO_LENGTH_MISMATCH;
}
break;
case IOCTL_ASYMAC_TRACE:
#if DBG
DbgPrint("AsyncIOCtlRequest: IOCTL_ASYMAC_TRACE.\n");
if ( InBufLength >= sizeof(TraceLevel) ) {
CHAR *pTraceLevel=pBufOut;
TraceLevel=*pTraceLevel;
} else {
status = STATUS_INFO_LENGTH_MISMATCH;
}
#endif
return status;
break;
case IOCTL_ASYMAC_DCDCHANGE:
DbgTracef(0,("AsyncIOCtlRequest: IOCTL_ASYMAC_DCDCHANGE.\n"));
if ( InBufLength >= sizeof(ASYMAC_DCDCHANGE) ) {
pDCDStruct = pBufOut;
} else {
status = STATUS_INFO_LENGTH_MISMATCH;
}
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
}
//
// Check if we already have an error (like STATUS_INFO_LENGTH_MISMATCH).
//
if ( status != STATUS_SUCCESS ) {
return status;
}
//
// Since most of IOCTL structs are similar
// we get the Adapter and hNdisEndPoint here using
// the StatsStruct (we could several of them)
//
pOpenStruct = pBufOut;
hNdisEndPoint = pOpenStruct->hNdisEndpoint;
//
// No error yet, let's go ahead and grab the global lock...
//
if ((Adapter = GlobalAdapter) == NULL ) {
return ASYNC_ERROR_NO_ADAPTER;
}
// there's a race condition right here that I am
// not bothering to get rid of because it would
// require the removal of this adapter in between
// here (which is, for all intensive purposes, impossible).
// Hmm... now that we have the lock we can do stuff
NdisAcquireSpinLock(&Adapter->Lock);
// Here we do the real work for the function call
switch ( funcCode ) {
case IOCTL_ASYMAC_OPEN:
{
PASYNC_INFO pNewInfo = NULL;
USHORT i;
PDEVICE_OBJECT deviceObject;
PFILE_OBJECT fileObject;
OBJECT_HANDLE_INFORMATION handleInformation;
//
// Get a new AsyncInfo
//
pNewInfo = (PASYNC_INFO)
ExAllocateFromNPagedLookasideList(&AsyncInfoList);
//
// Check if we could not find an open port
//
if ( pNewInfo == NULL ) {
NdisReleaseSpinLock(&Adapter->Lock);
return ASYNC_ERROR_NO_PORT_AVAILABLE;
}
RtlZeroMemory(pNewInfo, sizeof(ASYNC_INFO));
pNewInfo->Adapter = Adapter;
status =
AsyncGetFrameFromPool(pNewInfo, &pNewInfo->AsyncFrame);
if (status != NDIS_STATUS_SUCCESS) {
ExFreeToNPagedLookasideList(&AsyncInfoList, pNewInfo);
NdisReleaseSpinLock(&Adapter->Lock);
return ASYNC_ERROR_NO_PORT_AVAILABLE;
}
KeInitializeEvent(&pNewInfo->DetectEvent,
SynchronizationEvent,
TRUE);
// increment the reference count (don't kill this adapter)
InterlockedIncrement(&Adapter->RefCount);
// release spin lock so we can do some real work.
NdisReleaseSpinLock(&Adapter->Lock);
//
// Reference the file object so the target device can be found and
// the access rights mask can be used in the following checks for
// callers in user mode. Note that if the handle does not refer to
// a file object, then it will fail.
//
status = ObReferenceObjectByHandle(pOpenStruct->FileHandle,
FILE_READ_DATA | FILE_WRITE_DATA,
*IoFileObjectType,
UserMode,
(PVOID) &fileObject,
&handleInformation);
if (!NT_SUCCESS(status)) {
pNewInfo->PortState = PORT_CLOSED;
NdisAcquireSpinLock(&Adapter->Lock);
// RemoveEntryList(&pNewInfo->Linkage);
ExFreeToNPagedLookasideList(&Adapter->AsyncFrameList,
pNewInfo->AsyncFrame);
NdisReleaseSpinLock(&Adapter->Lock);
ExFreeToNPagedLookasideList(&AsyncInfoList,
pNewInfo);
return ASYNC_ERROR_NO_PORT_AVAILABLE;
}
//
// Init the portinfo block
//
InitializeListHead(&pNewInfo->DDCDQueue);
// Ok, we've gotten this far. We have a port.
// Own port, and check params...
// Nothing can be done to the port until it comes
// out of the PORT_OPENING state.
pNewInfo->PortState = PORT_OPENING;
NdisAllocateSpinLock(&pNewInfo->Lock);
//
// Get the address of the target device object. Note that this was already
// done for the no intermediate buffering case, but is done here again to
// speed up the turbo write path.
//
deviceObject = IoGetRelatedDeviceObject(fileObject);
ObReferenceObject(deviceObject);
// ok, we have a VALID handle of *something*
// we do NOT assume that the handle is anything
// in particular except a device which accepts
// non-buffered IO (no MDLs) Reads and Writes
// set new info...
pNewInfo->Handle = pOpenStruct->FileHandle;
//
// Tuck away link speed for line up
// and timeouts
//
pNewInfo->LinkSpeed = pOpenStruct->LinkSpeed;
//
// Return endpoint to RASMAN
//
pOpenStruct->hNdisEndpoint =
pNewInfo->hNdisEndPoint = pNewInfo;
// Get parameters set from Registry and return our capabilities
pNewInfo->QualOfConnect = pOpenStruct->QualOfConnect;
pNewInfo->PortState = PORT_FRAMING;
pNewInfo->FileObject = fileObject;
pNewInfo->DeviceObject = deviceObject;
pNewInfo->NdisLinkContext = NULL;
//
// Initialize the NDIS_WAN_GET_LINK_INFO structure.
//
pNewInfo->GetLinkInfo.MaxSendFrameSize = DEFAULT_PPP_MAX_FRAME_SIZE;
pNewInfo->GetLinkInfo.MaxRecvFrameSize = DEFAULT_PPP_MAX_FRAME_SIZE;
pNewInfo->GetLinkInfo.HeaderPadding = DEFAULT_PPP_MAX_FRAME_SIZE;
pNewInfo->GetLinkInfo.TailPadding = 4;
pNewInfo->GetLinkInfo.SendFramingBits = PPP_FRAMING;
pNewInfo->GetLinkInfo.RecvFramingBits = PPP_FRAMING;
pNewInfo->GetLinkInfo.SendCompressionBits = 0;
pNewInfo->GetLinkInfo.RecvCompressionBits = 0;
pNewInfo->GetLinkInfo.SendACCM = (ULONG) -1;
pNewInfo->GetLinkInfo.RecvACCM = (ULONG) -1;
ASYNC_ZERO_MEMORY(&(pNewInfo->SerialStats), sizeof(SERIAL_STATS));
NdisAcquireSpinLock(&Adapter->Lock);
InsertHeadList(&Adapter->ActivePorts, &pNewInfo->Linkage);
NdisReleaseSpinLock(&Adapter->Lock);
//
// Send a line up to the WAN wrapper.
//
AsyncSendLineUp(pNewInfo);
//
// We send a special IRP to the serial driver to set it in RAS friendly mode
// where it will not complete write requests until the packet has been transmitted
// on the wire. This is mostly important in case of intelligent controllers.
//
pNewInfo->WaitMaskToUse =
(SERIAL_EV_RXFLAG | SERIAL_EV_RLSD | SERIAL_EV_DSR |
SERIAL_EV_RX80FULL | SERIAL_EV_ERR) ;
{
NTSTATUS status;
PASYNC_IO_CTX AsyncIoCtx;
PIRP irp;
irp =
IoAllocateIrp(pNewInfo->DeviceObject->StackSize, (BOOLEAN)FALSE);
if (irp != NULL) {
AsyncIoCtx = AsyncAllocateIoCtx(TRUE, pNewInfo);
if (AsyncIoCtx == NULL) {
IoFreeIrp(irp);
irp = NULL;
}
}
if (irp != NULL) {
#define IOCTL_SERIAL_PRIVATE_RAS CTL_CODE(FILE_DEVICE_SERIAL_PORT,4000,METHOD_BUFFERED,FILE_ANY_ACCESS)
InitSerialIrp(irp,
pNewInfo,
IOCTL_SERIAL_PRIVATE_RAS,
sizeof(ULONG));
AsyncIoCtx->WriteBufferingEnabled =
Adapter->WriteBufferingEnabled;
irp->AssociatedIrp.SystemBuffer=
&AsyncIoCtx->WriteBufferingEnabled;
IoSetCompletionRoutine(irp, // irp to use
SerialIoSyncCompletionRoutine, // routine to call when irp is done
AsyncIoCtx, // 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.
//
KeClearEvent(&AsyncIoCtx->Event);
status = IoCallDriver(pNewInfo->DeviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&AsyncIoCtx->Event,
Executive,
KernelMode,
FALSE,
NULL);
status = AsyncIoCtx->IoStatus.Status;
}
IoFreeIrp(irp);
AsyncFreeIoCtx(AsyncIoCtx);
if (status == STATUS_SUCCESS) {
//
// this means that the driver below is DIGI. we should disable setting of the EV_ERR
// flags in this case.
//
pNewInfo->WaitMaskToUse &= ~SERIAL_EV_ERR;
}
}
}
//
// Start the detect framing out with a 6 byte read to get the header
//
pNewInfo->BytesWanted=6;
pNewInfo->BytesRead=0;
//
// Start reading.
//
AsyncStartReads(pNewInfo);
if (NdisInterlockedIncrement(&glConnectionCount) == 1) {
ObReferenceObject(AsyncDeviceObject);
}
break;
}
case IOCTL_ASYMAC_TRACE:
NdisReleaseSpinLock(&Adapter->Lock);
status = STATUS_SUCCESS;
break;
case IOCTL_ASYMAC_CLOSE:
case IOCTL_ASYMAC_DCDCHANGE:
{
PASYNC_INFO pNewInfo; // ptr to open port if found
USHORT i;
PLIST_ENTRY pListEntry;
BOOLEAN Valid = FALSE;
switch (funcCode) {
case IOCTL_ASYMAC_CLOSE:
{
NDIS_MAC_LINE_DOWN AsyncLineDown;
pNewInfo = (PASYNC_INFO)pCloseStruct->hNdisEndpoint;
// Verify that the pointer is a valid ASYNC_INFO
for (pListEntry=Adapter->ActivePorts.Flink;
pListEntry!=&Adapter->ActivePorts;
pListEntry=pListEntry->Flink)
{
if (&pNewInfo->Linkage==pListEntry)
{
Valid = TRUE;
break;
}
}
if (!Valid) {
status=ASYNC_ERROR_PORT_NOT_FOUND;
break;
}
// release spin lock so we can do some real work.
NdisReleaseSpinLock(&Adapter->Lock);
NdisAcquireSpinLock(&pNewInfo->Lock);
// ASSERT(pNewInfo->PortState == PORT_FRAMING);
if(pNewInfo->PortState != PORT_FRAMING)
{
KdPrint(("AsyncIOCtlRequest: IOCTL_ASYMAC_CLOSE."));
KdPrint(("PortState = %d != PORT_FRAMING\n", pNewInfo->PortState));
NdisReleaseSpinLock(&pNewInfo->Lock);
return ASYNC_ERROR_PORT_BAD_STATE;
// break;
}
AsyncLineDown.NdisLinkContext = pNewInfo->NdisLinkContext;
// Signal that port is closing.
pNewInfo->PortState = PORT_CLOSING;
//Set MUTEX to wait on
KeInitializeEvent(&pNewInfo->ClosingEvent, // Event
SynchronizationEvent, // Event type
(BOOLEAN)FALSE); // Not signalled state
NdisReleaseSpinLock(&pNewInfo->Lock);
//
// If we have an outstanding Detect worker
// wait for it to complete!
//
KeWaitForSingleObject(&pNewInfo->DetectEvent,
UserRequest,
KernelMode,
FALSE,
NULL);
//
// now we must send down an IRP do cancel
// any request pending in the serial driver
//
CancelSerialRequests(pNewInfo);
//
// Also, cancel any outstanding DDCD irps
//
AsyncCancelAllQueued(&pNewInfo->DDCDQueue);
// Synchronize closing with the read irp
li.LowPart = 5000 ;
li.HighPart = 0 ;
if (KeWaitForSingleObject (&pNewInfo->ClosingEvent,// PVOID Object,
UserRequest, // KWAIT_REASON WaitReason,
KernelMode, // KPROCESSOR_MODE WaitMode,
(BOOLEAN)FALSE, // BOOLEAN Alertable,
&li) == STATUS_TIMEOUT) {
// If the wait fails cause another flush
//
NTSTATUS status;
PIRP irp;
PASYNC_IO_CTX AsyncIoCtx;
irp=
IoAllocateIrp(pNewInfo->DeviceObject->StackSize, (BOOLEAN)FALSE);
if (irp == NULL)
goto DEREF ;
AsyncIoCtx = AsyncAllocateIoCtx(TRUE, pNewInfo);
if (AsyncIoCtx == NULL) {
IoFreeIrp(irp);
goto DEREF;
}
InitSerialIrp(irp,
pNewInfo,
IOCTL_SERIAL_PURGE,
sizeof(ULONG));
// kill all read and write threads.
AsyncIoCtx->SerialPurge =
SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT;
irp->AssociatedIrp.SystemBuffer=
&AsyncIoCtx->SerialPurge;
IoSetCompletionRoutine(irp, // irp to use
SerialIoSyncCompletionRoutine, // routine to call when irp is done
AsyncIoCtx, // 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.
//
KeClearEvent(&AsyncIoCtx->Event);
status = IoCallDriver(pNewInfo->DeviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&AsyncIoCtx->Event,
Executive,
KernelMode,
FALSE,
NULL);
status = AsyncIoCtx->IoStatus.Status;
}
IoFreeIrp(irp);
AsyncFreeIoCtx(AsyncIoCtx);
// if we do hit this code - wait for some time to let
// the read complete
//
KeDelayExecutionThread (KernelMode, FALSE, &li) ;
}
//
// Get rid of our reference to the serial port
//
DEREF:
ObDereferenceObject(pNewInfo->DeviceObject);
ObDereferenceObject(pNewInfo->FileObject);
NdisMIndicateStatus(Adapter->MiniportHandle,
NDIS_STATUS_WAN_LINE_DOWN, // General Status
&AsyncLineDown, // Specific Status
sizeof(NDIS_MAC_LINE_DOWN));
// reacquire spin lock
NdisAcquireSpinLock(&Adapter->Lock);
RemoveEntryList(&pNewInfo->Linkage);
// decrement the reference count because we're done.
InterlockedDecrement(&Adapter->RefCount);
pNewInfo->PortState = PORT_CLOSED;
NdisFreeSpinLock(&pNewInfo->Lock);
ExFreeToNPagedLookasideList(&Adapter->AsyncFrameList,
pNewInfo->AsyncFrame);
ExFreeToNPagedLookasideList(&AsyncInfoList,
pNewInfo);
if (NdisInterlockedDecrement(&glConnectionCount) == 0) {
ObDereferenceObject(AsyncDeviceObject);
}
break; // get out of case statement
}
case IOCTL_ASYMAC_DCDCHANGE:
pNewInfo = (PASYNC_INFO)pDCDStruct->hNdisEndpoint;
// Verify that the pointer is a valid ASYNC_INFO
for (pListEntry=Adapter->ActivePorts.Flink;
pListEntry!=&Adapter->ActivePorts;
pListEntry=pListEntry->Flink)
{
if (&pNewInfo->Linkage==pListEntry)
{
Valid = TRUE;
break;
}
}
//
// If the port is already closed, we WILL complain
//
if (!Valid || pNewInfo->PortState == PORT_CLOSED) {
status=ASYNC_ERROR_PORT_NOT_FOUND;
break;
}
//
// If any irps are pending, cancel all of them
// Only one irp can be outstanding at a time.
//
AsyncCancelAllQueued(&pNewInfo->DDCDQueue);
DbgTracef(0, ("ASYNC: Queueing up DDCD IRP\n"));
AsyncQueueIrp(&pNewInfo->DDCDQueue, pIrp);
//
// we'll have to wait for the SERIAL driver
// to flip DCD or DSR
//
status=STATUS_PENDING;
break;
} // end switch
NdisReleaseSpinLock(&Adapter->Lock);
return(status);
}
break;
} // end switch
return status;
}