/*++ Copyright (c) 1992 Microsoft Corporation Module Name: miniport.c Abstract: ATM Ethernet PVC driver Author: ADube - created Revision History: --*/ #include "precomp.h" #pragma hdrstop //-------------------------------------------------------------------------------- // // // Global Variables used by miniports // // // //-------------------------------------------------------------------------------- static NDIS_OID EthernetSupportedOids[] = { OID_GEN_SUPPORTED_LIST, OID_GEN_HARDWARE_STATUS, OID_GEN_MEDIA_CONNECT_STATUS, OID_GEN_MEDIA_SUPPORTED, OID_GEN_MEDIA_IN_USE, OID_GEN_MAXIMUM_LOOKAHEAD, OID_GEN_MAXIMUM_FRAME_SIZE, OID_GEN_MAXIMUM_TOTAL_SIZE, OID_GEN_MAC_OPTIONS, OID_GEN_PROTOCOL_OPTIONS, OID_GEN_LINK_SPEED, OID_GEN_TRANSMIT_BUFFER_SPACE, OID_GEN_RECEIVE_BUFFER_SPACE, OID_GEN_TRANSMIT_BLOCK_SIZE, OID_GEN_RECEIVE_BLOCK_SIZE, OID_GEN_MAXIMUM_SEND_PACKETS, OID_GEN_VENDOR_DESCRIPTION, OID_GEN_VENDOR_ID, OID_GEN_DRIVER_VERSION, OID_GEN_VENDOR_DRIVER_VERSION, OID_GEN_CURRENT_PACKET_FILTER, OID_GEN_CURRENT_LOOKAHEAD, OID_GEN_XMIT_OK, OID_GEN_RCV_OK, OID_GEN_XMIT_ERROR, OID_GEN_RCV_ERROR, OID_GEN_RCV_NO_BUFFER, OID_802_3_PERMANENT_ADDRESS, OID_802_3_CURRENT_ADDRESS, OID_802_3_MULTICAST_LIST, OID_802_3_MAXIMUM_LIST_SIZE, OID_802_3_RCV_ERROR_ALIGNMENT, OID_802_3_XMIT_ONE_COLLISION, OID_802_3_XMIT_MORE_COLLISIONS, OID_GEN_NETWORK_LAYER_ADDRESSES, }; MP_REG_ENTRY NICRegTable[] = { // reg value name Offset in MP_ADAPTER Field size Default Value Min Max {NDIS_STRING_CONST("VCI"), 0, MP_OFFSET(config.vci), MP_SIZE(config.vci), 0, 0, 65535}, {NDIS_STRING_CONST("VPI"), 0, MP_OFFSET(config.vpi), MP_SIZE(config.vpi), 0, 0, 255}, {NDIS_STRING_CONST("Encap"), 0, MP_OFFSET(Encap), MP_SIZE(Encap), 2, 0, 3}, }; BOOLEAN g_bDumpPackets = FALSE; BOOLEAN g_fDiscardNonUnicastPackets = DISCARD_NON_UNICAST; //-------------------------------------------------------------// // // // Pre defined LLC, SNAP and Other Headers for encapsulation // // // //-------------------------------------------------------------// // // Ethernet Encapsulation // UCHAR LLCSnapEthernet[] = { 0xaa, 0xaa,0x03, // LLC 0x00, 0x80,0xc2, // OUI 0x00, 0x07, // PID 0x00, 0x00 // PAD }; // // Ip v4 encapsulation // UCHAR LLCSnapIpv4[8] = { 0xaa, 0xaa,0x03, // LLC 0x00, 0x00,0x00, // OUI 0x08, 0x00 // PID }; UCHAR gPaddingBytes[MINIMUM_ETHERNET_LENGTH] = { 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0 }; //-------------------------------------------------------------------------------- // // // miniports functions // // // //-------------------------------------------------------------------------------- VOID epvcReturnPacketUsingAllocation( IN PEPVC_I_MINIPORT pMiniport, IN PNDIS_PACKET Packet, OUT PNDIS_PACKET *ppOriginalPacket, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: Extracts the original packet frees all the ndis buffers in new packet returns the original packet Arguments: Return Value: --*/ { PNDIS_PACKET pOrigPacket = NULL; PEPVC_PKT_CONTEXT pPktContext = NULL; TRACE (TL_T, TM_Recv , ("==>epvcReturnPacketUsingAllocation pMiniport %p, pPacket %p", pMiniport, Packet)); pPktContext = (PEPVC_PKT_CONTEXT )(Packet->MiniportReservedEx); pOrigPacket = pPktContext->pOriginalPacket; if (pMiniport->fDoIpEncapsulation == TRUE) { // // Extract the lookaside buffer from the packet // PNDIS_BUFFER pBuffer = Packet->Private.Head; PEPVC_IP_RCV_BUFFER pIpBuffer= pPktContext ->Stack.ipv4Recv.pIpBuffer; if (pIpBuffer == NULL) { return ; // early return because of failure } ASSERT (pIpBuffer == NdisBufferVirtualAddress (pBuffer)); // // Free the Lookaside Buffer // epvcFreeToNPagedLookasideList (&pMiniport->rcv.LookasideList, (PVOID)pIpBuffer); // // In this case, we have allocated a new ndis buffer // so delete it and free the local memory epvcFreeBuffer (pBuffer); // // The original packet is unchanged and well./ // } else { // // This code path is used in both Ethernet and Ethernet+LLC encaps // // We only need to free the head of the packet as that was allocated // by us PNDIS_BUFFER pNdisBuffer = Packet->Private.Head; if (pNdisBuffer != NULL) { epvcFreeBuffer (pNdisBuffer); } } epvcFreePacket(Packet,&pMiniport->PktPool.Recv); *ppOriginalPacket = pOrigPacket; TRACE (TL_T, TM_Recv , ("<==epvcReturnPacketUsingAllocation pOrigPacket %p", *ppOriginalPacket)); return; } VOID epvcReturnPacketUsingStacks ( IN PEPVC_I_MINIPORT pMiniport, IN PNDIS_PACKET Packet, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: ipv4 - Restores the orginal Head and tail to this packet Arguments: Return Value: --*/ { PEPVC_PKT_CONTEXT pPktContext = NULL; BOOLEAN Remaining = FALSE; // Unused PNDIS_BUFFER pOldHead = NULL; PNDIS_BUFFER pOldTail = NULL; TRACE (TL_T, TM_Recv , ("==>epvcReturnPacketUsingStacks pMiniport %p, pPacket %p", pMiniport, Packet)); pPktContext = (PEPVC_PKT_CONTEXT ) NdisIMGetCurrentPacketStack(Packet, &Remaining); if (pMiniport->fDoIpEncapsulation == TRUE) { // // Extract the lookaside buffer from the packet // PNDIS_BUFFER pBuffer = Packet->Private.Head; PEPVC_IP_RCV_BUFFER pIpBuffer= pPktContext ->Stack.ipv4Recv.pIpBuffer; if (pIpBuffer == NULL) { return; // early return } // // Extract the old head and tail from the packet // pOldHead = pIpBuffer->pOldHead; pOldTail = pIpBuffer->pOldTail; // check to see if we are in this code path because of a failure if (pOldHead == NULL) { return; // early return } ASSERT (pOldHead != NULL); ASSERT (pOldTail != NULL); ASSERT (&pIpBuffer->u.Byte[0] == NdisBufferVirtualAddress (pBuffer)); // // Set The original Head and Tail // Packet->Private.Head = pOldHead; Packet->Private.Tail = pOldTail; Packet->Private.ValidCounts= FALSE; // // Free the Lookaside Buffer // epvcFreeToNPagedLookasideList (&pMiniport->rcv.LookasideList, (PVOID)pIpBuffer); // // In this case, we have allocated a new ndis buffer // so delete it and free the local memory epvcFreeBuffer (pBuffer); } else { // // This code path is used in both Ethernet and Ethernet+LLC encaps // // // We need to free the head as that was locally allocated/ // We need to revert back to the old Head and tail stored // in the context // if (pPktContext->Stack.EthLLC.pOldHead == NULL) { return ; //early return } epvcFreeBuffer (Packet->Private.Head); Packet->Private.Head = pPktContext->Stack.EthLLC.pOldHead; Packet->Private.Tail = pPktContext->Stack.EthLLC.pOldTail; Packet->Private.ValidCounts= FALSE; } TRACE (TL_T, TM_Recv , ("<==epvcReturnPacketUsingStacks ",pMiniport, Packet)); return; } VOID epvcProcessReturnPacket ( IN PEPVC_I_MINIPORT pMiniport, IN PNDIS_PACKET Packet, OUT PPNDIS_PACKET ppOrigPacket, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: Free all the locally allocated structures in the packet (packet , mdl, memory) Also be able to handle failure cases Arguments: Return Value: --*/ { ENTER("epvcProcessReturnPacket", 0x7fafa89d) PNDIS_PACKET pOrigPacket = NULL; TRACE (TL_T, TM_Recv , ("==>epvcProcessReturnPacket pMiniport %p, pPacket %p", pMiniport, Packet)); if (Packet == NULL) { return; } // // Packet stacking: Check if this packet belongs to us. // if (NdisGetPoolFromPacket(Packet) != pMiniport->PktPool.Recv.Handle) { // // We reused the original packet in a receive indication. // epvcReturnPacketUsingStacks (pMiniport, Packet, pSR); pOrigPacket = Packet; } else { // // This is a packet allocated from this IM's receive packet pool. // Reclaim our packet, and return the original to the driver below. // epvcReturnPacketUsingAllocation(pMiniport, Packet, &pOrigPacket, pSR); } // // Update the output variable // if (ppOrigPacket) { *ppOrigPacket = pOrigPacket; } EXIT() } VOID EpvcReturnPacket( IN NDIS_HANDLE MiniportAdapterContext, IN PNDIS_PACKET Packet ) /*++ Routine Description: Arguments: Return Value: --*/ { ENTER ("EpvcReturnPacket",0x58d2259e) PEPVC_I_MINIPORT pMiniport = (PEPVC_I_MINIPORT)MiniportAdapterContext; PNDIS_PACKET pOrigPacket = NULL; RM_DECLARE_STACK_RECORD (SR); // Free all the locally allocated structures in the packet // epvcProcessReturnPacket (pMiniport, Packet, &pOrigPacket ,&SR); // Return the original packet to ndis // if (pOrigPacket != NULL) { epvcReturnPacketToNdis(pMiniport, pOrigPacket, &SR); } else { ASSERT (!"Original packet is NULL\n"); } EXIT(); } NDIS_STATUS MPTransferData( OUT PNDIS_PACKET Packet, OUT PUINT BytesTransferred, IN NDIS_HANDLE MiniportAdapterContext, IN NDIS_HANDLE MiniportReceiveContext, IN UINT ByteOffset, IN UINT BytesToTransfer ) /*++ Routine Description: Miniport's transfer data handler. Arguments: Packet Destination packet BytesTransferred Place-holder for how much data was copied MiniportAdapterContext Pointer to the adapter structure MiniportReceiveContext Context ByteOffset Offset into the packet for copying data BytesToTransfer How much to copy. Return Value: Status of transfer --*/ { PEPVC_I_MINIPORT pMiniport= (PEPVC_I_MINIPORT)MiniportAdapterContext; NDIS_STATUS Status; // // Return, if the device is OFF // if (MiniportTestFlag (pMiniport, fMP_MiniportInitialized) == FALSE) { return NDIS_STATUS_FAILURE; } NdisTransferData(&Status, pMiniport->pAdapter->bind.BindingHandle, MiniportReceiveContext, ByteOffset, BytesToTransfer, Packet, BytesTransferred); return(Status); } NDIS_STATUS MPReset( OUT PBOOLEAN AddressingReset, IN NDIS_HANDLE MiniportAdapterContext ) /*++ Routine Description: Reset Handler. We just don't do anything. Arguments: AddressingReset To let NDIS know whether we need help from it with our reset MiniportAdapterContext Pointer to our adapter Return Value: --*/ { PADAPT pAdapt = (PADAPT)MiniportAdapterContext; *AddressingReset = FALSE; return(NDIS_STATUS_SUCCESS); } // // The functions that do the LBFO work and bundling. // If LBFO is turned off, then the Set Scondary API is never called and there are no bundles // //-------------------------------------------------------------------------------- // // // Intermediate Miniports. We have one instantiation per address family. // // Entry points used by the RM Apis // // // // // // // //-------------------------------------------------------------------------------- PRM_OBJECT_HEADER epvcIMiniportCreate( PRM_OBJECT_HEADER pParentObject, PVOID pCreateParams, PRM_STACK_RECORD psr ) /*++ Routine Description: Allocate and initialize an object of type EPVC_I_MINIPORT. Arguments: pParentObject - Object that is to be the parent of the adapter. pCreateParams - Actually a pointer to a EPVC_I_MINIPORT_PARAMS structure, which contains information required to create the adapter. Return Value: Pointer to the allocated and initialized object on success. NULL otherwise. --*/ { PEPVC_I_MINIPORT pIM; PEPVC_I_MINIPORT_PARAMS pParams = (PEPVC_I_MINIPORT_PARAMS)pCreateParams; NDIS_STATUS Status = NDIS_STATUS_FAILURE; extern RM_STATIC_OBJECT_INFO EpvcGlobals_I_MiniportStaticInfo; ENTER("IMiniport Create", 0x075b24c1); TRACE (TL_V, TM_Pt, ("--> epvcIMiniportCreate") ); EPVC_ALLOCSTRUCT(pIM, TAG_MINIPORT ); do { if (pIM == NULL) { break; } EPVC_ZEROSTRUCT(pIM); pIM->Hdr.Sig = TAG_MINIPORT; // // Do all the initialization work here // RmInitializeLock( &pIM->Lock, LOCKLEVEL_MINIPORT ); RmInitializeHeader( pParentObject, &pIM->Hdr, TAG_MINIPORT, &pIM->Lock, &EpvcGlobals_I_MiniportStaticInfo, NULL, psr ); // // Now initialize the adapter structure with the parameters // that were passed in. // Status = epvcCopyUnicodeString( &(pIM->ndis.DeviceName), pParams->pDeviceName, TRUE // Upcase ); if (FAIL(Status)) { pIM->ndis.DeviceName.Buffer=NULL; // so we don't try to free it later break; } // // initialize the informational stuff on the miniport // pIM->pAdapter = pParams->pAdapter; pIM->info.PacketFilter = 0; pIM->info.CurLookAhead = pParams->CurLookAhead; pIM->info.NumberOfMiniports = pParams->NumberOfMiniports; pIM->info.LinkSpeed = pParams->LinkSpeed.Outbound; pIM->info.MediaState = pParams->MediaState; // // Start by using the real ATM card's MAC address // NdisMoveMemory( &pIM->MacAddressEth, &pIM->pAdapter->info.MacAddress, sizeof(MAC_ADDRESS) ); // // Not Elan number zero so generate a locally // administered address by manipulating the first two bytes. // pIM->MacAddressEth.Byte[0] = 0x02 | (((UCHAR)pIM->info.NumberOfMiniports & 0x3f) << 2); pIM->MacAddressEth.Byte[1] = (pIM->pAdapter->info.MacAddress.Byte[1] & 0x3f) | ((UCHAR)pIM->info.NumberOfMiniports & 0x3f); pIM->info.MacAddressDummy = pIM->MacAddressEth; pIM->info.MacAddressDummy.Byte[0]++; pIM->info.MacAddressDummy.Byte[1]++; pIM->info.MacAddressDummy.Byte[2]++; { // // Create a Dummy Mac address for receive indications // pIM->info.MacAddressDest = pIM->MacAddressEth; } { // // Create an Ethernet Header to be used // PEPVC_ETH_HEADER pRcvEnetHeader = &pIM->RcvEnetHeader ; pRcvEnetHeader->eh_daddr = pIM->info.MacAddressDest; pRcvEnetHeader->eh_saddr = pIM->info.MacAddressDummy; pRcvEnetHeader->eh_type = net_short (IP_PROT_TYPE ); } pIM->info.McastAddrCount = 0; Status = NDIS_STATUS_SUCCESS; } while(FALSE); if (FAIL(Status)) { if (pIM != NULL) { epvcIMiniportDelete ((PRM_OBJECT_HEADER) pIM, psr); pIM = NULL; } } TRACE (TL_V, TM_Pt, ("<-- epvcIMiniportCreate pIMiniport. %p",pIM) ); return (PRM_OBJECT_HEADER) pIM; } VOID epvcIMiniportDelete ( PRM_OBJECT_HEADER pObj, PRM_STACK_RECORD psr ) /*++ Routine Description: Free an object of type EPVC_I_MINIPORT. Arguments: pHdr - Actually a pointer to the EPVC_I_MINIPORT to be deleted. --*/ { PEPVC_I_MINIPORT pMiniport = (PEPVC_I_MINIPORT) pObj; TRACE (TL_V, TM_Pt, ("-- epvcIMiniportDelete pAdapter %p",pMiniport) ); pMiniport->Hdr.Sig = TAG_FREED; EPVC_FREE (pMiniport); } BOOLEAN epvcIMiniportCompareKey( PVOID pKey, PRM_HASH_LINK pItem ) /*++ Routine Description: Hash comparison function for EPVC_I_MINIPORT. Arguments: pKey - Points to a Epvc Protocol object. pItem - Points to EPVC_I_MINIPORT.Hdr.HashLink. Return Value: TRUE IFF the key (adapter name) exactly matches the key of the specified adapter object. --*/ { PEPVC_I_MINIPORT pIM = NULL; PNDIS_STRING pName = (PNDIS_STRING) pKey; BOOLEAN fCompare; pIM = CONTAINING_RECORD(pItem, EPVC_I_MINIPORT, Hdr.HashLink); // // TODO: maybe case-insensitive compare? // if ( (pIM->ndis.DeviceName.Length == pName->Length) && NdisEqualMemory(pIM->ndis.DeviceName.Buffer, pName->Buffer, pName->Length)) { return TRUE; } else { return FALSE; } TRACE (TL_V, TM_Pt, ("-- epvcProtocolCompareKey pIM %p, pKey, return %x",pIM, pKey, fCompare ) ); return fCompare; } ULONG epvcIMiniportHash( PVOID pKey ) /*++ Routine Description: Hash function responsible for returning a hash of pKey, which we expect to be a pointer to an Epvc Protocol block. Return Value: ULONG-sized hash of the string. --*/ { TRACE(TL_T, TM_Mp, ("epvcIMiniportHash %x", pKey)); { PNDIS_STRING pName = (PNDIS_STRING) pKey; WCHAR *pwch = pName->Buffer; WCHAR *pwchEnd = pName->Buffer + pName->Length/sizeof(*pwch); ULONG Hash = 0; for (;pwch < pwchEnd; pwch++) { Hash ^= (Hash<<1) ^ *pwch; } return Hash; } } NDIS_STATUS epvcTaskVcSetup( IN struct _RM_TASK * pTask, IN RM_TASK_OPERATION Code, IN UINT_PTR UserParam, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: Arguments: UserParam for (Code == RM_TASKOP_START) : UnbindContext --*/ { ENTER("epvcTaskVcSetup", 0x64085960) NDIS_STATUS Status = NDIS_STATUS_FAILURE; PEPVC_I_MINIPORT pMiniport = (PEPVC_I_MINIPORT ) RM_PARENT_OBJECT(pTask); PTASK_VC pTaskVc = (PTASK_VC) pTask; PEPVC_ADAPTER pAdapter = (PEPVC_ADAPTER)pMiniport->Hdr.pParentObject; NDIS_HANDLE NdisVcHandle = NULL; PCO_CALL_PARAMETERS pCallParameters = NULL; enum { Stage_Start =0, // default Stage_CreateVc, Stage_MakeCall, Stage_DeleteVc, // in case of failure Stage_TaskCompleted, Stage_End }; // To be used in pTask->Hdr.State to indicate the state of the Task TRACE ( TL_T, TM_Pt, ("==> epvcTaskVcSetup %x",pTask->Hdr.State ) ); switch (pTask->Hdr.State) { case Stage_Start: { LOCKOBJ (pMiniport, pSR); if (epvcIsThisTaskPrimary ( pTask, &(PRM_TASK)(pMiniport->vc.pTaskVc)) == FALSE) { PRM_TASK pOtherTask = (PRM_TASK)(pMiniport->vc.pTaskVc); RmTmpReferenceObject (&pOtherTask->Hdr, pSR); // // Set The state so we restart this code after main task completes // pTask->Hdr.State = Stage_Start; UNLOCKOBJ(pMiniport, pSR); RmPendTaskOnOtherTask (pTask, 0, pOtherTask, pSR); RmTmpDereferenceObject(&pOtherTask->Hdr,pSR); Status = NDIS_STATUS_PENDING; break; } // // We are the primary task // ASSERT (pMiniport->vc.pTaskVc == pTaskVc); // // Check to see if our work is already done // if (MiniportTestFlag(pMiniport, fMP_MakeCallSucceeded) == TRUE) { // // Our work had been done. So break out and complete the task // Status = NDIS_STATUS_SUCCESS; pTaskVc->ReturnStatus = NDIS_STATUS_SUCCESS; pTask->Hdr.State = Stage_TaskCompleted; UNLOCKOBJ(pMiniport, pSR); break; } MiniportClearFlag (pMiniport,fMP_InfoCallClosed); MiniportSetFlag (pMiniport, fMP_InfoMakingCall); UNLOCKOBJ(pMiniport, pSR); // // Now begin the real work // // // Set up the call parameters. If it fails ,then exit // epvcSetupMakeCallParameters(pMiniport, &pCallParameters); if (pCallParameters == NULL) { Status = NDIS_STATUS_FAILURE; pTaskVc->ReturnStatus = NDIS_STATUS_FAILURE; pTask->Hdr.State = Stage_TaskCompleted; break; } // // Create Vc - Syncronous call // ASSERT (pAdapter->Hdr.Sig = TAG_ADAPTER); Status = epvcCoCreateVc(pAdapter->bind.BindingHandle, pMiniport->af.AfHandle OPTIONAL, // For CM signalling VCs pMiniport, &NdisVcHandle); ASSERT (PEND(Status) == FALSE); // this is a synchronous call if (FAIL(Status) == TRUE) { // // We have failed. This task is done. There are not // resources to be freed, although a flag has to be // cleared // NdisVcHandle = NULL; pMiniport->vc.VcHandle = NULL; pTask->Hdr.State = Stage_TaskCompleted; break; } ASSERT (Status == NDIS_STATUS_SUCCESS); // // Store the Vc Handle // LOCKOBJ (pMiniport, pSR); pMiniport->vc.VcHandle = NdisVcHandle; epvcLinkToExternal( &pMiniport->Hdr, 0xf52962f1, (UINT_PTR)pMiniport->vc.VcHandle, EPVC_ASSOC_MINIPORT_OPEN_VC, " VcHandle %p\n", pSR); UNLOCKOBJ (pMiniport, pSR); // // Do a Make Call // pTask->Hdr.State = Stage_MakeCall; RmSuspendTask(pTask, 0, pSR); Status = epvcClMakeCall(NdisVcHandle, pCallParameters, NULL, //Party Context NULL // PartyHandle ); if (NDIS_STATUS_PENDING !=Status) { EpvcCoMakeCallComplete(Status, pMiniport, NULL, 0); } break; } case Stage_MakeCall: { // // The make call has been completed. // If we have succeeded then we update our flags // and exit. // // If the make call has failed, then I need to delete the VC // ASSERT (NDIS_STATUS_CALL_ACTIVE != pTaskVc->ReturnStatus); if (NDIS_STATUS_SUCCESS == pTaskVc->ReturnStatus) { LOCKOBJ(pMiniport, pSR); MiniportSetFlag (pMiniport, fMP_MakeCallSucceeded); MiniportClearFlag (pMiniport, fMP_InfoMakingCall); UNLOCKOBJ (pMiniport, pSR); } else { NDIS_HANDLE VcHandle = NULL; // // Delete the VC, as we do not want a VC without an active // Make call on it. // ASSERT (NDIS_STATUS_SUCCESS == pTaskVc->ReturnStatus); LOCKOBJ(pMiniport, pSR); VcHandle = pMiniport->vc.VcHandle; epvcUnlinkFromExternal( &pMiniport->Hdr, 0xa914405a, (UINT_PTR)pMiniport->vc.VcHandle, EPVC_ASSOC_MINIPORT_OPEN_VC, pSR); pMiniport->vc.VcHandle = NULL; UNLOCKOBJ (pMiniport, pSR); TRACE (TL_I, TM_Mp,("Deleting Vc because of a failure in MakeCall")); Status = epvcCoDeleteVc(VcHandle); // // TODO: Fix Failure case // ASSERT (NDIS_STATUS_SUCCESS == Status ); } // // This task is over. Now do the indications // pTask->Hdr.State = Stage_TaskCompleted; Status = NDIS_STATUS_SUCCESS; break; } case Stage_End: { Status = NDIS_STATUS_SUCCESS; break; } default: { ASSERTEX(!"Unknown task op", pTask); } } // end of switch if ( Stage_TaskCompleted == pTask->Hdr.State ) { pTask->Hdr.State = Stage_End; ASSERT (NDIS_STATUS_PENDING !=Status ); // // Do any cleanup indications to NDIS over here // epvcVcSetupDone ( pTaskVc, pMiniport); LOCKOBJ(pMiniport, pSR); pMiniport->vc.pTaskVc = NULL; UNLOCKOBJ (pMiniport, pSR); } TRACE ( TL_T, TM_Mp, ("<== epvcTaskVcSetup , Status %x",Status) ); RM_ASSERT_NOLOCKS(pSR); EXIT() return Status; } VOID epvcVcSetupDone ( PTASK_VC pTaskVc, PEPVC_I_MINIPORT pMiniport ) /*++ Routine Description: If the task was queued because of SetPacket Filter request then this function completes the request. If the task was run because of the Indicate Media Connect event, then this thread indicates a Media Connect to NDIS Arguments: Status - Did the VcSetup Succeed or Fail pTaskVc - Task in question pMiniport - the Miniport that the task operated on Return Value: None: --*/ { if (TaskCause_NdisRequest == pTaskVc->Cause ) { // // Since requests are serialized, we don't acquire the lock // TRACE (TL_V, TM_Rq, ("Completing SetPacketFilter Request %x", pTaskVc->ReturnStatus )); if (pTaskVc->ReturnStatus == NDIS_STATUS_SUCCESS) { pMiniport->info.PacketFilter = pTaskVc->PacketFilter; } NdisMSetInformationComplete (pMiniport->ndis.MiniportAdapterHandle, pTaskVc->ReturnStatus); } else { ASSERT (TaskCause_MediaConnect == pTaskVc->Cause ); pMiniport->info.MediaState = NdisMediaStateConnected; NdisMIndicateStatus ( pMiniport->ndis.MiniportAdapterHandle, NDIS_STATUS_MEDIA_CONNECT, NULL, 0); } } NDIS_STATUS epvcTaskVcTeardown( IN struct _RM_TASK * pTask, IN RM_TASK_OPERATION Code, IN UINT_PTR UserParam, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: Arguments: UserParam for (Code == RM_TASKOP_START) : UnbindContext --*/ { ENTER("epvcTaskVcTeardown", 0x68c96c4d) NDIS_STATUS Status = NDIS_STATUS_FAILURE; PEPVC_I_MINIPORT pMiniport = (PEPVC_I_MINIPORT ) RM_PARENT_OBJECT(pTask); PTASK_VC pTaskVc = (PTASK_VC) pTask; PEPVC_ADAPTER pAdapter = (PEPVC_ADAPTER)pMiniport->Hdr.pParentObject; NDIS_HANDLE NdisVcHandle = NULL; PCO_CALL_PARAMETERS pCallParameters = NULL; enum { Stage_Start =0, // default Stage_CloseCallComplete, Stage_DeleteVc, Stage_TaskCompleted, Stage_End }; // To be used in pTask->Hdr.State to indicate the state of the Task TRACE ( TL_T, TM_Pt, ("==> epvcTaskVcTeardown %x",pTask->Hdr.State ) ); switch (pTask->Hdr.State) { case Stage_Start: { LOCKOBJ (pMiniport, pSR); if (epvcIsThisTaskPrimary ( pTask, &(PRM_TASK)(pMiniport->vc.pTaskVc)) == FALSE) { PRM_TASK pOtherTask = (PRM_TASK)(pMiniport->vc.pTaskVc); RmTmpReferenceObject (&pOtherTask->Hdr, pSR); // // Set The state so we restart this code after main task completes // pTask->Hdr.State = Stage_Start; UNLOCKOBJ(pMiniport, pSR); RmPendTaskOnOtherTask (pTask, 0, pOtherTask, pSR); RmTmpDereferenceObject(&pOtherTask->Hdr,pSR); Status = NDIS_STATUS_PENDING; break; } // // We are the primary task // ASSERT (pMiniport->vc.pTaskVc == pTaskVc); // // Check to see if our work is already done // if (MiniportTestFlag(pMiniport, fMP_MakeCallSucceeded) == FALSE) { // // Our work had been done. So break out and complete the task // Status = NDIS_STATUS_SUCCESS; pTask->Hdr.State = Stage_TaskCompleted; UNLOCKOBJ(pMiniport, pSR); break; } MiniportClearFlag (pMiniport, fMP_MakeCallSucceeded); MiniportSetFlag (pMiniport, fMP_InfoClosingCall); UNLOCKOBJ(pMiniport, pSR); // // Now close the call - Asynchronously. // pTask->Hdr.State = Stage_CloseCallComplete; RmSuspendTask (pTask, 0, pSR); Status = epvcClCloseCall( pMiniport->vc.VcHandle); if (NDIS_STATUS_PENDING != Status) { EpvcCoCloseCallComplete (Status, pMiniport, NULL ); } Status = NDIS_STATUS_PENDING; break; } case Stage_CloseCallComplete: { NDIS_HANDLE VcHandle = NULL; LOCKOBJ(pMiniport, pSR); VcHandle = pMiniport->vc.VcHandle; epvcUnlinkFromExternal(&pMiniport->Hdr, 0x5d7b5ea8, (UINT_PTR)pMiniport->vc.VcHandle, EPVC_ASSOC_MINIPORT_OPEN_VC, pSR); pMiniport->vc.VcHandle = NULL; UNLOCKOBJ(pMiniport, pSR); Status = epvcCoDeleteVc(VcHandle); // // This is an assertion because the DeleteVc cannot fail. // We do a DeleteVc in one place only and it is serialized. // ASSERT (Status == NDIS_STATUS_SUCCESS); pTask->Hdr.State = Stage_TaskCompleted; break; } case Stage_End: { Status = NDIS_STATUS_SUCCESS; break; } default: { ASSERTEX(!"Unknown task op", pTask); } } if (Stage_TaskCompleted == pTask->Hdr.State ) { pTask->Hdr.State = Stage_End; // // Complete the request or the Media Disconnect; // epvcVcTeardownDone(pTaskVc, pMiniport); LOCKOBJ (pMiniport, pSR); // // Update informational flags // MiniportClearFlag (pMiniport, fMP_InfoClosingCall); MiniportSetFlag (pMiniport, fMP_InfoCallClosed); pMiniport->vc.pTaskVc = NULL; UNLOCKOBJ(pMiniport, pSR); } TRACE ( TL_T, TM_Mp, ("<== epvcTaskVcTeardown , Status %x",Status) ); RM_ASSERT_NOLOCKS(pSR); EXIT() return Status; } VOID epvcVcTeardownDone( PTASK_VC pTaskVc, PEPVC_I_MINIPORT pMiniport ) { TRACE ( TL_T, TM_Mp, ("==> epvcVcTeardownDone ") ); switch (pTaskVc->Cause) { case TaskCause_NdisRequest: { ASSERT (pTaskVc->ReturnStatus != NDIS_STATUS_PENDING); // // Since requests are serialized, we don't acquire the lock // pMiniport->info.PacketFilter = pTaskVc->PacketFilter; NdisMSetInformationComplete(pMiniport->ndis.MiniportAdapterHandle, pTaskVc->ReturnStatus); break; } case TaskCause_MediaDisconnect: { pMiniport->info.MediaState = NdisMediaStateDisconnected; epvcMIndicateStatus ( pMiniport, NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0); break; } default: { // Do nothing. // } } TRACE ( TL_T, TM_Mp, ("<== epvcVcTeardownDone ") ); } NDIS_STATUS EpvcInitialize( OUT PNDIS_STATUS OpenErrorStatus, OUT PUINT SelectedMediumIndex, IN PNDIS_MEDIUM MediumArray, IN UINT MediumArraySize, IN NDIS_HANDLE MiniportAdapterHandle, IN NDIS_HANDLE WrapperConfigurationContext ) /*++ Routine Description: This is the initialize handler which gets called as a result of the BindAdapter handler calling NdisIMInitializeDeviceInstanceEx(). The context parameter which we pass there is the adapter structure which we retreive here. We also need to initialize the Power Management variable. LoadBalalncing- We keep a global list of all the passthru miniports installed and bundle two of them together if they have the same BundleId (read from registry) Arguments: OpenErrorStatus Not used by us. SelectedMediumIndex Place-holder for what media we are using MediumArray Array of ndis media passed down to us to pick from MediumArraySize Size of the array MiniportAdapterHandle The handle NDIS uses to refer to us WrapperConfigurationContext For use by NdisOpenConfiguration Return Value: NDIS_STATUS_SUCCESS unless something goes wrong --*/ { ENTER ("EpvcInitialize", 0xa935a2a5) UINT i; PEPVC_I_MINIPORT pMiniport = NULL; NDIS_STATUS Status = NDIS_STATUS_FAILURE; KIRQL OldIrql; enum { Stage_Start, Stage_AllocatedPacketPools, Stage_AllocatedLookasideLists }; ULONG State = Stage_Start; RM_DECLARE_STACK_RECORD (SR); TRACE (TL_T, TM_Mp, ("==>EpvcInitialize MiniportAdapterHandle %x", MiniportAdapterHandle)); // // Start off by retrieving the adapter context and storing the Miniport handle in it // pMiniport = NdisIMGetDeviceContext(MiniportAdapterHandle); if (pMiniport->Hdr.Sig != TAG_MINIPORT) { ASSERT (pMiniport->Hdr.Sig == TAG_MINIPORT); return NDIS_STATUS_FAILURE; } pMiniport->ndis.MiniportAdapterHandle = MiniportAdapterHandle; // // Make sure the medium saved is one of the ones being offered // for (i = 0; i < MediumArraySize; i++) { if (MediumArray[i] == ATMEPVC_MP_MEDIUM ) { *SelectedMediumIndex = i; break; } } if (i == MediumArraySize) { return(NDIS_STATUS_UNSUPPORTED_MEDIA); } // // Set the attributes now. The NDIS_ATTRIBUTE_DESERIALIZE is the key. This enables us // to make up-calls to NDIS w/o having to call NdisIMSwitchToMiniport/NdisIMQueueCallBack. // This also forces us to protect our data using spinlocks where appropriate. Also in this // case NDIS does not queue packets on out behalf. Since this is a very simple pass-thru // miniport, we do not have a need to protect anything. However in a general case there // will be a need to use per-adapter spin-locks for the packet queues at the very least. // NdisMSetAttributesEx(MiniportAdapterHandle, pMiniport, 0, // CheckForHangTimeInSeconds NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT| NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER | NDIS_ATTRIBUTE_DESERIALIZE, 0); // // We are done, with the no failure stuff. From now on we need to undo // do { Status = epvcMiniportReadConfig(pMiniport, WrapperConfigurationContext,&SR ); if (Status != NDIS_STATUS_SUCCESS) { // // Undo Configuration values // ASSERT (Status == NDIS_STATUS_SUCCESS); break; } epvcInitializeMiniportParameters(pMiniport); // // allocate Packet pools. // Status = epvcInitializeMiniportPacketPools (pMiniport); if (Status != NDIS_STATUS_SUCCESS) { ASSERT (Status == NDIS_STATUS_SUCCESS); break; } State = Stage_AllocatedPacketPools; // // Allocate lookaside lists // epvcInitializeMiniportLookasideLists(pMiniport); State = Stage_AllocatedLookasideLists; Status = NDIS_STATUS_SUCCESS; } while (FALSE); TRACE (TL_T, TM_Mp, ("<==EpvcInitialize pMiniport %x, Status %x", pMiniport, Status )); if (Status == NDIS_STATUS_SUCCESS) { BOOLEAN fSetDeInit = FALSE; LOCKOBJ(pMiniport, &SR); MiniportSetFlag(pMiniport, ,fMP_MiniportInitialized); if (MiniportTestFlag (pMiniport, fMP_MiniportCancelInstance)) { fSetDeInit = TRUE; } UNLOCKOBJ(pMiniport, &SR); // // Check to see if we have a DeInit Waiting for us. // This will only be set if a Cancel Device Instance fails. // if (fSetDeInit == TRUE) { epvcSetEvent (&pMiniport->pnp.DeInitEvent); } } else { // // Undo Code // ASSERT (FAIL(Status) == TRUE); switch (State) { case Stage_AllocatedLookasideLists: epvcDeleteMiniportLookasideLists (pMiniport); FALL_THROUGH case Stage_AllocatedPacketPools: epvcDeleteMiniportPacketPools(pMiniport); FALL_THROUGH default: break; } } RM_ASSERT_CLEAR(&SR); EXIT(); return Status; } VOID EpvcHalt( IN NDIS_HANDLE MiniportAdapterContext ) /*++ Routine Description: Halt handler. All the hard-work for clean-up is done here. Arguments: MiniportAdapterContext Pointer to the Adapter Return Value: None. --*/ { ENTER("EpvcHalt",0x6b407ae1) PEPVC_I_MINIPORT pMiniport = (PEPVC_I_MINIPORT)MiniportAdapterContext; PEPVC_ADAPTER pAdapter = pMiniport->pAdapter; PRM_TASK pTask = NULL; NDIS_STATUS Status = NDIS_STATUS_FAILURE; RM_DECLARE_STACK_RECORD (SR); TRACE (TL_V, TM_Mp, ("==>Epvc MPHaltMiniport")); do { LOCKOBJ (pMiniport, &SR); // // Clear the flag so we can block all sends/receives/requests // MiniportClearFlag(pMiniport, fMP_MiniportInitialized); MiniportSetFlag(pMiniport, fMP_InfoHalting); // // Ref the miniport, this indirectly refs the adpater as well // RmTmpReferenceObject (&pMiniport->Hdr, &SR); // // Kick of the miniport halt task and wait for it to complete // Status = epvcAllocateTask( &pMiniport->Hdr, // pParentObject, epvcTaskHaltMiniport, // pfnHandler, 0, // Timeout, "Task: Halt Intermediate Miniport", // szDescription &pTask, &SR ); if (FAIL(Status)) { pTask = NULL; break; } // // Reference the task so it is around until our Wait for completion // is complete // RmTmpReferenceObject (&pTask->Hdr, &SR); UNLOCKOBJ (pMiniport, &SR); // // This Kicks of the task that will close the Call, Delete // the VC and close the AF. We do this all synchronously // { PTASK_HALT pHalt = (PTASK_HALT) pTask; epvcInitializeEvent (&pHalt->CompleteEvent); RmStartTask(pTask, 0, &SR); TRACE (TL_V, TM_Mp, ("About to Wait - for Halt Complete Event")); epvcWaitEvent (&pHalt->CompleteEvent, WAIT_INFINITE); TRACE (TL_V, TM_Mp, ("Wait Complete- for Halt Complete Event")); } LOCKOBJ (pMiniport, &SR); // // Deref the task . Ref was made above. // RmTmpDereferenceObject (&pTask->Hdr, &SR); } while (FALSE); MiniportClearFlag(pMiniport, fMP_InfoHalting); UNLOCKOBJ(pMiniport, &SR); RmTmpDereferenceObject(&pMiniport->Hdr, &SR); RM_ASSERT_CLEAR(&SR); TRACE (TL_V, TM_Mp, ("<==Epvc MPHaltMiniport")); } VOID epvcSetPacketFilterWorkItem ( PNDIS_WORK_ITEM pWorkItem, PVOID Context ) /*++ Routine Description: Decrements the refcount on the filter and processes the new packet filter Return Value: None --*/ { ENTER ("epvcSetPacketFilterWorkItem ",0x3e1cdbba ) PEPVC_I_MINIPORT pMiniport = NULL; PRM_TASK pTask = NULL; NDIS_STATUS Status = NDIS_STATUS_FAILURE; UINT Filter ; RM_DECLARE_STACK_RECORD (SR); do { pMiniport = CONTAINING_RECORD (pWorkItem, EPVC_I_MINIPORT, vc.PacketFilterWorkItem) ; // // Dereference the workitem off the miniport // epvcUnlinkFromExternal( &pMiniport->Hdr, 0xa1f5e3cc, (UINT_PTR)pWorkItem, EPVC_ASSOC_SET_FILTER_WORKITEM, &SR); // // Start the task to create or delete the VC // Filter = pMiniport->vc.NewFilter ; // // If this is a repition, then succeed it synchronously // if (Filter == pMiniport->info.PacketFilter) { Status = NDIS_STATUS_SUCCESS; break; } LOCKOBJ(pMiniport, &SR); // // Are we moving to a Zero filter value // if (Filter == 0) { // // Delete the Vc, so that we stop doing any receives // Status = epvcAllocateTask( &pMiniport->Hdr, // pParentObject, epvcTaskVcTeardown, // pfnHandler, 0, // Timeout, "Task: Delete Vc", // szDescription &pTask, &SR ); } else { // // We are moving a non-zero values // // // Create the Vc, so that we can send // Status = epvcAllocateTask( &pMiniport->Hdr, // pParentObject, epvcTaskVcSetup, // pfnHandler, 0, // Timeout, "Task: Create Vc", // szDescription &pTask, &SR ); } UNLOCKOBJ(pMiniport, &SR); if (FAIL(Status) == TRUE) { // Ugly situation. We'll just leave things as they are... // pTask = NULL; TR_WARN(("FATAL: couldn't allocate create/ delete Vc task!\n")); ASSERT (0); break; } // // Update the cause if the task // ((PTASK_VC)pTask)->Cause = TaskCause_NdisRequest; ((PTASK_VC)pTask)->PacketFilter = Filter ; RmStartTask(pTask, 0, &SR); Status = NDIS_STATUS_PENDING; } while (FALSE); // // complete the request if the task has not been started // if (PEND(Status) != TRUE) { NdisMSetInformationComplete (pMiniport->ndis.MiniportAdapterHandle, Status); } EXIT(); } NDIS_STATUS epvcSetPacketFilter( IN PEPVC_I_MINIPORT pMiniport, IN ULONG Filter, PRM_STACK_RECORD pSR ) /*++ Routine Description: This routine is called when a miniport get a set packet filter. It validates the arguments, If all is well then it process the request For a non-zero filter, a create VC and a Make call is done. For a zero filter, the call is closed and the Vc Deleted Return Value: NDIS_STATUS_SUCCESS unless something goes wrong --*/ { ENTER ("epvcSetPacketFilter", 0x97c6b961) NDIS_STATUS Status = NDIS_STATUS_PENDING; PNDIS_WORK_ITEM pSetFilterWorItem = &pMiniport->vc.PacketFilterWorkItem; PRM_TASK pTask = NULL; TRACE (TL_T, TM_Mp, ("==>epvcSetPacketFilter Filter %X", Filter )); do { LOCKOBJ (pMiniport, pSR); epvcLinkToExternal( &pMiniport->Hdr, 0x20bc1fbf, (UINT_PTR)pSetFilterWorItem, EPVC_ASSOC_SET_FILTER_WORKITEM, " PacketFilterWorkItem %p\n", pSR); // // Update the cause of the task // UNLOCKOBJ(pMiniport, pSR); // // Now schedule the work item so it runs at passive level and pass the Vc as // an argument // pMiniport->vc.NewFilter = Filter; NdisInitializeWorkItem ( pSetFilterWorItem , (NDIS_PROC)epvcSetPacketFilterWorkItem , (PVOID)pTask ); NdisScheduleWorkItem (pSetFilterWorItem ); Status = NDIS_STATUS_PENDING; } while (FALSE); TRACE (TL_T, TM_Mp, ("<==epvcSetPacketFilter %x", Status)); EXIT(); return Status; } NDIS_STATUS EpvcMpQueryInformation( IN NDIS_HANDLE MiniportAdapterContext, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesWritten, OUT PULONG BytesNeeded ) /*++ Routine Description: The QueryInformation Handler for the virtual miniport. Arguments: MiniportAdapterContext - a pointer to the Elan. Oid - the NDIS_OID to process. InformationBuffer - a pointer into the NdisRequest->InformationBuffer into which store the result of the query. InformationBufferLength - a pointer to the number of bytes left in the InformationBuffer. BytesWritten - a pointer to the number of bytes written into the InformationBuffer. BytesNeeded - If there is not enough room in the information buffer then this will contain the number of bytes needed to complete the request. Return Value: The function value is the status of the operation. --*/ { ENTER ("EpvcMpQueryInformation", 0x3da2473b) UINT BytesLeft = InformationBufferLength; PUCHAR InfoBuffer = (PUCHAR)(InformationBuffer); NDIS_STATUS StatusToReturn = NDIS_STATUS_SUCCESS; NDIS_HARDWARE_STATUS HardwareStatus = NdisHardwareStatusReady; NDIS_MEDIUM Medium; PEPVC_I_MINIPORT pMiniport = NULL; PEPVC_ADAPTER pAdapter= NULL; ULONG GenericULong =0; USHORT GenericUShort=0; UCHAR GenericArray[6]; UINT MoveBytes = sizeof(ULONG); PVOID MoveSource = (PVOID)(&GenericULong); ULONG i=0; BOOLEAN IsShuttingDown = FALSE; RM_DECLARE_STACK_RECORD (SR); TRACE(TL_T, TM_Rq, ("==>EpvcMpQueryInformation pMiniport %x, Oid, Buffer %x, Length, %x", pMiniport, Oid, InformationBuffer, InformationBufferLength)); pMiniport = (PEPVC_I_MINIPORT)MiniportAdapterContext; LOCKOBJ(pMiniport, &SR); IsShuttingDown = (! MiniportTestFlag(pMiniport, fMP_MiniportInitialized)); pAdapter = pMiniport->pAdapter; UNLOCKOBJ(pMiniport,&SR); // // Switch on request type // switch (Oid) { case OID_GEN_MAC_OPTIONS: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MAC_OPTIONS")); GenericULong = NDIS_MAC_OPTION_NO_LOOPBACK; break; case OID_GEN_SUPPORTED_LIST: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_SUPPORTED_LIST")); MoveSource = (PVOID)(EthernetSupportedOids); MoveBytes = sizeof(EthernetSupportedOids); break; case OID_GEN_HARDWARE_STATUS: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_HARDWARE_STATUS")); HardwareStatus = NdisHardwareStatusReady; MoveSource = (PVOID)(&HardwareStatus); MoveBytes = sizeof(NDIS_HARDWARE_STATUS); break; case OID_GEN_MEDIA_CONNECT_STATUS: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MEDIA_CONNECT_STATUS")); MoveSource = (PVOID)(&pMiniport->info.MediaState); MoveBytes = sizeof(NDIS_MEDIA_STATE); break; case OID_GEN_MEDIA_SUPPORTED: case OID_GEN_MEDIA_IN_USE: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MEDIA_SUPPORTED")); Medium = ATMEPVC_MP_MEDIUM; MoveSource = (PVOID) (&Medium); MoveBytes = sizeof(NDIS_MEDIUM); break; case OID_GEN_MAXIMUM_LOOKAHEAD: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MAXIMUM_LOOKAHEAD")); GenericULong = pMiniport->info.CurLookAhead; break; case OID_GEN_CURRENT_LOOKAHEAD: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_CURRENT_LOOKAHEAD")); GenericULong = pMiniport->info.CurLookAhead ; break; case OID_GEN_MAXIMUM_FRAME_SIZE: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MAXIMUM_FRAME_SIZE")); // // Similiar to AtmLane . Take the size of the Ethernet frame and strip the // ethernet header off. // GenericULong = EPVC_MAX_FRAME_SIZE - EPVC_ETH_HEADERSIZE ; break; case OID_GEN_MAXIMUM_TOTAL_SIZE: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MAXIMUM_TOTAL_SIZE")); // // This value is inclusive of headers // GenericULong = EPVC_MAX_FRAME_SIZE; break; case OID_GEN_TRANSMIT_BLOCK_SIZE: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_TRANSMIT_BLOCK_SIZE")); // // This is inclusive of headers. // GenericULong = EPVC_MAX_FRAME_SIZE; break; case OID_GEN_RECEIVE_BLOCK_SIZE: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_RECEIVE_BLOCK_SIZE")); GenericULong = EPVC_MAX_FRAME_SIZE ; break; case OID_GEN_MAXIMUM_SEND_PACKETS: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MAXIMUM_SEND_PACKETS")); GenericULong = 32; // XXX What is our limit? From adapter? break; case OID_GEN_LINK_SPEED: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_LINK_SPEED")); GenericULong = pMiniport->info.LinkSpeed; break; case OID_GEN_TRANSMIT_BUFFER_SPACE: case OID_GEN_RECEIVE_BUFFER_SPACE: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_RECEIVE_BUFFER_SPACE")); GenericULong = 32 * 1024; // XXX What should this really be? break; case OID_GEN_VENDOR_ID: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_VENDOR_ID")); NdisMoveMemory( (PVOID)&GenericULong, &pMiniport->MacAddressEth, 3 ); GenericULong &= 0xFFFFFF00; MoveSource = (PVOID)(&GenericULong); MoveBytes = sizeof(GenericULong); break; case OID_GEN_VENDOR_DESCRIPTION: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_VENDOR_DESCRIPTION")); MoveSource = (PVOID)"Microsoft ATM Ethernet Emulation"; MoveBytes = 28; break; case OID_GEN_DRIVER_VERSION: case OID_GEN_VENDOR_DRIVER_VERSION: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_DRIVER_VERSION")); GenericUShort = ((USHORT)5 << 8) | 0; MoveSource = (PVOID)(&GenericUShort); MoveBytes = sizeof(GenericUShort); break; case OID_802_3_PERMANENT_ADDRESS: case OID_802_3_CURRENT_ADDRESS: TRACE (TL_V, TM_Rq,(" Miniport Query OID_802_3_CURRENT_ADDRESS")); NdisMoveMemory((PCHAR)GenericArray, &pMiniport->MacAddressEth, sizeof(MAC_ADDRESS)); MoveSource = (PVOID)(GenericArray); MoveBytes = sizeof(MAC_ADDRESS); break; case OID_802_3_MULTICAST_LIST: TRACE (TL_V, TM_Rq,(" Miniport Query OID_802_3_MULTICAST_LIST")); MoveSource = (PVOID) &pMiniport->info.McastAddrs[0]; MoveBytes = pMiniport->info.McastAddrCount * sizeof(MAC_ADDRESS); break; case OID_802_3_MAXIMUM_LIST_SIZE: TRACE (TL_V, TM_Rq,(" Miniport Query OID_802_3_MAXIMUM_LIST_SIZE")); GenericULong = MCAST_LIST_SIZE; break; case OID_GEN_XMIT_OK: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_XMIT_OK")); GenericULong = (UINT)(pMiniport->count.FramesXmitOk); break; case OID_GEN_RCV_OK: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_RCV_OK")); GenericULong = (UINT)(pMiniport->count.FramesRecvOk); break; case OID_GEN_RCV_ERROR: TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_RCV_OK")); GenericULong = pMiniport->count.RecvDropped ; break; case OID_GEN_XMIT_ERROR: case OID_GEN_RCV_NO_BUFFER: case OID_802_3_RCV_ERROR_ALIGNMENT: case OID_802_3_XMIT_ONE_COLLISION: case OID_802_3_XMIT_MORE_COLLISIONS: TRACE (TL_V, TM_Rq,(" Miniport Query - Unimplemented Stats Oid")); GenericULong = 0; break; default: StatusToReturn = NDIS_STATUS_INVALID_OID; break; } if (StatusToReturn == NDIS_STATUS_SUCCESS) { if (MoveBytes > BytesLeft) { // // Not enough room in InformationBuffer. Punt // *BytesNeeded = MoveBytes; StatusToReturn = NDIS_STATUS_INVALID_LENGTH; } else { // // Store and print result. // NdisMoveMemory(InfoBuffer, MoveSource, MoveBytes); TRACE (TL_V, TM_Rq, ("Query Request Oid %x", Oid)); DUMPDW( TL_V, TM_Rq, MoveSource, MoveBytes); (*BytesWritten) = MoveBytes; } } TRACE(TL_T, TM_Rq, ("<==EpvcMpQueryInformation Status %x",StatusToReturn)); RM_ASSERT_CLEAR(&SR); return StatusToReturn; } NDIS_STATUS EpvcMpSetInformation( IN NDIS_HANDLE MiniportAdapterContext, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesRead, OUT PULONG BytesNeeded ) /*++ Routine Description: Handles a set operation for a single OID. Arguments: MiniportAdapterContext - a pointer to the Elan. Oid - the NDIS_OID to process. InformationBuffer - Holds the data to be set. InformationBufferLength - The length of InformationBuffer. BytesRead - If the call is successful, returns the number of bytes read from InformationBuffer. BytesNeeded - If there is not enough data in InformationBuffer to satisfy the OID, returns the amount of storage needed. Return Value: NDIS_STATUS_SUCCESS NDIS_STATUS_PENDING NDIS_STATUS_INVALID_LENGTH NDIS_STATUS_INVALID_OID --*/ { ENTER ("EpvcMpSetInformation", 0x619a7528) NDIS_STATUS StatusToReturn = NDIS_STATUS_SUCCESS; UINT BytesLeft = InformationBufferLength; PUCHAR InfoBuffer = (PUCHAR)(InformationBuffer); UINT OidLength; ULONG LookAhead; ULONG Filter; PEPVC_I_MINIPORT pMiniport = NULL; PEPVC_ADAPTER pAdapter = NULL; BOOLEAN IsShuttingDown = FALSE; RM_DECLARE_STACK_RECORD (SR); TRACE(TL_T, TM_Mp, ("==>EpvcMpSetInformation pMiniport %x, Oid, Buffer %x, Length, %x", pMiniport, Oid, InformationBuffer, InformationBufferLength)); pMiniport = (PEPVC_I_MINIPORT)MiniportAdapterContext; LOCKOBJ(pMiniport, &SR); IsShuttingDown =(! MiniportTestFlag(pMiniport, fMP_MiniportInitialized)); pAdapter = pMiniport->pAdapter; UNLOCKOBJ(pMiniport,&SR); if (IsShuttingDown) { TRACE (TL_I, TM_Mp,(" Miniport shutting down. Trivially succeeding Set OID %x \n", Oid )); *BytesRead = 0; *BytesNeeded = 0; StatusToReturn = NDIS_STATUS_SUCCESS; return (StatusToReturn); } // // Get Oid and Length of request // OidLength = BytesLeft; switch (Oid) { case OID_802_3_MULTICAST_LIST: TRACE (TL_V, TM_Rq,(" Miniport Set OID_802_3_MULTICAST_LIST")); if (OidLength % sizeof(MAC_ADDRESS)) { StatusToReturn = NDIS_STATUS_INVALID_LENGTH; *BytesRead = 0; *BytesNeeded = 0; break; } if (OidLength > (MCAST_LIST_SIZE * sizeof(MAC_ADDRESS))) { StatusToReturn = NDIS_STATUS_MULTICAST_FULL; *BytesRead = 0; *BytesNeeded = 0; break; } NdisZeroMemory( &pMiniport->info.McastAddrs[0], MCAST_LIST_SIZE * sizeof(MAC_ADDRESS) ); NdisMoveMemory( &pMiniport->info.McastAddrs[0], InfoBuffer, OidLength ); pMiniport->info.McastAddrCount = OidLength / sizeof(MAC_ADDRESS); break; case OID_GEN_CURRENT_PACKET_FILTER: TRACE (TL_V, TM_Rq,(" Miniport Set OID_GEN_CURRENT_PACKET_FILTER")); // // Verify length // if (OidLength != sizeof(ULONG)) { StatusToReturn = NDIS_STATUS_INVALID_LENGTH; *BytesRead = 0; *BytesNeeded = sizeof(ULONG); ASSERT (0); break; } BytesLeft = sizeof (ULONG); // // Store the new value. // NdisMoveMemory(&Filter, InfoBuffer, BytesLeft ); // // Don't allow promisc mode, because we can't support that. // if (Filter & NDIS_PACKET_TYPE_PROMISCUOUS) { StatusToReturn = NDIS_STATUS_NOT_SUPPORTED; break; } StatusToReturn = epvcSetPacketFilter(pMiniport, Filter, &SR); break; case OID_802_5_CURRENT_FUNCTIONAL: case OID_802_5_CURRENT_GROUP: TRACE (TL_V, TM_Rq,(" Miniport Set OID_802_5_CURRENT_GROUP")); // XXX just accept whatever for now ??? break; case OID_GEN_CURRENT_LOOKAHEAD: TRACE (TL_V, TM_Rq,(" Miniport Set OID_GEN_CURRENT_LOOKAHEAD")); // // Verify length // if (OidLength != 4) { StatusToReturn = NDIS_STATUS_INVALID_LENGTH; *BytesRead = 0; *BytesNeeded = 0; break; } // // Store the new value. // NdisMoveMemory(&LookAhead, InfoBuffer, 4); ASSERT (pMiniport->pAdapter != NULL); if (LookAhead <= pAdapter->info.MaxAAL5PacketSize) { pMiniport->info.CurLookAhead = LookAhead; TRACE (TL_V, TM_Mp, ("New Lookahead size %x \n",pMiniport->info.CurLookAhead )); } else { StatusToReturn = NDIS_STATUS_INVALID_LENGTH; } break; case OID_GEN_NETWORK_LAYER_ADDRESSES: TRACE (TL_V, TM_Rq,(" Miniport Set OID_GEN_NETWORK_LAYER_ADDRESSES")); StatusToReturn = epvcMpSetNetworkAddresses( pMiniport, InformationBuffer, InformationBufferLength, &SR, BytesRead, BytesNeeded); break; default: StatusToReturn = NDIS_STATUS_INVALID_OID; *BytesRead = 0; *BytesNeeded = 0; break; } if (StatusToReturn == NDIS_STATUS_SUCCESS) { *BytesRead = BytesLeft; *BytesNeeded = 0; DUMPDW( TL_V, TM_Rq, InformationBuffer, *BytesRead ); } TRACE(TL_T, TM_Mp, ("<==EpvcMpSetInformation Status %x",StatusToReturn)); RM_ASSERT_CLEAR(&SR); return StatusToReturn; } VOID epvcMPLocalRequestComplete ( PEPVC_NDIS_REQUEST pEpvcRequest, NDIS_STATUS Status ) /*++ Routine Description: Miniport's local Request Completion handler for the occasion when a locally allocated NdisRequest was sent to the miniport below us. We look up to see if a request to our miniport edge initiated this request. If so, we complete the Set/Query Assumes that the epvcRequest was allocated from the HEAP Arguments: pEpvcRequest - Locally allocated Request structure Return Value: --*/ { ENTER("epvcMPLocalRequestComplete ", 0x77d107ae) PEPVC_I_MINIPORT pMiniport = pEpvcRequest->pMiniport; RM_DECLARE_STACK_RECORD (SR); // // First complete the request that we have pended // do { if (pMiniport == NULL || pEpvcRequest->fPendedRequest == FALSE) { // // No pended request to complete // break; } if (pEpvcRequest->fSet == TRUE) { NdisMSetInformationComplete (pMiniport->ndis.MiniportAdapterHandle, Status); } else { NdisMQueryInformationComplete (pMiniport->ndis.MiniportAdapterHandle, Status); } } while (FALSE); if (pMiniport != NULL) { // // Deref the miniport // epvcUnlinkFromExternal( &pMiniport->Hdr, //pHdr 0xaa625b37, // Luid (UINT_PTR)pEpvcRequest,// External entity EPVC_ASSOC_MINIPORT_REQUEST, // AssocID &SR ); } // // now free the memory that was allocated. // NdisFreeMemory (pEpvcRequest, sizeof (*pEpvcRequest), 0); } NDIS_STATUS epvcMpSetNetworkAddresses( IN PEPVC_I_MINIPORT pMiniport, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, IN PRM_STACK_RECORD pSR, OUT PULONG BytesRead, OUT PULONG BytesNeeded ) /*++ Routine Description: Called when the protocol above us wants to let us know about the network address(es) assigned to this interface. If this is TCP/IP, then we reformat and send a request to the ATM Call Manager to set its atmfMyIpNmAddress object. We pick the first IP address given to us. Arguments: pMiniport - Pointer to the ELAN InformationBuffer - Holds the data to be set. InformationBufferLength - The length of InformationBuffer. BytesRead - If the call is successful, returns the number of bytes read from InformationBuffer. BytesNeeded - If there is not enough data in InformationBuffer to satisfy the OID, returns the amount of storage needed. Return Value: NDIS_STATUS_SUCCESS NDIS_STATUS_PENDING NDIS_STATUS_INVALID_LENGTH --*/ { ENTER("epvcMpSetNetworkAddresses" , 0x385441e2) NETWORK_ADDRESS_LIST UNALIGNED * pAddrList = NULL; NETWORK_ADDRESS UNALIGNED * pAddr = NULL; NETWORK_ADDRESS_IP UNALIGNED * pIpAddr= NULL; ULONG Size; PUCHAR pNetworkAddr = NULL; NDIS_HANDLE NdisAdapterHandle; NDIS_HANDLE NdisAfHandle; NDIS_STATUS Status; PEPVC_ADAPTER pAdapter = (PEPVC_ADAPTER)pMiniport->pAdapter; // // Initialize. // *BytesRead = 0; Status = NDIS_STATUS_SUCCESS; pAddrList = (NETWORK_ADDRESS_LIST UNALIGNED *)InformationBuffer; do { *BytesNeeded = sizeof(*pAddrList) - FIELD_OFFSET(NETWORK_ADDRESS_LIST, Address) + sizeof(NETWORK_ADDRESS) - FIELD_OFFSET(NETWORK_ADDRESS, Address); if (InformationBufferLength < *BytesNeeded) { Status = NDIS_STATUS_INVALID_LENGTH; break; } if (pAddrList->AddressType != NDIS_PROTOCOL_ID_TCP_IP) { // Not interesting. break; } if (pAddrList->AddressCount <= 0) { Status = NDIS_STATUS_INVALID_DATA; break; } pAddr = (NETWORK_ADDRESS UNALIGNED *)&pAddrList->Address[0]; if ((pAddr->AddressLength > InformationBufferLength - *BytesNeeded) || (pAddr->AddressLength == 0)) { Status = NDIS_STATUS_INVALID_LENGTH; break; } if (pAddr->AddressType != NDIS_PROTOCOL_ID_TCP_IP) { // Not interesting. break; } if (pAddr->AddressLength < sizeof(NETWORK_ADDRESS_IP)) { Status = NDIS_STATUS_INVALID_LENGTH; break; } pIpAddr = (NETWORK_ADDRESS_IP UNALIGNED *)&pAddr->Address[0]; // // Allocate an NDIS request to send down to the call manager. // Size = sizeof(pIpAddr->in_addr); Status = epvcAllocateMemoryWithTag(&pNetworkAddr, Size, TAG_DEFAULT ); if ((FAIL(Status) == TRUE) || pNetworkAddr == NULL) { Status = NDIS_STATUS_RESOURCES; pNetworkAddr = NULL; break; } // // Copy the network address in. // NdisMoveMemory(pNetworkAddr, &pIpAddr->in_addr, sizeof(pIpAddr->in_addr)); TRACE (TL_V, TM_Mp, (" Set network layer addr: length %d\n", pAddr->AddressLength)); #if DBG if (pAddr->AddressLength >= 4) { TRACE(TL_V, TM_Mp, ("Network layer addr: %d.%d.%d.%d\n", pNetworkAddr[0], pNetworkAddr[1], pNetworkAddr[2], pNetworkAddr[3])); } #endif // DBG // // Send off the request. // { PEPVC_NDIS_REQUEST pRequest; do { Status = epvcAllocateMemoryWithTag (&pRequest, sizeof(*pRequest), TAG_DEFAULT) ; if (Status != NDIS_STATUS_SUCCESS) { pRequest = NULL; break; } // // There is no failure code path in prepareandsendrequest. // Our completion handler will get called and free the memory // Status = epvcPrepareAndSendNdisRequest( pAdapter, pRequest, epvcMPLocalRequestComplete, OID_ATM_MY_IP_NM_ADDRESS, pNetworkAddr, sizeof(pIpAddr->in_addr), NdisRequestSetInformation, pMiniport, TRUE, // We have Pended a Request TRUE, // The Pended request is a Set pSR ); } while (FALSE); } break; } while (FALSE); EXIT(); return (Status); } VOID epvcSetupMakeCallParameters( PEPVC_I_MINIPORT pMiniport, PCO_CALL_PARAMETERS *ppCallParameters ) /*++ Routine Description: Sets up the Call parameters after reading the information from the miniport block Arguments: pMiniport - Miniport in question ppCallParameter - Location of Call Parameters Return Value: return value *ppCallParamter is NULL on Failure --*/ { ULONG RequestSize = 0; NDIS_STATUS Status = NDIS_STATUS_FAILURE; PCO_CALL_PARAMETERS pCallParameters = NULL; PCO_CALL_MANAGER_PARAMETERS pCallMgrParameters = NULL; PCO_MEDIA_PARAMETERS pMediaParameters = NULL; PATM_MEDIA_PARAMETERS pAtmMediaParameters = NULL; do { Status = epvcAllocateMemoryWithTag( &pCallParameters, CALL_PARAMETER_SIZE, TAG_DEFAULT); if (Status != NDIS_STATUS_SUCCESS || pCallParameters == NULL) { pCallParameters = NULL; Status = NDIS_STATUS_RESOURCES; break; } NdisZeroMemory (pCallParameters, CALL_PARAMETER_SIZE); // // Distribute space and link up pointers amongst the various // structures for the PVC. // // pCallParameters------->+----------------------------+ // | CO_CALL_PARAMETERS | // pCallMgrParameters---->+----------------------------+ // | CO_CALL_MANAGER_PARAMETERS | // pMediaParameters------>+----------------------------+ // | CO_MEDIA_PARAMETERS | // pAtmMediaParameters--->+----------------------------+ // | ATM_MEDIA_PARAMETERS | // +----------------------------+ // pCallMgrParameters = (PCO_CALL_MANAGER_PARAMETERS) ((PUCHAR)pCallParameters + sizeof(CO_CALL_PARAMETERS)); pCallParameters->CallMgrParameters = pCallMgrParameters; pCallMgrParameters->CallMgrSpecific.ParamType = 0; pCallMgrParameters->CallMgrSpecific.Length = 0; pMediaParameters = (PCO_MEDIA_PARAMETERS) pCallMgrParameters->CallMgrSpecific.Parameters; pCallParameters->MediaParameters = pMediaParameters; pAtmMediaParameters = (PATM_MEDIA_PARAMETERS) pMediaParameters->MediaSpecific.Parameters; // // Call Manager generic flow paramters: // pCallMgrParameters->Transmit.TokenRate = pMiniport->pAdapter->info.LinkSpeed.Outbound/8*100; // cnvt decibits to bytes pCallMgrParameters->Transmit.PeakBandwidth = pMiniport->pAdapter->info.LinkSpeed.Outbound/8*100; // cnvt decibits to bytes pCallMgrParameters->Transmit.ServiceType = SERVICETYPE_BESTEFFORT; pCallMgrParameters->Receive.TokenRate = pMiniport->pAdapter->info.LinkSpeed.Inbound/8*100; // cnvt decibits to bytes pCallMgrParameters->Receive.PeakBandwidth = pMiniport->pAdapter->info.LinkSpeed.Inbound/8*100; // cnvt decibits to bytes pCallMgrParameters->Receive.ServiceType = SERVICETYPE_BESTEFFORT; // // use 1516 per spec // pCallMgrParameters->Transmit.TokenBucketSize = pCallMgrParameters->Transmit.MaxSduSize = pCallMgrParameters->Receive.TokenBucketSize = pCallMgrParameters->Receive.MaxSduSize = 1516; // // PVC Generic and ATM-specific Media Parameters // pMediaParameters->Flags = TRANSMIT_VC | RECEIVE_VC; pMediaParameters->MediaSpecific.ParamType = ATM_MEDIA_SPECIFIC; pMediaParameters->MediaSpecific.Length = sizeof(ATM_MEDIA_PARAMETERS); pAtmMediaParameters->ConnectionId.Vpi = pMiniport->config.vpi; //0 pAtmMediaParameters->ConnectionId.Vci = pMiniport->config.vci; TRACE (TL_I, TM_Mp, ("Miniport Configuration vci %x ,vpi %x", pMiniport->config.vci , pMiniport->config.vpi )); ASSERT (pMiniport->MaxAcceptablePkt > 1000); pAtmMediaParameters->AALType = AAL_TYPE_AAL5; pAtmMediaParameters->Transmit.PeakCellRate = LINKSPEED_TO_CPS(pMiniport->pAdapter->info.LinkSpeed.Outbound); pAtmMediaParameters->Transmit.MaxSduSize = pMiniport->MaxAcceptablePkt ; pAtmMediaParameters->Transmit.ServiceCategory = ATM_SERVICE_CATEGORY_UBR; pAtmMediaParameters->Receive.PeakCellRate = LINKSPEED_TO_CPS(pMiniport->pAdapter->info.LinkSpeed.Outbound); pAtmMediaParameters->Receive.MaxSduSize = pMiniport->MaxAcceptablePkt ; pAtmMediaParameters->Receive.ServiceCategory = ATM_SERVICE_CATEGORY_UBR; // // Set PVC flag here // pCallParameters->Flags |= PERMANENT_VC; } while (FALSE); if (Status == NDIS_STATUS_SUCCESS && pCallParameters != NULL) { // // Set up the return value here // *ppCallParameters = pCallParameters; } else { // // Clear the Failure case // *ppCallParameters = NULL; } } VOID epvcRefRecvPkt( PNDIS_PACKET pNdisPacket, PRM_OBJECT_HEADER pHdr // either an adapter or a miniport ) { // The following macros are just so that we can make // the proper debug association // depending on how closely we are tracking outstanding packets. // #define OUR_EXTERNAL_ENTITY ((UINT_PTR) pNdisPacket) #define szEPVCASSOC_EXTLINK_INDICATED_PKT_FORMAT " indicated pkt 0x%p\n" // // If ARPDBG_REF_EVERY_PKT // We add an "external" link for EVERY packet. We'll later remove this // reference when the send completes for this packet. // else // Only a transition from zero to non-zero outstanding sends, we // add an "external" link. We'll later remove this link when the // transition from non-zero to zero happens. // #if RM_EXTRA_CHECKING RM_DECLARE_STACK_RECORD(sr) epvcLinkToExternal ( pHdr, // pHdr 0x92036e12, // LUID OUR_EXTERNAL_ENTITY, // External entity EPVC_ASSOC_EXTLINK_INDICATED_PKT, // AssocID szEPVCASSOC_EXTLINK_INDICATED_PKT_FORMAT , &sr ); #else // !RM_EXTRA_CHECKING RmLinkToExternalFast(pHdr); #endif // !RM_EXTRA_CHECKING #undef OUR_EXTERNAL_ENTITY #undef szEPVCASSOC_EXTLINK_INDICATED_PKT_FORMAT #if RM_EXTRA_CHECKING RM_ASSERT_CLEAR(&sr); #endif } VOID epvcDerefRecvPkt ( PNDIS_PACKET pNdisPacket, PRM_OBJECT_HEADER pHdr ) { // The following macros are just so that we can make // the proper debug association // depending on how closely we are tracking outstanding send packets. // #if RM_EXTRA_CHECKING RM_DECLARE_STACK_RECORD(sr) epvcUnlinkFromExternal( pHdr, // pHdr 0x110ad55b, // LUID (UINT_PTR)pNdisPacket, // External entity EPVC_ASSOC_EXTLINK_INDICATED_PKT, // AssocID &sr ); #else // !RM_EXTRA_CHECKING RmUnlinkFromExternalFast (pHdr); #endif // !RM_EXTRA_CHECKING #if RM_EXTRA_CHECKING RM_ASSERT_CLEAR(&sr); #endif } VOID epvcDerefSendPkt ( PNDIS_PACKET pNdisPacket, PRM_OBJECT_HEADER pHdr ) { // The following macros are just so that we can make // the proper debug association // depending on how closely we are tracking outstanding send packets. // #if RM_EXTRA_CHECKING RM_DECLARE_STACK_RECORD(sr) epvcUnlinkFromExternal( pHdr, // pHdr 0xf43e0a10, // LUID (UINT_PTR)pNdisPacket, // External entity EPVC_ASSOC_EXTLINK_PKT_TO_SEND, // AssocID &sr ); #else // !RM_EXTRA_CHECKING RmUnlinkFromExternalFast (pHdr); #endif // !RM_EXTRA_CHECKING #if RM_EXTRA_CHECKING RM_ASSERT_CLEAR(&sr); #endif } VOID epvcRefSendPkt( PNDIS_PACKET pNdisPacket, PRM_OBJECT_HEADER pHdr // either an adapter or a miniport ) { // The following macros are just so that we can make // the proper debug association // depending on how closely we are tracking outstanding send packets. // #define OUR_EXTERNAL_ENTITY ((UINT_PTR) pNdisPacket) #define szEPVCASSOC_EXTLINK_DEST_TO_PKT_FORMAT " send pkt 0x%p\n" #if RM_EXTRA_CHECKING RM_DECLARE_STACK_RECORD(sr) epvcLinkToExternal ( pHdr, // pHdr 0xabd17475, // LUID OUR_EXTERNAL_ENTITY, // External entity EPVC_ASSOC_EXTLINK_PKT_TO_SEND, // AssocID szEPVCASSOC_EXTLINK_DEST_TO_PKT_FORMAT , &sr ); #else // !RM_EXTRA_CHECKING RmLinkToExternalFast(pHdr); #endif // !RM_EXTRA_CHECKING #undef OUR_EXTERNAL_ENTITY #undef szEPVCASSOC_EXTLINK_DEST_TO_PKT_FORMAT #if RM_EXTRA_CHECKING RM_ASSERT_CLEAR(&sr); #endif } VOID epvcExtractPktInfo ( PEPVC_I_MINIPORT pMiniport, PNDIS_PACKET pPacket , PEPVC_SEND_STRUCT pSendStruct ) /*++ Routine Description: Arguments: Return Value: --*/ { pSendStruct->pOldPacket = pPacket; pSendStruct->pMiniport = pMiniport; epvcSetSendPktStats(); } NDIS_STATUS epvcSendRoutine( IN PEPVC_I_MINIPORT pMiniport, IN PNDIS_PACKET Packet, PRM_STACK_RECORD pSR ) /*++ Routine Description: This routine does all the hard work. It responds to arps if necessary.. It removes Ethernet Headers if necessary It allocates a new packet if necessary It sends the new packet on the wire Arguments: pMiniport - Miniport in question Packet - Packet to be sent Return Value: Returns Pending, otherwise expects the calling routine to complete the packet --*/ { NDIS_STATUS Status = NDIS_STATUS_FAILURE; PNDIS_PACKET pNewPkt = NULL; EPVC_SEND_STRUCT SendStruct; TRACE (TL_T, TM_Send, ("==>epvcSendRoutine") ); EPVC_ZEROSTRUCT (&SendStruct); do { epvcExtractPktInfo (pMiniport, Packet, &SendStruct ); // // if we are doing IP encapsulation, then respond // to the Arp // if (pMiniport->fDoIpEncapsulation == TRUE) { // // We need to do some special processing for this packet // SendStruct.fIsThisAnArp = \ epvcCheckAndReturnArps (pMiniport, Packet , &SendStruct, pSR); if (SendStruct.fIsThisAnArp == TRUE ) { Status = NDIS_STATUS_SUCCESS; break ; // Arps are not sent to the atm driver } if (SendStruct.fNotIPv4Pkt == TRUE) { // This is not an IPv4 packet. Fail the send. Status = NDIS_STATUS_FAILURE; break; } } // // Allocate a new packet to be sent // epvcGetSendPkt(pMiniport, Packet, &SendStruct, pSR); if (SendStruct.pNewPacket == NULL) { ASSERTAndBreak (SendStruct.pNewPacket != NULL); } // // SendStruct.pNewPacket is guaranteed to have the NdisBuffers Set up // // Remove Ethernet Header - if necessary // Status = epvcRemoveEthernetHeader (&SendStruct, pSR); if (Status != NDIS_STATUS_SUCCESS) { ASSERTAndBreak (Status == NDIS_STATUS_SUCCESS) } // // Add Ethernet Padding - if necessary // Status = epvcAddEthernetTail (&SendStruct, pSR); if (Status != NDIS_STATUS_SUCCESS) { ASSERTAndBreak (Status == NDIS_STATUS_SUCCESS) } // // Add Ethernet Pad 0x00 0x00 to head of packet - if necessary // Status = epvcAddEthernetPad (&SendStruct, pSR); if (Status != NDIS_STATUS_SUCCESS) { ASSERTAndBreak (Status == NDIS_STATUS_SUCCESS) } // // Add LLC Encapsulation - if necessary // Status = epvcAddLLCEncapsulation (pMiniport , Packet, SendStruct.pNewPacket, pSR); if (Status != NDIS_STATUS_SUCCESS) { ASSERTAndBreak (Status == NDIS_STATUS_SUCCESS) } // // set the context information for the send complete // epvcSetPacketContext (&SendStruct, pSR); // // Only Send if successful // epvcDumpPkt (SendStruct.pNewPacket); Status = epvcAdapterSend(pMiniport, SendStruct.pNewPacket, pSR); } while (FALSE); if (Status != NDIS_STATUS_PENDING && // We had a failure SendStruct.pNewPacket != NULL ) // but we were able to get a packet { epvcFreeSendPkt (pMiniport, &SendStruct); } TRACE (TL_T, TM_Send, ("<==epvcSendRoutine") ); return Status; } VOID EpvcSendPackets( IN NDIS_HANDLE MiniportAdapterContext, IN PPNDIS_PACKET PacketArray, IN UINT NumberOfPackets ) /*++ Routine Description: Send Packet Array handler. Either this or our SendPacket handler is called based on which one is enabled in our Miniport Characteristics. Arguments: MiniportAdapterContext Pointer to our adapter PacketArray Set of packets to send NumberOfPackets Self-explanatory Return Value: None --*/ { PEPVC_I_MINIPORT pMiniport = (PEPVC_I_MINIPORT)MiniportAdapterContext; NDIS_STATUS Status = NDIS_STATUS_FAILURE; UINT i; RM_DECLARE_STACK_RECORD (SR); DBGMARK(0xdaab68c3); TRACE (TL_T, TM_Send, ("==>EpvcSendPackets pMiniport %p, pPktArray %p, Num %x", pMiniport, PacketArray, NumberOfPackets)); for (i = 0; i < NumberOfPackets; i++) { PEPVC_PKT_CONTEXT Rsvd; PNDIS_PACKET Packet = NULL; Packet = PacketArray[i]; epvcValidatePacket (Packet); Status= epvcSendRoutine (pMiniport, Packet, &SR); if (Status != NDIS_STATUS_PENDING) { epvcMSendComplete(pMiniport, Packet , Status); } } TRACE (TL_T, TM_Send, ("<==EpvcSendPackets ")); RM_ASSERT_CLEAR(&SR); return; } VOID epvcFreeSendPkt( PEPVC_I_MINIPORT pMiniport, IN PEPVC_SEND_STRUCT pSendStruct ) /*++ Routine Description: Pops the packet stack if stacks were used or free the new packet after copying the per packet info Arguments: pMiniport - which the packet was sent to pSentPkt - The packet that is being sent. ppPkt - the new packet that was allocated or the old one if a stack was available --*/ { ENTER ("epvcFreeSendPkt", 0xff3ce0fd) PNDIS_PACKET pOldPkt = pSendStruct->pOldPacket; PNDIS_PACKET pNewPkt = pSendStruct->pNewPacket; TRACE (TL_T, TM_Send, ("==>epvcFreeSendPkt pNewPkt %x, pPOldPkt ",pNewPkt, pOldPkt)); // // Remove the ethernet padding - if necessary // epvcRemoveEthernetPad (pMiniport, pNewPkt); // // Remove the Ethernet Tail- if necessary // epvcRemoveEthernetTail(pMiniport, pNewPkt, &pSendStruct->Context); // // If the two packets are the same, then we used Packet Stacks // if (pNewPkt != NULL && pSendStruct->fUsingStacks== FALSE) { NdisIMCopySendCompletePerPacketInfo (pOldPkt, pNewPkt); epvcFreePacket(pNewPkt,&pMiniport->PktPool.Send); pNewPkt = pSendStruct->pNewPacket = NULL; } TRACE (TL_T, TM_Send, ("<==epvcFreeSendPkt pNewPkt %x, pPOldPkt ",pNewPkt, pOldPkt)); EXIT() return; } VOID epvcGetSendPkt ( IN PEPVC_I_MINIPORT pMiniport, IN PNDIS_PACKET pSentPkt, OUT PEPVC_SEND_STRUCT pSendStruct, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: Allocates an NdisPkt or pushes a Pkt Stack to get a valid NdisPkt that can be sent to the adapter below. Arguments: pMiniport - which the packet was sent to pSentPkt - The packet that is being sent. ppPkt - the new packet that was allocated or the old one if a stack was available --*/ { ENTER ("epvcGetSendPkt", 0x5734054f) NDIS_STATUS Status = NDIS_STATUS_FAILURE; PNDIS_PACKET pNewPkt = NULL; BOOLEAN Remaining = FALSE; PVOID MediaSpecificInfo = NULL; UINT MediaSpecificInfoSize = 0; TRACE (TL_T, TM_Send, ("==>epvcGetSendPkt pSentPkt %x",pSentPkt)); do { #if PKT_STACKS // // Packet stacks: Check if we can use the same packet for sending down. // pStack = NdisIMGetCurrentPacketStack(Packet, &Remaining); if (Remaining) { // // We can reuse "Packet". // // NOTE: if we needed to keep per-packet information in packets // sent down, we can use pStack->IMReserved[]. // pNewPkt = pSentPkt; pSendStruct->pPktStack = pStack; pSendStruct->fUsingStacks = TRUE; break; } #endif pSendStruct->fUsingStacks = FALSE; epvcAllocatePacket(&Status, &pNewPkt, &pMiniport->PktPool.Send); if (Status == NDIS_STATUS_SUCCESS) { PNDIS_PACKET_EXTENSION Old, New; PEPVC_PKT_CONTEXT Rsvd = NULL; Rsvd = (PEPVC_PKT_CONTEXT)(pNewPkt->ProtocolReserved); Rsvd->pOriginalPacket = pSentPkt; pNewPkt->Private.Flags = NdisGetPacketFlags(pSentPkt); pNewPkt->Private.Head = pSentPkt->Private.Head; pNewPkt->Private.Tail = pSentPkt->Private.Tail; // // Copy the OOB Offset from the original packet to the new // packet. // NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(pNewPkt), NDIS_OOB_DATA_FROM_PACKET(pSentPkt), sizeof(NDIS_PACKET_OOB_DATA)); // // Copy relevant parts of the per packet info into the new packet // NdisIMCopySendPerPacketInfo(pNewPkt, pSentPkt); // // Copy the Media specific information // NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(pSentPkt, &MediaSpecificInfo, &MediaSpecificInfoSize); if (MediaSpecificInfo || MediaSpecificInfoSize) { NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(pNewPkt, MediaSpecificInfo, MediaSpecificInfoSize); } } else { pNewPkt = NULL; } } while (FALSE); pSendStruct->pNewPacket = pNewPkt; TRACE (TL_T, TM_Send, ("<==epvcGetSendPkt pSentPkt %p ppNewPkt %p",pSentPkt, pSendStruct->pNewPacket )); EXIT() return; } NDIS_STATUS epvcAdapterSend( IN PEPVC_I_MINIPORT pMiniport, IN PNDIS_PACKET pPkt, PRM_STACK_RECORD pSR ) { BOOLEAN fDoSend = FALSE; PEPVC_ADAPTER pAdapter = pMiniport->pAdapter; NDIS_STATUS Status = NDIS_STATUS_FAILURE; ENTER("epvcAdapterSend", 0x5b014909) TRACE (TL_T, TM_Send, (" ==>epvcAdapterSend" ) ) do { // // Check to see if we have a valid Send Case // LOCKOBJ (pMiniport, pSR); fDoSend = MiniportTestFlag (pMiniport, fMP_MakeCallSucceeded); if (fDoSend == FALSE) { TRACE (TL_V, TM_Send,("Send - MakeCall Not Succeeded")); } // // Add an association while holding the lock // if (fDoSend == TRUE) { epvcRefSendPkt(pPkt, &pMiniport->Hdr); } UNLOCKOBJ (pMiniport, pSR); if (fDoSend == TRUE) { epvcCoSendPackets(pMiniport->vc.VcHandle, &pPkt, 1 ); Status = NDIS_STATUS_PENDING; } else { Status = NDIS_STATUS_FAILURE; } } while (FALSE); TRACE (TL_T, TM_Send, (" <==epvcAdapterSend fDoSend %x, Status %x", fDoSend, Status ) ) return Status; } VOID epvcFormulateArpResponse ( IN PEPVC_I_MINIPORT pMiniport, IN PEPVC_ARP_CONTEXT pArpContext, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: This allocates an Arp Packet, looks at the Arp Request, formulates a response and sends it up back to the protocol Arguments: pMiniport - which the packet was sent to pArpContext - Contains all the information relating to the Arp. the Context Is Allocated on the stack Return: --*/ { ENTER("epvcFormulateArpResponse", 0x7a763fce) PEPVC_ARP_PACKET pResponse = NULL; NDIS_STATUS Status = NDIS_STATUS_FAILURE; PTASK_ARP pTask = NULL; TRACE (TL_T, TM_Send, ("==>epvcFormulateArpResponse pMiniport %x, pArpContext %x", pMiniport, pArpContext)) do { // // Allocate a buffer from a lookaside list // Status = epvcAllocateTask( &pMiniport->Hdr, // pParentObject, epvcTaskRespondToArp, // pfnHandler, 0, // Timeout, "Task: Arp Response", // szDescription &(PRM_TASK)pTask, pSR ); if (FAIL(Status)) { pTask = NULL; break; } // // Set up Arp Response // pResponse = &pTask->Pkt; EPVC_ZEROSTRUCT (pResponse); { // // Construct the Ethernet Header // PEPVC_ETH_HEADER pRespHeader = &pResponse->Header; PEPVC_ETH_HEADER pSrcHeader = (PEPVC_ETH_HEADER)pArpContext->pEthHeader; ASSERT (pSrcHeader != NULL); ASSERT (pRespHeader != NULL); // // set up the Eth header // NdisMoveMemory (&pRespHeader->eh_daddr, &pSrcHeader->eh_saddr, ARP_802_ADDR_LENGTH ) ; NdisMoveMemory ( &pRespHeader->eh_saddr, &pMiniport->info.MacAddressDummy, ARP_802_ADDR_LENGTH ); pRespHeader->eh_type = pSrcHeader->eh_type; // copy 08 06 over } { // // Construct the Arp Response // PEPVC_ARP_BODY pRespBody = &pResponse->Body; PEPVC_ARP_BODY pSrcBody = pArpContext ->pBody; ASSERT (pRespBody != NULL); ASSERT (pSrcBody != NULL); pRespBody->hw = pSrcBody->hw; // Hardware address space. = 00 01 pRespBody->pro = pSrcBody->pro; // Protocol address space. = 08 00 pRespBody->hlen = ARP_802_ADDR_LENGTH; // 6 pRespBody->plen = sizeof (IP_ADDR); // 4 pRespBody->opcode = net_short(ARP_RESPONSE); // Opcode. pRespBody->SenderHwAddr= pMiniport->info.MacAddressDummy; // Source HW address. pRespBody->SenderIpAddr = pSrcBody->DestIPAddr ; // Source protocol address. pRespBody->DestHwAddr = pSrcBody->SenderHwAddr; // Destination HW address. pRespBody->DestIPAddr = pSrcBody->SenderIpAddr; // Destination protocol address. } // // So we have the packet ready for transmission. // RmStartTask ((PRM_TASK)pTask, 0 , pSR); } while (FALSE); TRACE (TL_T, TM_Send, ("<==epvcFormulateArpResponse ")) } NDIS_STATUS epvcTaskRespondToArp( IN struct _RM_TASK * pTask, IN RM_TASK_OPERATION Code, IN UINT_PTR UserParam, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: This function queues a zero timeout timer and indicates a receive Arguments: Return: --*/ { ENTER("epvcTaskRespondToArp", 0xd05c4942) NDIS_STATUS Status = NDIS_STATUS_FAILURE; PEPVC_I_MINIPORT pMiniport = (PEPVC_I_MINIPORT ) RM_PARENT_OBJECT(pTask); PTASK_ARP pTaskArp = (PTASK_ARP) pTask; PEPVC_ADAPTER pAdapter = (PEPVC_ADAPTER)pMiniport->Hdr.pParentObject; enum { Stage_Start =0, // default Stage_DoAllocations, Stage_QueuedTimer, Stage_PacketReturned, Stage_TaskCompleted, Stage_End }; // To be used in pTask->Hdr.State to indicate the state of the Task TRACE ( TL_T, TM_Pt, ("==> epvcTaskRespondToArp %x",pTask->Hdr.State ) ); switch (pTask->Hdr.State) { case Stage_Start: { LOCKOBJ (pMiniport, pSR); if (epvcIsThisTaskPrimary ( pTask, &(PRM_TASK)(pMiniport->arps.pTask)) == FALSE) { PRM_TASK pOtherTask = (PRM_TASK)(pMiniport->arps.pTask); RmTmpReferenceObject (&pOtherTask->Hdr, pSR); // // Set The state so we restart this code after main task completes // pTask->Hdr.State = Stage_Start; UNLOCKOBJ(pMiniport, pSR); RmPendTaskOnOtherTask (pTask, 0, pOtherTask, pSR); RmTmpDereferenceObject(&pOtherTask->Hdr,pSR); Status = NDIS_STATUS_PENDING; break; } // // We are the primary task // // // Check to see if the miniport is still active. // If it is halting, then we don't need to do any work // if (MiniportTestFlag(pMiniport, fMP_MiniportInitialized) == FALSE) { // // Our work had been done. So break out and complete the task // Status = NDIS_STATUS_SUCCESS; pTask->Hdr.State = Stage_TaskCompleted; UNLOCKOBJ(pMiniport, pSR); break; } UNLOCKOBJ(pMiniport, pSR); pTask->Hdr.State = Stage_DoAllocations; FALL_THROUGH } case Stage_DoAllocations: { PNDIS_BUFFER pBuffer = NULL; TRACE (TL_V, TM_Send, ("epvcTaskRespondToArp Stage_DoAllocations Task %p", pTask) ); // // Allocate An NDis Buffer // epvcAllocateBuffer(&Status, &pBuffer, NULL, // Pool Handle (PVOID)&pTaskArp->Pkt, sizeof(pTaskArp->Pkt) ); //Length ASSERT (sizeof(pTaskArp->Pkt) == 0x2a); if (FAIL(Status) == TRUE) { pBuffer = NULL; pTask->Hdr.State = Stage_TaskCompleted; ASSERTAndBreak (!FAIL(Status)); break; } // // Allocate An Ndis Packet // epvcAllocatePacket (&Status, &pTaskArp->pNdisPacket, &pMiniport->PktPool.Recv); if (FAIL(Status) == TRUE) { pTask->Hdr.State = Stage_TaskCompleted; pTaskArp->pNdisPacket = NULL; // // Undo allocations // epvcFreeBuffer (pBuffer); ASSERTAndBreak( !FAIL(Status) ); } // // Set up the Ndis Buffer within the NdisPacket // { PNDIS_PACKET_PRIVATE pPktPriv = &pTaskArp->pNdisPacket->Private; pPktPriv->Head = pBuffer; pPktPriv->Tail = pBuffer; pBuffer->Next = NULL; } // // Set up the Arp response // // // Queue the timer // NdisMInitializeTimer ( &pTaskArp->Timer, pMiniport->ndis.MiniportAdapterHandle, epvcArpTimer, pTaskArp ); pTask->Hdr.State = Stage_QueuedTimer; // // Now prepare to be called back througha timer to do the // receive indication // RmSuspendTask(pTask, 0,pSR); Status = NDIS_STATUS_PENDING; NdisMSetTimer (&pTaskArp->Timer, 0); // Zero timeout break; } case Stage_QueuedTimer: { TRACE (TL_V, TM_Send, ("epvcTaskRespondToArp Stage_QueuedTimer Task %p", pTask) ); // // The miniport could have been halted during the timer // if (MiniportTestFlag (pMiniport, fMP_MiniportInitialized) == FALSE) { pTask->Hdr.State = Stage_TaskCompleted; ASSERTAndBreak(MiniportTestFlag (pMiniport, fMP_MiniportInitialized) == TRUE); } NDIS_SET_PACKET_HEADER_SIZE(pTaskArp->pNdisPacket , sizeof (pMiniport->RcvEnetHeader)) ; NDIS_SET_PACKET_STATUS (pTaskArp->pNdisPacket, NDIS_STATUS_RESOURCES); pTask->Hdr.State = Stage_PacketReturned; epvcMIndicateReceivePacket (pMiniport, &pTaskArp->pNdisPacket, 1 ); FALL_THROUGH } case Stage_PacketReturned: { pTask->Hdr.State = Stage_TaskCompleted; Status = NDIS_STATUS_SUCCESS; break; } case Stage_TaskCompleted: case Stage_End : { Status = NDIS_STATUS_SUCCESS; break; } default: { ASSERTEX(!"Unknown task op", pTask); } } if (pTask->Hdr.State == Stage_TaskCompleted) { // // Free the packet // pTask->Hdr.State = Stage_End; if (pTaskArp->pNdisPacket != NULL) { // // Free the buffer // PNDIS_PACKET_PRIVATE pPrivate = & pTaskArp->pNdisPacket->Private; if (pPrivate -> Head != NULL) { epvcFreeBuffer (pPrivate->Head ); pPrivate->Head = pPrivate->Tail = NULL; } // // free the arp packet // epvcFreePacket (pTaskArp->pNdisPacket , &pMiniport->PktPool.Recv); pTaskArp->pNdisPacket = NULL; } LOCKOBJ (pMiniport, pSR); epvcClearPrimaryTask (&(PRM_TASK)(pMiniport->arps.pTask)); UNLOCKOBJ (pMiniport, pSR); Status = NDIS_STATUS_SUCCESS; } TRACE ( TL_T, TM_Pt, ("<== epvcTaskRespondToArp %x",Status) ); return Status; } VOID epvcArpTimer( IN PVOID SystemSpecific1, IN PVOID FunctionContext, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3 ) /*++ Routine Description: Resume the epvcTaskRespondToArp Task Arguments: Return: --*/ { ENTER ("epvcArpTimer",0xf2adae0e) PRM_TASK pTask = (PRM_TASK) FunctionContext; RM_DECLARE_STACK_RECORD (SR); RmResumeTask (pTask,0,&SR); EXIT() } BOOLEAN epvcCheckAndReturnArps ( IN PEPVC_I_MINIPORT pMiniport, IN PNDIS_PACKET pPkt, IN PEPVC_SEND_STRUCT pSendStruct, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: Looks at the packet that is being sent. If it is an Arp request, then it formulates a responses and queues a timer of timeout zero to return the Arp Arguments: pMiniport - which the packet was sent to pPkt - the packet being sent Return: True - if this is an Arp Request. --*/ { ENTER("epvcCheckAndReturnArps ", 0xb8e6a3c4) EPVC_ARP_CONTEXT ArpContext; TRACE (TL_T, TM_Send, ("==>epvcCheckAndReturnArps ")); EPVC_ZEROSTRUCT (&ArpContext); do { ArpContext.pFirstBuffer = pPkt->Private.Head; // // Do some sanity checks // if (ArpContext.pFirstBuffer == NULL) { break; } NdisQueryBufferSafe( ArpContext.pFirstBuffer , &(PVOID)ArpContext.pEthHeader, &ArpContext.BufferLength, LowPagePriority ); if (ArpContext.pEthHeader == NULL) { break; } // // It the is not an ARP request then ignore it -- // during testing only // if (ArpContext.pEthHeader->eh_daddr.Byte[0] == 0xff && ArpContext.pEthHeader->eh_daddr.Byte[1] == 0xff ) { pSendStruct->fNonUnicastPacket = TRUE; } if (ARP_ETYPE_ARP != net_short(ArpContext.pEthHeader->eh_type)) { // // This is not an Arp packet. Is this an IPv4 packet // if (IP_PROT_TYPE != net_short(ArpContext.pEthHeader->eh_type)) { // If this is not an IPv4 packet, then mark it so that it can // be discarded pSendStruct->fNotIPv4Pkt = TRUE; } break; } // // We'll parse the structure using pre-defined structs // ArpContext.pArpPkt = (PEPVC_ARP_PACKET)ArpContext.pEthHeader; ASSERT (ArpContext.BufferLength >= sizeof (EPVC_ARP_PACKET)); if (ArpContext.BufferLength < sizeof (EPVC_ARP_PACKET)) { // // TODO : Add Code to handle this case. // break; } ArpContext.pBody = (PEPVC_ARP_BODY)&ArpContext.pArpPkt->Body; TRACE (TL_V, TM_Send, ("Received an ARP %p, Body %x\n", ArpContext.pEthHeader, ArpContext.pBody)); // // Validate the Opcode, the prot type, hard size, prot size // if (ARP_REQUEST != net_short (ArpContext.pBody->opcode )) { // // This is not an Arp request // break; } if (IP_PROT_TYPE != net_short(ArpContext.pBody->pro) || ARP_802_ADDR_LENGTH != ArpContext.pBody->hlen || sizeof (IP_ADDR) != ArpContext.pBody->plen ) { // // these are just sanity checks // ASSERT (!"Invalid ARP Packet"); break; } // // We have a valid ArpRequest // ArpContext.fIsThisAnArp = TRUE; // // If tcp/ip is arping for itself, then do not respond... but return // TRUE, so that this packet is not sent on the wire // if (ArpContext.pArpPkt->Body.SenderIpAddr == ArpContext.pArpPkt->Body.DestIPAddr) { break; } // // Formulate and indicate an Arp Response // epvcFormulateArpResponse (pMiniport, &ArpContext, pSR); } while (FALSE); EXIT() return ArpContext.fIsThisAnArp ; TRACE (TL_T, TM_Send, ("<==epvcCheckAndReturnArps ")); } NDIS_STATUS epvcRemoveEthernetHeader( PEPVC_SEND_STRUCT pSendStruct, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: Expects that the new packet is already set up with the Ndis Bufferz Arguments: pSendStruct - Contains all the arguments that are needed. Return: True - if this is an Arp Request. --*/ { ENTER ("epvcAddLLCEncapsulation" , 0x3ec589c9) BOOLEAN fUsedPktStack = pSendStruct->fUsingStacks; NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE; PNDIS_PACKET pNewPkt = pSendStruct->pNewPacket; PEPVC_I_MINIPORT pMiniport = pSendStruct->pMiniport; TRACE (TL_T, TM_Send, ("==>epvcRemoveEthernetHeader ")); do { ULONG BufferLength = 0; PNDIS_BUFFER pBuffer = NULL; if (pMiniport->fDoIpEncapsulation == FALSE) { NdisStatus = NDIS_STATUS_SUCCESS; break; // we are done } // // There are three ways we can be given a ether net header // 1. In a seperate MDL - most often // 2. As part of a large MDL - We need to adhust the Virtual address // 3. EthernetHeader is seperated across multiple // MDLs - not implemented or expected // pBuffer = pNewPkt->Private.Head; BufferLength = NdisBufferLength (pBuffer); if (BufferLength < sizeof (EPVC_ETH_HEADER) ) { ASSERTAndBreak (BufferLength >= sizeof (EPVC_ETH_HEADER)) ; // we are done } // // At this point the first buffer is going to be replaced so keep a record of it // pSendStruct->Context.Stack.ipv4Send.pOldHeadNdisBuffer = pBuffer; // // New we check to see if all we need to do is make the // Packet->Private.Head point to the next MDL // if (BufferLength == sizeof (EPVC_ETH_HEADER)) { // // These are error conditions that should not // be handled in our software // ASSERT (pBuffer->Next != NULL); // no tcp header after the Eth header pNewPkt->Private.Head = pBuffer->Next; NdisStatus = NDIS_STATUS_SUCCESS; break ; // we are done } if (BufferLength > sizeof (EPVC_ETH_HEADER)) { // // Allocate a new NDIS Buffer pointing to start of the IP header w // within the current Head (pBuffer) // PNDIS_BUFFER pNewBuffer = NULL; PUCHAR pIpHeader = NdisBufferVirtualAddress(pBuffer); UINT LenRemaining = BufferLength - sizeof (EPVC_ETH_HEADER); if (pIpHeader == NULL) { // // we did not get the virtual address from the system. // Start to fail this packet // ASSERTAndBreak(pIpHeader != NULL); } // // Now move the Ip Header past the Ethernet Header (where it currently points to) // pIpHeader += sizeof (EPVC_ETH_HEADER) ; // // Now allocate the new NdisBuffer // epvcAllocateBuffer ( &NdisStatus, &pNewBuffer, NULL, pIpHeader, LenRemaining); if (NdisStatus != NDIS_STATUS_SUCCESS) { pNewBuffer = NULL; ASSERTAndBreak (!"Ndis Buffer Allocation failed"); } // // Make the New Buffer the Head of the new packet // // We might have to make it the tail if there is // only one ndis buffer in the packet // if (pNewPkt->Private.Head == pNewPkt->Private.Tail) { pNewPkt->Private.Tail = pNewBuffer; } pNewBuffer->Next= pNewPkt->Private.Head->Next; pNewPkt->Private.Head = pNewBuffer; NdisStatus = NDIS_STATUS_SUCCESS; break ; // we are done } } while (FALSE); TRACE (TL_T, TM_Send, ("<==epvcRemoveEthernetHeader ")); return NdisStatus ; } VOID epvcSetPacketContext ( IN PEPVC_SEND_STRUCT pSendStruct, PRM_STACK_RECORD pSR ) /*++ Routine Description: No allocations, just add a few pointers and exit Arguments: pSendStruct - Contains all the arguments that are needed. Return: None: --*/ { PNDIS_PACKET pPkt = pSendStruct->pNewPacket; PEPVC_PKT_CONTEXT pContext = NULL; PEPVC_STACK_CONTEXT pStack = NULL; // // first point the context to the correct place // in the new ndis pakcet // if (pSendStruct->fUsingStacks == TRUE) { pStack = (PEPVC_STACK_CONTEXT)(&pSendStruct->pPktStack->IMReserved[0]); } else { PEPVC_PKT_CONTEXT pContext = NULL; pContext = (PEPVC_PKT_CONTEXT )(&pPkt->ProtocolReserved[0]); pContext->pOriginalPacket = pSendStruct->pOldPacket; pStack = &pContext->Stack; } // // Update the packet // ASSERT (sizeof (pStack) <= (2 *sizeof (PVOID) )); // // Now copy the stack portion of the context over // into the packet // *pStack = pSendStruct->Context.Stack; } NDIS_STATUS epvcAddLLCEncapsulation ( PEPVC_I_MINIPORT pMiniport , PNDIS_PACKET pOldPkt, PNDIS_PACKET pNewPkt, PRM_STACK_RECORD pSR ) /*++ Routine Description: Expects that the new packet is already set up with the Ndis Bufferz Arguments: pSendStruct - Contains all the arguments that are needed. Return: True - if this is an Arp Request. --*/ { ENTER ("epvcAddLLCEncapsulation" , 0x3ec589c9) BOOLEAN fDoSend = TRUE; BOOLEAN fUsedPktStack = (pOldPkt == pNewPkt); NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS; PNDIS_BUFFER pNewBuffer = NULL; TRACE (TL_T, TM_Send, ("==>epvcAddLLCEncapsulation ")); do { if (pMiniport->fAddLLCHeader == FALSE) { break; // we are done } // // Allocate an MDL that points to the LLC Header // epvcAllocateBuffer ( &NdisStatus, &pNewBuffer, NULL, pMiniport->pLllcHeader, pMiniport->LlcHeaderLength); if (NdisStatus != NDIS_STATUS_SUCCESS) { pNewBuffer = NULL; break; } // // Insert the New Buffer as the Head of the new Packet // pNewBuffer->Next = pNewPkt->Private.Head; pNewPkt->Private.Head = pNewBuffer; pNewPkt->Private.ValidCounts= FALSE; NdisStatus = NDIS_STATUS_SUCCESS; } while (FALSE); if (NdisStatus != NDIS_STATUS_SUCCESS) { if (pNewBuffer!= NULL) { epvcFreeBuffer (pNewBuffer); pNewBuffer = NULL; } } TRACE (TL_T, TM_Send, ("<==epvcAddLLCEncapsulation ")); return NdisStatus ; } NDIS_STATUS epvcRemoveSendEncapsulation ( PEPVC_I_MINIPORT pMiniport , PNDIS_PACKET pNewPkt ) { return NDIS_STATUS_SUCCESS; } NDIS_STATUS epvcRemoveRecvEncapsulation ( PEPVC_I_MINIPORT pMiniport , PNDIS_PACKET pNewPkt ) { return NDIS_STATUS_SUCCESS; } VOID epvcDumpPkt ( IN PNDIS_PACKET pPkt ) { PNDIS_BUFFER pPrevBuffer; do { PNDIS_BUFFER pBuffer = NULL; if (g_bDumpPackets == FALSE) { break; } pBuffer = pPkt->Private.Head; // // Now iterate through all the buffers // and print out the packet. // TRACE (TL_A, TM_Mp, ("pPkt %p, Head %p, tail %p\n ", pPkt, pPkt->Private.Head, pPkt->Private.Tail)); // // As we always expect the first buffer to be present // I do not check // do { PVOID pVa = NULL; ULONG Len = 0; pPrevBuffer = NULL; Len = NdisBufferLength (pBuffer); pVa = NdisBufferVirtualAddress(pBuffer); pPrevBuffer = pBuffer; pBuffer = pBuffer->Next; if (pVa == NULL) { continue; } DbgPrint ("Mdl %p, Va %p. Len %x\n", pPrevBuffer, pVa,Len); Dump( (CHAR* )pVa, Len, 0, 1 ); } while (pBuffer != NULL); } while (FALSE); } NDIS_STATUS epvcMiniportReadConfig( IN PEPVC_I_MINIPORT pMiniport, NDIS_HANDLE WrapperConfigurationContext, PRM_STACK_RECORD pSR ) { NDIS_STATUS Status = NDIS_STATUS_SUCCESS; NDIS_HANDLE ConfigurationHandle; PMP_REG_ENTRY pRegEntry; UINT i; UINT value; PUCHAR pointer; PNDIS_CONFIGURATION_PARAMETER ReturnedValue; PUCHAR NetworkAddress; UINT Length; // Open the registry for this pMiniport NdisOpenConfiguration( &Status, &ConfigurationHandle, WrapperConfigurationContext); if(Status != NDIS_STATUS_SUCCESS) { TRACE (TL_I, TM_Mp,("NdisOpenConfiguration failed\n")); return Status; } // read all the registry values for(i = 0, pRegEntry = NICRegTable; i < NIC_NUM_REG_PARAMS; i++, pRegEntry++) { pointer = (PUCHAR) pMiniport + pRegEntry->FieldOffset; // Get the configuration value for a specific parameter. Under NT the // parameters are all read in as DWORDs. NdisReadConfiguration( &Status, &ReturnedValue, ConfigurationHandle, &pRegEntry->RegName, NdisParameterInteger); // If the parameter was present, then check its value for validity. if(Status == NDIS_STATUS_SUCCESS) { // Check that param value is not too small or too large if(ReturnedValue->ParameterData.IntegerData < pRegEntry->Min || ReturnedValue->ParameterData.IntegerData > pRegEntry->Max) { value = pRegEntry->Default; } else { value = ReturnedValue->ParameterData.IntegerData; } TRACE (TL_I, TM_Mp, ("= 0x%x", value)); } else if(pRegEntry->bRequired) { TRACE (TL_I, TM_Mp,(" -- failed")); ASSERT(FALSE); Status = NDIS_STATUS_FAILURE; break; } else { value = pRegEntry->Default; TRACE (TL_I, TM_Mp,("= 0x%x (default)", value)); Status = NDIS_STATUS_SUCCESS; } // Store the value in the pMiniport structure. switch(pRegEntry->FieldSize) { case 1: *((PUCHAR) pointer) = (UCHAR) value; break; case 2: *((PUSHORT) pointer) = (USHORT) value; break; case 4: *((PULONG) pointer) = (ULONG) value; break; default: TRACE (TL_I,TM_Mp, ("Bogus field size %d", pRegEntry->FieldSize)); break; } } // Read NetworkAddress registry value // Use it as the current address if any // Close the registry NdisCloseConfiguration(ConfigurationHandle); TRACE (TL_I, TM_Mp,("vci %d\n", pMiniport->config.vci)); TRACE (TL_I, TM_Mp,("vpi %d\n", pMiniport->config.vpi)); TRACE (TL_I, TM_Mp,("Encap Type %x\n", pMiniport->Encap)); TRACE (TL_T, TM_Mp, ("<-- NICReadRegParameters, Status=%x", Status)); return Status; } ULONG gDbgMpFlags =0;; ULONG gRmFlags = 0; NDIS_STATUS epvcTaskCloseAddressFamily( IN struct _RM_TASK * pTask, IN RM_TASK_OPERATION Code, IN UINT_PTR UserParam, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: This is the task used to close the Af. It Deinitializes the miniport then calls the Close Miniport Task to Close the Af. Arguments: ProtocolBindingContext Pointer to the adapter structure Status Completion status Return Value: None. --*/ { ENTER ("epvcTaskCloseAddressFamily", 0x20a02c3f) NDIS_STATUS Status = NDIS_STATUS_FAILURE; PEPVC_I_MINIPORT pMiniport = (PEPVC_I_MINIPORT ) RM_PARENT_OBJECT(pTask); PTASK_AF pAfTask = (PTASK_AF) pTask; BOOLEAN fNeedToHalt = FALSE; BOOLEAN fNeedToCancel = FALSE; BOOLEAN fCloseAf = FALSE; ULONG State; enum { Stage_Start =0, // default Stage_MiniportHalted, Stage_CloseAfComplete, Stage_TaskCompleted, Stage_End }; // To be used in pTask->Hdr.State to indicate the state of the Task TRACE ( TL_T, TM_Pt, ("==> epvcTaskCloseAddressFamily State %x", pTask->Hdr.State) ); State = pTask->Hdr.State; switch(State) { case Stage_Start: { // // Check to see if the miniport has already opened an address family. // If so exit // LOCKOBJ (pMiniport, pSR ); gDbgMpFlags = pMiniport->Hdr.State; if (epvcIsThisTaskPrimary ( pTask, &(PRM_TASK)(pMiniport->af.pCloseAfTask)) == FALSE) { PRM_TASK pOtherTask = (PRM_TASK)(pMiniport->af.pCloseAfTask); TRACE (TL_I, TM_Mp, (" Task is not primary\n")); RmTmpReferenceObject (&pOtherTask->Hdr, pSR); // // Set The state so we restart this code after main task completes // pTask->Hdr.State = Stage_Start; UNLOCKOBJ(pMiniport, pSR); RmPendTaskOnOtherTask (pTask, 0, pOtherTask, pSR); RmTmpDereferenceObject(&pOtherTask->Hdr,pSR); Status = NDIS_STATUS_PENDING; break; } // // We are the primary task // ASSERT (pMiniport->af.pCloseAfTask == pAfTask); // // Check to see if our work is already done // if (MiniportTestFlag (pMiniport, fMP_AddressFamilyOpened) == FALSE) { // // quietly exit as the address family is already closed // UNLOCKOBJ(pMiniport, pSR); ASSERT (MiniportTestFlag (pMiniport, fMP_AddressFamilyOpened) == TRUE); State = Stage_TaskCompleted; // we're finished. Status = NDIS_STATUS_FAILURE; // Exit gRmFlags = pTask->Hdr.RmState; break; } // // Now do we need to halt the miniport. // if (MiniportTestFlag (pMiniport, fMP_MiniportInitialized) == TRUE) { // // Our Halt Handler has not been called, // fNeedToHalt = TRUE; } else { // // We are not in the middle of a halt, so this probably // an unbind or Af_Close before the Init Handler. // // This task is not part of the halt code path // ASSERT (pAfTask->Cause != TaskCause_MiniportHalt); fNeedToCancel = TRUE; } if (fNeedToHalt || fNeedToCancel) { MiniportClearFlag (pMiniport, fMP_DevInstanceInitialized); } UNLOCKOBJ(pMiniport,pSR); // // Call Ndis to Deinitialize the miniport, The miniport is already Refed // TRACE ( TL_T, TM_Pt, ("epvcTaskCloseAddressFamily ----") ); if (TRUE == fNeedToHalt ) { epvcIMDeInitializeDeviceInstance (pMiniport); } if (TRUE == fNeedToCancel ) { epvcCancelDeviceInstance (pMiniport, pSR); } // // If the dev instance was not halted, then this thread has to close the Af // pTask->Hdr.State = Stage_MiniportHalted; if (fNeedToHalt == TRUE ) { // // otherwise the work is over, the halt will close the address family // State = Stage_TaskCompleted; break; } FALL_THROUGH } case Stage_MiniportHalted: { // // If the Af is still open, and the miniport halt was never fired, then // issue the close Af // fCloseAf = (MiniportTestFlag(pMiniport, fMP_AddressFamilyOpened) == TRUE); if (fCloseAf == TRUE) { PRM_TASK pAfTask = NULL; // // We need to start a task to complete the Close Call And DeleteVC // Status = epvcAllocateTask( &pMiniport->Hdr, // pParentObject, epvcTaskCloseIMiniport, // pfnHandler, 0, // Timeout, "Task: Close Miniport", // szDescription &pAfTask , pSR ); if (FAIL(Status)) { State = Stage_TaskCompleted; pAfTask = NULL; ASSERT (Status == NDIS_STATUS_SUCCESS); break; } ((PTASK_AF)pAfTask)->Cause = TaskCause_AfCloseRequest; // // Now we will pend the halt on the completion of the delete VC // task // pTask->Hdr.State = Stage_CloseAfComplete; RmPendTaskOnOtherTask(pTask, 0, pAfTask, pSR ); // // Start the Af TearDown // RmStartTask (pAfTask , 0, pSR); // // Exit - We expect to complete this task in another thread // Status = NDIS_STATUS_PENDING; break; } else // Close Af == FALSE { State = Stage_TaskCompleted; } break; } case Stage_CloseAfComplete: { State = Stage_TaskCompleted ; break; } case Stage_End: { Status = NDIS_STATUS_SUCCESS; break; } default: { ASSERTEX(!"Unknown task op", pTask); } } if (Stage_TaskCompleted == State ) { pTask->Hdr.State = Stage_End; Status = NDIS_STATUS_SUCCESS; // // Clear the task here // LOCKOBJ(pMiniport, pSR); pMiniport->af.pCloseAfTask= NULL; UNLOCKOBJ(pMiniport, pSR); // // Set the complete event here // if (pAfTask->Cause == TaskCause_ProtocolUnbind) { epvcSetEvent (&pAfTask->CompleteEvent); } } RM_ASSERT_NOLOCKS(pSR); TRACE ( TL_T, TM_Pt, ("<== epvcTaskCloseAddressFamily Status %x", Status) ); EXIT(); return Status; } VOID epvcInitializeMiniportLookasideLists ( IN PEPVC_I_MINIPORT pMiniport ) /*++ Routine Description: Initialize all the lookaside lists in the adapter block Arguments: Return Value: None. --*/ { USHORT DefaultDepth = 15; extern const UINT MaxEthernetFrameSize ; TRACE( TL_T, TM_Mp, ( "==> nicInitializeMiniportLookasideLists pMiniport %x ", pMiniport ) ); switch (pMiniport->Encap) { case IPV4_ENCAP_TYPE: case IPV4_LLC_SNAP_ENCAP_TYPE: { epvcInitializeLookasideList ( &pMiniport->arps.LookasideList, sizeof (EPVC_TASK), TAG_TASK, DefaultDepth ); epvcInitializeLookasideList ( &pMiniport->rcv.LookasideList, sizeof (EPVC_IP_RCV_BUFFER), TAG_RCV , DefaultDepth ); break; } case ETHERNET_ENCAP_TYPE: case ETHERNET_LLC_SNAP_ENCAP_TYPE: { break; } default: { } } TRACE( TL_T, TM_Mp, ( "<== nicInitializeMiniportLookasideLists " ) ); } VOID epvcDeleteMiniportLookasideLists ( IN PEPVC_I_MINIPORT pMiniport ) /*++ Routine Description: Delete all the lookaside lists in the adapter block Arguments: Return Value: None. --*/ { TRACE( TL_T, TM_Mp, ( "== epvcDeleteMiniportLookasideLists pMiniport %x ", pMiniport) ); // // Deletes the lookaside lists if they have been allocated // epvcDeleteLookasideList (&pMiniport->rcv.LookasideList); epvcDeleteLookasideList (&pMiniport->arps.LookasideList); } NDIS_STATUS epvcInitializeMiniportPacketPools ( IN PEPVC_I_MINIPORT pMiniport ) /*++ Routine Description: Initializr all the packet pools in the miniport Arguments: Return Value: None. --*/ { NDIS_STATUS Status = NDIS_STATUS_FAILURE; TRACE( TL_T, TM_Mp, ( "==> epvcInitializeMiniportPacketPools pMiniport %x ", pMiniport ) ); do { epvcAllocatePacketPool (&Status, &pMiniport->PktPool.Send, MIN_PACKET_POOL_SIZE, MAX_PACKET_POOL_SIZE - MIN_PACKET_POOL_SIZE, sizeof(EPVC_PKT_CONTEXT)); if (Status != NDIS_STATUS_SUCCESS) { EPVC_ZEROSTRUCT (&pMiniport->PktPool.Send); ASSERT (Status == NDIS_STATUS_SUCCESS); break; } epvcAllocatePacketPool (&Status, &pMiniport->PktPool.Recv, MIN_PACKET_POOL_SIZE, MAX_PACKET_POOL_SIZE - MIN_PACKET_POOL_SIZE, PROTOCOL_RESERVED_SIZE_IN_PACKET); if (Status != NDIS_STATUS_SUCCESS) { EPVC_ZEROSTRUCT (&pMiniport->PktPool.Recv); ASSERT (Status == NDIS_STATUS_SUCCESS); break; } } while ( FALSE); if (Status != NDIS_STATUS_SUCCESS) { epvcDeleteMiniportPacketPools (pMiniport); } TRACE( TL_T, TM_Mp, ( "<== epvcInitializeMiniportPacketPools Status %x ", Status ) ); return Status; } VOID epvcDeleteMiniportPacketPools ( IN PEPVC_I_MINIPORT pMiniport ) /*++ Routine Description: Delete all the packet pools in the miniport block Arguments: Return Value: None. --*/ { TRACE( TL_T, TM_Mp, ( "== epvcDeleteMiniportPacketPools pMiniport %x ", pMiniport ) ); // // Freeing packet pools // if (pMiniport->PktPool.Recv.Handle != NULL) { epvcFreePacketPool (&pMiniport->PktPool.Recv); } if (pMiniport->PktPool.Send.Handle != NULL) { epvcFreePacketPool (&pMiniport->PktPool.Send); } } VOID epvcInitializeMiniportParameters( PEPVC_I_MINIPORT pMiniport ) { //ipv4 - 0 //ipv4 with llc header = 1 //Ethernet - 2 //Ethernet with llc header- 3 // // Defaults for all flags are FALSE // pMiniport->fDoIpEncapsulation = FALSE; pMiniport->fAddLLCHeader = FALSE; switch (pMiniport->Encap ) { case IPV4_ENCAP_TYPE: { pMiniport->fDoIpEncapsulation = TRUE; pMiniport->MinAcceptablePkt =sizeof (IPHeader) ; pMiniport->MaxAcceptablePkt = EPVC_MAX_FRAME_SIZE -EPVC_ETH_HEADERSIZE ; break; } case IPV4_LLC_SNAP_ENCAP_TYPE: { pMiniport->fAddLLCHeader = TRUE; pMiniport->fDoIpEncapsulation = TRUE; pMiniport->pLllcHeader = &LLCSnapIpv4[0]; pMiniport->LlcHeaderLength = sizeof(LLCSnapIpv4); pMiniport->MinAcceptablePkt = sizeof (IPHeader) + sizeof(LLCSnapIpv4); pMiniport->MaxAcceptablePkt = EPVC_MAX_FRAME_SIZE + sizeof(LLCSnapIpv4)-EPVC_ETH_HEADERSIZE ; break; } case ETHERNET_LLC_SNAP_ENCAP_TYPE: { pMiniport->fAddLLCHeader = TRUE; pMiniport->pLllcHeader = &LLCSnapEthernet[0]; pMiniport->LlcHeaderLength = sizeof(LLCSnapEthernet); pMiniport->MinAcceptablePkt = MIN_ETHERNET_SIZE + sizeof(LLCSnapEthernet); pMiniport->MaxAcceptablePkt = EPVC_MAX_FRAME_SIZE +sizeof(LLCSnapEthernet); break; } case ETHERNET_ENCAP_TYPE: { pMiniport->MinAcceptablePkt = MIN_ETHERNET_SIZE; pMiniport->MaxAcceptablePkt = EPVC_MAX_FRAME_SIZE + EPVC_ETH_ENCAP_SIZE; break; } default: { ASSERT (!"Not supported - defaulting to Ethernet Encapsulation"); } } } NDIS_STATUS epvcTaskHaltMiniport( IN struct _RM_TASK * pTask, IN RM_TASK_OPERATION Code, IN UINT_PTR UserParam, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: Task handler for opening address families on an underlying adapters. The number of address families instantiated is determined by the configuration read in the registry Arguments: UserParam for (Code == RM_TASKOP_START) : UnbindContext --*/ { ENTER("epvcTaskHaltMiniport", 0xaac34d81) NDIS_STATUS Status = NDIS_STATUS_FAILURE; PEPVC_I_MINIPORT pMiniport = (PEPVC_I_MINIPORT ) RM_PARENT_OBJECT(pTask); PEPVC_ADAPTER pAdapter = pMiniport->pAdapter; PTASK_HALT pTaskHalt = (PTASK_HALT)pTask; BOOLEAN fTaskCompleted = FALSE; ULONG State; enum { Stage_Start =0, // default Stage_DeleteVc, Stage_CloseAfComplete, Stage_TaskCompleted, Stage_End }; // To be used in pTask->Hdr.State to indicate the state of the Task TRACE(TL_T, TM_Mp, ("==>epvcTaskHaltMiniport State %x", pTask->Hdr.State)); State = pTask->Hdr.State; switch (pTask->Hdr.State) { case Stage_Start: { TRACE (TL_V, TM_Mp, (" Task Halt miniport Stage_Start")); // // Check to see if the miniport has already halting. // If so exit // LOCKOBJ (pMiniport, pSR ); if (epvcIsThisTaskPrimary ( pTask, &(PRM_TASK)(pMiniport->pnp.pTaskHalt)) == FALSE) { PRM_TASK pOtherTask = (PRM_TASK)(pMiniport->pnp.pTaskHalt); RmTmpReferenceObject (&pOtherTask->Hdr, pSR); // // Set The state so we restart this code after main task completes // pTask->Hdr.State = Stage_Start; UNLOCKOBJ(pMiniport, pSR); RmPendTaskOnOtherTask (pTask, 0, pOtherTask, pSR); RmTmpDereferenceObject(&pOtherTask->Hdr,pSR); Status = NDIS_STATUS_PENDING; break; } // We are the primary task and we have the lock // ASSERT (pMiniport->pnp.pTaskHalt == pTaskHalt); // // Lets close the Call and Delete the Vc // UNLOCKOBJ (pMiniport, pSR); if (MiniportTestFlag (pMiniport, fMP_MakeCallSucceeded) == TRUE) { PRM_TASK pVcTask = NULL; // // We need to start a task to complete the Close Call And DeleteVC // Status = epvcAllocateTask( &pMiniport->Hdr, // pParentObject, epvcTaskVcTeardown, // pfnHandler, 0, // Timeout, "Task: TearDown Vc", // szDescription &pVcTask , pSR ); if (FAIL(Status)) { fTaskCompleted = TRUE; pVcTask = NULL; ASSERT (Status == NDIS_STATUS_SUCCESS); break; } // // Now we will pend the halt on the completion of the delete VC // task // pTask->Hdr.State = Stage_DeleteVc; RmPendTaskOnOtherTask(pTask, 0, pVcTask, pSR ); // // Start the Vc TearDown // RmStartTask (pVcTask , 0, pSR); // // Exit - We expect to complete this task in another thread // Status = NDIS_STATUS_PENDING; break; } else //if (MiniportTestFlag (pMiniport, fMP_MakeCallSucceeded) == TRUE) { pTask->Hdr.State = Stage_DeleteVc; // // Continue On - the Vc has already been deleted // } } case Stage_DeleteVc: { // // Now we check to see if the address family is still // open for this miniport // TRACE (TL_V, TM_Mp, (" Task Halt miniport Stage_DeleteVc")); if (MiniportTestFlag(pMiniport, fMP_AddressFamilyOpened) == TRUE) { PRM_TASK pAfTask = NULL; // // We need to start a task to complete the Close Call And DeleteVC // Status = epvcAllocateTask( &pMiniport->Hdr, // pParentObject, epvcTaskCloseIMiniport, // pfnHandler, 0, // Timeout, "Task: Close Miniport", // szDescription &pAfTask , pSR ); if (FAIL(Status)) { fTaskCompleted = TRUE; pAfTask = NULL; ASSERT (Status == NDIS_STATUS_SUCCESS); break; } ((PTASK_AF)pAfTask)->Cause = TaskCause_MiniportHalt; // // Now we will pend the halt on the completion of the delete VC // task // pTask->Hdr.State = Stage_CloseAfComplete; RmPendTaskOnOtherTask(pTask, 0, pAfTask, pSR ); // // Start the Af TearDown // RmStartTask (pAfTask , 0, pSR); // // Exit - We expect to complete this task in another thread // Status = NDIS_STATUS_PENDING; break; } else //if (MiniportTestFlag (pMiniport, fMP_MakeCallSucceeded) == TRUE) { pTask->Hdr.State = Stage_CloseAfComplete; // // Continue On - the Af has already been deleted // } } case Stage_CloseAfComplete: { // // Free all miniport resources here .- packet pools etc. // TRACE (TL_V, TM_Mp, (" Task Halt miniport Stage_CloseAfComplete")); // // Freeing Lookaside lists // epvcDeleteMiniportLookasideLists (pMiniport); // // Freeing packet pools // epvcDeleteMiniportPacketPools(pMiniport); // // If the miniport is halting we do not shut down the protocol's adapter // object // fTaskCompleted = TRUE; Status = NDIS_STATUS_SUCCESS; break; } case Stage_TaskCompleted: { ASSERT(0); break; } case Stage_End: { TRACE (TL_V, TM_Mp, (" Task Halt miniport Stage_End")); Status = NDIS_STATUS_SUCCESS; break; } default: { ASSERT (pTask->Hdr.State <= Stage_End); } } // end of switch // // if this thread has completed the postprocessing, // then signal the event. // if (TRUE == fTaskCompleted) { BOOLEAN fSetWaitEvent = FALSE; TRACE (TL_V, TM_Mp, ("Task Halt Miniport - Stage End")); pTask->Hdr.State = Stage_End; if (FAIL(Status)) { ASSERT (0); } LOCKOBJ (pMiniport, pSR); pMiniport->pnp.pTaskHalt = NULL; if (MiniportTestFlag (pMiniport, fMP_WaitingForHalt)== TRUE) { MiniportClearFlag (pMiniport, fMP_WaitingForHalt); fSetWaitEvent = TRUE; } UNLOCKOBJ (pMiniport, pSR); // // This first event is for the MiniportHalt handler // which fired off this task // epvcSetEvent (&pTaskHalt->CompleteEvent); // // This second event is for the epvcMiniportDoUnbind // which wants to wait until the Halt is complete , // before it shuts off the lower binding to the phy. adapter // if (fSetWaitEvent) { epvcSetEvent (&pMiniport->pnp.HaltCompleteEvent); } Status = NDIS_STATUS_SUCCESS; } TRACE(TL_T, TM_Mp, ("<==epvcTaskHaltMiniport Status %x", Status)); EXIT() RM_ASSERT_NOLOCKS(pSR); return Status; } NDIS_STATUS epvcAddEthernetTail( PEPVC_SEND_STRUCT pSendStruct, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: Makes sure the ethernet packet is greater than 64 bytes Arguments: pSendStruct - Contains all the arguments that are needed. Return: Success - if the padding was not needed or the MDL was successfully appended --*/ { ENTER ("epvcAddEthernetTail" , 0x3ec589c9) NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE; PNDIS_PACKET pNewPkt = pSendStruct->pNewPacket; PEPVC_I_MINIPORT pMiniport = pSendStruct->pMiniport; ULONG PacketLength = 0; ULONG LengthRemaining = 0; PNDIS_BUFFER pNewTailBuffer = NULL; PNDIS_BUFFER pLastBuffer; TRACE (TL_T, TM_Send, ("==>epvcAddEthernetTail")); do { ULONG BufferLength = 0; PNDIS_BUFFER pBuffer = NULL; if (pMiniport->fDoIpEncapsulation == TRUE) { NdisStatus = NDIS_STATUS_SUCCESS; break; // we are done } // // Check the length of the Ethernet packet // NdisQueryPacketLength(pNewPkt, &PacketLength); // // Is the packet length greater than 64 // if (PacketLength >= MINIMUM_ETHERNET_LENGTH) { NdisStatus= NDIS_STATUS_SUCCESS; break; } // // Add padding to fill up the minimum Ethernet frame length. // This is a new buffer that is appended to the original // NDIS_BUFFER chain. // LengthRemaining = MINIMUM_ETHERNET_LENGTH - PacketLength; NdisAllocateBuffer(&NdisStatus, &pNewTailBuffer, NULL, &gPaddingBytes,LengthRemaining); if (NdisStatus != NDIS_STATUS_SUCCESS || pNewTailBuffer == NULL) { pNewTailBuffer = NULL; NdisStatus = NDIS_STATUS_RESOURCES; break; } // // Append the new buffer to the tail of the packet. // // // Locate the last NDIS_BUFFER in the packet. Do it the hard // way since Packet->Private.Tail is not reliable: // pLastBuffer = pNewPkt->Private.Head; while (pLastBuffer->Next != NULL) { pLastBuffer = pLastBuffer->Next; } // // Save a pointer to this last MDL so that we can set its // Next field back to NULL when we complete this send. // pSendStruct->Context.Stack.EthernetSend.pOldLastNdisBuffer = pLastBuffer; // // Append the new buffer to the tail of the chain. // pLastBuffer->Next = pNewTailBuffer; pNewTailBuffer->Next = NULL; // // Update our packet. // pNewPkt->Private.Tail = pNewTailBuffer; pNewPkt->Private.ValidCounts = FALSE; NdisStatus = NDIS_STATUS_SUCCESS; break ; // we are done } while (FALSE); if (NdisStatus != NDIS_STATUS_SUCCESS && pNewTailBuffer != NULL) { NdisFreeBuffer (pNewTailBuffer); } TRACE (TL_T, TM_Send, ("<==epvcAddEthernetTail ")); return NdisStatus ; } VOID epvcRemoveEthernetTail ( IN PEPVC_I_MINIPORT pMiniport, IN PNDIS_PACKET pPacket, IN PEPVC_PKT_CONTEXT pContext ) /*++ Routine Description: Removes the extra MDL that was added to make this packet greater than MINIUMUM_ETHERNET_SIZE Used for Ethernet , Eth +LLC Encapsulations only Arguments: pMiniport - Miniport structure pPacket - Packet allocated by EPVC pContext - Context of the packet - used to store the original last mdl Return: None --*/ { PNDIS_BUFFER pOldLastNdisBuffer = NULL; do { // // Ethernet encapsulation ? If not, then exit // if (pMiniport->fDoIpEncapsulation == TRUE) { break; // there was no ethernet encapsulation, so exit } // // if there is no old buffer, then we can exit // pOldLastNdisBuffer = pContext->Stack.EthernetSend.pOldLastNdisBuffer; if (pOldLastNdisBuffer == NULL) { break; } // // Free the last buffer in the packet (this is the padding // we added for a runt packet). // NdisFreeBuffer(pPacket->Private.Tail); // // Set the Next pointer of the original "last buffer" to NULL. // pOldLastNdisBuffer->Next = NULL; } while (FALSE); } NDIS_STATUS epvcAddEthernetPad( PEPVC_SEND_STRUCT pSendStruct, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: Makes sure the ethernet packet w/o LLC header has a pad of 0x00, 0x00 Arguments: pSendStruct - Contains all the arguments that are needed. Return: Success - if the padding was not needed or the MDL was successfully added --*/ { ENTER ("epvcAddEthernetPad" , 0x3ec589c9) NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE; PNDIS_PACKET pNewPkt = pSendStruct->pNewPacket; PEPVC_I_MINIPORT pMiniport = pSendStruct->pMiniport; PNDIS_BUFFER pPaddingBuffer = NULL; TRACE (TL_T, TM_Send, ("==>epvcAddEthernetPad")); do { ULONG BufferLength = 0; PNDIS_BUFFER pBuffer = NULL; if (pMiniport->Encap != ETHERNET_ENCAP_TYPE) { NdisStatus = NDIS_STATUS_SUCCESS; break; // we are done } // // It is pure Ethernet. We need to precede the packet // with a 00,00 // NdisAllocateBuffer(&NdisStatus, &pPaddingBuffer, NULL, &gPaddingBytes, ETHERNET_PADDING_LENGTH); if (NdisStatus != NDIS_STATUS_SUCCESS || pPaddingBuffer == NULL) { pPaddingBuffer = NULL; NdisStatus = NDIS_STATUS_RESOURCES; break; } // // no more allocations - we cannot fail from here // NdisStatus = NDIS_STATUS_SUCCESS; // // Add the new buffer to the head of the packet // NdisChainBufferAtFront(pNewPkt,pPaddingBuffer); break ; // we are done } while (FALSE); if (NdisStatus != NDIS_STATUS_SUCCESS && pPaddingBuffer != NULL) { NdisFreeBuffer (pPaddingBuffer); } TRACE (TL_T, TM_Send, ("<==epvcAddEthernetPad ")); return NdisStatus ; } VOID epvcRemoveEthernetPad ( IN PEPVC_I_MINIPORT pMiniport, IN PNDIS_PACKET pPacket ) /*++ Routine Description: Removes the padding that was added to the head of the packet Ethernet Head Used for Ethernet Encapsulation only Arguments: pMiniport - Miniport structure pPacket - Packet Return: None --*/ { PNDIS_BUFFER pPaddingBuffer= NULL; do { if (pMiniport->Encap != ETHERNET_ENCAP_TYPE) { break; // we are done } // // it is in pure ethernet mode - remove the Padding // // // First - a simple sanity check // { PNDIS_BUFFER pBuffer = pPacket->Private.Head; ULONG PaddingLength = NdisBufferLength(pBuffer); if (PaddingLength !=ETHERNET_PADDING_LENGTH) { // this is not our MDL ASSERT (PaddingLength !=ETHERNET_PADDING_LENGTH); break; } } // // Free the padding buffer at the front of the Packet // NdisUnchainBufferAtFront(pPacket,&pPaddingBuffer ); NdisFreeBuffer (pPaddingBuffer ); } while (FALSE); } VOID epvcCancelDeviceInstance( IN PEPVC_I_MINIPORT pMiniport , IN PRM_STACK_RECORD pSR ) /*++ Routine Description: This function cancels an outstanding Device Instance. If the NDIS call fails. it waits for an event in the miniprot to fire. After that it goes ahead and DeInitializes the Device Instance Arguments: pMiniport - Miniport in question. Return Value: Success --*/ { ENTER("epvcCancelDeviceInstance", 0x0e42d778) NDIS_STATUS Status = NDIS_STATUS_SUCCESS; UINT iteration =0; BOOLEAN bWaitSuccess = FALSE; BOOLEAN fNeedToInitEvent = FALSE; do { LOCKOBJ (pMiniport, pSR); // Prepare the event, and mark the structure as being Canceled epvcResetEvent (&pMiniport->pnp.DeInitEvent); // Set the flag to mark it as cancelled MiniportSetFlag (pMiniport, fMP_MiniportCancelInstance); UNLOCKOBJ (pMiniport, pSR); // Cancel the device instance Status = epvcIMCancelInitializeDeviceInstance(pMiniport); if (Status == NDIS_STATUS_SUCCESS) { break; } // // If the Cancel has not Succeeded then we should wait for // the Initialize to complete // { BOOLEAN bWaitSuccessful; bWaitSuccessful = epvcWaitEvent (&pMiniport->pnp.DeInitEvent,WAIT_INFINITE); if (bWaitSuccessful == FALSE) { ASSERT (bWaitSuccessful == TRUE); } } // // If cancel fails. Wait for the miniport to be initialized // ASSERT (pMiniport->ndis.MiniportAdapterHandle != NULL); // // If cancel fails. Wait for the miniport to be initialized // TRACE (TL_N, TM_Mp, ("Call DeInit after Cancel failed %p , ",pMiniport)); epvcIMDeInitializeDeviceInstance (pMiniport); Status = NDIS_STATUS_SUCCESS; } while (FALSE); LOCKOBJ(pMiniport, pSR); MiniportClearFlag (pMiniport, fMP_MiniportCancelInstance); UNLOCKOBJ (pMiniport, pSR); return ; }