blob: 5836549914d78a02363464ca34f7c740b10de2a7 [file] [log] [blame]
/*
* 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 NETADAPTER object for the
* network adapter.
*/
#include "pch.hpp"
#include "adapter.tmh"
PAGED
_IRQL_requires_max_(PASSIVE_LEVEL)
NDIS_STATUS
AdapterInitialize(
_In_ NDIS_HANDLE MiniportAdapterHandle,
_In_ POTTMP_ADAPTER_CONTEXT AdapterContext
)
/*++
Routine Description:
AdapterInitialize function is called to initialize the Network Adapter
at the time of Pnp Add device.
This routine initializes the context of the adapter object
Arguments:
MiniportAdapterHandle - Handle to the NDIS Miniport Adapter object.
AdapterContext - The context associated with the adapter
Return Value:
NTSTATUS - A failure here will indicate a fatal error in the driver.
--*/
{
NDIS_STATUS Status;
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 };
//
// First, set the registration attributes.
//
AdapterRegistration.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES;
AdapterRegistration.Header.Size = sizeof(AdapterRegistration);
AdapterRegistration.Header.Revision = NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_2;
AdapterRegistration.MiniportAdapterContext = AdapterContext;
AdapterRegistration.AttributeFlags = NDIS_MINIPORT_ATTRIBUTES_SURPRISE_REMOVE_OK | NDIS_MINIPORT_ATTRIBUTES_NDIS_WDM | NDIS_MINIPORT_ATTRIBUTES_NO_PAUSE_ON_SUSPEND;
AdapterRegistration.InterfaceType = NdisInterfacePNPBus;
NDIS_DECLARE_MINIPORT_ADAPTER_CONTEXT(_OTTMP_ADAPTER_CONTEXT);
Status = NdisMSetMiniportAttributes(
MiniportAdapterHandle,
(PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&AdapterRegistration);
if (NDIS_STATUS_SUCCESS != Status)
{
LogError(DRIVER_DEFAULT, "[%p] NdisSetOptionalHandlers Status %!NDIS_STATUS!", AdapterContext, Status);
break;
}
//
// Next, set the general attributes.
//
AdapterGeneral.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES;
AdapterGeneral.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2;
AdapterGeneral.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2;
//
// Specify the medium type that the NIC can support but not
// necessarily the medium type that the NIC currently uses.
//
AdapterGeneral.MediaType = NIC_MEDIUM_TYPE;
//
// Specifiy medium type that the NIC currently uses.
//
AdapterGeneral.PhysicalMediumType = NdisPhysicalMediumNative802_15_4;
//
// We have to lie about the MTU, so that TCPIP will bind to us.
// Specifically, we have to rely that the ThreadLwf will fragment
// the packets appropriately.
//
AdapterGeneral.MtuSize = HW_MAX_FRAME_SIZE;
AdapterGeneral.MaxXmitLinkSpeed = NIC_RECV_XMIT_SPEED;
AdapterGeneral.XmitLinkSpeed = NIC_RECV_XMIT_SPEED;
AdapterGeneral.MaxRcvLinkSpeed = NIC_RECV_XMIT_SPEED;
AdapterGeneral.RcvLinkSpeed = NIC_RECV_XMIT_SPEED;
AdapterGeneral.MediaConnectState = MediaConnectStateConnected;
AdapterGeneral.MediaDuplexState = MediaDuplexStateFull;
//
// The maximum number of bytes the NIC can provide as lookahead data.
// If that value is different from the size of the lookahead buffer
// supported by bound protocols, NDIS will call MiniportOidRequest to
// set the size of the lookahead buffer provided by the miniport driver
// to the minimum of the miniport driver and protocol(s) values. If the
// driver always indicates up full packets with
// NdisMIndicateReceiveNetBufferLists, it should set this value to the
// maximum total frame size, which excludes the header.
//
// Upper-layer drivers examine lookahead data to determine whether a
// packet that is associated with the lookahead data is intended for
// one or more of their clients. If the underlying driver supports
// multipacket receive indications, bound protocols are given full net
// packets on every indication. Consequently, this value is identical
// to that returned for OID_GEN_RECEIVE_BLOCK_SIZE.
//
AdapterGeneral.LookaheadSize = HW_MAX_FRAME_SIZE;
AdapterGeneral.PowerManagementCapabilities = NULL;
AdapterGeneral.MacOptions = NIC_MAC_OPTIONS;
AdapterGeneral.SupportedPacketFilters = NIC_SUPPORTED_FILTERS;
//
// The maximum number of multicast addresses the NIC driver can manage.
// This list is global for all protocols bound to (or above) the NIC.
// Consequently, a protocol can receive NDIS_STATUS_MULTICAST_FULL from
// the NIC driver when attempting to set the multicast address list,
// even if the number of elements in the given list is less than the
// number originally returned for this query.
//
AdapterGeneral.MaxMulticastListSize = NIC_MAX_MCAST_LIST;
AdapterGeneral.MacAddressLength = NIC_MACADDR_SIZE;
//
// Return the MAC address of the NIC burnt in the hardware.
//
memcpy(AdapterGeneral.PermanentMacAddress, &AdapterContext->ExtendedAddress, sizeof(AdapterContext->ExtendedAddress));
memcpy(AdapterGeneral.CurrentMacAddress, &AdapterContext->ExtendedAddress, sizeof(AdapterContext->ExtendedAddress));
AdapterGeneral.RecvScaleCapabilities = NULL;
AdapterGeneral.AccessType = NET_IF_ACCESS_BROADCAST;
AdapterGeneral.DirectionType = NET_IF_DIRECTION_SENDRECEIVE;
AdapterGeneral.ConnectionType = NET_IF_CONNECTION_DEDICATED;
AdapterGeneral.IfType = IF_TYPE_IEEE802154;
AdapterGeneral.IfConnectorPresent = TRUE;
AdapterGeneral.SupportedStatistics = NIC_SUPPORTED_STATISTICS;
AdapterGeneral.SupportedPauseFunctions = NdisPauseFunctionsUnsupported;
AdapterGeneral.DataBackFillSize = 0;
AdapterGeneral.ContextBackFillSize = 0;
//
// The SupportedOidList is an array of OIDs for objects that the
// underlying driver or its NIC supports. Objects include general,
// media-specific, and implementation-specific objects. NDIS forwards a
// subset of the returned list to protocols that make this query. That
// is, NDIS filters any supported statistics OIDs out of the list
// because protocols never make statistics queries.
//
AdapterGeneral.SupportedOidList = NICSupportedOids;
AdapterGeneral.SupportedOidListLength = SizeOfNICSupportedOids;
AdapterGeneral.AutoNegotiationFlags = NDIS_LINK_STATE_DUPLEX_AUTO_NEGOTIATED;
//
// Set the power management capabilities. All 0 basically means we don't
// support Dx for anything.
//
PmCapabilities.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
PmCapabilities.Header.Size = NDIS_SIZEOF_NDIS_PM_CAPABILITIES_REVISION_2;
PmCapabilities.Header.Revision = NDIS_PM_CAPABILITIES_REVISION_2;
AdapterGeneral.PowerManagementCapabilitiesEx = &PmCapabilities;
Status = NdisMSetMiniportAttributes(
MiniportAdapterHandle,
(PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&AdapterGeneral);
if (NDIS_STATUS_SUCCESS != Status)
{
LogError(DRIVER_DEFAULT, "[%p] NdisSetOptionalHandlers failed %!NDIS_STATUS!", AdapterContext, Status);
break;
}
} while (FALSE);
LogFuncExitNDIS(DRIVER_DEFAULT, Status);
return Status;
}
PAGED
_IRQL_requires_(PASSIVE_LEVEL)
VOID
AdapterUninitialize(
_In_ POTTMP_ADAPTER_CONTEXT AdapterContext
)
{
LogFuncEntry(DRIVER_DEFAULT);
PAGED_CODE();
SerialUninitialize(AdapterContext);
if (AdapterContext->Device)
{
WdfObjectDelete(AdapterContext->Device);
AdapterContext->Device = nullptr;
}
NdisFreeMemory(AdapterContext, 0, 0);
LogFuncExit(DRIVER_DEFAULT);
}
PAGED
_IRQL_requires_( PASSIVE_LEVEL )
_Function_class_( MINIPORT_RESTART )
NDIS_STATUS
MPRestart(
_In_ NDIS_HANDLE MiniportAdapterContext,
_In_ PNDIS_MINIPORT_RESTART_PARAMETERS /* RestartParameters */
)
/*++
Routine Description:
When a miniport receives a restart request, it enters into a Restarting
state. The miniport may begin indicating received data (e.g., using
NdisMIndicateReceiveNetBufferLists), handling status indications, and
processing OID requests in the Restarting state. However, no sends will be
requested while the miniport is in the Restarting state.
Once the miniport is ready to send data, it has entered the Running state.
The miniport informs NDIS that it is in the Running state by returning
NDIS_STATUS_SUCCESS from this MiniportRestart function; or if this function
has already returned NDIS_STATUS_PENDING, by calling NdisMRestartComplete.
MiniportRestart runs at IRQL = PASSIVE_LEVEL.
Arguments:
MiniportAdapterContext Pointer to the Adapter
RestartParameters Additional information about the restart operation
Return Value:
If the miniport is able to immediately enter the Running state, it should
return NDIS_STATUS_SUCCESS.
If the miniport is still in the Restarting state, it should return
NDIS_STATUS_PENDING now, and call NdisMRestartComplete when the miniport
has entered the Running state.
Other NDIS_STATUS codes indicate errors. If an error is encountered, the
miniport must return to the Paused state (i.e., stop indicating receives).
--*/
{
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
POTTMP_ADAPTER_CONTEXT AdapterContext = (POTTMP_ADAPTER_CONTEXT)MiniportAdapterContext;
LogFuncEntry(DRIVER_DEFAULT);
PAGED_CODE();
// Set the running flag
AdapterContext->IsRunning = true;
LogFuncExitNDIS(DRIVER_DEFAULT, status);
return status;
}
PAGED
_IRQL_requires_( PASSIVE_LEVEL )
_Function_class_( MINIPORT_PAUSE )
NDIS_STATUS
MPPause(
_In_ NDIS_HANDLE MiniportAdapterContext,
_In_ PNDIS_MINIPORT_PAUSE_PARAMETERS /* MiniportPauseParameters */
)
/*++
Routine Description:
When a miniport receives a pause request, it enters into a Pausing state.
The miniport should not indicate up any more network data. Any pending
send requests must be completed, and new requests must be rejected with
NDIS_STATUS_PAUSED.
Once all sends have been completed and all recieve NBLs have returned to
the miniport, the miniport enters the Paused state.
While paused, the miniport can still service interrupts from the hardware
(to, for example, continue to indicate NDIS_STATUS_MEDIA_CONNECT
notifications).
The miniport must continue to be able to handle status indications and OID
requests. MiniportPause is different from MiniportHalt because, in
general, the MiniportPause operation won't release any resources.
MiniportPause must not attempt to acquire any resources where allocation
can fail, since MiniportPause itself must not fail.
MiniportPause runs at IRQL = PASSIVE_LEVEL.
Arguments:
MiniportAdapterContext Pointer to the Adapter
MiniportPauseParameters Additional information about the pause operation
Return Value:
If the miniport is able to immediately enter the Paused state, it should
return NDIS_STATUS_SUCCESS.
If the miniport must wait for send completions or pending receive NBLs, it
should return NDIS_STATUS_PENDING now, and call NDISMPauseComplete when the
miniport has entered the Paused state.
No other return value is permitted. The pause operation must not fail.
--*/
{
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
POTTMP_ADAPTER_CONTEXT AdapterContext = (POTTMP_ADAPTER_CONTEXT)MiniportAdapterContext;
LogFuncEntry(DRIVER_DEFAULT);
PAGED_CODE();
// Clear the flag to indicate we are no longer running
AdapterContext->IsRunning = false;
LogFuncExitNDIS(DRIVER_DEFAULT, status);
return status;
}
VOID
MPSendNetBufferLists(
_In_ NDIS_HANDLE MiniportAdapterContext,
_In_ PNET_BUFFER_LIST NetBufferLists,
_In_ NDIS_PORT_NUMBER /* PortNumber */,
_In_ ULONG SendFlags
)
/*++
Routine Description:
Send Packet Array handler. Called by NDIS whenever a protocol
bound to our miniport sends one or more packets.
The input packet descriptor pointers have been ordered according
to the order in which the packets should be sent over the network
by the protocol driver that set up the packet array. The NDIS
library preserves the protocol-determined ordering when it submits
each packet array to MiniportSendPackets
As a deserialized driver, we are responsible for holding incoming send
packets in our internal queue until they can be transmitted over the
network and for preserving the protocol-determined ordering of packet
descriptors incoming to its MiniportSendPackets function.
A deserialized miniport driver must complete each incoming send packet
with NdisMSendComplete, and it cannot call NdisMSendResourcesAvailable.
Runs at IRQL <= DISPATCH_LEVEL
Arguments:
MiniportAdapterContext Pointer to our adapter
NetBufferLists Head of a list of NBLs to send
PortNumber A miniport adapter port. Default is 0.
SendFlags Additional flags for the send operation
Return Value:
None. Write status directly into each NBL with the NET_BUFFER_LIST_STATUS
macro.
--*/
{
POTTMP_ADAPTER_CONTEXT AdapterContext = (POTTMP_ADAPTER_CONTEXT)MiniportAdapterContext;
PNET_BUFFER_LIST FailedNbls = NULL;
LogFuncEntryMsg(DRIVER_DEFAULT, "NetBufferList: %p", NetBufferLists);
PNET_BUFFER_LIST CurrNbl = NetBufferLists;
while (CurrNbl)
{
PNET_BUFFER_LIST NextNbl = CurrNbl->Next;
CurrNbl->Next = NULL;
// Only allow one NB per NBL
if (CurrNbl->FirstNetBuffer == NULL ||
CurrNbl->FirstNetBuffer->Next != NULL)
{
CurrNbl->Status = STATUS_INVALID_PARAMETER;
}
else
{
// Try to queue up for send
NTSTATUS status = SerialSendData(AdapterContext, CurrNbl);
if (!NT_SUCCESS(status)) {
CurrNbl->Status = status;
}
else {
CurrNbl = NULL;
}
}
// If we still have the CurrNbl, it failed, so move it to the failure list
if (CurrNbl) {
CurrNbl->Next = FailedNbls;
FailedNbls = CurrNbl;
}
CurrNbl = NextNbl;
}
// Complete any failures
if (FailedNbls) {
NdisMSendNetBufferListsComplete(AdapterContext->Adapter, FailedNbls, (SendFlags & NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL));
}
LogFuncExit(DRIVER_DEFAULT);
}
VOID
MPCancelSend(
_In_ NDIS_HANDLE /* MiniportAdapterContext */,
_In_ PVOID /* CancelId */
)
/*++
Routine Description:
MiniportCancelSend cancels the transmission of all NET_BUFFER_LISTs that
are marked with a specified cancellation identifier. Miniport drivers
that queue send packets for more than one second should export this
handler. When a protocol driver or intermediate driver calls the
NdisCancelSendNetBufferLists function, NDIS calls the MiniportCancelSend
function of the appropriate lower-level driver (miniport driver or
intermediate driver) on the binding.
Runs at IRQL <= DISPATCH_LEVEL.
Arguments:
MiniportAdapterContext Pointer to our adapter
CancelId All the packets with this Id should be cancelled
Return Value:
None.
--*/
{
LogFuncEntry(DRIVER_DEFAULT);
LogFuncExit(DRIVER_DEFAULT);
}
VOID
MPReturnNetBufferLists(
_In_ NDIS_HANDLE /* MiniportAdapterContext */,
_In_ PNET_BUFFER_LIST NetBufferLists,
_In_ ULONG /* ReturnFlags */
)
/*++
Routine Description:
NDIS Miniport entry point called whenever protocols are done with one or
NBLs that we indicated up with NdisMIndicateReceiveNetBufferLists.
Note that the list of NBLs may be chained together from multiple separate
lists that were indicated up individually.
Arguments:
MiniportAdapterContext Pointer to our adapter
NetBufferLists NBLs being returned
ReturnFlags May contain the NDIS_RETURN_FLAGS_DISPATCH_LEVEL
flag, which if is set, indicates we can get a
small perf win by not checking or raising the
IRQL
Return Value:
None.
--*/
{
LogFuncEntry(DRIVER_DEFAULT);
// Iterate through all the NetBufferLists
for (PNET_BUFFER_LIST pNblNext, pNbl = NetBufferLists; pNbl; pNbl = pNblNext)
{
// Save next to temporary and clear member variable
pNblNext = NET_BUFFER_LIST_NEXT_NBL(pNbl);
NET_BUFFER_LIST_NEXT_NBL(pNbl) = NULL;
// Iterate through all the Netbuffers
for (PNET_BUFFER pNbNext, pNb = NET_BUFFER_LIST_FIRST_NB(pNbl); pNb; pNb = pNbNext)
{
pNbNext = NET_BUFFER_NEXT_NB(pNb);
NdisFreeNetBuffer(pNb);
}
NdisFreeNetBufferList(pNbl);
}
LogFuncExit(DRIVER_DEFAULT);
}