text: /**
  ********************************************************************************
  \file   ndis-imMiniport.c
  
  \brief  Miniport implementation of NDIS intermediate driver
  
  This implements the miniport section of the NDIS intermediate driver. The
  miniport provides a communication interface for other NDIS protocol drivers.
  This interface will act as a virtual Ethernet interface with which socket based
  communication can be achieved for non-PLK packets.
  
  The current implementation has only basic miniport functionalities such as
  send, receive, lower layer binding and communication.
  
  The following features will be added in future:
  1. Statistics for send and receive.
  2. Ethernet status update and state change handling.
  3. OID request handling.
  4. Additional PNP capabilities.
  
  \ingroup ndis_intermediate
  *******************************************************************************/
  
  /*------------------------------------------------------------------------------k
  Copyright (c) 2015, Kalycito Infotech Private Limited
  All rights reserved.
  
  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
      * Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
      * Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
      * Neither the name of the copyright holders nor the
        names of its contributors may be used to endorse or promote products
        derived from this software without specific prior written permission.
  
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  ------------------------------------------------------------------------------*/
  
  //------------------------------------------------------------------------------
  // includes
  //------------------------------------------------------------------------------
  #include "ndis-imInternal.h"
  
  //============================================================================//
  //            G L O B A L   D E F I N I T I O N S                             //
  //============================================================================//
  
  //------------------------------------------------------------------------------
  // const defines
  //------------------------------------------------------------------------------
  
  //------------------------------------------------------------------------------
  // module global vars
  //------------------------------------------------------------------------------
  
  //------------------------------------------------------------------------------
  // global function prototypes
  //------------------------------------------------------------------------------
  
  //============================================================================//
  //            P R I V A T E   D E F I N I T I O N S                           //
  //============================================================================//
  
  //------------------------------------------------------------------------------
  // const defines
  //------------------------------------------------------------------------------
  
  //------------------------------------------------------------------------------
  // local types
  //------------------------------------------------------------------------------
  
  //------------------------------------------------------------------------------
  // local vars
  //------------------------------------------------------------------------------
  // List of OIDs supported by this miniport.
  NDIS_OID                aVEthSupportedOids[] =
  {
      OID_GEN_SUPPORTED_LIST,
      OID_GEN_HARDWARE_STATUS,
      OID_GEN_MEDIA_SUPPORTED,
      OID_GEN_MEDIA_IN_USE,
      OID_GEN_MAXIMUM_LOOKAHEAD,
      OID_GEN_MAXIMUM_FRAME_SIZE,
      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_VENDOR_ID,
      OID_GEN_VENDOR_DESCRIPTION,
      OID_GEN_VENDOR_DRIVER_VERSION,
      OID_GEN_CURRENT_PACKET_FILTER,
      OID_GEN_CURRENT_LOOKAHEAD,
      OID_GEN_DRIVER_VERSION,
      OID_GEN_MAXIMUM_TOTAL_SIZE,
      OID_GEN_PROTOCOL_OPTIONS,
      OID_GEN_MAC_OPTIONS,
      OID_GEN_MEDIA_CONNECT_STATUS,
      OID_GEN_MAXIMUM_SEND_PACKETS,
      OID_GEN_XMIT_OK,
      OID_GEN_RCV_OK,
      OID_GEN_XMIT_ERROR,
      OID_GEN_RCV_ERROR,
      OID_GEN_RCV_NO_BUFFER,
      OID_GEN_RCV_CRC_ERROR,
      OID_GEN_TRANSMIT_QUEUE_LENGTH,
      OID_GEN_STATISTICS,
      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_802_3_XMIT_DEFERRED,
      OID_802_3_XMIT_MAX_COLLISIONS,
      OID_802_3_RCV_OVERRUN,
      OID_802_3_XMIT_UNDERRUN,
      OID_802_3_XMIT_HEARTBEAT_FAILURE,
      OID_802_3_XMIT_TIMES_CRS_LOST,
      OID_802_3_XMIT_LATE_COLLISIONS,
      OID_PNP_CAPABILITIES,
      OID_PNP_SET_POWER,
      OID_PNP_QUERY_POWER,
      OID_PNP_ADD_WAKE_UP_PATTERN,
      OID_PNP_REMOVE_WAKE_UP_PATTERN,
      OID_PNP_ENABLE_WAKE_UP
  };
  
  static BOOLEAN          fInitialize_l = FALSE;
  static tVEthInstance*   pVEthInstance_l;
  
  //------------------------------------------------------------------------------
  // local function prototypes
  //------------------------------------------------------------------------------
  static void        miniportFreeVethRxBuff(tVEthInstance* pVEthInstance_p);
  static NDIS_STATUS miniportAllocateVEthRxBuff(tVEthInstance* pVEthInstance_p);
  
  //============================================================================//
  //            P U B L I C   F U N C T I O N S                                 //
  //============================================================================//
  
  //------------------------------------------------------------------------------
  /**
  \brief  Miniport receive handler
  
  This is the receive handler for the NDIS miniport. The receive handler forwards
  the received frames to the protocol drivers enabled on this miniport.
  
  \param  pDataBuff_p     Pointer to the data buffer.
  \param  size_p          Size of the buffer.
  
  \return The function returns an NDIS_STATUS error code.
  
  \ingroup module_ndis
  */
  //------------------------------------------------------------------------------
  NDIS_STATUS miniport_handleReceive(UINT8* pDataBuff_p, size_t size_p)
  {
      PLIST_ENTRY        pRxLink;
      tVEthRcvBufInfo*   pVethRxInfo = NULL;
      PNET_BUFFER        pNetBuffer = NULL;
  
      if (pDataBuff_p == NULL || size_p == 0)
          return NDIS_STATUS_INVALID_PACKET;
  
      if (pVEthInstance_l == NULL)
          return NDIS_STATUS_RESOURCES;
  
      if (pVEthInstance_l->fMiniportPaused)
      {
          // Receive indication should not be forwarded in paused state.
          // We just drop the packet for now.
          // TODO: Implement a waitqueue to handle the received packet
          // indication after entering running state.
          return NDIS_STATUS_SUCCESS;
      }
  
      if (IsListEmpty(&pVEthInstance_l->rxList))
      {
          return NDIS_STATUS_RESOURCES;
      }
  
      pRxLink = NdisInterlockedRemoveHeadList(&pVEthInstance_l->rxList,
                                              &pVEthInstance_l->rxListLock);
  
      if (pRxLink == NULL)
          return NDIS_STATUS_RESOURCES;
  
      pVethRxInfo = CONTAINING_RECORD(pRxLink, tVEthRcvBufInfo, rxLink);
  
      if (pVethRxInfo->pData == NULL || pVethRxInfo == NULL)
          return NDIS_STATUS_RESOURCES;
  
      NdisMoveMemory(pVethRxInfo->pData, pDataBuff_p, size_p);
  
      // Update the size
      pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pVethRxInfo->pNbl);
      NET_BUFFER_DATA_LENGTH(pNetBuffer) = size_p;
  
      // Indicate the received packet to upper layer protocols
      NdisMIndicateReceiveNetBufferLists(pVEthInstance_l->hMiniportAdapterHandle,
                                         pVethRxInfo->pNbl, 0, 1, 0);
  
      pVEthInstance_l->receiveIndication++;
  
      return NDIS_STATUS_SUCCESS;
  }
  
  //============================================================================//
  //            P R I V A T E   F U N C T I O N S                               //
  //============================================================================//
  /// \name Private Functions
  /// \{
  
  //------------------------------------------------------------------------------
  /**
  \brief  Miniport initialization routine
  
  This is the miniport initialization routine which gets called as a result of the
  call to NdisIMInitializeDeviceInstanceEx. The context parameter
  'VEthInstance' which is passed during the miniport initialization is retrieved
  here for processing.
  
  \param  adapterHandle_p     NDIS handle for this miniport adapter.
  \param  driverContext_p     Handle passed to NDIS when the driver is registered.
  \param  initParams_p        Miniport initialization parameters such
                              as the device context and resources.
  
  \return The function returns an NDIS_STATUS error code.
  */
  //------------------------------------------------------------------------------
  NDIS_STATUS miniportInitialize(NDIS_HANDLE adapterHandle_p,
                                 NDIS_HANDLE driverContext_p,
                                 PNDIS_MINIPORT_INIT_PARAMETERS initParams_p)
  {
      tVEthInstance*                      pVEthInstance;
      NDIS_STATUS                         status = NDIS_STATUS_FAILURE;
      NDIS_MINIPORT_ADAPTER_ATTRIBUTES    miniportAttributes;
      NDIS_CONFIGURATION_OBJECT           configObject;
      NDIS_HANDLE                         configHandle;
      PVOID                               pMacAddress;
      UINT                                macLength;
      NET_IFINDEX                         highLayerIfIndex;
      NET_IFINDEX                         lowerLayerIfIndex;
      ULONG                               packetFilter = NDIS_PACKET_TYPE_PROMISCUOUS;
  
      UNREFERENCED_PARAMETER(driverContext_p);
  
      TRACE("%s()... \n", __FUNCTION__);
  
      if (fInitialize_l)
          return NDIS_STATUS_SUCCESS;
  
      pVEthInstance = (tVEthInstance*)initParams_p->IMDeviceInstanceContext;
      pVEthInstance_l = pVEthInstance;
      NdisZeroMemory(&miniportAttributes, sizeof(NDIS_MINIPORT_ADAPTER_ATTRIBUTES));
  
      pVEthInstance->hMiniportAdapterHandle = adapterHandle_p;
  
      // Register IOCTL interface here
      ndis_createDrvIntf();
  
      miniportAttributes.RegistrationAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES;
      miniportAttributes.RegistrationAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1;
      miniportAttributes.RegistrationAttributes.Header.Size = sizeof(NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES);
  
      miniportAttributes.RegistrationAttributes.MiniportAdapterContext = (NDIS_HANDLE)pVEthInstance;
      miniportAttributes.RegistrationAttributes.AttributeFlags = NDIS_MINIPORT_ATTRIBUTES_NO_HALT_ON_SUSPEND;
      miniportAttributes.RegistrationAttributes.CheckForHangTimeInSeconds = 0;
      miniportAttributes.RegistrationAttributes.InterfaceType = 0;
  
      NDIS_DECLARE_MINIPORT_ADAPTER_CONTEXT(tVEthInstance);
      status = NdisMSetMiniportAttributes(adapterHandle_p, &miniportAttributes);
  
      if (status != NDIS_STATUS_SUCCESS)
      {
          goto Exit;
      }
  
      configObject.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT;
      configObject.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1;
      configObject.Header.Size = sizeof(NDIS_CONFIGURATION_OBJECT);
      configObject.NdisHandle = pVEthInstance->hMiniportAdapterHandle;
      configObject.Flags = 0;
  
      status = NdisOpenConfigurationEx(&configObject, &configHandle);
  
      if (status != NDIS_STATUS_SUCCESS)
      {
          goto Exit;
      }
  
      NdisReadNetworkAddress(&status, &pMacAddress, &macLength, configHandle);
  
      if ((status == NDIS_STATUS_SUCCESS) && (macLength == ETH_LENGTH_OF_ADDRESS) &&
          (!ETH_IS_MULTICAST(pMacAddress)))
      {
          ETH_COPY_NETWORK_ADDRESS(pVEthInstance->aCurrentAddress, pMacAddress);
      }
      else
      {
          ETH_COPY_NETWORK_ADDRESS(pVEthInstance->aCurrentAddress, pVEthInstance->aPermanentAddress);
      }
  
      status = NDIS_STATUS_SUCCESS;
  
      NdisCloseConfiguration(configHandle);
  
      miniportAttributes.GeneralAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES;
      miniportAttributes.GeneralAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1;
      miniportAttributes.GeneralAttributes.Header.Size = sizeof(NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES);
      miniportAttributes.GeneralAttributes.MediaType = NdisMedium802_3;
  
      miniportAttributes.GeneralAttributes.MtuSize = pVEthInstance->pProtocolInstance->bindParameters.MtuSize;
      miniportAttributes.GeneralAttributes.MaxXmitLinkSpeed = pVEthInstance->pProtocolInstance->bindParameters.MaxXmitLinkSpeed;
      miniportAttributes.GeneralAttributes.MaxRcvLinkSpeed = pVEthInstance->pProtocolInstance->bindParameters.MaxRcvLinkSpeed;
      miniportAttributes.GeneralAttributes.XmitLinkSpeed = pVEthInstance->pProtocolInstance->bindParameters.XmitLinkSpeed;
      miniportAttributes.GeneralAttributes.RcvLinkSpeed = pVEthInstance->pProtocolInstance->bindParameters.RcvLinkSpeed;
  
      miniportAttributes.GeneralAttributes.MediaConnectState = pVEthInstance->pProtocolInstance->lastLinkState.MediaConnectState;
      miniportAttributes.GeneralAttributes.MediaDuplexState = pVEthInstance->pProtocolInstance->lastLinkState.MediaDuplexState;
      miniportAttributes.GeneralAttributes.XmitLinkSpeed = pVEthInstance->pProtocolInstance->lastLinkState.XmitLinkSpeed;
      miniportAttributes.GeneralAttributes.RcvLinkSpeed = pVEthInstance->pProtocolInstance->lastLinkState.RcvLinkSpeed;
  
      pVEthInstance->lastLinkStatus = NDIS_STATUS_LINK_STATE;
  
      pVEthInstance->lastLinkState = pVEthInstance->pProtocolInstance->lastLinkState;
  
      miniportAttributes.GeneralAttributes.LookaheadSize = pVEthInstance->pProtocolInstance->bindParameters.LookaheadSize;
      miniportAttributes.GeneralAttributes.MaxMulticastListSize = pVEthInstance->pProtocolInstance->bindParameters.MaxMulticastListSize;
      miniportAttributes.GeneralAttributes.MacAddressLength = pVEthInstance->pProtocolInstance->bindParameters.MacAddressLength;
  
      miniportAttributes.GeneralAttributes.PhysicalMediumType = pVEthInstance->pProtocolInstance->bindParameters.PhysicalMediumType;
      miniportAttributes.GeneralAttributes.AccessType = pVEthInstance->pProtocolInstance->bindParameters.AccessType;
      miniportAttributes.GeneralAttributes.DirectionType = pVEthInstance->pProtocolInstance->bindParameters.DirectionType;
      miniportAttributes.GeneralAttributes.ConnectionType = pVEthInstance->pProtocolInstance->bindParameters.ConnectionType;
      miniportAttributes.GeneralAttributes.IfType = pVEthInstance->pProtocolInstance->bindParameters.IfType;
      miniportAttributes.GeneralAttributes.IfConnectorPresent = FALSE; // RFC 2665 TRUE if physical adapter
      miniportAttributes.GeneralAttributes.RecvScaleCapabilities = NULL;
      miniportAttributes.GeneralAttributes.MacOptions = NDIS_MAC_OPTION_NO_LOOPBACK;
  
      miniportAttributes.GeneralAttributes.SupportedPacketFilters = pVEthInstance->pProtocolInstance->bindParameters.SupportedPacketFilters;
  
      miniportAttributes.GeneralAttributes.SupportedStatistics = NDIS_STATISTICS_XMIT_OK_SUPPORTED |
                                                                 NDIS_STATISTICS_RCV_OK_SUPPORTED |
                                                                 NDIS_STATISTICS_XMIT_ERROR_SUPPORTED |
                                                                 NDIS_STATISTICS_RCV_ERROR_SUPPORTED |
                                                                 NDIS_STATISTICS_RCV_CRC_ERROR_SUPPORTED |
                                                                 NDIS_STATISTICS_RCV_NO_BUFFER_SUPPORTED |
                                                                 NDIS_STATISTICS_TRANSMIT_QUEUE_LENGTH_SUPPORTED |
                                                                 NDIS_STATISTICS_GEN_STATISTICS_SUPPORTED;
  
      NdisMoveMemory(&miniportAttributes.GeneralAttributes.CurrentMacAddress,
                     &pVEthInstance->aCurrentAddress,
                     ETH_LENGTH_OF_ADDRESS);
  
      NdisMoveMemory(&miniportAttributes.GeneralAttributes.PermanentMacAddress,
                     &pVEthInstance->aPermanentAddress,
                     ETH_LENGTH_OF_ADDRESS);
      miniportAttributes.GeneralAttributes.PowerManagementCapabilities = NULL;
      miniportAttributes.GeneralAttributes.SupportedOidList = aVEthSupportedOids;
      miniportAttributes.GeneralAttributes.SupportedOidListLength = sizeof(aVEthSupportedOids);
  
      status = NdisMSetMiniportAttributes(adapterHandle_p, &miniportAttributes);
  
      // Allocate buffers
      status = miniportAllocateVEthRxBuff(pVEthInstance);
  
      if (status != NDIS_STATUS_SUCCESS)
      {
          TRACE("%s() Unable to allocate VEth resources\n", __FUNCTION__);
          goto Exit;
      }
  
  Exit:
  
      if (status == NDIS_STATUS_SUCCESS)
      {
          pVEthInstance->fMiniportInitPending = FALSE;
  
          // Save the IfIndex
          highLayerIfIndex = initParams_p->IfIndex;
          lowerLayerIfIndex = pVEthInstance->pProtocolInstance->bindParameters.BoundIfIndex;
  
          status = NdisIfAddIfStackEntry(highLayerIfIndex, lowerLayerIfIndex);
  
          if (status == NDIS_STATUS_SUCCESS)
          {
              pVEthInstance->ifIndex = highLayerIfIndex;
          }
          else
          {
              TRACE("Unable to set the NDIS stack order (0x%X)\n", status);
          }
  
          // We do not fail initialization if the add fails.
          status = NDIS_STATUS_SUCCESS;
      }
      else
      {
          pVEthInstance->hMiniportAdapterHandle = NULL;
      }
  
      if (status == NDIS_STATUS_SUCCESS)
      {
          protocol_sendOidRequest(NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &packetFilter,
                                  sizeof(packetFilter));
      }
  
      // We set the event even if initialization fails, the error will be identified using NULL check.
      NdisSetEvent(&pVEthInstance->miniportInitEvent);
  
      TRACE("%s() - OK \n", __FUNCTION__);
  
      return status;
  }
  
  //------------------------------------------------------------------------------
  /**
  \brief Miniport OID request handler
  
  This routine handles the OID requests directed towards this miniport.
  
  \note OIDs are not processed in current implementation.
  
  \param  adapterContext_p        Pointer to the adapter structure.
  \param  ndisRequest_p           Pointer to NDIS_OID_REQUEST sent down by NDIS.
  
  \return The function returns an NDIS_STATUS error code.
  */
  //------------------------------------------------------------------------------
  NDIS_STATUS miniportOidRequest(NDIS_HANDLE adapterContext_p,
                                 PNDIS_OID_REQUEST ndisRequest_p)
  {
      NDIS_REQUEST_TYPE    requestType;
      NDIS_STATUS          status;
  
      UNREFERENCED_PARAMETER(adapterContext_p);
  
      requestType = ndisRequest_p->RequestType;
  
      switch (requestType)
      {
          case NdisRequestMethod:
          case NdisRequestSetInformation:
          case NdisRequestQueryInformation:
          case NdisRequestQueryStatistics:
              // Do nothing for now
              status = NDIS_STATUS_SUCCESS;
              break;
  
          default:
              status = NDIS_STATUS_NOT_SUPPORTED;
              break;
      }
  
      return status;
  }
  
  //------------------------------------------------------------------------------
  /**
  \brief Halt handler
  
  Stop all pending I/O on the VEth and then unbind it from lower miniport.
  
  This functions updates the driver state to indicate that the miniport is
  halted and cannot handle requests.
  
  \param  adapterContext_p    Pointer to the adapter structure.
  \param  haltAction_p        The reason why the adapter is being halted.
  
  */
  //------------------------------------------------------------------------------
  VOID miniportHalt(NDIS_HANDLE adapterContext_p, NDIS_HALT_ACTION haltAction_p)
  {
      tVEthInstance*   pVEthInstance = (tVEthInstance*)adapterContext_p;
      NET_IFINDEX      lowerLayerIfIndex;
  
      UNREFERENCED_PARAMETER(haltAction_p);
  
      TRACE("%s()...\n", __FUNCTION__);
  
      pVEthInstance->fMiniportHalting = TRUE;
  
      // The driver should wait for the completion of all requests
      // because freeing the resources before completion, may result in
      // system crash.
      while (pVEthInstance->sendRequests)
      {
          // Wait for completion of ASynchronous sends on VEth
          NdisMSleep(500);
      }
  
      while (pVEthInstance->receiveIndication)
      {
          // Wait for all the receive packets to be returned back to us
          NdisMSleep(500);
      }
  
      miniportFreeVethRxBuff(pVEthInstance);
  
      ndis_closeDrvIntf();
  
      if (pVEthInstance->ifIndex != 0)
      {
          // Remove driver from the stack
          lowerLayerIfIndex = pVEthInstance->pProtocolInstance->bindParameters.BoundIfIndex;
          NdisIfDeleteIfStackEntry(pVEthInstance->ifIndex, lowerLayerIfIndex);
  
          pVEthInstance->ifIndex = 0;
      }
  
      // Free the VETh instance
      pVEthInstance->hMiniportAdapterHandle = NULL;
  
      protocol_freeVEthInstance(pVEthInstance);
  
      TRACE("%s() - OK\n", __FUNCTION__);
  }
  
  //------------------------------------------------------------------------------
  /**
  \brief Miniport PNP event handler
  
  This handler is called to notify us of PnP events directed to our miniport
  device object.
  
  \param  adapterContext_p    Pointer to the adapter structure.
  \param  pPnpEvent_p         Pointer to the PNP event.
  
  */
  //------------------------------------------------------------------------------
  VOID miniportPnpEventNotify(NDIS_HANDLE adapterContext_p, PNET_DEVICE_PNP_EVENT pPnpEvent_p)
  {
      // Not a real device
      UNREFERENCED_PARAMETER(adapterContext_p);
      UNREFERENCED_PARAMETER(pPnpEvent_p);
  }
  
  //------------------------------------------------------------------------------
  /**
  \brief Miniport Shut-down Handler
  
  This handler is called to notify us of an impending system shutdown.
  Since this is not a hardware driver, there isn't anything specific
  we need to do about this.
  
  \param  adapterContext_p    Pointer to the adapter structure.
  \param  shutdownAction_p    Specific reason to shut down the adapter.
  
  */
  //------------------------------------------------------------------------------
  VOID miniportShutdown(NDIS_HANDLE adapterContext_p, NDIS_SHUTDOWN_ACTION shutdownAction_p)
  {
      UNREFERENCED_PARAMETER(adapterContext_p);
      UNREFERENCED_PARAMETER(shutdownAction_p);
  }
  
  //------------------------------------------------------------------------------
  /**
  \brief Miniport Unload Handler
  
  This handler is used to unload the miniport during the uninstallation.
  
  \param  driverObject_p  Pointer to the system's driver object structure
                          for this driver.
  
  */
  //------------------------------------------------------------------------------
  VOID miniportUnload(PDRIVER_OBJECT driverObject_p)
  {
      TRACE("%s()...\n", __FUNCTION__);
      UNREFERENCED_PARAMETER(driverObject_p);
      if (driverInstance_g.hProtocolHandle != NULL)
      {
          NdisDeregisterProtocolDriver(driverInstance_g.hProtocolHandle);
      }
  
      NdisMDeregisterMiniportDriver(driverInstance_g.hMiniportHandle);
      TRACE("%s() - OK\n", __FUNCTION__);
  }
  
  //------------------------------------------------------------------------------
  /**
  \brief Miniport pause handler
  
  This handler is used to pause the miniport. When paused, all network data flow
  through this miniport will be stopped.
  
  In current implementation, all frames received during the paused state are
  dropped. In future the state change handling shall be implemented to queue
  the receive frames and indicate them when the interface is restarted.
  
  \param  adapterContext_p    Pointer to the adapter structure.
  \param  pauseParams_p       Pause parameters.
  
  \return The function returns an NDIS_STATUS error code.
  */
  //------------------------------------------------------------------------------
  NDIS_STATUS miniportPause(NDIS_HANDLE adapterContext_p,
                            PNDIS_MINIPORT_PAUSE_PARAMETERS pauseParams_p)
  {
      tVEthInstance*   pVEthInstance = (tVEthInstance*)adapterContext_p;
      NDIS_STATUS      status = NDIS_STATUS_SUCCESS;
  
      UNREFERENCED_PARAMETER(pauseParams_p);
  
      TRACE("%s()...\n", __FUNCTION__);
      NdisAcquireSpinLock(&pVEthInstance->pauseLock);
      pVEthInstance->fMiniportPaused = TRUE;
      NdisReleaseSpinLock(&pVEthInstance->pauseLock);
  
      TRACE("%s() - OK\n", __FUNCTION__);
      return status;
  }
  
  //------------------------------------------------------------------------------
  /**
  \brief Miniport restart handler
  
  This handler is used to restart the miniport.  When the miniport is
  back in the restart state, it can indicate NET_BUFFER_LISTs to the
  upper binding protocols.
  
  \param  adapterContext_p    Pointer to the adapter structure.
  \param  restartParams_p     Restart parameters for miniport.
  
  \return The function returns an NDIS_STATUS error code.
  */
  //------------------------------------------------------------------------------
  NDIS_STATUS miniportRestart(NDIS_HANDLE adapterContext_p,
                              PNDIS_MINIPORT_RESTART_PARAMETERS restartParams_p)
  {
      tVEthInstance*   pVEthInstance = (tVEthInstance*)adapterContext_p;
      NDIS_STATUS      status = NDIS_STATUS_SUCCESS;
  
      UNREFERENCED_PARAMETER(restartParams_p);
  
      TRACE("%s()... \n", __FUNCTION__);
      NdisAcquireSpinLock(&pVEthInstance->pauseLock);
      pVEthInstance->fMiniportPaused = FALSE;
      NdisReleaseSpinLock(&pVEthInstance->pauseLock);
      TRACE("%s() - OK \n", __FUNCTION__);
      return status;
  }
  
  //------------------------------------------------------------------------------
  /**
  \brief Miniport send request handler
  
  Send NET_BUFFER_LISTs to the Kernel layer of stack for ASync scheduling.
  
  \param  adapterContext_p    Pointer to the adapter structure.
  \param  netBufferLists_p    Set of NET_BUFFER_LISTs to send.
  \param  portNumber_p        A port number that identifies a miniport adapter port.
  \param  sendFlags_p         Flags that define attributes for the send operation.
  
  */
  //------------------------------------------------------------------------------
  VOID miniportSendNetBufferLists(NDIS_HANDLE adapterContext_p,
                                  PNET_BUFFER_LIST netBufferLists_p,
                                  NDIS_PORT_NUMBER portNumber_p, ULONG sendFlags_p)
  {
      tVEthInstance*      pVEthInstance = (tVEthInstance*)adapterContext_p;
      NDIS_STATUS         status = NDIS_STATUS_SUCCESS;
      PNET_BUFFER_LIST    pCurrentNbl = netBufferLists_p;
      ULONG               completeFlags = 0;
      PUCHAR              pVethTxBuff;
      PUCHAR              pVethData;
      PMDL                pMdl;
      ULONG               totalLength;
      ULONG               txLength;
      ULONG               offset = 0;                 // CurrentMdlOffset
  
      UNREFERENCED_PARAMETER(portNumber_p);
  
      // If the VEth transmit callback is not registered, mark all the packets to
      // be sent and complete the send requests with NdisMSendNetBufferListsComplete().
      if (pVEthInstance->pfnVEthSendCb == NULL)
      {
          PNET_BUFFER_LIST    tempNetBufferList;
  
          for (tempNetBufferList = pCurrentNbl;
               tempNetBufferList != NULL;
               tempNetBufferList = NET_BUFFER_LIST_NEXT_NBL(tempNetBufferList))
          {
              NET_BUFFER_LIST_STATUS(tempNetBufferList) = status;
          }
          if (NDIS_TEST_SEND_AT_DISPATCH_LEVEL(sendFlags_p))
          {
              NDIS_SET_SEND_COMPLETE_FLAG(completeFlags, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
          }
  
          NdisMSendNetBufferListsComplete(pVEthInstance->hMiniportAdapterHandle,
                                          pCurrentNbl,
                                          completeFlags);
          goto Exit;
      }
  
      // Handle the send request.
  
      // Allocate a new buffer to copy the transmit frame.
      pVethTxBuff = NdisAllocateMemoryWithTagPriority(driverInstance_g.hMiniportHandle,
                                                      OPLK_MAX_FRAME_SIZE,
                                                      OPLK_MEM_TAG, NormalPoolPriority);
  
      if (pVethTxBuff == NULL)
      {
          TRACE("%s() Failed to allocate memory for VEth Tx frame ", __FUNCTION__);
          status = NDIS_STATUS_RESOURCES;
          goto Exit;
      }
  
      // Loop through the NetBufferList and copy the frame data into the send buffer.
      while (netBufferLists_p != NULL)
      {
          ULONG                   bytesAvailable = 0;
          PUCHAR                  pRxDataSrc;
          ULONG                   bytesToCopy = 0;
  
          pVethData = pVethTxBuff;
  
          // Current NetBufferList to process
          pCurrentNbl = netBufferLists_p;
  
          // Get the next NetBufferList to process
          netBufferLists_p = NET_BUFFER_LIST_NEXT_NBL(netBufferLists_p);
          NET_BUFFER_LIST_NEXT_NBL(pCurrentNbl) = NULL;
  
          // Increment send count to identify the pending send requests.
          pVEthInstance->sendRequests++;
  
          // Extract the MDL for the frame in the NetBufferList
          pMdl = NET_BUFFER_CURRENT_MDL(NET_BUFFER_LIST_FIRST_NB(pCurrentNbl));
          txLength = totalLength = NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(pCurrentNbl));
  
          if (totalLength > OPLK_MAX_FRAME_SIZE)
          {
              break;
          }
  
          // Get the offset for the start of the frame in the NetBufferList buffer.
          offset = NET_BUFFER_CURRENT_MDL_OFFSET(NET_BUFFER_LIST_FIRST_NB(pCurrentNbl));
  
          // Process the MDL link list for the NetBufferList to extract complete frame to send.
          while ((pMdl != NULL) && (totalLength > 0))
          {
              pRxDataSrc = NULL;
              NdisQueryMdl(pMdl, &pRxDataSrc, &bytesAvailable, NormalPagePriority);
              if (pRxDataSrc == NULL)
              {
                  break;
              }
  
              bytesToCopy = bytesAvailable - offset;
              bytesToCopy = min(bytesToCopy, totalLength);
              pRxDataSrc = pRxDataSrc + offset;
  
              // Frame is copied into the local buffer.
              NdisMoveMemory(pVethData, pRxDataSrc, bytesToCopy);
              pVethData = (PUCHAR)((ULONG_PTR)pVethData + bytesToCopy);
              totalLength -= bytesToCopy;
  
              offset = 0;
              NdisGetNextMdl(pMdl, &pMdl);
          }
  
          // Forward the frame to be sent.
          pVEthInstance->pfnVEthSendCb(pVethTxBuff, txLength);
  
          // Requests are not deferred, so decrease the send counter.
          pVEthInstance->sendRequests--;
  
          // Mark the status of the NetBufferList
          NET_BUFFER_LIST_STATUS(pCurrentNbl) = status;
  
          // If the caller forwarded the packets at DISPATCH_LEVEL IRQL,
          // specify the complete flags to handle send complete at same IRQL.
          if (NDIS_TEST_SEND_AT_DISPATCH_LEVEL(sendFlags_p))
          {
              NDIS_SET_SEND_COMPLETE_FLAG(completeFlags, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
          }
  
          // Complete the send request
          NdisMSendNetBufferListsComplete(pVEthInstance->hMiniportAdapterHandle,
                                          pCurrentNbl,
                                          completeFlags);
      }
  
      if (pVethTxBuff != NULL)
      {
          NdisFreeMemory(pVethTxBuff, 0, 0);
      }
  
  Exit:
      return;
  }
  
  //------------------------------------------------------------------------------
  /**
  \brief Miniport receive complete handler
  
  NDIS Miniport entry point called whenever protocol drivers complete processing
  of a received packet that was indicated up and was queued to be freed later.
  
  \param  adapterContext_p        Pointer to the adapter structure.
  \param  netBufferLists_p        A pointer to a linked list of NET_BUFFER_LIST
                                  structures that NDIS is returning to the
                                  miniport driver.
  \param  returnFlags_p           Return flags.
  
  \return The function returns an NDIS_STATUS error code.
  */
  //------------------------------------------------------------------------------
  VOID miniportReturnNetBufferLists(NDIS_HANDLE adapterContext_p,
                                    PNET_BUFFER_LIST netBufferLists_p, ULONG returnFlags_p)
  {
      tVEthInstance*      pVEthInstance = (tVEthInstance*)adapterContext_p;
      tVEthRcvBufInfo*    pVethRxInfo = NULL;
  
      UNREFERENCED_PARAMETER(returnFlags_p);
  
      while (netBufferLists_p != NULL)
      {
          pVethRxInfo = VETHINFO_FROM_NBL(netBufferLists_p);
  
          if (pVethRxInfo != NULL)
          {
              NdisInterlockedInsertTailList(&pVEthInstance->rxList,
                                            &pVethRxInfo->rxLink,
                                            &pVEthInstance->rxListLock);
  
              pVEthInstance_l->receiveIndication--;
          }
  
          netBufferLists_p = NET_BUFFER_LIST_NEXT_NBL(netBufferLists_p);
      }
  }
  
  //------------------------------------------------------------------------------
  /**
  \brief Miniport cancel send handler
  
  The miniport entry point to handle cancellation of all packets that
  match the given CancelID. If we have queued any packets that match this CancelID,
  then we should dequeue them and call NdisMSendCompleteNetBufferLists for
  all such packets. All the packets canceled, should be updated with a status
  of NDIS_STATUS_REQUEST_ABORTED.
  
  We should also call NdisCancelSendPackets in turn, on each lower binding
  that this adapter corresponds to. This is to enable the miniports below to
  cancel any matching packets.
  
  \param  adapterContext_p    Pointer to the adapter structure.
  \param  cancelId_p          ID of NetBufferLists to be canceled.
  
  */
  //------------------------------------------------------------------------------
  VOID miniportCancelSendNetBufferLists(NDIS_HANDLE adapterContext_p, PVOID cancelId_p)
  {
      // We don't pend any sends so this is not required.
      UNREFERENCED_PARAMETER(adapterContext_p);
      UNREFERENCED_PARAMETER(cancelId_p);
  }
  
  //------------------------------------------------------------------------------
  /**
  \brief Miniport cancel OID request handler
  
  The miniport entry point to handle cancellation of an OID request.
  
  \param  adapterContext_p    Pointer to the adapter structure.
  \param  requestId_p         RequestId to be canceled.
  
  */
  //------------------------------------------------------------------------------
  VOID miniportCancelOidRequest(NDIS_HANDLE adapterContext_p, PVOID requestId_p)
  {
      UNREFERENCED_PARAMETER(adapterContext_p);
      UNREFERENCED_PARAMETER(requestId_p);
  }
  
  //------------------------------------------------------------------------------
  /**
  \brief Allocate VEth receive buffers
  
  This routine allocates all the resources required for VETH interface to handle
  packet receives. The receive buffers are prepared and stored in a link list
  which is accessed from the receive handler.
  
  \param  pVEthInstance_p     Pointer to the VEth instance structure.
  
  */
  //------------------------------------------------------------------------------
  NDIS_STATUS miniportAllocateVEthRxBuff(tVEthInstance* pVEthInstance_p)
  {
      UINT                               index;
      NDIS_STATUS                        status = NDIS_STATUS_SUCCESS;
      NET_BUFFER_LIST_POOL_PARAMETERS    poolParameters;
  
      NdisInitializeListHead(&pVEthInstance_p->rxList);
      NdisAllocateSpinLock(&pVEthInstance_p->rxListLock);
  
      NdisZeroMemory(&poolParameters, sizeof(NET_BUFFER_LIST_POOL_PARAMETERS));
  
      poolParameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
      poolParameters.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
      poolParameters.Header.Size = sizeof(poolParameters);
      poolParameters.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT;       // Sequenced packet exchanger
      poolParameters.ContextSize = 0;
      poolParameters.fAllocateNetBuffer = TRUE;
      poolParameters.PoolTag = OPLK_MEM_TAG;
  
      pVEthInstance_p->hReceiveNblPool = NdisAllocateNetBufferListPool(driverInstance_g.hMiniportHandle,
                                                                      &poolParameters);
      if (pVEthInstance_p->hReceiveNblPool == NULL)
      {
          TRACE("%s(): failed to alloc send net buffer list pool\n", __FUNCTION__);
          status = NDIS_STATUS_RESOURCES;
          goto Exit;
      }
  
      // Allocate Tx memory
      pVEthInstance_p->pReceiveBuf = NdisAllocateMemoryWithTagPriority(driverInstance_g.hProtocolHandle,
                                                                       (OPLK_MAX_FRAME_SIZE * OPLK_MAX_VETH_BUFF),
                                                                       OPLK_MEM_TAG, NormalPoolPriority);
      if (pVEthInstance_p->pReceiveBuf == NULL)
      {
          TRACE("%s() Failed to allocate VETH Rx buffers\n", __FUNCTION__);
          status = NDIS_STATUS_RESOURCES;
          goto Exit;
      }
  
      pVEthInstance_p->pReceiveBufInfo = NdisAllocateMemoryWithTagPriority(driverInstance_g.hProtocolHandle,
                                                                           (sizeof(tVEthRcvBufInfo) * OPLK_MAX_VETH_BUFF),
                                                                           OPLK_MEM_TAG, NormalPoolPriority);
      if (pVEthInstance_p->pReceiveBufInfo == NULL)
      {
          TRACE("%s() Failed to allocate VETH Rx buffers info\n", __FUNCTION__);
          status = NDIS_STATUS_RESOURCES;
          goto Exit;
      }
  
      // Initialize the queue
      for (index = 0; index < OPLK_MAX_VETH_BUFF; index++)
      {
          tVEthRcvBufInfo*   pVethRxInfo = &pVEthInstance_p->pReceiveBufInfo[index];
  
          if (pVethRxInfo != NULL)
          {
              pVethRxInfo->fFree = TRUE;
              pVethRxInfo->maxLength = OPLK_MAX_FRAME_SIZE;
              pVethRxInfo->pData = (void*)(((UCHAR*)pVEthInstance_p->pReceiveBuf) +
                                           (OPLK_MAX_FRAME_SIZE * index));
  
              // Allocate MDL to define the buffers
              pVethRxInfo->pMdl = NdisAllocateMdl(pVEthInstance_p->hMiniportAdapterHandle,
                                                  pVethRxInfo->pData, OPLK_MAX_FRAME_SIZE);
  
              if (pVethRxInfo->pMdl == NULL)
              {
                  TRACE("%s() Error Allocating MDL\n", __FUNCTION__);
                  status = NDIS_STATUS_RESOURCES;
                  goto Exit;
              }
  
              // Allocate empty NetBufferLists
              pVethRxInfo->pNbl = NdisAllocateNetBufferAndNetBufferList(pVEthInstance_p->hReceiveNblPool,
                                                                        0, 0, pVethRxInfo->pMdl, 0, 0);
  
              if (pVethRxInfo->pNbl == NULL)
              {
                  TRACE("%s() Failed to allocate Tx NBL\n", __FUNCTION__);
                  status = NDIS_STATUS_RESOURCES;
                  goto Exit;
              }
  
              // Mark the NetBufferList as allocated by this protocol driver
              NBL_SET_PROT_RSVD_FLAG(pVethRxInfo->pNbl, OPLK_ALLOCATED_NBL);
              pVethRxInfo->pNbl->SourceHandle = pVEthInstance_p->hMiniportAdapterHandle;
              VETHINFO_FROM_NBL(pVethRxInfo->pNbl) = pVethRxInfo;
              NdisInterlockedInsertTailList(&pVEthInstance_p->rxList, &pVethRxInfo->rxLink,
                                            &pVEthInstance_p->rxListLock);
          }
      }
  
  Exit:
      if (status != NDIS_STATUS_SUCCESS)
      {
          miniportFreeVethRxBuff(pVEthInstance_p);
      }
  
      return status;
  }
  
  //------------------------------------------------------------------------------
  /**
  \brief Free VEth receive buffers
  
  This routine frees all the receive resources allocated during initialization.
  
  \param  pVEthInstance_p     Pointer to the VEth instance structure.
  
  */
  //------------------------------------------------------------------------------
  void miniportFreeVethRxBuff(tVEthInstance* pVEthInstance_p)
  {
      PLIST_ENTRY         pRxLink;
      tVEthRcvBufInfo*    pRxBufInfo;
  
      if (pVEthInstance_p == NULL)
          return;
  
      if (pVEthInstance_p->rxList.Flink != NULL)
      {
          while (!IsListEmpty(&pVEthInstance_p->rxList))
          {
              pRxLink = NdisInterlockedRemoveHeadList(&pVEthInstance_p->rxList,
                                                      &pVEthInstance_p->rxListLock);
              pRxBufInfo = CONTAINING_RECORD(pRxLink, tVEthRcvBufInfo, rxLink);
              if (pRxBufInfo->pNbl != NULL)
              {
                  NdisFreeNetBufferList(pRxBufInfo->pNbl);
                  pRxBufInfo->pNbl = NULL;
              }
  
              if (pRxBufInfo->pMdl != NULL)
              {
                  NdisFreeMdl(pRxBufInfo->pMdl);
                  pRxBufInfo->pMdl = NULL;
              }
          }
      }
  
      if (pVEthInstance_p->hReceiveNblPool != NULL)
      {
          NdisFreeNetBufferListPool(pVEthInstance_p->hReceiveNblPool);
          pVEthInstance_p->hReceiveNblPool = NULL;
      }
  
      if (pVEthInstance_p->pReceiveBufInfo != NULL)
      {
          NdisFreeMemory(pVEthInstance_p->pReceiveBufInfo, 0, 0);
          pVEthInstance_p->pReceiveBufInfo = NULL;
      }
  
      if (pVEthInstance_p->pReceiveBuf != NULL)
      {
          NdisFreeMemory(pVEthInstance_p->pReceiveBuf, 0, 0);
          pVEthInstance_p->pReceiveBuf = NULL;
      }
  
      NdisFreeSpinLock(&pVEthInstance_p->rxListLock);
  }
  
  /// \}
  