////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2001  Microsoft Corporation
//
// Module Name:
//    tdipnp.cpp
//
// Abstract:
//    This module contains the tdi pnp functions called from the tdilib.sys
//
/////////////////////////////////////////////////////////////////////////////


#include "sysvars.h"
extern "C"
{
#pragma warning(disable: NAMELESS_STRUCT_UNION)
#include "tdiinfo.h"
#pragma warning(default: NAMELESS_STRUCT_UNION)
}

////////////////////////////////////////////////////////
// private defines and prototypes
////////////////////////////////////////////////////////


VOID
TSPrintTdiContext(
   PTDI_PNP_CONTEXT  Context
   );


VOID
TSRemoveFromDeviceList(
   PTA_ADDRESS       pTaAddress,
   PCWSTR            pDeviceName,
   ULONG             ulNameLength
   );

VOID
TSAddToDeviceList(
   PTA_ADDRESS    pTaAddress,
   PCWSTR         pDeviceName,
   ULONG          ulNameLength
   );

const PCHAR strFunc1 = "TSPnpBindCallback";
const PCHAR strFunc2 = "TSPnpPowerHandler";
const PCHAR strFunc3 = "TSPnpAddAddressCallback";
const PCHAR strFunc4 = "TSPnpDelAddressCallback";

const PCHAR strFunc5 = "TSGetNumDevices";
const PCHAR strFunc6 = "TSGetDevice";
const PCHAR strFunc7 = "TSGetAddress";

//const PCHAR strFuncP1 = "TSPrintTdiContext";
const PCHAR strFuncP2 = "TSAddToDeviceList";
//const PCHAR strFuncP3 = "TSRemoveFromDeviceList";

///////////////////////////////////////////////////////
// public functions
///////////////////////////////////////////////////////



// ---------------------------------------
//
// Function:   TSPnpBindCallback
//
// Arguments:  TdiPnpOpcode      -- callback type
//             pusDeviceName     -- name of device to deal with
//             pwstrBindingList  -- information from registry Linkage key
//                                  (if appropriate)
//
// Returns:    none
//
// Descript:   This function is called by tdi.sys when tdisample.sys
//             registers its PnpCallbackHandlers.  It is called several
//             times, with the reason for each call in TdiPnpOpcode
//
//             Currently, it just writes the information passed in to the
//             debugger
//
// ---------------------------------------


VOID
TSPnpBindCallback(TDI_PNP_OPCODE    TdiPnpOpcode,
                  PUNICODE_STRING   pusDeviceName,
                  PWSTR             pwstrBindingList)
{
   if (ulDebugLevel & ulDebugShowHandlers)
   {
      if (pusDeviceName) 
      {
         DebugPrint1("DeviceName: %wZ\r\n", pusDeviceName);
      } 
      else 
      {
         DebugPrint0("DeviceName: NULL\n");
      }

      DebugPrint0("OPCODE: ");

      switch (TdiPnpOpcode) 
      {
         case TDI_PNP_OP_MIN:
            DebugPrint0("TDI_PNP_OP_MIN\n");
            break;

         case TDI_PNP_OP_ADD:
            DebugPrint0("TDI_PNP_OP_ADD\n");
            break;

         case TDI_PNP_OP_DEL:
            DebugPrint0("TDI_PNP_OP_DEL\n");
            break;

         case TDI_PNP_OP_UPDATE:
            DebugPrint0("TDI_PNP_OP_UPDATE\n");
            break;
    
         case TDI_PNP_OP_PROVIDERREADY:
            DebugPrint0("TDI_PNP_OP_PROVIDERREADY\n");
            break;
    
         case TDI_PNP_OP_NETREADY:
            DebugPrint0("TDI_PNP_OP_NETREADY\n");
            break;

         default:
            DebugPrint1("INCORRECT OPCODE FROM TDI!! [0x%08x]\n", 
                         TdiPnpOpcode);
            DbgBreakPoint();
            break;

      }

      //
      // this is the information from the registry under
      // HKLM/SYSTEM/CurrentControlSet/Services/clientname/Linkage/Bind
      //
      if( pwstrBindingList == NULL ) 
      {
         DebugPrint0("Bindinglist is NULL\n");
      } 
      else 
      {
         ULONG_PTR ulStrLen;

         DebugPrint0("BindingList:\n");
         while (*pwstrBindingList)
         {
            DebugPrint1("%ws\n", pwstrBindingList);
            ulStrLen = 1 + wcslen(pwstrBindingList);
            pwstrBindingList += ulStrLen;
         }
         DebugPrint0("\n");
      }
   }
}


