|
|
/*++
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
#if DBG
#define __FILE_SIG__ 'tcoi'
#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);
KeInitializeEvent(&pNewInfo->AsyncEvent, SynchronizationEvent, TRUE);
// increment the reference count (don't kill this adapter)
InterlockedIncrement(&Adapter->RefCount);
//
// Initialize the refcount on the new asyncinfo block.
//
pNewInfo->RefCount++;
#if DBG
InitializeListHead(&pNewInfo->lePendingRequests);
{
PENDING_REQUEST *_Request = ExAllocatePoolWithTag(NonPagedPool, sizeof(PENDING_REQUEST), 'nepA'); if(NULL != _Request) { _Request->pvContext = pNewInfo; _Request->Sig = __FILE_SIG__; _Request->lineNum = __LINE__; InsertTailList(&pNewInfo->lePendingRequests, &_Request->le); } \ } #endif
pNewInfo->Flags |= ASYNC_FLAG_ASYNCMAC_OPEN;
//
// Set signalled state of event to not-signalled.
//
KeClearEvent(&pNewInfo->AsyncEvent);
// 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;
//
// Initialize the Extended ACCM information so that we always
// escape 0x7D and 0x7E and we never escape 0x5E
//
pNewInfo->ExtendedACCM[0] = (ULONG) -1; pNewInfo->ExtendedACCM[3] = (ULONG) 0x60000000;
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 retStatus; 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); retStatus = IoCallDriver(pNewInfo->DeviceObject, irp); if (retStatus == STATUS_PENDING) { KeWaitForSingleObject(&AsyncIoCtx->Event, Executive, KernelMode, FALSE, NULL); retStatus = AsyncIoCtx->IoStatus.Status; }
IoFreeIrp(irp); AsyncFreeIoCtx(AsyncIoCtx);
if (retStatus == 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.QuadPart = Int32x32To64(10000, -10000); // wait for 10 secs
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 retStatus; 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); retStatus = IoCallDriver(pNewInfo->DeviceObject, irp);
if (retStatus == STATUS_PENDING) { KeWaitForSingleObject(&AsyncIoCtx->Event, Executive, KernelMode, FALSE, NULL); retStatus = 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));
pNewInfo->Flags &= ~(ASYNC_FLAG_ASYNCMAC_OPEN); //
// Deref the ref applied in IOCTL_ASYNCMAC_OPEN
//
DEREF_ASYNCINFO(pNewInfo, pNewInfo);
//
// Wait for ref on pNewInfo to go to 0
//
KeWaitForSingleObject(&pNewInfo->AsyncEvent, UserRequest, KernelMode, FALSE, NULL); // 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; }
|