// Copyright (c) 1997, Microsoft Corporation, all rights reserved // Copyright (c) 1997, Parallel Technologies, Inc., all rights reserved // // mp.c // RAS DirectParallel WAN mini-port/call-manager driver // Mini-port routines // // 01/07/97 Steve Cobb // 09/15/97 Jay Lowe, Parallel Technologies, Inc. #include "ptiwan.h" #include "ptilink.h" // The adapter control block address is recorded in this global as a debugging // aid. This global must not be read by any code. // ADAPTERCB* g_pDebugAdapter; // Default settings for the NDIS_WAN_CO_INFO capabilities of an adapter. // static NDIS_WAN_CO_INFO g_infoDefaults = { PTI_MaxFrameSize, // MaxFrameSize 1, // MaxSendWindow (placeholder) PPP_FRAMING // FramingBits | PPP_COMPRESS_ADDRESS_CONTROL | PPP_COMPRESS_PROTOCOL_FIELD, 0, // DesiredACCM }; // String constants for Win9x UNIMODEM emulation // CHAR g_szClient[] = "CLIENT"; #define CLIENTLEN 6 CHAR g_szClientServer[] = "CLIENTSERVER"; #define CLIENTSERVERLEN 12 // Async framing definitions. // #define PPPFLAGBYTE 0x7E #define PPPESCBYTE 0x7D #if DBG BOOLEAN g_fAssumeWin9x = FALSE; BOOLEAN g_fNoAccmFastPath = FALSE; #endif NDIS_PNP_CAPABILITIES PnpCaps = { 0, // Flags { NdisDeviceStateUnspecified, NdisDeviceStateUnspecified, NdisDeviceStateUnspecified } }; //----------------------------------------------------------------------------- // Local prototypes (alphabetically) //----------------------------------------------------------------------------- VOID AsyncFromHdlcFraming( IN UCHAR* pInBuf, IN ULONG ulInBufLen, OUT UCHAR* pOutBuf, OUT ULONG* pulOutBufLen, IN ULONG ulAccmMask ); USHORT CalculatePppFcs( IN UCHAR* pBuf, IN ULONG ulBufLen ); VOID FreeAdapter( IN ADAPTERCB* pAdapter ); NDIS_STATUS RegistrySettings( IN OUT ADAPTERCB* pAdapter, IN NDIS_HANDLE WrapperConfigurationContext ); BOOLEAN HdlcFromAsyncFraming( IN UCHAR* pInBuf, IN ULONG ulInBufLen, OUT UCHAR* pOutBuf, OUT ULONG* pulOutBufLen ); NDIS_STATUS QueryInformation( IN ADAPTERCB* pAdapter, IN VCCB* pLink, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesWritten, OUT PULONG BytesNeeded ); NDIS_STATUS SetInformation( IN ADAPTERCB* pAdapter, IN VCCB* pLink, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesRead, OUT PULONG BytesNeeded ); //----------------------------------------------------------------------------- // Mini-port handlers //----------------------------------------------------------------------------- NDIS_STATUS PtiInit( OUT PNDIS_STATUS OpenErrorStatus, OUT PUINT SelectedMediumIndex, IN PNDIS_MEDIUM MediumArray, IN UINT MediumArraySize, IN NDIS_HANDLE MiniportAdapterHandle, IN NDIS_HANDLE WrapperConfigurationContext ) // Standard 'MiniportInitialize' routine called by NDIS to initialize a // new WAN adapter. See DDK doc. The driver will receive no requests // until this initialization has completed. // { NDIS_STATUS status; ADAPTERCB* pAdapter; TRACE( TL_N, TM_Init, ( "PtiInit" ) ); #ifdef TESTMODE DbgBreakPoint(); #endif status = *OpenErrorStatus = NDIS_STATUS_SUCCESS; // Find the medium index in the array of media, looking for the only one // we support, 'NdisMediumCoWan'. // { UINT i; for (i = 0; i < MediumArraySize; ++i) { if (MediumArray[ i ] == NdisMediumCoWan) { break; } } if (i >= MediumArraySize) { TRACE( TL_A, TM_Init, ( "medium?" ) ); return NDIS_STATUS_FAILURE; } *SelectedMediumIndex = i; } // Allocate and zero a control block for the new adapter. // pAdapter = ALLOC_NONPAGED( sizeof(*pAdapter), MTAG_ADAPTERCB ); TRACE( TL_N, TM_Init, ( "PtiInit: pAdapter=$%p", pAdapter ) ); if (!pAdapter) { return NDIS_STATUS_RESOURCES; } NdisZeroMemory( pAdapter, sizeof(*pAdapter) ); // The adapter control block address is recorded in 'g_pDebugAdapter' as a // debugging aid only. This global is not to be read by any code. // g_pDebugAdapter = pAdapter; // Set a marker for easier memory dump browsing and future assertions. // pAdapter->ulTag = MTAG_ADAPTERCB; // Save the NDIS handle associated with this adapter for use in future // NdixXxx calls. // pAdapter->MiniportAdapterHandle = MiniportAdapterHandle; // Copy defaults NDISWAN information. Some of these are updated below. // NdisMoveMemory( &pAdapter->info, &g_infoDefaults, sizeof(pAdapter->info) ); do { // Read/write this adapter's registry settings. // status = RegistrySettings( pAdapter, WrapperConfigurationContext ); if (status != NDIS_STATUS_SUCCESS) { // Set 'usMaxVcs' to 0 as an indication to FreeAdapter that the // lookaside lists and pools were not initialized. // pAdapter->usMaxVcs = 0; break; } // Initialize lookaside lists, buffer pools, and packet pool. On NT, // lookaside depths are optimized by the system based on usage // regardless of the depth set, but choose something reasonable // anyway. // { NdisInitializeNPagedLookasideList( &pAdapter->llistWorkItems, NULL, NULL, 0, sizeof(NDIS_WORK_ITEM), MTAG_WORKITEM, 4 ); NdisInitializeNPagedLookasideList( &pAdapter->llistVcs, NULL, NULL, 0, sizeof(VCCB), MTAG_VCCB, 4 ); InitBufferPool( &pAdapter->poolFrameBuffers, PTI_FrameBufferSize, 0, 10, 0, TRUE, MTAG_FBUFPOOL ); InitPacketPool( &pAdapter->poolPackets, 0, 0, 10, 0, MTAG_PACKETPOOL ); } // Inform NDIS of the attributes of our adapter. Set the // 'MiniportAdapterContext' returned to us by NDIS when it calls our // handlers to the address of our adapter control block. Turn off // hardware oriented timeouts. // NdisMSetAttributesEx( MiniportAdapterHandle, (NDIS_HANDLE)pAdapter, (UINT)-1, NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT, NdisInterfaceInternal ); // Register the address family of our call manager with NDIS for the // newly bound adapter. We use the mini-port form of // RegisterAddressFamily instead of the protocol form, though that // would also work. With the protocol form, our internal call manager // would have to go thru NDIS to talk to the mini-port instead of just // calling directly. Since the DirectParallel call manager is not // useful with anything but the DirectParallel mini-port, this would be a waste. // The mini-port form also causes the call manager VC context to // automatically map to the mini-port VC context, which is exactly // what we want. // // NDIS notifies all call manager clients of the new family we // register. The TAPI proxy is the only client expected to be // interested. NDISWAN will receive the notification, but ignore it // and wait for the TAPI proxy to notify it of the proxied version. // { NDIS_CALL_MANAGER_CHARACTERISTICS ncmc; CO_ADDRESS_FAMILY family; NdisZeroMemory( &family, sizeof(family) ); family.MajorVersion = NDIS_MajorVersion; family.MinorVersion = NDIS_MinorVersion; family.AddressFamily = CO_ADDRESS_FAMILY_TAPI_PROXY; NdisZeroMemory( &ncmc, sizeof(ncmc) ); ncmc.MajorVersion = NDIS_MajorVersion; ncmc.MinorVersion = NDIS_MinorVersion; ncmc.CmCreateVcHandler = PtiCmCreateVc; ncmc.CmDeleteVcHandler = PtiCmDeleteVc; ncmc.CmOpenAfHandler = PtiCmOpenAf; ncmc.CmCloseAfHandler = PtiCmCloseAf; ncmc.CmRegisterSapHandler = PtiCmRegisterSap; ncmc.CmDeregisterSapHandler = PtiCmDeregisterSap; ncmc.CmMakeCallHandler = PtiCmMakeCall; ncmc.CmCloseCallHandler = PtiCmCloseCall; ncmc.CmIncomingCallCompleteHandler = PtiCmIncomingCallComplete; // no CmAddPartyHandler // no CmDropPartyHandler ncmc.CmActivateVcCompleteHandler = PtiCmActivateVcComplete; ncmc.CmDeactivateVcCompleteHandler = PtiCmDeactivateVcComplete; ncmc.CmModifyCallQoSHandler = PtiCmModifyCallQoS; ncmc.CmRequestHandler = PtiCmRequest; TRACE( TL_I, TM_Mp, ( "PtiInit: NdisMCmRegAf" ) ); status = NdisMCmRegisterAddressFamily( MiniportAdapterHandle, &family, &ncmc, sizeof(ncmc) ); TRACE( TL_I, TM_Mp, ( "PtiInit: NdisMCmRegAf=$%x", status ) ); } } while (FALSE); if (status == NDIS_STATUS_SUCCESS) { // Add a reference that will eventually be removed by an NDIS call to // the LmpHalt handler. // ReferenceAdapter( pAdapter ); } else { // Failed, so undo whatever portion succeeded. // if (pAdapter) { FreeAdapter( pAdapter ); } } TRACE( TL_V, TM_Init, ( "PtiInit: Exit: status=$%x", status ) ); return status; } VOID PtiHalt( IN NDIS_HANDLE MiniportAdapterContext ) // Standard 'MiniportHalt' routine called by NDIS to deallocate all // resources attached to the adapter. NDIS does not make any other calls // for this mini-port adapter during or after this call. NDIS will not // call this routine when packets indicated as received have not been // returned, or when any VC is created and known to NDIS. Runs at PASSIVE // IRQL. // { ADAPTERCB* pAdapter; TRACE( TL_N, TM_Mp, ( "PtiHalt" ) ); pAdapter = (ADAPTERCB* )MiniportAdapterContext; if (pAdapter->ulTag != MTAG_ADAPTERCB) { ASSERT( !"Atag?" ); return; } DereferenceAdapter( pAdapter ); TRACE( TL_V, TM_Mp, ( "PtiHalt: Exit" ) ); } NDIS_STATUS PtiReset( OUT PBOOLEAN AddressingReset, IN NDIS_HANDLE MiniportAdapterContext ) // Standard 'MiniportReset' routine called by NDIS to reset the driver's // software state. // { TRACE( TL_N, TM_Mp, ( "PtiReset" ) ); return NDIS_STATUS_NOT_RESETTABLE; } VOID PtiReturnPacket( IN NDIS_HANDLE MiniportAdapterContext, IN PNDIS_PACKET Packet ) // Standard 'MiniportReturnPacket' routine called by NDIS when a packet // used to indicate a receive has been released by the driver above. // { VCCB* pVc; CHAR* pBuffer; ADAPTERCB* pAdapter; NDIS_BUFFER* pTrimmedBuffer; PACKETHEAD* pHead; PACKETPOOL* pPool; TRACE( TL_V, TM_Recv, ( "PtiReturnPacket" ) ); // Unpack the context information we stashed earlier. // pHead = *((PACKETHEAD** )(&Packet->MiniportReserved[ 0 ])); pBuffer = *((CHAR** )(&Packet->MiniportReserved[ sizeof(VOID*) ])); // Find the adapter from the PACKETHEAD address. // pPool = PacketPoolFromPacketHead( pHead ); pAdapter = CONTAINING_RECORD( pPool, ADAPTERCB, poolPackets ); ASSERT( pAdapter->ulTag == MTAG_ADAPTERCB ); // Free the descriptor created by NdisCopyBuffer. // NdisUnchainBufferAtFront( Packet, &pTrimmedBuffer ); if (pTrimmedBuffer) { NdisFreeBuffer( pTrimmedBuffer ); } // Free the buffer and packet back to the pools. // FreeBufferToPool( &pAdapter->poolFrameBuffers, pBuffer, TRUE ); FreePacketToPool( &pAdapter->poolPackets, pHead, TRUE ); TRACE( TL_V, TM_Recv, ( "PtiReturnPacket: Exit" ) ); } NDIS_STATUS PtiCoActivateVc( IN NDIS_HANDLE MiniportVcContext, IN OUT PCO_CALL_PARAMETERS CallParameters ) // Standard 'MiniportCoActivateVc' routine called by NDIS in response to a // protocol's request to activate a virtual circuit. // { ASSERT( !"PtiCoActVc?" ); return NDIS_STATUS_SUCCESS; } NDIS_STATUS PtiCoDeactivateVc( IN NDIS_HANDLE MiniportVcContext ) // Standard 'MiniportCoDeactivateVc' routine called by NDIS in response to // a protocol's request to de-activate a virtual circuit. // { ASSERT( !"PtiCoDeactVc?" ); return NDIS_STATUS_SUCCESS; } VOID PtiCoSendPackets( IN NDIS_HANDLE MiniportVcContext, IN PPNDIS_PACKET PacketArray, IN UINT NumberOfPackets ) // Standard 'MiniportCoSendPackets' routine called by NDIS in response to // a protocol's request to send packets on a virtual circuit. // { UINT i; NDIS_STATUS status; NDIS_PACKET** ppPacket; VCCB* pVc; ADAPTERCB* pAdapter; ULONG ulLength; NDIS_PACKET* pPacket; PNDIS_BUFFER pBuffer; PVOID pFrameVirtualAddress; KIRQL oldIrql; TRACE( TL_V, TM_Send, ( "PtiCoSendPackets: pVc=$%p, nPackets=$%x", MiniportVcContext, NumberOfPackets ) ); pVc = (VCCB* )MiniportVcContext; ASSERT( pVc->ulTag == MTAG_VCCB ); pAdapter = pVc->pAdapter; ReferenceVc( pVc ); for (i = 0, ppPacket = PacketArray; i < NumberOfPackets; ++i, ++ppPacket) { NDIS_PACKET* pPacket = *ppPacket; if (ReferenceCall( pVc )) { // Send the packet and call NdisMCoSendComplete to notify caller // NDIS_SET_PACKET_STATUS( pPacket, NDIS_STATUS_PENDING ); // Request the first buffer descriptor // NdisQueryPacket( pPacket, NULL, NULL, &pBuffer, NULL ); // While pBuffer <> NULL do { UCHAR* pAsyncBuf; ULONG ulAsyncLen; // request buffer address and length // NdisQueryBuffer( pBuffer, &pFrameVirtualAddress, &ulLength ); if (IsWin9xPeer( pVc )) { pAsyncBuf = (UCHAR* ) GetBufferFromPool( &pAdapter->poolFrameBuffers ); if (!pAsyncBuf) { status = NDIS_STATUS_FAILURE; TRACE( TL_A, TM_Send, ( "PtiSP: !pAsyncBuf" ) ); break; } AsyncFromHdlcFraming( pFrameVirtualAddress, ulLength, pAsyncBuf, &ulAsyncLen, pVc->linkinfo.SendACCM ); pFrameVirtualAddress = pAsyncBuf; ulLength = ulAsyncLen; } else { pAsyncBuf = NULL; } // send the buffer // KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); status = (NDIS_STATUS) PtiWrite( pVc->PtiExtension, pFrameVirtualAddress, ulLength, PID_STANDARD ); KeLowerIrql(oldIrql); TRACE( TL_N, TM_Send, ( "PtiCoSendPackets=%x: $%x bytes", status, ulLength ) ); #ifdef TESTMODE if ( g_ulTraceMask & TM_Data ) { if (pFrameVirtualAddress != NULL) { Dump( pFrameVirtualAddress, ulLength, 0, TRUE ); } } #endif if (pAsyncBuf) { FreeBufferToPool( &pAdapter->poolFrameBuffers, pAsyncBuf, TRUE ); } if (!NT_SUCCESS(status)){ break; } // get next pBuffer // NdisGetNextBuffer( pBuffer, &pBuffer ); // With current NDISWAN behavior only one NDIS_BUFFER will // ever be received. If multiples are received, we need to // coalesce the chained buffers into an input buffer for the // call to AsyncFromHdlcFraming above. For that matter, this // would send partial PPP frames, which, it seems to me, would // be discarded as fragments on the other end. Tony, am I // wrong? To avoid a useless copy, we will skip that for now, // but acknowledge here that the current code is not strictly // correct. (SLC) // ASSERT( !pBuffer ); } while ( pBuffer != NULL ); NDIS_SET_PACKET_STATUS( pPacket, status ); TRACE( TL_V, TM_Send, ( "PtiCoSendPackets: NdisMCoSendComp: status=$%x", status ) ); NdisMCoSendComplete( status, pVc->NdisVcHandle, pPacket ); TRACE( TL_V, TM_Send, ( "PtiCoSendPackets: NdisMCoSendComp done" ) ); pVc->ulTotalPackets++; DereferenceCall( pVc ); } else { TRACE( TL_A, TM_Send, ( "Send to inactive call ignored" ) ); NDIS_SET_PACKET_STATUS( pPacket, NDIS_STATUS_FAILURE ); NdisMCoSendComplete( status, pVc->NdisVcHandle, pPacket ); } } DereferenceVc( pVc ); TRACE( TL_V, TM_Send, ( "PtiCoSendPackets: Exit" ) ); } NDIS_STATUS PtiCoRequest( IN NDIS_HANDLE MiniportAdapterContext, IN NDIS_HANDLE MiniportVcContext, IN OUT PNDIS_REQUEST NdisRequest ) // Standard 'MiniportCoRequestHandler' routine called by NDIS in response // to a protocol's request information from the mini-port. Unlike the // Query/SetInformation handlers that this routine obsoletes, requests are // not serialized. // { ADAPTERCB* pAdapter; VCCB* pVc; NDIS_STATUS status; TRACE( TL_N, TM_Mp, ( "PtiCoReq" ) ); pAdapter = (ADAPTERCB* )MiniportAdapterContext; if (pAdapter->ulTag != MTAG_ADAPTERCB) { ASSERT( !"Atag?" ); return NDIS_STATUS_INVALID_DATA; } pVc = (VCCB* )MiniportVcContext; if (pVc && pVc->ulTag != MTAG_VCCB) { ASSERT( !"Vtag?" ); return NDIS_STATUS_INVALID_DATA; } switch (NdisRequest->RequestType) { case NdisRequestQueryInformation: { status = QueryInformation( pAdapter, pVc, NdisRequest->DATA.QUERY_INFORMATION.Oid, NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer, NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength, &NdisRequest->DATA.QUERY_INFORMATION.BytesWritten, &NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded ); break; } case NdisRequestSetInformation: { status = SetInformation( pAdapter, pVc, NdisRequest->DATA.SET_INFORMATION.Oid, NdisRequest->DATA.SET_INFORMATION.InformationBuffer, NdisRequest->DATA.SET_INFORMATION.InformationBufferLength, &NdisRequest->DATA.SET_INFORMATION.BytesRead, &NdisRequest->DATA.SET_INFORMATION.BytesNeeded ); break; } default: { status = NDIS_STATUS_NOT_SUPPORTED; TRACE( TL_A, TM_Mp, ( "PtiCoReq: type=%d unsupported", NdisRequest->RequestType ) ); break; } } TRACE( TL_V, TM_Mp, ( "PtiCoReq: Exit: $%x", status ) ); return status; } //----------------------------------------------------------------------------- // Callback routines ... called by the PtiLink layer below //----------------------------------------------------------------------------- PVOID PtiCbGetReadBuffer( IN PVOID Context, OUT PULONG BufferSize, OUT PVOID* RequestContext ) // PtiLink is requesting a read buffer, get one and return it // This is a the start of a receive event ... { VCCB* pVc; ADAPTERCB* pAdapter; PCHAR pBuffer; TRACE( TL_V, TM_Spec, ( "PtiCbGetReadBuffer: pVc=$%p", Context ) ); pVc = (VCCB* )Context; if (pVc->ulTag != MTAG_VCCB) { ASSERT( !"Vtag?" ); return NULL; } pAdapter = pVc->pAdapter; // the pVc is our context for use of this buffer // *RequestContext = pVc; // Give caller the length of this buffer // *BufferSize = PTI_FrameBufferSize; // ask for a buffer, caller must check for NULL // pBuffer = GetBufferFromPool( &pAdapter->poolFrameBuffers ); TRACE( TL_V, TM_Spec, ( "PtiCbGetReadBuffer: Exit: Issuing pBuffer=$%p", pBuffer ) ); // return the buffer to the caller // this is a raw system va // return pBuffer; } VOID PtiRx( IN PVOID Context, IN PVOID pBuffer, IN NTSTATUS Status, IN ULONG ulLength, IN PVOID RequestContext ) // Ptilink has completed a read, i.e., receive complete // buffer now belongs to this layer // // Context -- is the pVC // // pBuffer -- is the pointer to buffer previously allocated // to the PtiLink driver via the PtiCbGetReadBuffer function // // Status -- one of: NT_SUCCESS = good packet received // DATA_OVERRUN = header failure // BUFFER_TOO_SMALL= pBuffer is too small to receive packet // // ulLength - packet length // // RequestContext -- don't care // // General Note: PtiLink below us sends and receives link manangement // packets using the Our and His structures ... link management packets to // not flow through here. Link events are announced to us via our // registered callback (PtiCbLinkEventHandler) below. We have nothing to // do with Tx/Rx of link pkts. // { VCCB* pVc; ADAPTERCB* pAdapter; NDIS_STATUS status; NDIS_STATUS writestatus; NDIS_PACKET* pPacket; NDIS_BUFFER* pNdisBuffer; PACKETHEAD* pHead; ULONGLONG ullTimeReceived; KIRQL oldIrql; UCHAR* pHdlcBuf; ULONG ulHdlcLen; UCHAR* pTmp; TRACE( TL_N, TM_Recv, ( "PtiRx=%x: bytes=$%x", Status, ulLength ) ); TRACE( TL_V, TM_Recv, ( "PtiRx=%x, pVc=$%p, pBuf=$%p, bytes=$%x", Status, Context, pBuffer, ulLength ) ); pVc = (VCCB* )Context; if (pVc->ulTag != MTAG_VCCB) { ASSERT( !"Vtag?" ); return; } ReferenceVc( pVc ); pAdapter = pVc->pAdapter; // NOT A REAL DATA PACKET // return any buffers used for non-data or losing reads // if ( !NT_SUCCESS( Status ) ){ TRACE( TL_A, TM_Pool, ( "PtiRx: Status != SUCCESS, freeing buffer", Status ) ); if ( pBuffer != NULL ) { FreeBufferToPool( &pAdapter->poolFrameBuffers, pBuffer, TRUE ); } DereferenceVc( pVc ); return; } #ifdef TESTMODE if ( g_ulTraceMask & TM_Data ) { if (pBuffer != NULL) { Dump( pBuffer, ulLength, 0, TRUE ); } } #endif // INCOMING CALL ... NO VC EXISTS YET for this incoming data packet // if (ReferenceSap( pAdapter )) { if (!(ReadFlags( &pAdapter->pListenVc->ulFlags ) & VCBF_CallInProgress)) { // Setting in Listen VC here. // SetFlags( &pAdapter->pListenVc->ulFlags, VCBF_CallInProgress ); // This is the start of an incoming call which may also start via // LINK_OPEN event to PtiCbLinkEventHandler // // Ignore this packet and proceed to dispatch an incoming call // TRACE( TL_V, TM_Recv, ( "PtiRx: Incoming call", Status ) ); // Free the buffer associated with this read ... we throw away the // data thus losing one packet off the front of an attempt to // connect, unless the LPKT_OPEN function beats us (a LPKT_OPEN // notification occurs before first data packet is received ... it // could happen either way.) // if (pBuffer != NULL ) { FreeBufferToPool( &pAdapter->poolFrameBuffers, pBuffer, TRUE ); } // set up a VC for the incoming call // SetupVcAsynchronously( pAdapter ); DereferenceVc( pVc ); DereferenceSap( pAdapter ); return; } DereferenceSap( pAdapter ); } // NOW HAVE A REAL DATA PACKET if (ReferenceCall( pVc )) { do { if (IsWin9xPeer( pVc )) { if (pVc->ulTotalPackets < 4) { // If packet matches "CLIENT", we emit one saying // "CLIENTSERVER" // // If packet matches "CLIENTSERVER", throw it away // // This hack emulates the Win9x UNIMODEM behavior which is // required to allow Win9x systems to connect to us. // // Also, it appears that sending the "CLIENT" packet up // the stack causes RasTapi to disconnect us immediately. // It wants to see PPP? // if ( StrCmp( pBuffer, g_szClientServer, CLIENTSERVERLEN ) == 0 ) { // throw away packets containing "CLIENTSERVER" // FreeBufferToPool( &pAdapter->poolFrameBuffers, pBuffer, TRUE ); TRACE( TL_V, TM_Recv, ( "PtiRx: CLIENTSERVER ignored", Status ) ); pVc->ulTotalPackets++; break; } else if ( StrCmp( pBuffer, g_szClient, CLIENTLEN ) == 0 ) { // when we see "CLIENT", throw away and respond // "CLIENTSERVER". // TRACE( TL_V, TM_Recv, ( "PtiRx: See CLIENT", Status ) ); KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); writestatus = (NDIS_STATUS) PtiWrite( pVc->PtiExtension, g_szClientServer, CLIENTSERVERLEN, PID_STANDARD ); KeLowerIrql(oldIrql); FreeBufferToPool( &pAdapter->poolFrameBuffers, pBuffer, TRUE ); TRACE( TL_V, TM_Recv, ( "PtiRx: CLIENTSERVER sent", Status ) ); pVc->ulTotalPackets++; break; } } // Un-byte-stuff the received buffer into a second buffer, // then swap it with the received buffer. // pHdlcBuf = (UCHAR* ) GetBufferFromPool( &pAdapter->poolFrameBuffers ); if (!pHdlcBuf) { FreeBufferToPool( &pAdapter->poolFrameBuffers, pBuffer, TRUE ); TRACE( TL_A, TM_Recv, ( "PtiRx: !Alloc HDLC" ) ); break; } HdlcFromAsyncFraming( pBuffer, ulLength, pHdlcBuf, &ulHdlcLen ); pTmp = pBuffer; pBuffer = pHdlcBuf; ulLength = ulHdlcLen; FreeBufferToPool( &pAdapter->poolFrameBuffers, pTmp, TRUE ); } // Note the time if client's call parameters indicated interest in // time received. // if (ReadFlags( &pVc->ulFlags ) & VCBF_IndicateTimeReceived) { NdisGetCurrentSystemTime( (LARGE_INTEGER* )&ullTimeReceived ); } else { ullTimeReceived = 0; } TRACE( TL_V, TM_Recv, ( "PtiRx: Rx Packet: nBytes=$%x", ulLength ) ); // Get a packet from the packet pool // pPacket = GetPacketFromPool( &pAdapter->poolPackets, &pHead ); if (!pPacket) { // Packet descriptor pool is maxed. // ASSERT( !"GetPfP?" ); break; } // Hook the NDIS_BUFFER to the packet. The "copy" here refers to // descriptor information only. The packet data is not copied. // NdisCopyBuffer( &status, &pNdisBuffer, PoolHandleForNdisCopyBufferFromBuffer( pBuffer ), NdisBufferFromBuffer( pBuffer ), 0, ulLength ); if (status != STATUS_SUCCESS) { // Can't get a MDL which likely means the system is toast. // FreePacketToPool( &pAdapter->poolPackets, pHead, TRUE ); TRACE( TL_A, TM_Recv, ( "NdisCopyBuffer=%08x?", status ) ); break; } NdisChainBufferAtFront( pPacket, pNdisBuffer ); // Stash the time the packet was received in the packet. // NDIS_SET_PACKET_TIME_RECEIVED( pPacket, ullTimeReceived ); // Pre-set the packet to success, since a random value of // NDIS_STATUS_RESOURCES would prevent our ReturnPackets handler // from getting called. // NDIS_SET_PACKET_STATUS( pPacket, NDIS_STATUS_SUCCESS ); // Stash our context information with the packet for clean-up use // in PtiReturnPacket, then indicate the packet to NDISWAN. // *((PACKETHEAD** )(&pPacket->MiniportReserved[ 0 ])) = pHead; *((CHAR** )(&pPacket->MiniportReserved[ sizeof(VOID*) ])) = pBuffer; TRACE( TL_V, TM_Recv, ( "PtiRx: NdisMCoIndRecPkt: hVc=$%p, pPacket=$%p, len=$%x", pVc->NdisVcHandle, pPacket, ulLength ) ); NdisMCoIndicateReceivePacket( pVc->NdisVcHandle, &pPacket, 1 ); TRACE( TL_V, TM_Recv, ( "PtiRx: NdisMCoIndRecPkt done" ) ); // Tell NDIS our "receive process" is complete. Since we deal // with one packet at a time and NDISWAN does also, this doesn't // accomplish anything, but the consensus is it's bad form to omit // it. // TRACE( TL_V, TM_Recv, ( "PtiRx: NdisMCoRecComp" ) ); NdisMCoReceiveComplete( pAdapter->MiniportAdapterHandle ); TRACE( TL_V, TM_Recv, ( "PtiRx: NdisMCoRecComp done" ) ); pVc->ulTotalPackets++; } while (FALSE); DereferenceCall( pVc ); } else { TRACE( TL_A, TM_Recv, ( "Receive on inactive call ignored" ) ); } DereferenceVc( pVc ); return; } VOID PtiCbLinkEventHandler( IN PVOID Context, IN ULONG PtiLinkEventId, IN ULONG PtiLinkEventData ) // Ptilink is reporting a link management event (Link up or down) // { VCCB* pVc; ADAPTERCB* pAdapter; pVc = (VCCB* )Context; if (pVc->ulTag != MTAG_VCCB) { ASSERT( !"Vtag?" ); return; } pAdapter = pVc->pAdapter; switch (PtiLinkEventId) { case PTILINK_LINK_UP: { TRACE( TL_A, TM_Cm, ( "LinkEvent: LINK UP, pVc=$%p", pVc ) ); // peer is initiating a call (also happens in PtiRx) // break; } case PTILINK_LINK_DOWN: { TRACE( TL_A, TM_Cm, ( "LinkEvent: LINK DOWN, pVc=$%p", pVc ) ); // peer is closing a call // if (pVc == pAdapter->pListenVc) { TRACE( TL_A, TM_Cm, ( "LinkEvent: LINK DOWN on ListenVc ignored" ) ); break; } NdisAcquireSpinLock( &pVc->lockV ); { pVc->status = NDIS_STATUS_TAPI_DISCONNECTMODE_NORMAL; CallTransitionComplete( pVc ); } NdisReleaseSpinLock( &pVc->lockV ); TRACE( TL_A, TM_Cm, ( "LinkEvent: LINK DOWN causing disconnect" ) ); CompleteVc( pVc ); break; } default: { TRACE( TL_A, TM_Cm, ( "LinkEvent: Bad LinkEvent = %d", PtiLinkEventId ) ); break; } } } //----------------------------------------------------------------------------- // Mini-port utility routines (alphabetically) // Some are used externally //----------------------------------------------------------------------------- VOID AsyncFromHdlcFraming( IN UCHAR* pInBuf, IN ULONG ulInBufLen, OUT UCHAR* pOutBuf, OUT ULONG* pulOutBufLen, IN ULONG ulAccmMask ) // Make a copy of PPP HDLC framed data buffer 'pInBuf' of length // 'ulInBufLen' bytes in caller's 'pOutBuf' buffer, converting to // byte-stuffed asynchronous PPP framed format in the process. // 'POutBufLen' is the length in bytes of the returned output buffer. Due // to the byte stuffing, caller must allow for up to twice the length of // 'pInfBuf'. 'UlAccmMask' is the bitmask of characters to be byte // stuffed. // // With current implementation, user must allow 2 extra bytes at the end // of the input buffer for stashing the FCS during byte-stuffing. // // This routine is adapted from the ASYNCMAC AssemblePppFrame routine, as // is the following description, which in turn was lifted from RFC 1331 // (May 1992). The PPP frame NDISWAN passes us for sends is the data from // the Address field through the Information field inclusive, without any // byte stuffing, of course. // // Asynchronously framed PPP packet: // // +----------+----------+----------+----------+------------... // | Flag | Address | Control | Protocol | Information // | 01111110 | 11111111 | 00000011 | 16 bits | * // +----------+----------+----------+----------+------------... // ...---+----------+----------+----------------- // | FCS | Flag | Inter-frame Fill // | 16 bits | 01111110 | or next Address // ...---+----------+----------+----------------- // // Frame Check Sequence (FCS) Field // // The Frame Check Sequence field is normally 16 bits (two octets). The // use of other FCS lengths may be defined at a later time, or by prior // agreement. // // The FCS field is calculated over all bits of the Address, Control, // Protocol and Information fields not including any start and stop bits // (asynchronous) and any bits (synchronous) or octets (asynchronous) // inserted for transparency. This does not include the Flag Sequences // or the FCS field itself. The FCS is transmitted with the coefficient // of the highest term first. // // Note: When octets are received which are flagged in the Async- // Control-Character-Map, they are discarded before calculating the // FCS. See the description in Appendix A. // // On asynchronous links, a character stuffing procedure is used. // The Control Escape octet is defined as binary 01111101 // (hexadecimal 0x7d) where the bit positions are numbered 87654321 // (not 76543210, BEWARE). // // After FCS computation, the transmitter examines the entire frame // between the two Flag Sequences. Each Flag Sequence, Control // Escape octet and octet with value less than hexadecimal 0x20 which // is flagged in the Remote Async-Control-Character-Map is replaced // by a two octet sequence consisting of the Control Escape octet and // the original octet with bit 6 complemented (i.e., exclusive-or'd // with hexadecimal 0x20). // // Prior to FCS computation, the receiver examines the entire frame // between the two Flag Sequences. Each octet with value less than // hexadecimal 0x20 is checked. If it is flagged in the Local // Async-Control-Character-Map, it is simply removed (it may have // been inserted by intervening data communications equipment). For // each Control Escape octet, that octet is also removed, but bit 6 // of the following octet is complemented. A Control Escape octet // immediately preceding the closing Flag Sequence indicates an // invalid frame. // // Note: The inclusion of all octets less than hexadecimal 0x20 // allows all ASCII control characters [10] excluding DEL (Delete) // to be transparently communicated through almost all known data // communications equipment. // // // The transmitter may also send octets with value in the range 0x40 // through 0xff (except 0x5e) in Control Escape format. Since these // octet values are not negotiable, this does not solve the problem // of receivers which cannot handle all non-control characters. // Also, since the technique does not affect the 8th bit, this does // not solve problems for communications links that can send only 7- // bit characters. // // A few examples may make this more clear. Packet data is // transmitted on the link as follows: // // 0x7e is encoded as 0x7d, 0x5e. // 0x7d is encoded as 0x7d, 0x5d. // // 0x01 is encoded as 0x7d, 0x21. // // Some modems with software flow control may intercept outgoing DC1 // and DC3 ignoring the 8th (parity) bit. This data would be // transmitted on the link as follows: // // 0x11 is encoded as 0x7d, 0x31. // 0x13 is encoded as 0x7d, 0x33. // 0x91 is encoded as 0x7d, 0xb1. // 0x93 is encoded as 0x7d, 0xb3. // { USHORT usFcs; UCHAR* pIn; UCHAR* pOut; ULONG ulInBytesLeft; pIn = pInBuf; ulInBytesLeft = ulInBufLen; pOut = pOutBuf; // Calculate the frame check sequence on the data. // TRACE( TL_I, TM_Data, ( "AfromH (send) dump:" ) ); DUMPB( TL_I, TM_Data, pInBuf, ulInBufLen ); usFcs = CalculatePppFcs( pInBuf, ulInBufLen ); usFcs ^= 0xFFFF; // Add the calculated FCS. Added to the input buffer for convenience as // it must be byte-stuffed along with the other data, though this uglies // the interface a bit. // pIn[ ulInBytesLeft ] = (UCHAR )usFcs; ++ulInBytesLeft; pIn[ ulInBytesLeft ] = (UCHAR )(usFcs >> 8); ++ulInBytesLeft; // Add the initial flag byte. // *pOut = PPPFLAGBYTE; ++pOut; // Because an empty control character mask is common, an optimized loop is // provided in that case. // if (ulAccmMask #ifdef TESTMODE || g_fNoAccmFastPath #endif ) { // Have bitmask...slower path. // while (ulInBytesLeft--) { UCHAR uch; uch = *pIn; ++pIn; if (((uch < 0x20) && ((1 << uch) & ulAccmMask)) || (uch == PPPESCBYTE) || (uch == PPPFLAGBYTE)) { // Byte stuff the character. // *pOut = PPPESCBYTE; ++pOut; *pOut = uch ^ 0x20; ++pOut; } else { // Copy the character as is. // *pOut = uch; ++pOut; } } } else { // No bitmask...fast path. // while (ulInBytesLeft--) { UCHAR uch; uch = *pIn; ++pIn; if ((uch == PPPESCBYTE) || (uch == PPPFLAGBYTE)) { // Byte stuff the character. // *pOut = PPPESCBYTE; ++pOut; *pOut = uch ^ 0x20; ++pOut; } else { // Copy the character as is. // *pOut = uch; ++pOut; } } } // Add the trailing flag byte. // *pOut = PPPFLAGBYTE; ++pOut; // Calculate length of output. // *pulOutBufLen = (ULONG )(pOut - pOutBuf); } USHORT CalculatePppFcs( IN UCHAR* pBuf, IN ULONG ulBufLen ) // Return the PPP Frame Check Sequence on 'ulBufLen' bytes starting at // 'pBuf'. // // (Taken from ASYNCMAC) // { static USHORT ausFcsTable[ 256 ] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; register USHORT usFcs; usFcs = 0xFFFF; while (ulBufLen--) { usFcs = (usFcs >> 8) ^ ausFcsTable[ (usFcs ^ (USHORT )*pBuf) & 0xFF ]; ++pBuf; } return usFcs; } VOID DereferenceAdapter( IN ADAPTERCB* pAdapter ) // Removes a reference from the adapter control block 'pAdapter', and when // frees the adapter resources when the last reference is removed. // { LONG lRef; lRef = NdisInterlockedDecrement( &pAdapter->lRef ); TRACE( TL_N, TM_Ref, ( "DerefA to %d", lRef ) ); ASSERT( lRef >= 0 ); if (lRef == 0) { FreeAdapter( pAdapter ); } } VOID DereferenceVc( IN VCCB* pVc ) // Removes a reference to the VC control block 'pVc', and when frees the // block when the last reference is removed. // { LONG lRef; lRef = NdisInterlockedDecrement( &pVc->lRef ); TRACE( TL_N, TM_Ref, ( "DerefVc to %d", lRef ) ); ASSERT( lRef >= 0 ); if (lRef == 0) { ADAPTERCB* pAdapter; // now for an interesting bit ... // // if we have a listenVc allocated, then revert to using that // pAdapter = pVc->pAdapter; if (pAdapter->ulTag != MTAG_ADAPTERCB) { ASSERT( !"Atag?" ); return; } if (pAdapter->pListenVc && pAdapter->pListenVc->hPtiLink) { TRACE( TL_V, TM_Mp, ( "DerefVc: Reverting to pVc=$%p", pAdapter->pListenVc ) ); ClearFlags( &pAdapter->pListenVc->ulFlags, VCBF_CallInProgress ); // reregister using the listen Vc // TRACE( TL_V, TM_Mp, ( "DerefVc: RegCb pLV=$%p", pAdapter->pListenVc ) ); PtiRegisterCallbacks(pAdapter->pListenVc->Extension, // the PTILINKx extension PtiCbGetReadBuffer, // our get buffer routine PtiRx, // our receive complete routine PtiCbLinkEventHandler, // our link event handler pAdapter->pListenVc); // our new context } // Can make these assumptions because NDIS will not call the delete-VC // handler while the VC is active. All the nasty VC clean up occurs // before the VC is deactivated and the call closed. // pVc->ulTag = MTAG_FREED; FREE_VCCB( pAdapter, pVc ); DereferenceAdapter( pAdapter ); TRACE( TL_I, TM_Mp, ( "pVc=$%p freed", pVc ) ); } } VOID FreeAdapter( IN ADAPTERCB* pAdapter ) // Frees all resources allocated for adapter 'pAdapter', including // 'pAdapter' itself. // { BOOLEAN fSuccess; // Setting 'usMaxVcs' to 0 is PtiInitialize's way of telling us that the // lookaside lists and pools were not initialized. // if (pAdapter->usMaxVcs) { NdisDeleteNPagedLookasideList( &pAdapter->llistWorkItems ); NdisDeleteNPagedLookasideList( &pAdapter->llistVcs ); } TRACE( TL_V, TM_Mp, ( "FreeAdapter" ) ); pAdapter->ulTag = MTAG_FREED; FREE_NONPAGED( pAdapter ); } NDIS_STATUS RegistrySettings( IN OUT ADAPTERCB* pAdapter, IN NDIS_HANDLE WrapperConfigurationContext ) // Read this mini-port's registry settings into 'pAdapter' fields. Also // writes registry values read by RASTAPI, overriding SETUPs. // 'WrapperConfigurationContext' is the handle to passed to // MiniportInitialize. // { NDIS_STATUS status; NDIS_HANDLE hCfg; NDIS_CONFIGURATION_PARAMETER* pncp; NdisOpenConfiguration( &status, &hCfg, WrapperConfigurationContext ); if (status != NDIS_STATUS_SUCCESS) { return status; } do { // The delay in milliseconds to wait for PARPORT to initialize all the // parallel ports. With PnP there is no deterministic time at which // to do this. // { NDIS_STRING nstr = NDIS_STRING_CONST( "ParportDelayMs" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { pAdapter->ulParportDelayMs = pncp->ParameterData.IntegerData; } else { // Default is 3 seconds. // pAdapter->ulParportDelayMs = 3000; status = NDIS_STATUS_SUCCESS; } } // The secondary delay in milliseconds to wait for PARPORT to // initialize all the parallel ports, if there are no ports after the // initial delay above. // { NDIS_STRING nstr = NDIS_STRING_CONST( "ExtraParportDelayMs" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { pAdapter->ulExtraParportDelayMs = pncp->ParameterData.IntegerData; } else { // Default is 30 seconds. // pAdapter->ulExtraParportDelayMs = 30000; status = NDIS_STATUS_SUCCESS; } } // The number of VCs we must be able to provide. // { #if 0 NDIS_STRING nstr = NDIS_STRING_CONST( "MaxVcs" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { pAdapter->usMaxVcs = (USHORT )pncp->ParameterData.IntegerData; // Make sure it's a valid value. // if (pAdapter->usMaxVcs < 1) { status = NDIS_STATUS_INVALID_DATA; break; } } else { pAdapter->usMaxVcs = 1; status = NDIS_STATUS_SUCCESS; } #else // Registry value is currently ignored, and hard-coded maximum // used. // pAdapter->usMaxVcs = NPORTS; #endif } } while (FALSE); NdisCloseConfiguration( hCfg ); TRACE( TL_N, TM_Init, ( "Reg: vcs=%d ppd=%d", (UINT )pAdapter->usMaxVcs, (UINT )pAdapter->ulParportDelayMs ) ); return status; } BOOLEAN HdlcFromAsyncFraming( IN UCHAR* pInBuf, IN ULONG ulInBufLen, OUT UCHAR* pOutBuf, OUT ULONG* pulOutBufLen ) // Make a copy of asynchronously framed PPP data buffer 'pInBuf' of length // 'ulInBufLen' bytes in caller's 'pOutBuf' buffer, converting to PPP HDLC // framed format in the process. 'POutBufLen' is the length in bytes of // the returned output buffer. Caller must allow for up to the length of // 'pInBuf' in 'pOutBuf'. // // Returns true if the packet is valid, false if corrupt. // // Adapted from ASYNCMAC's AsyncPPPCompletionRoutine. // { UCHAR* pIn; UCHAR* pInEnd; UCHAR* pOut; USHORT usFcs; if (ulInBufLen < 5) { // Expecting at least 2 flag bytes, 1 data byte, and the FCS. // TRACE( TL_A, TM_Mp, ( "HfA: frame too short=%d", ulInBufLen ) ); return FALSE; } if (pInBuf[ 0 ] != PPPFLAGBYTE) { TRACE( TL_A, TM_Mp, ( "HfA: No head flag" ) ); return FALSE; } if (pInBuf[ ulInBufLen - 1 ] != PPPFLAGBYTE) { TRACE( TL_A, TM_Mp, ( "HfA: No tail flag" ) ); return FALSE; } pIn = pInBuf + 1; pInEnd = pInBuf + ulInBufLen - 1; pOut = pOutBuf; while (pIn < pInEnd) { if (*pIn == PPPESCBYTE) { ++pIn; *pOut = *pIn ^ 0x20; } else { *pOut = *pIn; } ++pOut; ++pIn; } *pulOutBufLen = (ULONG )(pOut - pOutBuf - 2); { USHORT usCalcFcs; usFcs = (USHORT )(pOut[ -2 ]) + (USHORT )(pOut[ -1 ] << 8); usFcs ^= 0xFFFF; TRACE( TL_I, TM_Data, ( "HfromA (recv) dump:" ) ); DUMPB( TL_I, TM_Data, pOutBuf, *pulOutBufLen ); usCalcFcs = CalculatePppFcs( pOutBuf, *pulOutBufLen ); if (usFcs != usCalcFcs) { TRACE( TL_A, TM_Mp, ( "HfA: FCS mismatch, R=$%04x C=$%04x, L=%d", (INT )usFcs, (INT )usCalcFcs, *pulOutBufLen ) ); return FALSE; } #if 0 #ifdef TESTMODE else { TRACE( TL_A, TM_Mp, ( "HfA: Good FCS, R=$%04x C=$%04x, L=%d", (INT )usFcs, (INT )usCalcFcs, *pulOutBufLen ) ); } #endif #endif } return TRUE; } BOOLEAN IsWin9xPeer( IN VCCB* pVc ) // Returns true if the link level has determined that the VC's peer is a // Win9x box, false otherwise. // { ULONG Platform; PPTI_EXTENSION pPtiExtension; #ifdef TESTMODE if (g_fAssumeWin9x) { return TRUE; } #endif pPtiExtension = (PPTI_EXTENSION )pVc->PtiExtension; // try to check the validity of the PtiExtension pointer // if ( pPtiExtension == NULL ) { TRACE( TL_A, TM_Recv, ( "PtiRx: pPtiExtension is NULL!" ) ); return FALSE; } Platform = (ULONG) pPtiExtension->His.VerPlat; TRACE( TL_V, TM_Recv, ( "IsWin9xPeer: far platform=$%x", Platform ) ); if (Platform == PLAT_WIN9X) { // Win9x -- we reformat the asynch framing used by Win9x DCC // and also play the CLIENT->CLIENTSERVER game // return TRUE; } // WinNT (or DOS maybe) // return FALSE; } NDIS_STATUS QueryInformation( IN ADAPTERCB* pAdapter, IN VCCB* pVc, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesWritten, OUT PULONG BytesNeeded ) // Handle QueryInformation requests. Arguments are as for the standard // NDIS 'MiniportQueryInformation' handler except this routine does not // count on being serialized with respect to other requests. // { NDIS_STATUS status; ULONG ulInfo; VOID* pInfo; ULONG ulInfoLen; status = NDIS_STATUS_SUCCESS; // The cases in this switch statement find or create a buffer containing // the requested information and point 'pInfo' at it, noting it's length // in 'ulInfoLen'. Since many of the OIDs return a ULONG, a 'ulInfo' // buffer is set up as the default. // ulInfo = 0; pInfo = &ulInfo; ulInfoLen = sizeof(ulInfo); switch (Oid) { case OID_GEN_MAXIMUM_LOOKAHEAD: { // Report the maximum number of bytes we can always provide as // lookahead data on receive indications. We always indicate full // packets so this is the same as the receive block size. And // since we always allocate enough for a full packet, the receive // block size is the same as the frame size. // TRACE( TL_N, TM_Mp, ( "QInfo(OID_GEN_MAXIMUM_LOOKAHEAD)" ) ); ulInfo = PTI_MaxFrameSize; break; } case OID_GEN_MAC_OPTIONS: { // Report a bitmask defining optional properties of the driver. // // NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA promises that our receive // buffer is not on a device-specific card. // // NDIS_MAC_OPTION_TRANSFERS_NOT_PEND promises we won't return // NDIS_STATUS_PENDING from our TransferData handler which is true // since we don't have one. // TRACE( TL_N, TM_Mp, ( "QInfo(OID_GEN_MAC_OPTIONS)" ) ); ulInfo = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | NDIS_MAC_OPTION_TRANSFERS_NOT_PEND; break; } case OID_WAN_MEDIUM_SUBTYPE: { // Report the media subtype we support. NDISWAN may use this in // the future (doesn't now) to provide framing differences for // different media. // TRACE( TL_N, TM_Mp, ( "QInfo(OID_WAN_MEDIUM_SUBTYPE)" ) ); ulInfo = NdisWanMediumParallel; break; } case OID_WAN_CO_GET_INFO: { // Report the capabilities of the adapter. // TRACE( TL_N, TM_Mp, ( "QInfo(OID_WAN_CO_GET_INFO)" ) ); pInfo = &pAdapter->info; ulInfoLen = sizeof(NDIS_WAN_CO_INFO); break; } case OID_WAN_CO_GET_LINK_INFO: { // Report the current state of the link. // TRACE( TL_N, TM_Mp, ( "QInfo(OID_WAN_CO_GET_LINK_INFO)" ) ); if (!pVc) return NDIS_STATUS_INVALID_DATA; pInfo = &pVc->linkinfo; ulInfoLen = sizeof(NDIS_WAN_CO_GET_LINK_INFO); break; } case OID_WAN_CO_GET_COMP_INFO: { // Report the type of compression we provide, which is none. // TRACE( TL_N, TM_Mp, ( "QInfo(OID_WAN_CO_GET_COMP_INFO)" ) ); status = NDIS_STATUS_NOT_SUPPORTED; ulInfoLen = 0; break; } case OID_WAN_CO_GET_STATS_INFO: { // Because DirectParallel doesn't do compression, NDISWAN will use // it's own statistics and not query ours. // ASSERT( !"OID_WAN_CO_GET_STATS_INFO?" ); status = NDIS_STATUS_NOT_SUPPORTED; ulInfoLen = 0; break; } case OID_GEN_SUPPORTED_LIST: { static ULONG aulSupportedOids[] = { OID_GEN_SUPPORTED_LIST, OID_GEN_MAXIMUM_LOOKAHEAD, OID_GEN_MAC_OPTIONS, OID_WAN_MEDIUM_SUBTYPE, OID_WAN_CO_GET_INFO, OID_WAN_CO_GET_LINK_INFO, OID_WAN_CO_SET_LINK_INFO, OID_CO_TAPI_CM_CAPS, OID_CO_TAPI_LINE_CAPS, OID_CO_TAPI_ADDRESS_CAPS, OID_CO_TAPI_GET_CALL_DIAGNOSTICS }; TRACE( TL_N, TM_Mp, ( "QInfo(OID_GEN_SUPPORTED_LIST)" ) ); pInfo = aulSupportedOids; ulInfoLen = sizeof(aulSupportedOids); break; } case OID_PNP_CAPABILITIES: { pInfo = &PnpCaps; ulInfoLen = sizeof(PnpCaps); break; } case OID_PNP_SET_POWER: break; case OID_PNP_QUERY_POWER: break; case OID_PNP_ENABLE_WAKE_UP: break; #if 0 // These OIDs are mandatory according to current doc, but since // NDISWAN never requests them they are omitted. // case OID_GEN_HARDWARE_STATUS: case OID_GEN_MEDIA_SUPPORTED: case OID_GEN_MEDIA_IN_USE: case OID_GEN_MEDIA_IN_USE: case OID_GEN_MAXIMUM_FRAME_SIZE: case OID_GEN_LINK_SPEED: case OID_GEN_TRANSMIT_BUFFER_SPACE: case OID_GEN_RECEIVE_BUFFER_SPACE: case OID_GEN_TRANSMIT_BLOCK_SIZE: case OID_GEN_RECEIVE_BLOCK_SIZE: case OID_GEN_VENDOR_ID: case OID_GEN_VENDOR_DESCRIPTION: case OID_GEN_VENDOR_DRIVER_VERSION: case OID_GEN_CURRENT_PACKET_FILTER: case OID_GEN_CURRENT_LOOKAHEAD: case OID_GEN_DRIVER_VERSION: case OID_GEN_MAXIMUM_TOTAL_SIZE: case OID_GEN_MAC_OPTIONS: case OID_GEN_MEDIA_CONNECT_STATUS: case OID_GEN_MAXIMUM_SEND_PACKETS: case OID_WAN_PERMANENT_ADDRESS: case OID_WAN_CURRENT_ADDRESS: case OID_WAN_QUALITY_OF_SERVICE: case OID_WAN_LINE_COUNT: #endif default: { TRACE( TL_A, TM_Mp, ( "QueryInfo: Oid=$%08x?", Oid ) ); status = NDIS_STATUS_NOT_SUPPORTED; // JAY per SLC ulInfoLen = 0; break; } } if (ulInfoLen > InformationBufferLength) { // Caller's buffer is too small. Tell him what he needs. // *BytesNeeded = ulInfoLen; status = NDIS_STATUS_INVALID_LENGTH; } else { // Copy the found result to caller's buffer. // if (ulInfoLen > 0) { NdisMoveMemory( InformationBuffer, pInfo, ulInfoLen ); DUMPDW( TL_N, TM_Mp, pInfo, ulInfoLen ); } *BytesNeeded = *BytesWritten = ulInfoLen; } return status; } VOID ReferenceAdapter( IN ADAPTERCB* pAdapter ) // Adds areference to the adapter block, 'pAdapter'. // { LONG lRef; lRef = NdisInterlockedIncrement( &pAdapter->lRef ); TRACE( TL_N, TM_Ref, ( "RefA to %d", lRef ) ); } VOID ReferenceVc( IN VCCB* pVc ) // Adds a reference to the VC control block 'pVc'. // { LONG lRef; lRef = NdisInterlockedIncrement( &pVc->lRef ); TRACE( TL_N, TM_Ref, ( "RefVc to %d", lRef ) ); } VOID SendClientString( IN PVOID pPtiExtension ) // Send "CLIENT" so Win9x, which views us as a NULL modem, is happy. // { KIRQL oldIrql; KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); PtiWrite( pPtiExtension, g_szClient, CLIENTLEN, PID_STANDARD ); KeLowerIrql(oldIrql); } NDIS_STATUS SetInformation( IN ADAPTERCB* pAdapter, IN VCCB* pVc, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesRead, OUT PULONG BytesNeeded ) // Handle SetInformation requests. Arguments are as for the standard NDIS // 'MiniportQueryInformation' handler except this routine does not count // on being serialized with respect to other requests. // { NDIS_STATUS status; status = NDIS_STATUS_SUCCESS; switch (Oid) { case OID_WAN_CO_SET_LINK_INFO: { // Read new link state settings. // TRACE( TL_N, TM_Mp, ( "SInfo(OID_WAN_CO_SET_LINK_INFO)" ) ); if (InformationBufferLength < sizeof(NDIS_WAN_CO_SET_LINK_INFO)) { status = NDIS_STATUS_INVALID_LENGTH; *BytesRead = 0; } else { if (!pVc) { return NDIS_STATUS_INVALID_DATA; } ASSERT( sizeof(pVc->linkinfo) == sizeof(NDIS_WAN_CO_SET_LINK_INFO) ); NdisMoveMemory( &pVc->linkinfo, InformationBuffer, sizeof(pVc->linkinfo) ); DUMPB( TL_N, TM_Mp, &pVc->linkinfo, sizeof(pVc->linkinfo) ); *BytesRead = sizeof(NDIS_WAN_CO_SET_LINK_INFO); } *BytesNeeded = sizeof(NDIS_WAN_CO_SET_LINK_INFO); } break; case OID_WAN_CO_SET_COMP_INFO: { // DirectParallel doesn't provide compression. // TRACE( TL_N, TM_Mp, ( "SInfo(OID_WAN_CO_SET_COMP_INFO)" ) ); status = NDIS_STATUS_NOT_SUPPORTED; *BytesRead = *BytesNeeded = 0; break; } #if 0 // These OIDs are mandatory according to current doc, but since // NDISWAN never requests them they are omitted. // case OID_GEN_CURRENT_PACKET_FILTER: case OID_GEN_CURRENT_LOOKAHEAD: case OID_GEN_PROTOCOL_OPTIONS: case OID_WAN_PROTOCOL_TYPE: case OID_WAN_HEADER_FORMAT: #endif default: { TRACE( TL_A, TM_Mp, ( "SetInfo: Oid=$%08x?", Oid ) ); status = NDIS_STATUS_NOT_SUPPORTED; // JAY per SLC *BytesRead = *BytesNeeded = 0; break; } } return status; }