// --------------------------------------
//
// Function:   TSPnpPowerHandler
//
// Arguments:  pusDeviceName  -- device name to deal with
//             pNetPnpEvent   -- power event to deal with
//             pTdiPnpContext1
//             pTdiPnpContext2
//
// Returns:    status of operation
//
// Descript:   This function deals with pnp and power management issues
//
//             Currently, it just outputs information to the debugger
//
// --------------------------------------


NTSTATUS
TSPnpPowerHandler(PUNICODE_STRING   pusDeviceName,
                  PNET_PNP_EVENT    pNetPnpEvent,
                  PTDI_PNP_CONTEXT  pTdiPnpContext1,
                  PTDI_PNP_CONTEXT  pTdiPnpContext2)

{
   if (ulDebugLevel & ulDebugShowHandlers)
   {
      if (pusDeviceName) 
      {
         DebugPrint1("DeviceName: %wZ\r\n", pusDeviceName);
      } 
      else 
      {
         DebugPrint0("DeviceName: NULL\n");
      }

      switch (pNetPnpEvent->NetEvent)
      {
         case NetEventSetPower:
         case NetEventQueryPower:
         {
            if (pNetPnpEvent->NetEvent == NetEventSetPower)
            {
               DebugPrint1("%s:  NetEventSetPower--", strFunc2);
            }
            else
            {
               DebugPrint1("%s:  NetEventQueryPower -- ", strFunc2);
            }
            NET_DEVICE_POWER_STATE  NetDevicePowerState
                                    = *(PNET_DEVICE_POWER_STATE)pNetPnpEvent->Buffer;

            switch (NetDevicePowerState)
            {
               case  NetDeviceStateUnspecified:
                  DebugPrint0("PowerStateUnspecified\n");
                  break;
               case NetDeviceStateD0:
                  DebugPrint0("PowerUp\n");
                  break;
               case NetDeviceStateD1:
               case NetDeviceStateD2:
               case NetDeviceStateD3:
                  DebugPrint0("PowerDown\n");
                  break;
            }
            break;
         }

         case NetEventQueryRemoveDevice:
            DebugPrint1("%s:  NetEventQueryRemoveDevice\n", strFunc2);
            break;
         case NetEventCancelRemoveDevice:
            DebugPrint1("%s:  NetEventCancelRemoveDevice\n", strFunc2);
            break;
         case NetEventReconfigure:
            DebugPrint1("%s:  NetEventReconfigure\n", strFunc2);
            break;
         case NetEventBindList:
            DebugPrint1("%s:  NetEventBindList\n", strFunc2);
            break;
         case NetEventBindsComplete:
            DebugPrint1("%s:  NetEventBindsComplete\n", strFunc2);
            break;
         case NetEventPnPCapabilities:
            DebugPrint1("%s:  NetEventPnPCapabilities\n", strFunc2);
            break;
      
      }

      if (pTdiPnpContext1)
      {
         DebugPrint0("TdiPnpContext1:\n");
         TSPrintTdiContext(pTdiPnpContext1);
      }
      if (pTdiPnpContext2)
      {
         DebugPrint0("TdiPnpContext2:\n");
         TSPrintTdiContext(pTdiPnpContext2);
      }
   }
   return STATUS_SUCCESS;
}

// -----------------------------------------------
//
// Function:   TSPnpAddAddressCallback
//
// Arguments:  pTaAddress  -- address to register
//             pusDeviceName -- device name associated with address
//             pTdiPnpContext
//
// Returns:    none
//
// Descript:   called by tdi.sys.  When called, tdisample adds this device
//             to its registered list, if it recognizes the address format
//
// -----------------------------------------------

