| /* |
| * 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); |
| } |