| /* |
| * Copyright (c) 2016, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. 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. |
| * 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. |
| */ |
| |
| /** |
| * @file |
| * This module implements code to manage the WDFDEVICE object for the |
| * network adapter. |
| */ |
| |
| #include <initguid.h> |
| #include "pch.hpp" |
| #include "device.tmh" |
| |
| PAGED |
| _IRQL_requires_( PASSIVE_LEVEL ) |
| _Function_class_( MINIPORT_INITIALIZE ) |
| NDIS_STATUS |
| MPInitializeEx( |
| _In_ NDIS_HANDLE MiniportAdapterHandle, |
| _In_ NDIS_HANDLE MiniportDriverContext, |
| _In_ PNDIS_MINIPORT_INIT_PARAMETERS /* MiniportInitParameters */ |
| ) |
| /*++ |
| Routine Description: |
| |
| The MiniportInitialize function is a required function that sets up a |
| NIC (or virtual NIC) for network I/O operations, claims all hardware |
| resources necessary to the NIC in the registry, and allocates resources |
| the driver needs to carry out network I/O operations. |
| |
| MiniportInitialize runs at IRQL = PASSIVE_LEVEL. |
| |
| Arguments: |
| |
| Return Value: |
| |
| NDIS_STATUS_xxx code |
| |
| --*/ |
| { |
| NDIS_STATUS Status = NDIS_STATUS_FAILURE; |
| PGLOBALS pGlobals = (PGLOBALS)MiniportDriverContext; |
| PDEVICE_OBJECT pPdo = nullptr; |
| PDEVICE_OBJECT pFdo = nullptr; |
| PDEVICE_OBJECT pNextDeviceObject = nullptr; |
| WDF_OBJECT_ATTRIBUTES attributes; |
| WDFDEVICE device = nullptr; |
| POTTMP_DEVICE_CONTEXT deviceContext = nullptr; |
| |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| PAGED_CODE(); |
| |
| do |
| { |
| NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES AdapterRegistration = { 0 }; |
| NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES AdapterGeneral = { 0 }; |
| NDIS_PM_CAPABILITIES PmCapabilities = { 0 }; |
| |
| // |
| // NdisMGetDeviceProperty function enables us to get the: |
| // PDO - created by the bus driver to represent our device. |
| // FDO - created by NDIS to represent our miniport as a function |
| // driver. |
| // NextDeviceObject - deviceobject of another driver (filter) |
| // attached to us at the bottom. |
| // Since our driver is talking to NDISPROT, the NextDeviceObject |
| // is not useful. But if we were to talk to a driver that we |
| // are attached to as part of the devicestack then NextDeviceObject |
| // would be our target DeviceObject for sending read/write Requests. |
| // |
| |
| NdisMGetDeviceProperty( |
| MiniportAdapterHandle, |
| &pPdo, |
| &pFdo, |
| &pNextDeviceObject, |
| nullptr, |
| nullptr); |
| |
| WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, OTTMP_DEVICE_CONTEXT); |
| |
| NTSTATUS ntStatus = WdfDeviceMiniportCreate( |
| pGlobals->WdfDriver, |
| &attributes, |
| pFdo, |
| pNextDeviceObject, |
| pPdo, |
| &device); |
| |
| if (!NT_SUCCESS(ntStatus)) |
| { |
| LogError(DRIVER_DEFAULT, "WdfDeviceMiniportCreate failed %!STATUS!", ntStatus); |
| Status = NDIS_STATUS_FAILURE; |
| break; |
| } |
| |
| // |
| // Get WDF miniport device context. |
| // |
| deviceContext = GetDeviceContext(device); |
| deviceContext->Signature = OTTMP_DEVICE_CONTEXT_SIGNATURE; |
| deviceContext->Device = device; |
| |
| // |
| // Allocate adapter context structure and initialize all the |
| // memory resources for sending and receiving packets. |
| // |
| deviceContext->AdapterContext = (POTTMP_ADAPTER_CONTEXT)NdisAllocateMemoryWithTagPriority( |
| pGlobals->hDriver, |
| sizeof(OTTMP_ADAPTER_CONTEXT), |
| OTTMP_ADAPTER_CONTEXT_SIGNATURE, |
| NormalPoolPriority); |
| |
| if (deviceContext->AdapterContext == nullptr) |
| { |
| LogError(DRIVER_DEFAULT, "NdisAllocateMemoryWithTagPriority failed"); |
| Status = NDIS_STATUS_RESOURCES; |
| break; |
| } |
| |
| RtlZeroMemory(deviceContext->AdapterContext, sizeof(OTTMP_ADAPTER_CONTEXT)); |
| deviceContext->AdapterContext->Signature = OTTMP_ADAPTER_CONTEXT_SIGNATURE; |
| deviceContext->AdapterContext->Adapter = MiniportAdapterHandle; |
| deviceContext->AdapterContext->Device = deviceContext->Device; |
| deviceContext->AdapterContext->pGlobals = pGlobals; |
| |
| Status = AdapterInitialize( |
| MiniportAdapterHandle, |
| deviceContext->AdapterContext); |
| |
| if (NDIS_STATUS_SUCCESS != Status) |
| { |
| LogError(DRIVER_DEFAULT, "AdapterInitialize failed %!NDIS_STATUS!", Status); |
| break; |
| } |
| |
| ntStatus = SerialInitialize(deviceContext->AdapterContext); |
| if (!NT_SUCCESS(ntStatus)) |
| { |
| Status = NDIS_STATUS_FAILURE; |
| break; |
| } |
| |
| // Start the read loop |
| LogVerbose(DRIVER_DEFAULT, "Starting recv worker"); |
| WdfWorkItemEnqueue(deviceContext->AdapterContext->RecvWorkItem); |
| |
| } while (FALSE); |
| |
| if (Status != NDIS_STATUS_SUCCESS) |
| { |
| if (deviceContext && deviceContext->AdapterContext) |
| { |
| AdapterUninitialize(deviceContext->AdapterContext); |
| deviceContext->AdapterContext = nullptr; |
| } |
| } |
| |
| LogFuncExitNDIS(DRIVER_DEFAULT, Status); |
| |
| return Status; |
| } |
| |
| PAGED |
| VOID |
| MPHaltEx( |
| _In_ NDIS_HANDLE MiniportAdapterContext, |
| _In_ NDIS_HALT_ACTION HaltAction |
| ) |
| /*++ |
| |
| Routine Description: |
| |
| Halt handler is called when NDIS receives IRP_MN_STOP_DEVICE, |
| IRP_MN_SUPRISE_REMOVE or IRP_MN_REMOVE_DEVICE requests from the PNP |
| manager. Here, the driver should free all the resources acquired in |
| MiniportInitialize and stop access to the hardware. NDIS will not submit |
| any further request once this handler is invoked. |
| |
| 1) Free and unmap all I/O resources. |
| 2) Disable interrupt and deregister interrupt handler. |
| 3) Deregister shutdown handler regsitered by |
| NdisMRegisterAdapterShutdownHandler . |
| 4) Cancel all queued up timer callbacks. |
| 5) Finally wait indefinitely for all the outstanding receive |
| packets indicated to the protocol to return. |
| |
| MiniportHalt runs at IRQL = PASSIVE_LEVEL. |
| |
| |
| Arguments: |
| |
| MiniportAdapterContext Pointer to the Adapter |
| HaltAction The reason for halting the adapter |
| |
| Return Value: |
| |
| None. |
| |
| --*/ |
| { |
| POTTMP_ADAPTER_CONTEXT AdapterContext = (POTTMP_ADAPTER_CONTEXT)MiniportAdapterContext; |
| |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| PAGED_CODE(); |
| |
| // |
| // Call Shutdown handler to disable interrupt and turn the hardware off |
| // by issuing a full reset |
| // |
| if (HaltAction != NdisHaltDeviceSurpriseRemoved) |
| { |
| MPShutdownEx(MiniportAdapterContext, NdisShutdownPowerOff); |
| } |
| |
| AdapterUninitialize(AdapterContext); |
| |
| LogFuncExit(DRIVER_DEFAULT); |
| } |
| |
| VOID |
| MPShutdownEx( |
| _In_ NDIS_HANDLE /* MiniportAdapterContext */, |
| _In_ NDIS_SHUTDOWN_ACTION /* ShutdownAction */ |
| ) |
| /*++ |
| |
| Routine Description: |
| |
| The MiniportShutdownEx handler restores hardware to its initial state when |
| the system is shut down, whether by the user or because an unrecoverable |
| system error occurred. This is to ensure that the NIC is in a known |
| state and ready to be reinitialized when the machine is rebooted after |
| a system shutdown occurs for any reason, including a crash dump. |
| |
| Here just disable the interrupt and stop the DMA engine. Do not free |
| memory resources or wait for any packet transfers to complete. Do not call |
| into NDIS at this time. |
| |
| This can be called at aribitrary IRQL, including in the context of a |
| bugcheck. |
| |
| Arguments: |
| |
| MiniportAdapterContext Pointer to our adapter |
| ShutdownAction The reason why NDIS called the shutdown function |
| |
| Return Value: |
| |
| None. |
| |
| --*/ |
| { |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| LogFuncExit(DRIVER_DEFAULT); |
| } |
| |
| PAGED |
| VOID |
| MPDevicePnpEventNotify( |
| _In_ NDIS_HANDLE /* MiniportAdapterContext */, |
| _In_ PNET_DEVICE_PNP_EVENT NetDevicePnPEvent |
| ) |
| /*++ |
| |
| Routine Description: |
| |
| Runs at IRQL = PASSIVE_LEVEL in the context of system thread. |
| |
| Arguments: |
| |
| MiniportAdapterContext Pointer to our adapter |
| NetDevicePnPEvent Self-explanatory |
| |
| Return Value: |
| |
| None. |
| |
| --*/ |
| { |
| LogFuncEntry(DRIVER_DEFAULT); |
| |
| PAGED_CODE(); |
| |
| switch (NetDevicePnPEvent->DevicePnPEvent) |
| { |
| case NdisDevicePnPEventQueryRemoved: |
| // |
| // Called when NDIS receives IRP_MN_QUERY_REMOVE_DEVICE. |
| // |
| LogInfo(DRIVER_DEFAULT, "MPPnPEventNotify: NdisDevicePnPEventQueryRemoved"); |
| break; |
| |
| case NdisDevicePnPEventRemoved: |
| // |
| // Called when NDIS receives IRP_MN_REMOVE_DEVICE. |
| // NDIS calls MiniportHalt function after this call returns. |
| // |
| LogInfo(DRIVER_DEFAULT, "MPPnPEventNotify: NdisDevicePnPEventRemoved"); |
| break; |
| |
| case NdisDevicePnPEventSurpriseRemoved: |
| // |
| // Called when NDIS receives IRP_MN_SUPRISE_REMOVAL. |
| // NDIS calls MiniportHalt function after this call returns. |
| // |
| LogInfo(DRIVER_DEFAULT, "MPDevicePnpEventNotify: NdisDevicePnPEventSurpriseRemoved"); |
| break; |
| |
| case NdisDevicePnPEventQueryStopped: |
| // |
| // Called when NDIS receives IRP_MN_QUERY_STOP_DEVICE. ?? |
| // |
| LogInfo(DRIVER_DEFAULT, "MPPnPEventNotify: NdisDevicePnPEventQueryStopped"); |
| break; |
| |
| case NdisDevicePnPEventStopped: |
| // |
| // Called when NDIS receives IRP_MN_STOP_DEVICE. |
| // NDIS calls MiniportHalt function after this call returns. |
| // |
| // |
| LogInfo(DRIVER_DEFAULT, "MPPnPEventNotify: NdisDevicePnPEventStopped"); |
| break; |
| |
| case NdisDevicePnPEventPowerProfileChanged: |
| // |
| // After initializing a miniport driver and after miniport driver |
| // receives an OID_PNP_SET_POWER notification that specifies |
| // a device power state of NdisDeviceStateD0 (the powered-on state), |
| // NDIS calls the miniport's MiniportPnPEventNotify function with |
| // PnPEvent set to NdisDevicePnPEventPowerProfileChanged. |
| // |
| LogInfo(DRIVER_DEFAULT, "MPDevicePnpEventNotify: NdisDevicePnPEventPowerProfileChanged"); |
| |
| if (NetDevicePnPEvent->InformationBufferLength == sizeof( ULONG )) |
| { |
| const ULONG NdisPowerProfile = *((const ULONG *)NetDevicePnPEvent->InformationBuffer); |
| |
| if (NdisPowerProfile == NdisPowerProfileBattery) |
| { |
| LogInfo(DRIVER_DEFAULT, "The host system is running on battery power"); |
| } |
| else if (NdisPowerProfile == NdisPowerProfileAcOnLine) |
| { |
| LogInfo(DRIVER_DEFAULT, "The host system is running on AC power"); |
| } |
| } |
| break; |
| |
| default: |
| LogError(DRIVER_DEFAULT, "MPDevicePnpEventNotify: unknown PnP event 0x%x\n", NetDevicePnPEvent->DevicePnPEvent); |
| } |
| |
| LogFuncExit(DRIVER_DEFAULT); |
| } |