VOID
TSPnpAddAddressCallback(PTA_ADDRESS       pTaAddress, 
                        PUNICODE_STRING   pusDeviceName,
                        PTDI_PNP_CONTEXT  pTdiPnpContext)
{
   if (ulDebugLevel & ulDebugShowHandlers)
   {
      //
      // write info to debugger
      //
      DebugPrint1("DeviceName: %wZ\r\n", pusDeviceName);
      TSPrintTaAddress(pTaAddress);
      if (pTdiPnpContext)
      {
         DebugPrint0("TdiPnpContext:\n");
         TSPrintTdiContext(pTdiPnpContext);
      }
   }

   //
   // add this to our list of devices/addresses, if appropriate
   //
   TSAddToDeviceList(pTaAddress, 
                     pusDeviceName->Buffer,
                     pusDeviceName->Length);
}


// -----------------------------------------------
//
// Function:   TSDelAddAddressCallback
//
// Arguments:  pTaAddress  -- address to de-register
//             pusDeviceName -- device name associated with address
//             pTdiPnpContext
//
// Returns:    none
//
// Descript:   called by tdi.sys.  When called, tdisample removes this device
//             to its registered list, if it recognizes the address format
//
// -----------------------------------------------

VOID
TSPnpDelAddressCallback(PTA_ADDRESS       pTaAddress, 
                        PUNICODE_STRING   pusDeviceName,
                        PTDI_PNP_CONTEXT  pTdiPnpContext)
{
   if (ulDebugLevel & ulDebugShowHandlers)
   {
      DebugPrint1("DeviceName: %wZ\r\n", pusDeviceName);
      TSPrintTaAddress(pTaAddress);
      if (pTdiPnpContext)
      {
         DebugPrint0("TdiPnpContext:\n");
         TSPrintTdiContext(pTdiPnpContext);
      }
   }

   //
   // remove this from our list of devices/addresses, if appropriate
   //
   TSRemoveFromDeviceList(pTaAddress, 
                          pusDeviceName->Buffer,
                          pusDeviceName->Length);
}


// ----------------------------------------
//
// Function:   TSGetNumDevices
//
// Arguments:  pSendBuffer
//             pReceiveBuffer
//
// Returns:    none
//
// Descript:   Finds the number of devices in tdidevicelist,
//             and returns that value..
//
// ----------------------------------------


VOID
TSGetNumDevices(PSEND_BUFFER     pSendBuffer,
                PRECEIVE_BUFFER  pReceiveBuffer)
{
   ULONG    ulSlot        = 0;
   ULONG    ulAddressType = pSendBuffer->COMMAND_ARGS.GetDevArgs.ulAddressType;
  

   if (ulDebugLevel & ulDebugShowCommand)
   {
      DebugPrint1("\nCommand = ulGETNUMDEVICES\n"
                  "AddressType = 0x%08x\n",
                   ulAddressType);
   }

   TSAcquireSpinLock(&pTdiDevnodeList->TdiSpinLock);
   for (ULONG ulCount = 0; ulCount < ulMAX_DEVICE_NODES; ulCount++)
   {
      PTDI_DEVICE_NODE  pTdiDeviceNode = &(pTdiDevnodeList->TdiDeviceNode[ulCount]);
      
      if ((pTdiDeviceNode->ulState != ulDEVSTATE_UNUSED) &&
          (pTdiDeviceNode->pTaAddress->AddressType == (USHORT)ulAddressType))
      {
         ++ulSlot;
      }
   }
   TSReleaseSpinLock(&pTdiDevnodeList->TdiSpinLock);

   pReceiveBuffer->RESULTS.ulReturnValue = ulSlot;
}


// ----------------------------------------
//
// Function:   TSGetDevice
//
// Arguments:  pSendBuffer    -- arguments
//             pReceiveBuffer -- where to put result
//
// Returns:    NTSTATUS (success if finds slot, else false)
//
// Descript:   Finds the device name indicated, and returns
//             the string for that value
//
// ----------------------------------------


NTSTATUS
TSGetDevice(PSEND_BUFFER      pSendBuffer,
            PRECEIVE_BUFFER   pReceiveBuffer)
{
   ULONG    ulSlot        = 0;
   ULONG    ulAddressType = pSendBuffer->COMMAND_ARGS.GetDevArgs.ulAddressType;
   ULONG    ulSlotNum     = pSendBuffer->COMMAND_ARGS.GetDevArgs.ulSlotNum;


   if (ulDebugLevel & ulDebugShowCommand)
   {
      DebugPrint2("\nCommand = ulGETDEVICE\n"
                  "AddressType = 0x%08x\n"
                  "SlotNum = %d\n",
                   ulAddressType,
                   ulSlotNum);
   }

   TSAcquireSpinLock(&pTdiDevnodeList->TdiSpinLock);
   for (ULONG ulCount = 0; ulCount < ulMAX_DEVICE_NODES; ulCount++)
   {
      PTDI_DEVICE_NODE  pTdiDeviceNode = &(pTdiDevnodeList->TdiDeviceNode[ulCount]);

      if ((pTdiDeviceNode->ulState != ulDEVSTATE_UNUSED) &&
          (pTdiDeviceNode->pTaAddress->AddressType == (USHORT)ulAddressType))
      {
         if (ulSlot == ulSlotNum)
         {
            if (pTdiDeviceNode->ustrDeviceName.MaximumLength > (ulMAX_CNTSTRING_LENGTH * sizeof(WCHAR)))
            {
               DebugPrint0("string length problem!\n");
               DbgBreakPoint();
            }

            RtlZeroMemory(&pReceiveBuffer->RESULTS.ucsStringReturn.wcBuffer,
                          ulMAX_CNTSTRING_LENGTH * sizeof(WCHAR));

            pReceiveBuffer->RESULTS.ucsStringReturn.usLength
                            = pTdiDeviceNode->ustrDeviceName.Length;
            RtlCopyMemory(pReceiveBuffer->RESULTS.ucsStringReturn.wcBuffer,
                          pTdiDeviceNode->ustrDeviceName.Buffer,
                          pTdiDeviceNode->ustrDeviceName.Length);

            TSReleaseSpinLock(&pTdiDevnodeList->TdiSpinLock);
            if (pTdiDeviceNode->ulState == ulDEVSTATE_INUSE)
            {
               return STATUS_SUCCESS;
            }
            else
            {
               return STATUS_UNSUCCESSFUL;
            }
         }
         ++ulSlot;
      }
   }
   TSReleaseSpinLock(&pTdiDevnodeList->TdiSpinLock);

   return STATUS_UNSUCCESSFUL;
}

// ----------------------------------------
//
// Function:   TSGetAddress
//
// Arguments:  pSendBuffer    -- arguments
//             pReceiveBuffer -- where to put result
//
// Returns:    NTSTATUS (success if finds slot, else false)
//
// Descript:   Finds the device name indicated, and returns
//             the string for that value
//
// ----------------------------------------


NTSTATUS
TSGetAddress(PSEND_BUFFER     pSendBuffer,
             PRECEIVE_BUFFER  pReceiveBuffer)
{
   ULONG    ulSlot        = 0;
   ULONG    ulAddressType = pSendBuffer->COMMAND_ARGS.GetDevArgs.ulAddressType;
   ULONG    ulSlotNum     = pSendBuffer->COMMAND_ARGS.GetDevArgs.ulSlotNum;

   if (ulDebugLevel & ulDebugShowCommand)
   {
      DebugPrint2("\nCommand = ulGETADDRESS\n"
                  "AddressType = 0x%08x\n"
                  "SlotNum = %d\n",
                   ulAddressType,
                   ulSlotNum);
   }

   TSAcquireSpinLock(&pTdiDevnodeList->TdiSpinLock);
   for (ULONG ulCount = 0; ulCount < ulMAX_DEVICE_NODES; ulCount++)
   {
      PTDI_DEVICE_NODE  pTdiDeviceNode = &(pTdiDevnodeList->TdiDeviceNode[ulCount]);

      if ((pTdiDeviceNode->ulState != ulDEVSTATE_UNUSED) &&
          (pTdiDeviceNode->pTaAddress->AddressType == (USHORT)ulAddressType))
      {
         if (ulSlot == ulSlotNum)
         {
            ULONG ulLength = FIELD_OFFSET(TA_ADDRESS, Address)
                           + pTdiDeviceNode->pTaAddress->AddressLength;

            pReceiveBuffer->RESULTS.TransAddr.TAAddressCount = 1;
            RtlCopyMemory(&pReceiveBuffer->RESULTS.TransAddr.TaAddress,
                          pTdiDeviceNode->pTaAddress,
                          ulLength);

            TSReleaseSpinLock(&pTdiDevnodeList->TdiSpinLock);
            if (pTdiDeviceNode->ulState == ulDEVSTATE_INUSE)
            {
               return STATUS_SUCCESS;
            }
            else
            {
               return STATUS_UNSUCCESSFUL;
            }
         }
         ++ulSlot;
      }
   }
   TSReleaseSpinLock(&pTdiDevnodeList->TdiSpinLock);

   return STATUS_UNSUCCESSFUL;
}


//////////////////////////////////////////////////////
// private functions
//////////////////////////////////////////////////////


// ---------------------------------
//
// Function:   TSPrintTdiContext
//
// Arguments:  pTdiPnpContext -- context to dump
//
// Returns:    none
//
// Descript:   prints out information in pTdiPnpContext structure
//
// ---------------------------------

VOID
TSPrintTdiContext(PTDI_PNP_CONTEXT  pTdiPnpContext)
{
   if (pTdiPnpContext)
   {
      PUCHAR   pucTemp = pTdiPnpContext->ContextData;

      DebugPrint2("TdiPnpContextSize:  %u\n"
                  "TdiPnpContextType:  %u\n"
                  "TdiPnpContextData:  ",
                   pTdiPnpContext->ContextSize,
                   pTdiPnpContext->ContextType);
      
      for (ULONG ulCount = 0; ulCount < pTdiPnpContext->ContextSize; ulCount++)
      {
         DebugPrint1("%02x ", *pucTemp);
         ++pucTemp;
      }
      DebugPrint0("\n");
   }
}

// ------------------------------------------
//
// Function:   TSAddToDeviceList
//
// Arguments:  pTaAddress    -- current address structure
//             pusDeviceName -- actual name of device
//
// Returns:    none
//
// Descript:   Adds this device to our device list, if appropriate
//
// ------------------------------------------

VOID
TSAddToDeviceList(PTA_ADDRESS pTaAddress,
                  PCWSTR      pDeviceName,
                  ULONG       ulNameLength)
{
   //
   // scan list for first available slot.  For any slot before the first
   // available whose entry has been deleted, check to see if this is the
   // same device coming back
   //
   ULONG    ulLengthNeeded = FIELD_OFFSET(TA_ADDRESS, Address) 
                             + pTaAddress->AddressLength;
   ULONG    ulAddressType  = pTaAddress->AddressType;


   TSAcquireSpinLock(&pTdiDevnodeList->TdiSpinLock);
   for (ULONG ulCount = 0; ulCount < ulMAX_DEVICE_NODES; ulCount++)
   {
      PTDI_DEVICE_NODE  pTdiDeviceNode = &(pTdiDevnodeList->TdiDeviceNode[ulCount]);

      switch (pTdiDeviceNode->ulState)
      {
         //
         // this is first unused slot
         // allocate buffers and set
         //
         case ulDEVSTATE_UNUSED:
            if ((TSAllocateMemory((PVOID *)&pTdiDeviceNode->pTaAddress,
                                   ulLengthNeeded,
                                   strFuncP2,
                                   "TaAddress")) == STATUS_SUCCESS)
            {
               if ((TSAllocateMemory((PVOID *)&pTdiDeviceNode->ustrDeviceName.Buffer,
                                      ulNameLength+2,
                                      strFuncP2,
                                      "Buffer")) == STATUS_SUCCESS)
               {
                  RtlCopyMemory(pTdiDeviceNode->pTaAddress,
                                pTaAddress,
                                ulLengthNeeded);
                  
                  pTdiDeviceNode->ustrDeviceName.MaximumLength = (USHORT)(ulNameLength + 2);
                  pTdiDeviceNode->ustrDeviceName.Length        = (USHORT)ulNameLength;
                  RtlCopyMemory(pTdiDeviceNode->ustrDeviceName.Buffer,
                                pDeviceName,
                                ulNameLength);
                  pTdiDeviceNode->ulState = ulDEVSTATE_INUSE;
               }
               else
               {
                  TSFreeMemory(pTdiDeviceNode->pTaAddress);
               }
            }
            TSReleaseSpinLock(&pTdiDevnodeList->TdiSpinLock);
            return;

         //
         // device in slot has been removed.  See if this is the same
         // device coming back
         //
         case ulDEVSTATE_DELETED:
         {
            //
            // check for correct name
            //
            ULONG_PTR   ulCompareLength = RtlCompareMemory(pTdiDeviceNode->ustrDeviceName.Buffer,
                                                           pDeviceName,
                                                           ulNameLength);
            if (ulCompareLength == ulNameLength)
            {
               //
               // for tcpip, netbios, and appletalk this is enough
               // for ipx/spx, need to check address as well
               //
               if (ulAddressType == TDI_ADDRESS_TYPE_IPX)
               {
                  ulCompareLength = RtlCompareMemory(pTdiDeviceNode->pTaAddress,
                                                     pTaAddress,
                                                     pTaAddress->AddressLength + sizeof(ULONG));
                  
                  //
                  // if address is incorrect, not right ipx
                  //
                  if (ulCompareLength != pTaAddress->AddressLength + sizeof(ULONG))
                  {
                     break;
                  }
               }
               else
               {
                  //
                  // copy address info over in case it changed..
                  //
                  RtlCopyMemory(pTdiDeviceNode->pTaAddress,
                                pTaAddress,
                                ulLengthNeeded);
               }

               pTdiDeviceNode->ulState = ulDEVSTATE_INUSE;
               TSReleaseSpinLock(&pTdiDevnodeList->TdiSpinLock);
               return;
            }
         }
         break;

         //
         // device in slot is in used.  Leave it alone
         //
         case ulDEVSTATE_INUSE:
            break;
      }
   }
   TSReleaseSpinLock(&pTdiDevnodeList->TdiSpinLock);
}



// ------------------------------------------
//
// Function:   TSRemoveFromDeviceList
//
// Arguments:  pTaAddress    -- current address structure
//             pusDeviceName -- actual name of device
//
// Returns:    none
//
// Descript:   Remove this device from our device list, if is it
//             on it..
//
// ------------------------------------------

VOID
TSRemoveFromDeviceList(PTA_ADDRESS  pTaAddress,
                       PCWSTR       pDeviceName,
                       ULONG        ulNameLength)
{
   //
   // search list for the item to remove..
   //
   TSAcquireSpinLock(&pTdiDevnodeList->TdiSpinLock);
   for (ULONG ulCount = 0; ulCount < ulMAX_DEVICE_NODES; ulCount++)
   {
      PTDI_DEVICE_NODE  pTdiDeviceNode = &(pTdiDevnodeList->TdiDeviceNode[ulCount]);

      //
      // check to see that it is the right node...
      // first check to see if the address is correct
      //
      ULONG_PTR   ulCompareLength = RtlCompareMemory(pTdiDeviceNode->pTaAddress,
                                                     pTaAddress,
                                                     pTaAddress->AddressLength + sizeof(ULONG));
      
      //
      // if address is correct, check for correct name
      //
      if (ulCompareLength == pTaAddress->AddressLength + sizeof(ULONG))
      {
         ulCompareLength = RtlCompareMemory(pTdiDeviceNode->ustrDeviceName.Buffer,
                                            pDeviceName,
                                            ulNameLength);

         //
         // if this matches, it's the right node.  Delete it!
         //
         if (ulCompareLength == ulNameLength)
         {
            pTdiDeviceNode->ulState = ulDEVSTATE_DELETED;
            break;
         }
      }
   }
   TSReleaseSpinLock(&pTdiDevnodeList->TdiSpinLock);

}


/////////////////////////////////////////////////////////////////
// end of file tdipnp.cpp
/////////////////////////////////////////////////////////////////