From 1d999ccea310e20b72fd903e39383c9499726d2b Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Wed, 7 Dec 2016 14:57:32 -0800 Subject: [PATCH] Add Windows Tunnel Mode Miniport Driver (ottmp) (#993) * Add Windows Tunnel Mode Miniport Driver (ottmp) --- .appveyor.yml | 2 + .appveyor/make_release.cmd | 3 + etc/visual-studio/openthread.sln | 21 + etc/visual-studio/ottmp.vcxproj | 102 ++ etc/visual-studio/ottmp.vcxproj.filters | 21 + examples/drivers/windows/otLwf/otLwf.inf | 128 +- examples/drivers/windows/ottmp/adapter.cpp | 547 ++++++++ examples/drivers/windows/ottmp/adapter.hpp | 224 ++++ examples/drivers/windows/ottmp/device.cpp | 383 ++++++ examples/drivers/windows/ottmp/device.hpp | 72 + examples/drivers/windows/ottmp/driver.cpp | 273 ++++ examples/drivers/windows/ottmp/driver.hpp | 58 + examples/drivers/windows/ottmp/hardware.hpp | 153 +++ examples/drivers/windows/ottmp/hdlc.cpp | 343 +++++ examples/drivers/windows/ottmp/hdlc.hpp | 72 + examples/drivers/windows/ottmp/oid.cpp | 408 ++++++ examples/drivers/windows/ottmp/oid.hpp | 73 ++ examples/drivers/windows/ottmp/ottmp.inf | 121 ++ examples/drivers/windows/ottmp/ottmp.rc | 67 + examples/drivers/windows/ottmp/pch.hpp | 74 ++ examples/drivers/windows/ottmp/serial.cpp | 1308 +++++++++++++++++++ examples/drivers/windows/ottmp/serial.hpp | 122 ++ 22 files changed, 4510 insertions(+), 65 deletions(-) create mode 100644 etc/visual-studio/ottmp.vcxproj create mode 100644 etc/visual-studio/ottmp.vcxproj.filters create mode 100644 examples/drivers/windows/ottmp/adapter.cpp create mode 100644 examples/drivers/windows/ottmp/adapter.hpp create mode 100644 examples/drivers/windows/ottmp/device.cpp create mode 100644 examples/drivers/windows/ottmp/device.hpp create mode 100644 examples/drivers/windows/ottmp/driver.cpp create mode 100644 examples/drivers/windows/ottmp/driver.hpp create mode 100644 examples/drivers/windows/ottmp/hardware.hpp create mode 100644 examples/drivers/windows/ottmp/hdlc.cpp create mode 100644 examples/drivers/windows/ottmp/hdlc.hpp create mode 100644 examples/drivers/windows/ottmp/oid.cpp create mode 100644 examples/drivers/windows/ottmp/oid.hpp create mode 100644 examples/drivers/windows/ottmp/ottmp.inf create mode 100644 examples/drivers/windows/ottmp/ottmp.rc create mode 100644 examples/drivers/windows/ottmp/pch.hpp create mode 100644 examples/drivers/windows/ottmp/serial.cpp create mode 100644 examples/drivers/windows/ottmp/serial.hpp diff --git a/.appveyor.yml b/.appveyor.yml index 8f35dc7b5..3e5f481b2 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -42,6 +42,8 @@ install: - cmd: reg import .appveyor\ThreadEtw.reg - ps: Restart-Computer -Force - ps: Start-Sleep -s 10 +before_build: + - cmd: ren "C:\Program Files (x86)\Windows Kits\10\include\00wdf" "wdf" build: project: etc/visual-studio/openthread.sln verbosity: minimal diff --git a/.appveyor/make_release.cmd b/.appveyor/make_release.cmd index 8f3a10f47..e7bdddc8d 100644 --- a/.appveyor/make_release.cmd +++ b/.appveyor/make_release.cmd @@ -39,6 +39,9 @@ REM Copy the relavant binaries copy build\bin\%Platform2%\%Configuration%\sys\otlwf\* release copy build\bin\%Platform2%\%Configuration%\sys\otlwf.cer release copy build\bin\%Platform2%\%Configuration%\sys\otlwf.pdb release\symbols +copy build\bin\%Platform2%\%Configuration%\sys\ottmp\* release +copy build\bin\%Platform2%\%Configuration%\sys\ottmp.cer release +copy build\bin\%Platform2%\%Configuration%\sys\ottmp.pdb release\symbols copy build\bin\%Platform2%\%Configuration%\dll\otApi.dll release copy build\bin\%Platform2%\%Configuration%\dll\otApi.lib release\libs copy build\bin\%Platform2%\%Configuration%\dll\otApi.pdb release\symbols diff --git a/etc/visual-studio/openthread.sln b/etc/visual-studio/openthread.sln index 6ddf448f0..ca938ec83 100644 --- a/etc/visual-studio/openthread.sln +++ b/etc/visual-studio/openthread.sln @@ -59,6 +59,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenThread", "OpenThread.vc EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spinel_k", "spinel_k.vcxproj", "{A55766B5-58B6-4519-835E-5A4B7C164B5A}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ottmp", "ottmp.vcxproj", "{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -311,6 +313,24 @@ Global {A55766B5-58B6-4519-835E-5A4B7C164B5A}.Release|x86.ActiveCfg = Release|Win32 {A55766B5-58B6-4519-835E-5A4B7C164B5A}.Release|x86.Build.0 = Release|Win32 {A55766B5-58B6-4519-835E-5A4B7C164B5A}.Release|x86.Deploy.0 = Release|Win32 + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|ARM.ActiveCfg = Debug|ARM + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|ARM.Build.0 = Debug|ARM + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|ARM.Deploy.0 = Debug|ARM + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|x64.ActiveCfg = Debug|x64 + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|x64.Build.0 = Debug|x64 + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|x64.Deploy.0 = Debug|x64 + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|x86.ActiveCfg = Debug|Win32 + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|x86.Build.0 = Debug|Win32 + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|x86.Deploy.0 = Debug|Win32 + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|ARM.ActiveCfg = Release|ARM + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|ARM.Build.0 = Release|ARM + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|ARM.Deploy.0 = Release|ARM + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|x64.ActiveCfg = Release|x64 + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|x64.Build.0 = Release|x64 + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|x64.Deploy.0 = Release|x64 + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|x86.ActiveCfg = Release|Win32 + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|x86.Build.0 = Release|Win32 + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|x86.Deploy.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -334,5 +354,6 @@ Global {D5577E51-FA31-4802-8669-1DB32805935E} = {35F6AB71-5BD2-4D53-8A51-D75CCD4CCC3D} {F8C22844-9B93-4978-80DF-8AF2B37A7ABB} = {76EA541E-CAB1-4DB5-A39F-E3DB2A78CDDD} {A55766B5-58B6-4519-835E-5A4B7C164B5A} = {95BD0669-04C8-4EEB-B3CC-0535B03F4468} + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D} = {61E8A4A0-8138-49DB-97B4-3BEC87C8E133} EndGlobalSection EndGlobal diff --git a/etc/visual-studio/ottmp.vcxproj b/etc/visual-studio/ottmp.vcxproj new file mode 100644 index 000000000..2ca62f1ad --- /dev/null +++ b/etc/visual-studio/ottmp.vcxproj @@ -0,0 +1,102 @@ + + + + + {1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D} + {497e31cb-056b-4f31-abb8-447fd55ee5a5} + v4.5 + 12.0 + Thread + Windows10 + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + + true + + + false + + + + + + + + ..\..\build\bin\$(Platform)\$(Configuration)\sys\ + ..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\ + AllRules.ruleset + true + + + + + %(PreProcessorDefinitions); + NDIS_MINIPORT_DRIVER=1; + NDIS_WDM=1; + NDIS650_MINIPORT=1; + OTTMP_LEGACY=1; + + + %(AdditionalIncludeDirectories); + ..\..\include; + ..\..\examples\drivers\windows\include; + ..\..\examples\drivers\windows\ottmp; + + Level4 + true + %(DisableSpecificWarnings);4200;4201;4214 + true + ..\..\include\platform\logging-windows.h + -km %(WppAdditionalOptions) + ottmp + WPP_INIT_TRACING + true + + + + %(PreProcessorDefinitions); + NDIS_MINIPORT_DRIVER=1; + NDIS_WDM=1; + NDIS650_MINIPORT=1; + OTTMP_LEGACY=1; + + %(AdditionalIncludeDirectories);..;.; + + + + %(AdditionalDependencies); + ndis.lib; + + + + inf2cat /driver:$(TargetDir) /os:8_ARM + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/etc/visual-studio/ottmp.vcxproj.filters b/etc/visual-studio/ottmp.vcxproj.filters new file mode 100644 index 000000000..64ba1d03c --- /dev/null +++ b/etc/visual-studio/ottmp.vcxproj.filters @@ -0,0 +1,21 @@ + + + + + {BED10704-BFDA-427C-BB01-B08F20AB1718} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {A6C61B28-961C-4B63-892B-A7E5EB2A4B3E} + h;hpp;hxx;hm;inl;inc;xsd + + + {0CBFC63C-11CB-4E5F-95F1-187BE3F6DA44} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {6C2BAFD4-E244-4FF2-A002-FBCCF14AFA9D} + inf;inv;inx;mof;mc; + + + \ No newline at end of file diff --git a/examples/drivers/windows/otLwf/otLwf.inf b/examples/drivers/windows/otLwf/otLwf.inf index e2e0f2f16..fa90a24a7 100644 --- a/examples/drivers/windows/otLwf/otLwf.inf +++ b/examples/drivers/windows/otLwf/otLwf.inf @@ -26,83 +26,81 @@ ; POSSIBILITY OF SUCH DAMAGE. ; -[version] -; Do not change these values -Signature = "$Windows NT$" -Class = NetService -ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318} -Provider = %openthread% -DriverVer = -CatalogFile = otLwf.cat +[Version] +Signature = "$Windows NT$" +Class = NetService +ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318} +Provider = %OpenThread% +DriverVer = +PnpLockDown = 1 +CatalogFile = otLwf.cat [Manufacturer] -%openthread%=OpenThread,NT$ARCH$ +%OpenThread% = OpenThread,NT$ARCH$ -; otLwf can be used with netcfg.exe to install/uninstall the driver. [OpenThread.NT$ARCH$] -%otLwf_Desc%=Install, otLwf +%otlwf.DeviceDesc% = otlwf.ndi, otLwf -;------------------------------------------------------------------------- -; Installation Section -;------------------------------------------------------------------------- -[Install] -AddReg=Inst_Ndi -Characteristics=0x40000 -NetCfgInstanceId="{B3A3845A-164E-4727-B12E-32B8DCE1F6CD}" +;------------------------------------------------------------------------------- +; OpenThread NDIS Filter Driver +;------------------------------------------------------------------------------- +[otlwf.ndi] +Characteristics = 0x40000 +NetCfgInstanceId = "{B3A3845A-164E-4727-B12E-32B8DCE1F6CD}" +AddReg = otlwf.Reg +Copyfiles = otLwf.CopyFiles -Copyfiles = otLwf.copyfiles.sys +[otlwf.ndi.Services] +AddService = otLwf, , otlwf.Service + +[otlwf.ndi.Remove.Services] +; The SPSVCINST_STOPSERVICE flag instructs SCM to stop the NT service +; before uninstalling the driver. +DelService = otLwf, 0x200 ; SPSVCINST_STOPSERVICE + +;------------------------------------------------------------------------------- +; OpenThread NDIS Filter Common +;------------------------------------------------------------------------------- +[otlwf.Reg] +HKR, Ndi, Service, , "otLwf" +HKR, Ndi, CoServices, 0x00010000, "otLwf" +HKR, Ndi, FilterClass, , ms_medium_converter_top +HKR, Ndi, FilterType, 0x00010001, 0x00000002 +HKR, Ndi, FilterRunType, 0x00010001, 0x00000002 ;OPTIONAL filter +HKR, Ndi\Interfaces, UpperRange, , "noupper" +HKR, Ndi\Interfaces, LowerRange, , "nolower" +HKR, Ndi\Interfaces, FilterMediaTypes, , "802.15.4" + +;------------------------------------------------------------------------------- +; Driver and Service Section +;------------------------------------------------------------------------------- +[otLwf.CopyFiles] +otLwf.sys,,,2 + +[otlwf.Service] +DisplayName = %otlwf.Service.DispName% +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +StartType = 1 ;SERVICE_SYSTEM_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +ServiceBinary = %12%\otLwf.sys +LoadOrderGroup = NDIS +Description = %otlwf.DeviceDesc% [SourceDisksNames] -1=%otLwf_Desc%,"",, +1 = %otlwf.DeviceDesc%,"",, [SourceDisksFiles] ; TODO: Include any related files that should be installed with your driver. -otLwf.sys=1 +otLwf.sys = 1 [DestinationDirs] -DefaultDestDir=12 -otLwf.copyfiles.sys=12 - -[otLwf.copyfiles.sys] -otLwf.sys,,,2 - -;------------------------------------------------------------------------- -; Ndi installation support -;------------------------------------------------------------------------- -[Inst_Ndi] -HKR, Ndi,Service,,"otLwf" -HKR, Ndi,CoServices,0x00010000,"otLwf" -HKR, Ndi,HelpText,,%otLwf_HelpText% -HKR, Ndi,FilterClass,, ms_medium_converter_top -HKR, Ndi,FilterType,0x00010001,0x00000002 -HKR, Ndi,FilterRunType, 0x00010001, 0x00000002 ;OPTIONAL filter -HKR, Ndi\Interfaces,UpperRange,,"noupper" -HKR, Ndi\Interfaces,LowerRange,,"nolower" -HKR, Ndi\Interfaces, FilterMediaTypes,,"802.15.4" - -;------------------------------------------------------------------------- -; Service installation support -;------------------------------------------------------------------------- -[Install.Services] -AddService=otLwf,,otLwf_Service_Inst - -[otLwf_Service_Inst] -DisplayName = %otLwf_Desc% -ServiceType = 1 ;SERVICE_KERNEL_DRIVER -; Typically you will want your filter driver to start with SERVICE_SYSTEM_START. -; If it is an Optional filter, you may also use 3;SERVICE_DEMAND_START. -StartType = 1 ;SERVICE_SYSTEM_START -ErrorControl = 1 ;SERVICE_ERROR_NORMAL -ServiceBinary = %12%\otLwf.sys -LoadOrderGroup = NDIS -Description = %otLwf_Desc% - -[Install.Remove.Services] -; The SPSVCINST_STOPSERVICE flag instructs SCM to stop the NT service -; before uninstalling the driver. -DelService=otLwf,0x200 ; SPSVCINST_STOPSERVICE +DefaultDestDir = 12 +otLwf.CopyFiles = 12 +;------------------------------------------------------------------------------- +; Localizable Strings +;------------------------------------------------------------------------------- [Strings] -openthread = "OpenThread" -otLwf_Desc = "OpenThread NDIS LightWeight Filter" -otLwf_HelpText = "OpenThread NDIS LightWeight Filter" +OpenThread = "OpenThread" +otlwf.DeviceDesc = "OpenThread NDIS LightWeight Filter" +otlwf.Service.DispName = "OpenThread NDIS LightWeight Filter" diff --git a/examples/drivers/windows/ottmp/adapter.cpp b/examples/drivers/windows/ottmp/adapter.cpp new file mode 100644 index 000000000..583654991 --- /dev/null +++ b/examples/drivers/windows/ottmp/adapter.cpp @@ -0,0 +1,547 @@ +/* + * 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); +} diff --git a/examples/drivers/windows/ottmp/adapter.hpp b/examples/drivers/windows/ottmp/adapter.hpp new file mode 100644 index 000000000..100a4b42d --- /dev/null +++ b/examples/drivers/windows/ottmp/adapter.hpp @@ -0,0 +1,224 @@ +/* + * 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 + * Header file for the routines related to NETADAPTER Object for the + * Network Adapter + */ + +#define NIC_TAG_RECV_NBL ((ULONG)'rMVT') // TVMr +#define OTTMP_ADAPTER_CONTEXT_SIGNATURE 'pdAt' + +// The maximum size of one Spinel command / serial packet +#define MAX_SPINEL_COMMAND_LENGTH 1300 + +typedef struct _SERIAL_SEND_ITEM +{ + LIST_ENTRY Link; + PNET_BUFFER_LIST NetBufferList; + WDFMEMORY WdfMemory; + ULONG EncodedBufferLength; + _Field_size_bytes_(EncodedBufferLength) + UCHAR EncodedBuffer[0]; + +} SERIAL_SEND_ITEM, *PSERIAL_SEND_ITEM; + +const ULONG SERIAL_SEND_ITEM_SIZE = FIELD_OFFSET(SERIAL_SEND_ITEM, EncodedBuffer); + +typedef struct _OTTMP_ADAPTER_CONTEXT { + ULONG Signature; + +#ifdef OTTMP_LEGACY + NDIS_HANDLE Adapter; +#else + // + // Handle to the NETADAPTER object for this context + // + NETADAPTER Adapter; +#endif + + // + // Handle to the corresponding WDFDEVICE + // + WDFDEVICE Device; + + // Flag to indicate if the data path is enabled + bool IsConnected; + + // Flag to indicate if the Adapter has been started or not + bool IsRunning; + +#ifdef OTTMP_LEGACY + PGLOBALS pGlobals; +#else + // Receive packet pool + NETBUFFERLISTCOLLECTION ReceiveCollection; +#endif + + ULONGLONG ExtendedAddress; // TODO - Cache + + // + // Serial Device + // + + WDFIOTARGET WdfIoTarget; + + WDFSPINLOCK SendLock; + _Guarded_by_(SendLock) + LIST_ENTRY SendQueue; + bool SendQueueRunning; + WDFWORKITEM SendWorkItem; + + WDFWORKITEM RecvWorkItem; + WDFREQUEST RecvReadRequest; + + UCHAR RecvBuffer[MAX_SPINEL_COMMAND_LENGTH * 2]; + ULONG RecvBufferLength; + + // + // NIC configuration - This information is queried by the protocol drivers. + // Since this sample focuses on demonstrating the NDIS WDF model, + // it doesn't modify any these values during runtime. + // Please look at the netvmini630 sample to see how these values can be set + // and updated + // ------------------------------------------------------------------------- + // + ULONG PacketFilter; + ULONG ulLookahead; + ULONG64 ulLinkSendSpeed; + ULONG64 ulLinkRecvSpeed; + ULONG ulMaxBusySends; + ULONG ulMaxBusyRecvs; + + // + // Statistics - + // Since this sample focuses on demonstrating the NDIS WDF model, + // it doesn't modify any these values during runtime. + // Please look at the netvmini630 sample to see how these values can be set + // and updated + // ------------------------------------------------------------------------- + // + + // Packet counts + ULONG64 FramesRxDirected; + ULONG64 FramesRxMulticast; + ULONG64 FramesRxBroadcast; + ULONG64 FramesTxDirected; + ULONG64 FramesTxMulticast; + ULONG64 FramesTxBroadcast; + + // Byte counts + ULONG64 BytesRxDirected; + ULONG64 BytesRxMulticast; + ULONG64 BytesRxBroadcast; + ULONG64 BytesTxDirected; + ULONG64 BytesTxMulticast; + ULONG64 BytesTxBroadcast; + + // Count of transmit errors + ULONG TxAbortExcessCollisions; + ULONG TxLateCollisions; + ULONG TxDmaUnderrun; + ULONG TxLostCRS; + ULONG TxOKButDeferred; + ULONG OneRetry; + ULONG MoreThanOneRetry; + ULONG TotalRetries; + ULONG TransmitFailuresOther; + + // Count of receive errors + ULONG RxCrcErrors; + ULONG RxAlignmentErrors; + ULONG RxResourceErrors; + ULONG RxDmaOverrunErrors; + ULONG RxCdtFrames; + ULONG RxRuntErrors; + +} OTTMP_ADAPTER_CONTEXT, *POTTMP_ADAPTER_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(OTTMP_ADAPTER_CONTEXT, GetAdapterContext); + +typedef struct _WDF_DEVICE_INFO +{ + POTTMP_ADAPTER_CONTEXT AdapterContext; + +} WDF_DEVICE_INFO, *PWDF_DEVICE_INFO; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(WDF_DEVICE_INFO, GetWdfDeviceInfo); + +#ifdef OTTMP_LEGACY + +PAGED MINIPORT_PAUSE MPPause; +PAGED MINIPORT_RESTART MPRestart; + +MINIPORT_SEND_NET_BUFFER_LISTS MPSendNetBufferLists; +MINIPORT_RETURN_NET_BUFFER_LISTS MPReturnNetBufferLists; +MINIPORT_CANCEL_SEND MPCancelSend; + +PAGED +_IRQL_requires_max_(PASSIVE_LEVEL) +NDIS_STATUS +AdapterInitialize( + _In_ NDIS_HANDLE MiniportAdapterHandle, + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext + ); + +PAGED +_IRQL_requires_(PASSIVE_LEVEL) +VOID +AdapterUninitialize( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext + ); + +#else + +PAGED EVT_NET_ADAPTER_SET_CAPABILITIES EvtAdapterSetCapabilities; +PAGED EVT_NET_ADAPTER_START EvtAdapterStart; +PAGED EVT_NET_ADAPTER_PAUSE EvtAdapterPause; + +EVT_NET_ADAPTER_SEND_NET_BUFFER_LISTS EvtAdapterSendNetBufferLists; + +PAGED +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +AdapterInitialize( + _In_ NETADAPTER Adapter, + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, + _In_ POTTMP_DEVICE_CONTEXT DeviceContext + ); + +#endif + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +AdapterIndicateEnergyScanComplete( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, + _In_ NDIS_STATUS Status + ); + +EXT_CALLBACK AdapterEnergyScanTimerCallback; \ No newline at end of file diff --git a/examples/drivers/windows/ottmp/device.cpp b/examples/drivers/windows/ottmp/device.cpp new file mode 100644 index 000000000..9b7619cb4 --- /dev/null +++ b/examples/drivers/windows/ottmp/device.cpp @@ -0,0 +1,383 @@ +/* + * 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 +#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); +} diff --git a/examples/drivers/windows/ottmp/device.hpp b/examples/drivers/windows/ottmp/device.hpp new file mode 100644 index 000000000..cca9ef260 --- /dev/null +++ b/examples/drivers/windows/ottmp/device.hpp @@ -0,0 +1,72 @@ +/* + * 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 + * Header file for the routines related to WDFDEVICE representing the + * Network Adapter + */ + +#pragma once + +#define OTTMP_DEVICE_CONTEXT_SIGNATURE 'veDt' +typedef struct _OTTMP_DEVICE_CONTEXT { + // + // Signature for Sanity Check. + // + ULONG Signature; + + // + // Handle to the WDFDEVICE of which this is the context + // + WDFDEVICE Device; + + // + // Pointer to the Context of the corresponding NETADAPTER object + // + POTTMP_ADAPTER_CONTEXT AdapterContext; + +} OTTMP_DEVICE_CONTEXT, *POTTMP_DEVICE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(OTTMP_DEVICE_CONTEXT, GetDeviceContext); + +extern "C" { +#ifdef OTTMP_LEGACY +PAGED MINIPORT_INITIALIZE MPInitializeEx; +PAGED MINIPORT_HALT MPHaltEx; +MINIPORT_SHUTDOWN MPShutdownEx; +PAGED MINIPORT_DEVICE_PNP_EVENT_NOTIFY MPDevicePnpEventNotify; +#else +PAGED EVT_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd; +PAGED EVT_WDF_DEVICE_D0_EXIT EvtDeviceD0Exit; +PAGED EVT_WDF_DEVICE_PREPARE_HARDWARE EvtDevicePrepareHardware; +PAGED EVT_WDF_DEVICE_RELEASE_HARDWARE EvtDeviceReleaseHardware; +PAGED EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT EvtDeviceSelfManagedIoInit; +PAGED EVT_WDF_DEVICE_SELF_MANAGED_IO_CLEANUP EvtDeviceSelfManagedIoCleanup; +EVT_WDF_DEVICE_D0_ENTRY EvtDeviceD0Entry; +#endif +} diff --git a/examples/drivers/windows/ottmp/driver.cpp b/examples/drivers/windows/ottmp/driver.cpp new file mode 100644 index 000000000..348df824d --- /dev/null +++ b/examples/drivers/windows/ottmp/driver.cpp @@ -0,0 +1,273 @@ +/* + * 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 has code to deal with loading and unloading of the driver + */ + +#include "pch.hpp" +#include "driver.tmh" + +#ifdef OTTMP_LEGACY +static GLOBALS GlobalData = { 0 }; +#endif + +INITCODE +extern "C" +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + DriverEntry initializes the driver and is the first routine called by the + system after the driver is loaded. DriverEntry specifies the other entry + points in the function driver, such as EvtDevice and DriverUnload. + +Parameters Description: + + DriverObject - represents the instance of the function driver that is loaded + into memory. DriverEntry must initialize members of DriverObject before it + returns to the caller. DriverObject is allocated by the system before the + driver is loaded, and it is released by the system after the system unloads + the function driver from memory. + + RegistryPath - represents the driver specific path in the Registry. + The function driver can use the path to store driver related data between + reboots. The path does not store hardware instance specific data. + +Return Value: + + A success status as determined by NT_SUCCESS macro, if successful. + +--*/ +{ + NTSTATUS status; + WDF_DRIVER_CONFIG config; +#ifdef OTTMP_LEGACY + NDIS_STATUS ndisStatus = NDIS_STATUS_FAILURE; + NDIS_MINIPORT_DRIVER_CHARACTERISTICS MPChar = { 0 }; + NET_BUFFER_LIST_POOL_PARAMETERS NblParams = { 0 }; + NET_BUFFER_POOL_PARAMETERS NbParams = { 0 }; +#endif + + WPP_INIT_TRACING(DriverObject, RegistryPath); + + LogFuncEntry(DRIVER_DEFAULT); + + // + // Create the WdfDriver Object + // + WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); + +#ifdef OTTMP_LEGACY + // + // Set WdfDriverInitNoDispatchOverride flag to tell the framework + // not to provide dispatch routines for the driver. In other words, + // the framework must not intercept IRPs that the I/O manager has + // directed to the driver. In this case, it will be handled by NDIS + // port driver. + // + config.DriverInitFlags |= WdfDriverInitNoDispatchOverride; +#else + config.EvtDriverDeviceAdd = EvtDriverDeviceAdd; + config.EvtDriverUnload = EvtDriverUnload; +#endif + + status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, +#ifdef OTTMP_LEGACY + &GlobalData.WdfDriver +#else + NULL +#endif + ); + if (!NT_SUCCESS(status)) + { + LogError(DRIVER_DEFAULT, "WdfDriverCreate failed, %!STATUS!", status); + goto error; + } + +#ifdef OTTMP_LEGACY + MPChar.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS; + MPChar.Header.Revision = NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2; + MPChar.Header.Size = NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2; + + // Version Suff + MPChar.MajorNdisVersion = NDIS_MINIPORT_MAJOR_VERSION; + MPChar.MinorNdisVersion = NDIS_MINIPORT_MINOR_VERSION; + MPChar.MajorDriverVersion = NIC_VENDOR_DRIVER_VERSION_MAJOR; + MPChar.MinorDriverVersion = NIC_VENDOR_DRIVER_VERSION_MINOR; + + MPChar.InitializeHandlerEx = MPInitializeEx; + MPChar.HaltHandlerEx = MPHaltEx; + MPChar.UnloadHandler = MPDriverUnload; + MPChar.PauseHandler = MPPause; + MPChar.RestartHandler = MPRestart; + MPChar.OidRequestHandler = MPOidRequest; + MPChar.SendNetBufferListsHandler = MPSendNetBufferLists; + MPChar.ReturnNetBufferListsHandler = MPReturnNetBufferLists; + MPChar.CancelSendHandler = MPCancelSend; + MPChar.DevicePnPEventNotifyHandler = MPDevicePnpEventNotify; + MPChar.ShutdownHandlerEx = MPShutdownEx; + MPChar.CancelOidRequestHandler = MPCancelOidRequest; + + ndisStatus = NdisMRegisterMiniportDriver(DriverObject, + RegistryPath, + &GlobalData, + &MPChar, + &GlobalData.hDriver); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + LogError(DRIVER_DEFAULT, "NdisMRegisterMiniportDriver failed %!NDIS_STATUS!", ndisStatus); + status = STATUS_UNSUCCESSFUL; + goto error; + } + + NblParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + NblParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + NblParams.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + NblParams.PoolTag = NIC_TAG_RECV_NBL; + GlobalData.hNblPool = NdisAllocateNetBufferListPool(GlobalData.hDriver, &NblParams); + if (GlobalData.hNblPool == 0) + { + LogError(DRIVER_DEFAULT, "NdisAllocateNetBufferListPool failed"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto error; + } + + NbParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + NbParams.Header.Revision = NET_BUFFER_POOL_PARAMETERS_REVISION_1; + NbParams.Header.Size = NDIS_SIZEOF_NET_BUFFER_POOL_PARAMETERS_REVISION_1; + NbParams.PoolTag = NIC_TAG_RECV_NBL; + NbParams.DataSize = MAX_SPINEL_COMMAND_LENGTH; + GlobalData.hNbPool = NdisAllocateNetBufferPool(GlobalData.hDriver, &NbParams); + if (GlobalData.hNbPool == 0) + { + LogError(DRIVER_DEFAULT, "NdisAllocateNetBufferListPool failed"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto error; + } +#endif + +error: + + if (!NT_SUCCESS(status)) + { +#ifdef OTTMP_LEGACY + MPDriverUnload(DriverObject); +#else + WPP_CLEANUP(DriverObject); +#endif + } + + LogFuncExitNT(DRIVER_DEFAULT, status); + return status; +} + +#ifdef OTTMP_LEGACY + +PAGED +_Use_decl_annotations_ +VOID +MPDriverUnload( + _In_ PDRIVER_OBJECT DriverObject + ) +/*++ +Routine Description: + + MPDriverUnload will clean up the WPP resources that was allocated + for this driver. + +Arguments: + + DriverObject - Handle to a framework driver object created in DriverEntry + +--*/ +{ + LogFuncEntry(DRIVER_DEFAULT); + + PAGED_CODE(); + + if (GlobalData.WdfDriver) + { + NT_ASSERT(GlobalData.WdfDriver == WdfGetDriver()); + WdfDriverMiniportUnload(GlobalData.WdfDriver); + GlobalData.WdfDriver = nullptr; + } + + if (GlobalData.hNblPool) + { + NdisFreeNetBufferListPool(GlobalData.hNblPool); + GlobalData.hNblPool = nullptr; + } + + if (GlobalData.hDriver) + { + NdisMDeregisterMiniportDriver(GlobalData.hDriver); + GlobalData.hDriver = nullptr; + } + + LogFuncExit(DRIVER_DEFAULT); + +#pragma warning(suppress: 25024) // Dangerous cast + WPP_CLEANUP(DriverObject); +} + +#else + +PAGED +VOID +EvtDriverUnload( + WDFDRIVER Driver + ) +/*++ +Routine Description: + + EvtDriverUnload will clean up the WPP resources that was allocated + for this driver. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + +--*/ +{ + LogFuncEntry(DRIVER_DEFAULT); + + PAGED_CODE(); + + LogFuncExit(DRIVER_DEFAULT); + WPP_CLEANUP(WdfDriverWdmGetDriverObject(Driver)); +} + +#endif diff --git a/examples/drivers/windows/ottmp/driver.hpp b/examples/drivers/windows/ottmp/driver.hpp new file mode 100644 index 000000000..f63e6ad4e --- /dev/null +++ b/examples/drivers/windows/ottmp/driver.hpp @@ -0,0 +1,58 @@ +/* + * 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 + * Header file for the Driver Load / Unload routines + */ + +#pragma once + +extern "C" { +DRIVER_INITIALIZE DriverEntry; +} + +#ifdef OTTMP_LEGACY +PAGED MINIPORT_UNLOAD MPDriverUnload; + +typedef struct _GLOBALS +{ + WDFDRIVER WdfDriver; + NDIS_HANDLE hDriver; + NDIS_HANDLE hNblPool; + NDIS_HANDLE hNbPool; +} GLOBALS, *PGLOBALS; +#else +PAGED EVT_WDF_DRIVER_UNLOAD EvtDriverUnload; +#endif + +// +// Own Version +// +#define NIC_VENDOR_DRIVER_VERSION_MAJOR 1 +#define NIC_VENDOR_DRIVER_VERSION_MINOR 0 +#define NIC_VENDOR_DRIVER_VERSION ((NIC_VENDOR_DRIVER_VERSION_MAJOR << 16) | NIC_VENDOR_DRIVER_VERSION_MINOR) diff --git a/examples/drivers/windows/ottmp/hardware.hpp b/examples/drivers/windows/ottmp/hardware.hpp new file mode 100644 index 000000000..bedf3d470 --- /dev/null +++ b/examples/drivers/windows/ottmp/hardware.hpp @@ -0,0 +1,153 @@ +/* + * 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 defines constants that describe physical characteristics and + * limits of the underlying hardware. + */ + +#pragma once + +#ifndef IF_TYPE_IEEE802154 +#define IF_TYPE_IEEE802154 259 // IEEE 802.15.4 WPAN interface +#define NdisPhysicalMediumNative802_15_4 (NDIS_PHYSICAL_MEDIUM)20 +#endif + +// +// Link layer addressing +// ----------------------------------------------------------------------------- +// + +// Number of bytes in a hardware address. 802.15.4 uses 8 byte addresses. +#define NIC_MACADDR_SIZE 8 + +// +// Frames +// ----------------------------------------------------------------------------- +// + +#define HW_MAX_FRAME_SIZE 1280 + +// +// Medium properties +// ----------------------------------------------------------------------------- +// + +#define NIC_MEDIUM_TYPE NdisMediumIP + +// Claim to be 250kbps duplex +#define KILOBITS_PER_SECOND 1000ULL +#define MEGABITS_PER_SECOND 1000000ULL +#define NIC_RECV_XMIT_SPEED (250ULL*KILOBITS_PER_SECOND) + +// +// Hardware limits +// ----------------------------------------------------------------------------- +// + +// Max number of multicast addresses supported in hardware +#define NIC_MAX_MCAST_LIST 32 + +// Maximum number of uncompleted sends that a single adapter will permit +#define NIC_MAX_BUSY_SENDS 1024 + +// Maximum number of received packets that can be in the OS at any time +// (Also known as the receive pool size) +#define NIC_MAX_OUTSTANDING_RECEIVES 32 + + +// +// Physical adapter properties +// ----------------------------------------------------------------------------- +// + +// Change to your company name instead of using OpenThread +#define NIC_VENDOR_DESC "OpenThread" + +// Highest byte is the NIC byte plus three vendor bytes. This is normally +// obtained from the NIC. +#define NIC_VENDOR_ID 0x00FFFFFF + +#ifdef OTTMP_LEGACY +#define NIC_SUPPORTED_FILTERS ( \ + NDIS_PACKET_TYPE_DIRECTED | \ + NDIS_PACKET_TYPE_MULTICAST | \ + NDIS_PACKET_TYPE_BROADCAST | \ + NDIS_PACKET_TYPE_PROMISCUOUS | \ + NDIS_PACKET_TYPE_ALL_MULTICAST) +#else +#define NIC_SUPPORTED_FILTERS (NET_PACKET_FILTER_TYPES_FLAGS) ( \ + NET_PACKET_FILTER_TYPE_DIRECTED | \ + NET_PACKET_FILTER_TYPE_MULTICAST | \ + NET_PACKET_FILTER_TYPE_BROADCAST | \ + NET_PACKET_FILTER_TYPE_PROMISCUOUS | \ + NET_PACKET_FILTER_TYPE_ALL_MULTICAST) +#endif + +// +// Specify a bitmask that defines optional properties of the NIC. +// This miniport indicates receive with NdisMIndicateReceiveNetBufferLists +// function. Such a driver should set this NDIS_MAC_OPTION_TRANSFERS_NOT_PEND +// flag. +// +// NDIS_MAC_OPTION_NO_LOOPBACK tells NDIS that NIC has no internal +// loopback support so NDIS will manage loopbacks on behalf of +// this driver. +// +// NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA tells the protocol that +// our receive buffer is not on a device-specific card. If +// NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA is not set, multi-buffer +// indications are copied to a single flat buffer. +// +#define NIC_MAC_OPTIONS (\ + NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | \ + NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | \ + NDIS_MAC_OPTION_NO_LOOPBACK | \ + NDIS_MAC_OPTION_8021P_PRIORITY | \ + NDIS_MAC_OPTION_8021Q_VLAN) + +// NDIS 6.x miniports must support all counters in OID_GEN_STATISTICS. +#define NIC_SUPPORTED_STATISTICS (\ + NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_RCV_DISCARDS | \ + NDIS_STATISTICS_FLAGS_VALID_RCV_ERROR | \ + NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_XMIT_ERROR | \ + NDIS_STATISTICS_FLAGS_VALID_XMIT_DISCARDS | \ + NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_XMIT) diff --git a/examples/drivers/windows/ottmp/hdlc.cpp b/examples/drivers/windows/ottmp/hdlc.cpp new file mode 100644 index 000000000..89dc90702 --- /dev/null +++ b/examples/drivers/windows/ottmp/hdlc.cpp @@ -0,0 +1,343 @@ +/* + * 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 file implements an HDLC-lite encoder and decoder. + */ + +#include "pch.hpp" +#include "hdlc.tmh" + +/** + * FCS lookup table + */ +enum +{ + kInitFcs = 0xffff, ///< Initial FCS value. + kGoodFcs = 0xf0b8, ///< Good FCS value. +}; + +static bool +hdlc_byte_needs_escape(UCHAR byte) +{ + switch (byte) + { + case HdlcXOn: + case HdlcXOff: + case HdlcEscapeSequence: + case HdlcFlagSequence: + case HdlcSpecial: + return true; + + default: + return false; + } +} + +/** + * This method updates an FCS. + * + * @param[in] aFcs The FCS to update. + * @param[in] aByte The input byte value. + * + * @returns The updated FCS. + */ +USHORT UpdateFcs(USHORT aFcs, UCHAR aByte) +{ + const USHORT sFcsTable[256] = + { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 + }; + return (aFcs >> 8) ^ sFcsTable[(aFcs ^ aByte) & 0xff]; +} + +ULONG +HdlcComputeEncodedByteLength( + _In_ UCHAR aByte + ) +{ + return hdlc_byte_needs_escape(aByte) ? 2 : 1; +} + +ULONG +HdlcComputeEncodedLength( + _In_reads_bytes_(DecodedBufferLength) + PUCHAR DecodedBuffer, + _In_ ULONG DecodedBufferLength + ) +{ + USHORT Fcs = kInitFcs; + ULONG Length = 2; // Start and end sequence + + for (ULONG i = 0; i < DecodedBufferLength; i++) + { + Length += HdlcComputeEncodedByteLength(DecodedBuffer[i]); + Fcs = UpdateFcs(Fcs, DecodedBuffer[i]); + } + + Fcs ^= 0xffff; + + Length += HdlcComputeEncodedByteLength(Fcs & 0xff); + Length += HdlcComputeEncodedByteLength(Fcs >> 8); + + return Length; +} + +bool +HdlcEncodeByte( + _In_ UCHAR aByte, + _Inout_ ULONG& Offset, + _In_ ULONG EncodedBufferLength, + _Out_writes_bytes_(EncodedBufferLength) + PUCHAR EncodedBuffer + ) +{ + if (hdlc_byte_needs_escape(aByte)) + { + if (Offset + 2 > EncodedBufferLength) return false; + + EncodedBuffer[Offset++] = HdlcEscapeSequence; + EncodedBuffer[Offset++] = (aByte ^ 0x20); + } + else + { + if (Offset + 1 > EncodedBufferLength) return false; + + EncodedBuffer[Offset++] = aByte; + } + + return true; +} + +_Success_(return == true) +bool +HdlcEncodeBuffer( + _In_reads_bytes_(DecodedBufferLength) + PUCHAR DecodedBuffer, + _In_ ULONG DecodedBufferLength, + _Out_writes_bytes_(EncodedBufferLength) + PUCHAR EncodedBuffer, + _In_ ULONG EncodedBufferLength + ) +{ + USHORT Fcs = kInitFcs; + ULONG Offset = 0; + bool Complete = false; + + if (Offset + 1 > EncodedBufferLength) goto error; + EncodedBuffer[Offset++] = HdlcFlagSequence; + + for (ULONG i = 0; i < DecodedBufferLength; i++) + { + UCHAR aByte = DecodedBuffer[i]; + + if (!HdlcEncodeByte(aByte, Offset, EncodedBufferLength, EncodedBuffer)) { + goto error; + } + + Fcs = UpdateFcs(Fcs, aByte); + } + + Fcs ^= 0xffff; + + if (!HdlcEncodeByte(Fcs & 0xff, Offset, EncodedBufferLength, EncodedBuffer) || + !HdlcEncodeByte(Fcs >> 8, Offset, EncodedBufferLength, EncodedBuffer)) { + goto error; + } + + if (Offset + 1 > EncodedBufferLength) goto error; + EncodedBuffer[Offset++] = HdlcFlagSequence; + + NT_ASSERT(Offset == EncodedBufferLength); + Complete = true; + +error: + + return Complete; +} + +enum HdlcDecodeState +{ + kStateNoSync = 0, + kStateSync, + kStateEscaped, +}; + +_Success_(return == true) +bool +HdlcDecodeBuffer( + _In_reads_bytes_(*EncodedBufferLength) + PUCHAR EncodedBuffer, + _Inout_ PULONG EncodedBufferLength, + _Inout_ PULONG DecodedBufferLength, + _Out_writes_bytes_opt_(*DecodedBufferLength) + PUCHAR DecodedBuffer, + _Out_ bool* IsGood + ) +{ + UCHAR byte; + HdlcDecodeState state = kStateNoSync; + ULONG DecodedLength = 0; + USHORT Fcs = kInitFcs; + + for (ULONG i = 0; i < *EncodedBufferLength; i++) + { + byte = EncodedBuffer[i]; + + switch (state) + { + case kStateNoSync: + { + if (byte == HdlcFlagSequence) + { + if (i != 0) + { + // Set the output for junk length + *EncodedBufferLength = i; + + // Set the output 'IsGood' flag based on FCS + *IsGood = false; + + return true; + } + else + { + state = kStateSync; + } + } + break; + } + case kStateSync: + { + switch (byte) + { + case HdlcEscapeSequence: + { + state = kStateEscaped; + break; + } + case HdlcFlagSequence: + { + // If it wasn't enough buffer to be a complete sequence, maybe we are + // actually between sequences. Leave the trailing escape character. + if (i < sizeof(USHORT) + 2) + { + // Set the output encoded buffer length + *EncodedBufferLength = i; + + *IsGood = false; + } + else + { + // Set the output encoded buffer length + *EncodedBufferLength = i + 1; + + // Set the decoded buffer length (subtract 2 for the FCS) + *DecodedBufferLength = DecodedLength - 2; + + // Set the output 'IsGood' flag based on FCS + *IsGood = (Fcs == kGoodFcs); + } + + return true; + } + default: + { + Fcs = UpdateFcs(Fcs, byte); + DecodedLength++; + if (DecodedBuffer) + { + if (*DecodedBufferLength >= DecodedLength) + { + DecodedBuffer[DecodedLength - 1] = byte; + } + } + break; + } + } + break; + } + case kStateEscaped: + { + if (i + 1 < *EncodedBufferLength) + { + byte ^= 0x20; + Fcs = UpdateFcs(Fcs, byte); + DecodedLength++; + if (DecodedBuffer) + { + if (*DecodedBufferLength >= DecodedLength) + { + DecodedBuffer[DecodedLength - 1] = byte; + } + } + + state = kStateSync; + } + else + { + // Not enough buffer + } + break; + } + } + } + + return false; +} diff --git a/examples/drivers/windows/ottmp/hdlc.hpp b/examples/drivers/windows/ottmp/hdlc.hpp new file mode 100644 index 000000000..cb73394d6 --- /dev/null +++ b/examples/drivers/windows/ottmp/hdlc.hpp @@ -0,0 +1,72 @@ +/* + * 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 file includes definitions for an HDLC-lite encoder and decoder. + */ + +#pragma once + +enum +{ + HdlcXOn = 0x11, + HdlcXOff = 0x13, + HdlcFlagSequence = 0x7e, ///< HDLC Flag value + HdlcEscapeSequence = 0x7d, ///< HDLC Escape value + HdlcSpecial = 0xF8 +}; + +ULONG +HdlcComputeEncodedLength( + _In_reads_bytes_(DecodedBufferLength) + PUCHAR DecodedBuffer, + _In_ ULONG DecodedBufferLength + ); + +_Success_(return == true) +bool +HdlcEncodeBuffer( + _In_reads_bytes_(DecodedBufferLength) + PUCHAR DecodedBuffer, + _In_ ULONG DecodedBufferLength, + _Out_writes_bytes_(EncodedBufferLength) + PUCHAR EncodedBuffer, + _In_ ULONG EncodedBufferLength + ); + +_Success_(return == true) +bool +HdlcDecodeBuffer( + _In_reads_bytes_(*EncodedBufferLength) + PUCHAR EncodedBuffer, + _Inout_ PULONG EncodedBufferLength, + _Inout_ PULONG DecodedBufferLength, + _Out_writes_bytes_opt_(*DecodedBufferLength) + PUCHAR DecodedBuffer, + _Out_ bool* IsGood + ); diff --git a/examples/drivers/windows/ottmp/oid.cpp b/examples/drivers/windows/ottmp/oid.cpp new file mode 100644 index 000000000..4e34c9a52 --- /dev/null +++ b/examples/drivers/windows/ottmp/oid.cpp @@ -0,0 +1,408 @@ +/* + * 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. + */ + +#include "pch.hpp" +#include "oid.tmh" + +template +PAGED NDIS_STATUS RequestQuery( _In_ PNDIS_OID_REQUEST OidRequest, const _Datum & value ); + +#define VERIFY_NDIS_OBJECT_HEADER(_header, _type, _revision, _size) \ + (((_header).Type == _type) && \ + ((_header).Revision >= _revision) && \ + ((_header).Size >= _size)) + +#define VERIFY_NDIS_OBJECT_HEADER_PTR(_header, _type, _revision, _size) \ + (((_header)->Type == _type) && \ + ((_header)->Revision >= _revision) && \ + ((_header)->Size >= _size)) + +#define VERIFY_NDIS_REQUEST_OBJECT_HEADER(_buffer, _type, _revision, _size) \ + VERIFY_NDIS_OBJECT_HEADER_PTR(reinterpret_cast(_buffer), _type, _revision, _size) + +#define ASSIGN_NDIS_OBJECT_HEADER(_header, _type, _revision, _size) \ + (_header).Type = _type; \ + (_header).Revision = _revision; \ + (_header).Size = _size; + +#define ASSIGN_NDIS_OBJECT_HEADER_PTR(_header, _type, _revision, _size) \ + (_header)->Type = _type; \ + (_header)->Revision = _revision; \ + (_header)->Size = _size; + +// +// List of Supported OIDs. +// +NDIS_OID NICSupportedOids[] = +{ + // General + OID_GEN_CURRENT_LOOKAHEAD, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_INTERRUPT_MODERATION, + OID_GEN_LINK_PARAMETERS, // TODO: Mandatory Set + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_RCV_OK, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_STATISTICS, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_ID, + OID_GEN_XMIT_OK, + OID_GEN_LINK_PARAMETERS, // TODO: Mandatory Set + + // 802.3 Specific + OID_802_3_CURRENT_ADDRESS, + OID_802_3_PERMANENT_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, + + // PnP Stuff + OID_PNP_CAPABILITIES, // Optional + OID_PNP_QUERY_POWER, // Optional + + // OpenThread Stuff + OID_OT_CAPABILITIES, +}; + +const ULONG SizeOfNICSupportedOids = sizeof(NICSupportedOids); + +template +PAGED +NDIS_STATUS RequestQuery( _In_ PNDIS_OID_REQUEST OidRequest, const _Datum & value ) +{ + PAGED_CODE(); + NT_ASSERT( (OidRequest->RequestType == NdisRequestQueryInformation) || + (OidRequest->RequestType == NdisRequestQueryStatistics) ); + + if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof( _Datum )) + { + OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( _Datum ); + OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0; + return NDIS_STATUS_INVALID_LENGTH; + } + else + { + OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( _Datum ); + OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof( _Datum ); + _Datum * pData = reinterpret_cast<_Datum *>(OidRequest->DATA.QUERY_INFORMATION.InformationBuffer); + *pData = value; + return NDIS_STATUS_SUCCESS; + } +} + +PAGED +NDIS_STATUS RequestQuery32or64( _In_ PNDIS_OID_REQUEST OidRequest, const ULONG64 value ) +{ + PAGED_CODE(); + NT_ASSERT( (OidRequest->RequestType == NdisRequestQueryInformation) || + (OidRequest->RequestType == NdisRequestQueryStatistics) ); + + if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof( ULONG64 )) + { + OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( ULONG64 ); + OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof( ULONG64 ); + PULONG64 pData = reinterpret_cast(OidRequest->DATA.QUERY_INFORMATION.InformationBuffer); + *pData = value; + return NDIS_STATUS_SUCCESS; + } + else if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof( ULONG )) + { + OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( ULONG ); + OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof( ULONG ); + PULONG pData = reinterpret_cast(OidRequest->DATA.QUERY_INFORMATION.InformationBuffer); + *pData = (ULONG)value; + return NDIS_STATUS_SUCCESS; + } + else + { + OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( ULONG64 ); + OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0; + return NDIS_STATUS_INVALID_LENGTH; + } +} + +PAGED +NDIS_STATUS RequestQueryThreadCapabilities(_In_ PNDIS_OID_REQUEST OidRequest) +{ + PAGED_CODE(); + + OT_CAPABILITIES caps = { 0 }; + ASSIGN_NDIS_OBJECT_HEADER(caps.Header, NDIS_OBJECT_TYPE_DEFAULT, OT_CAPABILITIES_REVISION_1, SIZEOF_OT_CAPABILITIES_REVISION_1); + caps.MiniportMode = OT_MP_MODE_THREAD; // Thread Tunnel mode + + if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < SIZEOF_OT_CAPABILITIES_REVISION_1) + { + OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = SIZEOF_OT_CAPABILITIES_REVISION_1; + OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0; + return NDIS_STATUS_INVALID_LENGTH; + } + else + { + OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = SIZEOF_OT_CAPABILITIES_REVISION_1; + OidRequest->DATA.QUERY_INFORMATION.BytesWritten = SIZEOF_OT_CAPABILITIES_REVISION_1; + memcpy(OidRequest->DATA.QUERY_INFORMATION.InformationBuffer, &caps, SIZEOF_OT_CAPABILITIES_REVISION_1); + return NDIS_STATUS_SUCCESS; + } +} + +PAGED +NDIS_STATUS RequestQueryGenStatistics(_In_ PNDIS_OID_REQUEST OidRequest, _In_ POTTMP_ADAPTER_CONTEXT AdapterContext) +{ + PAGED_CODE(); + + NDIS_STATISTICS_INFO statistics = { 0 }; + ASSIGN_NDIS_OBJECT_HEADER(statistics.Header, NDIS_OBJECT_TYPE_DEFAULT, NDIS_SIZEOF_STATISTICS_INFO_REVISION_1, NDIS_STATISTICS_INFO_REVISION_1); + + statistics.SupportedStatistics = NIC_SUPPORTED_STATISTICS; + + /* Bytes in */ + statistics.ifHCInOctets = AdapterContext->BytesRxDirected + + AdapterContext->BytesRxMulticast + + AdapterContext->BytesRxBroadcast; + + statistics.ifHCInUcastOctets = AdapterContext->BytesRxDirected; + statistics.ifHCInMulticastOctets = AdapterContext->BytesRxMulticast; + statistics.ifHCInBroadcastOctets = AdapterContext->BytesRxBroadcast; + + /* Packets in */ + statistics.ifHCInUcastPkts = AdapterContext->FramesRxDirected; + statistics.ifHCInMulticastPkts = AdapterContext->FramesRxMulticast; + statistics.ifHCInBroadcastPkts = AdapterContext->FramesRxBroadcast; + + /* Errors in */ + statistics.ifInErrors = AdapterContext->RxCrcErrors + + AdapterContext->RxAlignmentErrors + + AdapterContext->RxDmaOverrunErrors + + AdapterContext->RxRuntErrors; + + statistics.ifInDiscards = AdapterContext->RxResourceErrors; + + /* Bytes out */ + statistics.ifHCOutOctets = AdapterContext->BytesTxDirected + + AdapterContext->BytesTxMulticast + + AdapterContext->BytesTxBroadcast; + + statistics.ifHCOutUcastOctets = AdapterContext->BytesTxDirected; + statistics.ifHCOutMulticastOctets = AdapterContext->BytesTxMulticast; + statistics.ifHCOutBroadcastOctets = AdapterContext->BytesTxBroadcast; + + /* Packets out */ + statistics.ifHCOutUcastPkts = AdapterContext->FramesTxDirected; + statistics.ifHCOutMulticastPkts = AdapterContext->FramesTxMulticast; + statistics.ifHCOutBroadcastPkts = AdapterContext->FramesTxBroadcast; + + /* Errors out */ + statistics.ifOutErrors = AdapterContext->TxAbortExcessCollisions + + AdapterContext->TxDmaUnderrun + + AdapterContext->TxLostCRS + + AdapterContext->TxLateCollisions+ + AdapterContext->TransmitFailuresOther; + + statistics.ifOutDiscards = 0ULL; + + return RequestQuery(OidRequest, statistics); +} + +PAGED +_Use_decl_annotations_ +NDIS_STATUS +MPOidRequest( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PNDIS_OID_REQUEST OidRequest + ) +{ + POTTMP_ADAPTER_CONTEXT AdapterContext = (POTTMP_ADAPTER_CONTEXT)MiniportAdapterContext; + NDIS_STATUS status = NDIS_STATUS_NOT_SUPPORTED; + bool fFailExpected = false; + + LogFuncEntry(DRIVER_DEFAULT); + + PAGED_CODE(); + + switch (OidRequest->RequestType) + { + case NdisRequestSetInformation: + switch (OidRequest->DATA.QUERY_INFORMATION.Oid) + { + case OID_802_3_MULTICAST_LIST: + // TODO: Check with Jeffrey how to better handle this + status = NDIS_STATUS_MULTICAST_FULL; + fFailExpected = true; + break; + + // + // Fake it until we make it :) + // We can't bind unless we report success for these OIDs + // + case OID_GEN_CURRENT_PACKET_FILTER: + case OID_PM_ADD_WOL_PATTERN: + case OID_PM_REMOVE_WOL_PATTERN: + case OID_GEN_CURRENT_LOOKAHEAD: + // TODO: Implement this + status = NDIS_STATUS_SUCCESS; + break; + + // Explicitly not supported + case OID_GEN_INTERRUPT_MODERATION: + status = NDIS_STATUS_NOT_SUPPORTED; + fFailExpected = true; + break; + + // Unknown + default: + status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + break; + case NdisRequestQueryInformation: + case NdisRequestQueryStatistics: + switch (OidRequest->DATA.QUERY_INFORMATION.Oid) + { + case OID_GEN_INTERRUPT_MODERATION: + { + static const NDIS_INTERRUPT_MODERATION_PARAMETERS nimp = { + { NDIS_OBJECT_TYPE_DEFAULT, NDIS_INTERRUPT_MODERATION_PARAMETERS_REVISION_1, NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1 }, + 0, + NdisInterruptModerationNotSupported }; + status = RequestQuery( OidRequest, nimp ); + break; + } + + case OID_GEN_RCV_OK: + status = RequestQuery32or64( OidRequest, AdapterContext->FramesRxBroadcast + + AdapterContext->FramesRxMulticast + + AdapterContext->FramesRxDirected ); + break; + + case OID_GEN_MAXIMUM_TOTAL_SIZE: + case OID_GEN_TRANSMIT_BLOCK_SIZE: + case OID_GEN_RECEIVE_BLOCK_SIZE: + status = RequestQuery( OidRequest, HW_MAX_FRAME_SIZE ); + break; + + case OID_GEN_RECEIVE_BUFFER_SPACE: + status = RequestQuery( OidRequest, HW_MAX_FRAME_SIZE * AdapterContext->ulMaxBusyRecvs ); + break; + + case OID_GEN_STATISTICS: + status = RequestQueryGenStatistics(OidRequest, AdapterContext); + break; + + case OID_GEN_TRANSMIT_BUFFER_SPACE: + status = RequestQuery( OidRequest, HW_MAX_FRAME_SIZE * AdapterContext->ulMaxBusySends ); + break; + + case OID_GEN_VENDOR_DESCRIPTION: + case OID_GEN_VENDOR_DRIVER_VERSION: + case OID_GEN_VENDOR_ID: + if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(NIC_VENDOR_DESC)) + { + OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( NIC_VENDOR_DESC ); + OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0; + status = NDIS_STATUS_INVALID_LENGTH; + } + else + { + OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( NIC_VENDOR_DESC ); + OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof( NIC_VENDOR_DESC ); + memcpy(OidRequest->DATA.QUERY_INFORMATION.InformationBuffer, NIC_VENDOR_DESC, sizeof(NIC_VENDOR_DESC)); + status = NDIS_STATUS_SUCCESS; + } + break; + + case OID_GEN_XMIT_OK: + status = RequestQuery32or64( OidRequest, AdapterContext->FramesTxBroadcast + + AdapterContext->FramesTxMulticast + + AdapterContext->FramesTxDirected ); + break; + + case OID_802_3_CURRENT_ADDRESS: + status = RequestQuery( OidRequest, AdapterContext->ExtendedAddress ); + break; + + case OID_802_3_PERMANENT_ADDRESS: + status = RequestQuery( OidRequest, AdapterContext->ExtendedAddress ); + break; + + case OID_PNP_CAPABILITIES: + // We do not support low power + status = NDIS_STATUS_NOT_SUPPORTED; + fFailExpected = true; + break; + + case OID_PNP_QUERY_POWER: + status = NDIS_STATUS_NOT_ACCEPTED; + fFailExpected = true; + break; + + case OID_OT_CAPABILITIES: + status = RequestQueryThreadCapabilities( OidRequest ); + break; + + default: + status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + break; + case NdisRequestMethod: + break; + default: + status = NDIS_STATUS_INVALID_OID; + break; + + } + UNREFERENCED_PARAMETER( OidRequest ); + + if (!fFailExpected && (status != NDIS_STATUS_SUCCESS)) + { + LogFuncExitMsg(DRIVER_DEFAULT, " Type:%u Oid:%u Status:%!NDIS_STATUS!", OidRequest->RequestType, OidRequest->DATA.QUERY_INFORMATION.Oid, status); + } + else + { + // By not using LogFuncExitNDIS, this won't get auto-promoted to a warning on non-0 statuses + LogFuncExitMsg(DRIVER_DEFAULT, " Type:%u Oid:%u Status:%!NDIS_STATUS!", OidRequest->RequestType, OidRequest->DATA.QUERY_INFORMATION.Oid, status); + } + return status; +} + +_Use_decl_annotations_ +VOID +MPCancelOidRequest( + _In_ NDIS_HANDLE /* MiniportAdapterContext */, + _In_ PVOID /* pRequestId */ +) +{ + LogFuncEntry(DRIVER_DEFAULT); + + LogFuncExit(DRIVER_DEFAULT); +} diff --git a/examples/drivers/windows/ottmp/oid.hpp b/examples/drivers/windows/ottmp/oid.hpp new file mode 100644 index 000000000..3c9b122dd --- /dev/null +++ b/examples/drivers/windows/ottmp/oid.hpp @@ -0,0 +1,73 @@ +/* + * 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 + * Header file for the routines related to OIDs + */ + +extern NDIS_OID NICSupportedOids[]; +extern const ULONG SizeOfNICSupportedOids; + +#ifdef OTTMP_LEGACY + +PAGED MINIPORT_OID_REQUEST MPOidRequest; +MINIPORT_CANCEL_OID_REQUEST MPCancelOidRequest; + +#else + +EVT_NET_REQUEST_QUERY_DATA EvtQueryUlong; +EVT_NET_REQUEST_QUERY_DATA EvtQueryGenVendorDescription; +EVT_NET_REQUEST_QUERY_DATA EvtQueryAddress; +EVT_NET_REQUEST_QUERY_DATA EvtQueryGenStatistics; +EVT_NET_REQUEST_QUERY_DATA EvtQueryGenInterruptModeration; +EVT_NET_REQUEST_QUERY_DATA EvtQueryGenXmitRcvOk; + +EVT_NET_REQUEST_QUERY_DATA EvtQueryThreadCapabilities; + +EVT_NET_REQUEST_DEFAULT_SET_DATA EvtDefaultSetData; + +NDIS_STATUS +AdapterSetInformation( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, + _In_ PNDIS_OID_REQUEST NdisSetRequest); + +NDIS_STATUS +AdapterQueryInformation( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, + _Inout_ PNDIS_OID_REQUEST NdisQueryRequest + ); + +PAGED +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +AddDefaultRequestSequentialQueue( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext + ); + + +#endif diff --git a/examples/drivers/windows/ottmp/ottmp.inf b/examples/drivers/windows/ottmp/ottmp.inf new file mode 100644 index 000000000..3a91afa9d --- /dev/null +++ b/examples/drivers/windows/ottmp/ottmp.inf @@ -0,0 +1,121 @@ +; +; 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. +; + +[Version] +Signature = "$Windows NT$" +Class = Net +ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318} +Provider = %OpenThread% +DriverVer = +PnpLockDown = 1 +CatalogFile = ottmp.cat + +[Manufacturer] +%OpenThread% = OpenThread,NT$ARCH$ + +[OpenThread.NT$ARCH$] +%ottmp.DeviceDesc% = ottmp.ndi, root\ottmp ; Root enumerated + +;------------------------------------------------------------------------------- +; OpenThread Tunnel Thread Adapter +;------------------------------------------------------------------------------- +[ottmp.ndi] +Characteristics = 0x4 ; NCF_PHYSICAL +*IfType = 259 ; IF_TYPE_IEEE802154 +*MediaType = 19 ; NdisMediumIP +*PhysicalMediaType = 20 ; NdisPhysicalMediumNative802_15_4 +*IfConnectorPresent = 0 +*AccessType = 2 ; NET_IF_ACCESS_BROADCAST +*ConnectionType = 1 ; NET_IF_CONNECTION_PASSIVE +*DirectionType = 0 ; NET_IF_DIRECTION_SENDRECEIVE +AddReg = ottmp.Reg +CopyFiles = ottmp.CopyFiles + +[ottmp.ndi.Services] +AddService = ottmp, 2, ottmp.Service, ottmp.EventLog + +;------------------------------------------------------------------------------- +; OpenThread Tunnel Miniport Common +;------------------------------------------------------------------------------- +[ottmp.Reg] +HKR, , BusNumber, 0, "0" +HKR, Ndi, Service, 0, "ottmp" +HKR, Ndi\Interfaces, UpperRange, 0, "flpp6" +HKR, Ndi\Interfaces, LowerRange, 0, "802.15.4" + +;------------------------------------------------------------------------------- +; WDF Section +;------------------------------------------------------------------------------- +[ottmp.ndi.Wdf] +KmdfService = ottmp, ottmp.wdfsect + +[ottmp.wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + +;------------------------------------------------------------------------------- +; Driver and Service Section +;------------------------------------------------------------------------------- +[ottmp.CopyFiles] +ottmp.sys,,,2 + +[ottmp.Service] +DisplayName = %ottmp.Service.DispName% +ServiceType = 1 ;%SERVICE_KERNEL_DRIVER% +StartType = 3 ;%SERVICE_DEMAND_START% +ErrorControl = 1 ;%SERVICE_ERROR_NORMAL% +ServiceBinary = %12%\ottmp.sys +LoadOrderGroup = NDIS +AddReg = TextModeFlags.Reg +Description = %ottmp.DeviceDesc% + +[ottmp.EventLog] +AddReg = ottmp.AddEventLog.Reg + +[ottmp.AddEventLog.Reg] +HKR, , EventMessageFile, 0x00020000, "%%SystemRoot%%\System32\netevent.dll" +HKR, , TypesSupported, 0x00010001, 7 + +[TextModeFlags.Reg] +HKR, , TextModeFlags, 0x00010001, 0x0001 + +[SourceDisksNames] +1 = %ottmp.DeviceDesc%,,,"" + +[SourceDisksFiles] +ottmp.sys = 1,, + +[DestinationDirs] +ottmp.CopyFiles = 12 + +;------------------------------------------------------------------------------- +; Localizable Strings +;------------------------------------------------------------------------------- +[Strings] +OpenThread = "OpenThread" +ottmp.DeviceDesc = "OpenThread Tunnel Thread Adapter" +ottmp.Service.DispName = "OpenThread Tunnel Miniport" diff --git a/examples/drivers/windows/ottmp/ottmp.rc b/examples/drivers/windows/ottmp/ottmp.rc new file mode 100644 index 000000000..26a7c387f --- /dev/null +++ b/examples/drivers/windows/ottmp/ottmp.rc @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#include +#include + +/*-----------------------------------------------*/ +/* the following lines are specific to this file */ +/*-----------------------------------------------*/ + +/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR + * and VER_INTERNALNAME_STR must be defined before including COMMON.VER + * The strings don't need a '\0', since common.ver has them. + */ +#define VER_FILETYPE VFT_DRV +/* possible values: VFT_UNKNOWN + VFT_APP + VFT_DLL + VFT_DRV + VFT_FONT + VFT_VXD + VFT_STATIC_LIB +*/ +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +/* possible values VFT2_UNKNOWN + VFT2_DRV_PRINTER + VFT2_DRV_KEYBOARD + VFT2_DRV_LANGUAGE + VFT2_DRV_DISPLAY + VFT2_DRV_MOUSE + VFT2_DRV_NETWORK + VFT2_DRV_SYSTEM + VFT2_DRV_INSTALLABLE + VFT2_DRV_SOUND + VFT2_DRV_COMM +*/ +#define VER_FILEDESCRIPTION_STR "OpenThread Tunnel Miniport" +#define VER_INTERNALNAME_STR "OTTMP.SYS" + + +#include "common.ver" // NT5.0 version file. + diff --git a/examples/drivers/windows/ottmp/pch.hpp b/examples/drivers/windows/ottmp/pch.hpp new file mode 100644 index 000000000..9f3af0e82 --- /dev/null +++ b/examples/drivers/windows/ottmp/pch.hpp @@ -0,0 +1,74 @@ +/* + * 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. + */ + +// +// system headers +// +extern "C" { +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef OTTMP_LEGACY +#include +#endif +#include +#include +#include +} + +// Intellisense definition for DbgRaiseAssertionFailure because for some reason Visual Studio can't +// find it. +#ifdef __INTELLISENSE__ +#define DbgRaiseAssertionFailure() ((void) 0) +#endif + +#include "otOID.h" + +#define CODE_SEG(seg) __declspec(code_seg(seg)) +#define INITCODE CODE_SEG("INIT") +#define PAGED CODE_SEG("PAGE") + +typedef struct _OTTMP_ADAPTER_CONTEXT *POTTMP_ADAPTER_CONTEXT; +typedef struct _OTTMP_DEVICE_CONTEXT *POTTMP_DEVICE_CONTEXT; + +#include "hardware.hpp" +#include "hdlc.hpp" +#include "driver.hpp" +#include "adapter.hpp" +#include "device.hpp" +#include "serial.hpp" +#include "oid.hpp" +#include "platform/logging-windows.h" diff --git a/examples/drivers/windows/ottmp/serial.cpp b/examples/drivers/windows/ottmp/serial.cpp new file mode 100644 index 000000000..6fe5e2e06 --- /dev/null +++ b/examples/drivers/windows/ottmp/serial.cpp @@ -0,0 +1,1308 @@ +/* + * 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. + */ + +#include "pch.hpp" +#include "serial.tmh" + +PAGED +_No_competing_thread_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SerialInitialize( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext + ) +/*++ +Routine Description: + + SerialInitialize attempts to find and open the first COM port available, with + the assumption that it should be for the Thread device. + +Arguments: + + AdapterContext - handle to a OTTMP Adapter + +Return Value: + + NTSTATUS - A failure here will indicate the serial COM port was not able + to be opened. +--*/ +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + PWSTR SymbolicLinkList = NULL; + + LogFuncEntry(DRIVER_DEFAULT); + + PAGED_CODE(); + + do + { + WDF_OBJECT_ATTRIBUTES attr = { 0 }; + WDF_WORKITEM_CONFIG config = { 0 }; + + // + // Send Queue Variables + // + + InitializeListHead(&AdapterContext->SendQueue); + AdapterContext->SendQueueRunning = false; + + WDF_OBJECT_ATTRIBUTES_INIT(&attr); + attr.ParentObject = AdapterContext->Device; + status = WdfSpinLockCreate(&attr, &AdapterContext->SendLock); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "WdfSpinLockCreate(lockSend) failed %!STATUS!", status); + break; + } + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, WDF_DEVICE_INFO); + attr.ParentObject = AdapterContext->Device; + WDF_WORKITEM_CONFIG_INIT(&config, SerialSendLoop); + + status = WdfWorkItemCreate(&config, &attr, &AdapterContext->SendWorkItem); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "WdfWorkItemCreate(SerialSendLoop) failed %!STATUS!", status); + break; + } + GetWdfDeviceInfo(AdapterContext->SendWorkItem)->AdapterContext = AdapterContext; + + // + // Receive Variables + // + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, WDF_DEVICE_INFO); + attr.ParentObject = AdapterContext->Device; + WDF_WORKITEM_CONFIG_INIT(&config, SerialRecvLoop); + + status = WdfWorkItemCreate(&config, &attr, &AdapterContext->RecvWorkItem); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "WdfWorkItemCreate(SerialRecvLoop) failed %!STATUS!", status); + break; + } + + GetWdfDeviceInfo(AdapterContext->RecvWorkItem)->AdapterContext = AdapterContext; + + // Query the system for device with SERIAL interface + status = + IoGetDeviceInterfaces( + &GUID_DEVINTERFACE_COMPORT, + NULL, + 0, + &SymbolicLinkList // List of symbolic names; separate by NULL, EOL with NULL+NULL. + ); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "IoGetDeviceInterfaces failed %!STATUS!", status); + break; + } + + // Make sure there is a COM port found + NT_ASSERT(SymbolicLinkList); + if (*SymbolicLinkList == NULL) { + status = STATUS_DEVICE_NOT_CONNECTED; + LogError(DRIVER_DEFAULT, "No COM ports found!"); + break; + } + +#if DBG + for (PCWSTR sym = SymbolicLinkList; *sym != NULL; sym += wcslen(sym) + 1) + { + LogVerbose(DRIVER_DEFAULT, "Symbolic Name found: %ws", sym); + } +#endif + + // Try to open each serial port until we get that one works or we exhaust them all + for (PCWSTR sym = SymbolicLinkList; *sym != NULL; sym += wcslen(sym) + 1) + { + // Initialize the target + status = SerialInitializeTarget(AdapterContext, sym); + + // Break on success + if (NT_SUCCESS(status)) { + break; + } + } + + } while (false); + + // Clean up on failure + if (!NT_SUCCESS(status)) { + SerialUninitialize(AdapterContext); + } + + if (SymbolicLinkList) { + ExFreePool(SymbolicLinkList); + SymbolicLinkList = NULL; + } + + LogFuncExitNT(DRIVER_DEFAULT, status); + + return status; +} + +PAGED +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +SerialUninitialize( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext + ) +/*++ +Routine Description: + + SerialUninitialize cleans up any cached Wdf IoTarget created from SerialInitialize. + +Arguments: + + AdapterContext - handle to a OTTMP Adapter +--*/ +{ + LogFuncEntry(DRIVER_DEFAULT); + + PAGED_CODE(); + + // TODO - Clean up work item + + // TODO - Clean up send queue + + SerialUninitializeTarget(AdapterContext); + + if (AdapterContext->RecvWorkItem) { + WdfWorkItemFlush(AdapterContext->RecvWorkItem); + } + + LogFuncExit(DRIVER_DEFAULT); +} + +PAGED +_No_competing_thread_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SerialInitializeTarget( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, + _In_ PCWSTR TargetName +) +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + WDFIOTARGET tempTarget = WDF_NO_HANDLE; + + LogFuncEntry(DRIVER_DEFAULT); + + PAGED_CODE(); + + do + { + DECLARE_UNICODE_STRING_SIZE(PortName, 64); // Maximum name length of the device path to a serial port + WDF_IO_TARGET_OPEN_PARAMS openParams = { 0 }; + WDF_OBJECT_ATTRIBUTES attr = { 0 }; + + // Create the Wdf IoTarget + status = WdfIoTargetCreate(AdapterContext->Device, WDF_NO_OBJECT_ATTRIBUTES, &tempTarget); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "WdfIoTargetCreate failed %!STATUS!", status); + break; + } + + // Try the COM port + LogInfo(DRIVER_DEFAULT, "Opening device: %ws", TargetName); + RtlInitUnicodeString(&PortName, TargetName); + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( + &openParams, + &PortName, + GENERIC_READ | GENERIC_WRITE); + + // Open the port on the target + status = WdfIoTargetOpen(tempTarget, &openParams); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "WdfIoTargetOpen(%wZ) failed %!STATUS!", &PortName, status); + break; + } + + AdapterContext->WdfIoTarget = tempTarget; + tempTarget = WDF_NO_HANDLE; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, WDF_DEVICE_INFO); + attr.ParentObject = AdapterContext->Device; + + status = WdfRequestCreate(&attr, AdapterContext->WdfIoTarget, &AdapterContext->RecvReadRequest); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "WdfRequestCreate failed %!STATUS!", status); + break; + } + + // Try to configure the target + status = SerialConfigure(AdapterContext); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "SerialConfigure failed %!STATUS!", status); + break; + } + + } while (false); + + // Clean up on failure + if (!NT_SUCCESS(status)) { + SerialUninitializeTarget(AdapterContext); + } + + if (tempTarget) { + WdfIoTargetClose(tempTarget); + } + + LogFuncExitNT(DRIVER_DEFAULT, status); + + return status; +} + +PAGED +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +SerialUninitializeTarget( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext +) +/*++ +Routine Description: + + SerialUninitializeTarget cleans up any cached Wdf IoTarget created from SerialInitializeTarget. + +Arguments: + + AdapterContext - handle to a OTTMP Adapter +--*/ +{ + LogFuncEntry(DRIVER_DEFAULT); + + PAGED_CODE(); + + if (AdapterContext->WdfIoTarget) { + // + // WdfIoTargetStop will cancel all the outstanding I/O and wait + // for them to complete before returning. WdfIoTargetStop with the + // action type WdfIoTargetCancelSentIo can be called at IRQL PASSIVE_LEVEL only. + // + WdfIoTargetStop(AdapterContext->WdfIoTarget, WdfIoTargetCancelSentIo); + WdfIoTargetClose(AdapterContext->WdfIoTarget); + AdapterContext->WdfIoTarget = NULL; + } + + LogFuncExit(DRIVER_DEFAULT); +} + +_Must_inspect_result_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +FORCEINLINE +SerialSendIoctl( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, + _In_ ULONG IoctlCode, + _In_opt_ PWDF_REQUEST_SEND_OPTIONS RequestOptions = NULL, + _In_opt_ PWDF_MEMORY_DESCRIPTOR InputBuffer = WDF_NO_HANDLE, + _In_opt_ PWDF_MEMORY_DESCRIPTOR OutputBuffer = WDF_NO_HANDLE, + _Out_opt_ PULONG_PTR BytesReturned = NULL + ) +/*++ +Routine Description: + + Helper/Wrapper function for WdfIoTargetSendIoctlSynchronously. + +--*/ +{ + return WdfIoTargetSendIoctlSynchronously( + AdapterContext->WdfIoTarget, + WDF_NO_HANDLE, + IoctlCode, + InputBuffer, + OutputBuffer, + RequestOptions, + BytesReturned); +} + +PAGED +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SerialConfigure( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext + ) +/*++ +Routine Description: + + SerialInitialize attempts to find and open the first COM port available, with + the assumption that it should be for the Thread device. + +Arguments: + + AdapterContext - handle to a OTTMP Adapter + +Return Value: + + NTSTATUS - A failure here will indicate the serial COM port was not able + to be configured as desired. +--*/ +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + WDF_MEMORY_DESCRIPTOR inputDesc; + WDF_REQUEST_SEND_OPTIONS wrso = { + sizeof(WDF_REQUEST_SEND_OPTIONS), + WDF_REQUEST_SEND_OPTION_TIMEOUT | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, + WDF_REL_TIMEOUT_IN_SEC(1) // Nothing should take more than a second to complete + }; + + LogFuncEntry(DRIVER_DEFAULT); + + PAGED_CODE(); + + do + { + // Initial reset of the device + status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_RESET_DEVICE, &wrso); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_RESET_DEVICE failed %!STATUS!", status); + break; + } + + // 8 bits, no parity, 1 stop bit + { + const SERIAL_LINE_CONTROL slc = { STOP_BIT_1, NO_PARITY, 8 }; + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&slc, sizeof(slc)); + + status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_LINE_CONTROL, &wrso, &inputDesc); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_LINE_CONTROL failed %!STATUS!", status ); + break; + } + } + + // Xon and Xoff characters + { + const SERIAL_CHARS sc = { 0, 0, 0, 0, 11, 13 }; + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&sc, sizeof(sc)); + + status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_CHARS, &wrso, &inputDesc); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_CHARS failed %!STATUS!", status); + break; + } + } + + // Baud rate + { + const SERIAL_BAUD_RATE sbr = { 115200 }; + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&sbr, sizeof(sbr)); + + status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_BAUD_RATE, &wrso, &inputDesc); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_BAUD_RATE failed %!STATUS!", status); + break; + } + } + + /*{ + // Only send if CTS is set, Set RTS before sending + const SERIAL_HANDFLOW shf = + { + SERIAL_CTS_HANDSHAKE, SERIAL_RTS_CONTROL, + MAX_SPINEL_COMMAND_LENGTH, MAX_SPINEL_COMMAND_LENGTH + }; + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&shf, sizeof(shf)); + + status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_HANDFLOW, &wrso, &inputDesc); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_HANDFLOW failed %!STATUS!", status); + // break; Ignore for now + } + } + + status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_XON, &wrso); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_XON failed %!STATUS!", status); + break; + } + + status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_RTS, &wrso); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_RTS failed %!STATUS!", status); + break; + } + + status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_DTR, &wrso); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_DTR failed %!STATUS!", status); + break; + }*/ + + { + const SERIAL_TIMEOUTS sto = { + 1, 0, 0, // On read, only timeout if more than 1ms *between* bytes (wait forever for first byte) + 1, 10 // Write times out after (1ms * n-bytes) + (10ms) + }; + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&sto, sizeof(sto)); + + status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_TIMEOUTS, &wrso, &inputDesc); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_TIMEOUTS failed %!STATUS!", status); + break; + } + } + + status = SerialFlushAndCheckStatus(AdapterContext); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "SerialFlushAndCheckStatus failed %!STATUS!", status); + break; + } + + } while (false); + + LogFuncExitNT(DRIVER_DEFAULT, status); + + return status; +} + +PAGED +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SerialCheckStatus( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, + _In_ bool DataExpected + ) +/*++ +Routine Description: + + SerialCheckStatus validates the current status of the serial COM port. + +Arguments: + + AdapterContext - handle to a OTTMP Adapter + +Return Value: + + NTSTATUS - A failure here will indicate the serial COM port is not in an + expected state. +--*/ +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + WDF_MEMORY_DESCRIPTOR outputDesc; + WDF_REQUEST_SEND_OPTIONS wrso = { + sizeof(WDF_REQUEST_SEND_OPTIONS), + WDF_REQUEST_SEND_OPTION_TIMEOUT | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, + WDF_REL_TIMEOUT_IN_SEC(1) // Nothing should take more than a second to complete + }; + ULONG_PTR bytesReturned = 0; + SERIAL_STATUS ss = { 0 }; + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDesc, (PVOID)&ss, sizeof(ss)); + + LogFuncEntry(DRIVER_DEFAULT); + + PAGED_CODE(); + + // Check to ensure we are ready to send + status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_GET_COMMSTATUS, &wrso, WDF_NO_HANDLE, &outputDesc, &bytesReturned); + + if (!NT_SUCCESS(status)) + { + LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_GET_COMMSTATUS failed %!STATUS!", status); + } + else if (bytesReturned >= sizeof(ss)) + { + if (ss.HoldReasons) + { + if (ss.HoldReasons != SERIAL_TX_WAITING_FOR_CTS) + { + LogError(DRIVER_DEFAULT, "HoldReasons is wrong (should only be CTS, but is %x)", ss.HoldReasons ); + status = STATUS_INVALID_DEVICE_STATE; + } + else if (!DataExpected) + { + LogError(DRIVER_DEFAULT, "Adapter already has data on init!?!?!"); + status = STATUS_INVALID_STATE_TRANSITION; + } + } + if (ss.Errors) + { + LogWarning(DRIVER_DEFAULT, "Unexpected Error %x", ss.Errors); + status = STATUS_UNSUCCESSFUL; + } + } + + LogFuncExitNT(DRIVER_DEFAULT, status); + + return status; +} + +PAGED +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SerialFlushAndCheckStatus( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext + ) +/*++ +Routine Description: + + SerialFlushAndCheckStatus flushed and validates the current status of the serial COM port. + +Arguments: + + AdapterContext - handle to a OTTMP Adapter + +Return Value: + + NTSTATUS - A failure here will indicate the serial COM port is not in an + expected state. +--*/ +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + + LogFuncEntry(DRIVER_DEFAULT); + + PAGED_CODE(); + + do + { + WDF_MEMORY_DESCRIPTOR inputDesc; + WDF_REQUEST_SEND_OPTIONS wrso = { + sizeof(WDF_REQUEST_SEND_OPTIONS), + WDF_REQUEST_SEND_OPTION_TIMEOUT | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, + WDF_REL_TIMEOUT_IN_SEC(1) // Nothing should take more than a second to complete + }; + const ULONG flags = SERIAL_PURGE_RXABORT | SERIAL_PURGE_RXCLEAR | SERIAL_PURGE_TXABORT | SERIAL_PURGE_TXCLEAR; + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&flags, sizeof(flags)); + status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_PURGE, &wrso, &inputDesc); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_PURGE failed %!STATUS!", status); + break; + } + + status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_CLEAR_STATS, &wrso); + + if (!NT_SUCCESS(status)) { + LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_CLEAR_STATS failed %!STATUS!", status); + break; + } + + status = SerialCheckStatus(AdapterContext, false); + for (int i = 0; !NT_SUCCESS(status) && i < 20; i++) + { + NdisMSleep(1); // just sleep enough to give up our quantum + status = SerialCheckStatus(AdapterContext, false); + } + + } while (false); + + LogFuncExitNT(DRIVER_DEFAULT, status); + + return status; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +bool +SerialPushSend( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, + _In_ PSERIAL_SEND_ITEM SendItem + ) +{ + WdfSpinLockAcquire(AdapterContext->SendLock); + + // Start the work item up if it's not already running + if (!AdapterContext->SendQueueRunning) + { + LogVerbose(DRIVER_DEFAULT, "Starting Send Work Item"); + AdapterContext->SendQueueRunning = true; + WdfWorkItemEnqueue(AdapterContext->SendWorkItem); + } + + // Insert the new item at the end of the list + InsertTailList(&AdapterContext->SendQueue, &SendItem->Link); + + WdfSpinLockRelease(AdapterContext->SendLock); + + return true; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +PSERIAL_SEND_ITEM +SerialPopSend( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext + ) +{ + PLIST_ENTRY current = NULL; + + // Grab the head of the list + // Careful, this might have gotten aborted, leaving the list empty + WdfSpinLockAcquire(AdapterContext->SendLock); + + if (!IsListEmpty(&AdapterContext->SendQueue)) + { + current = RemoveHeadList(&AdapterContext->SendQueue); + } + if (current == NULL) + { + // Do this under the lock, but the state is consumed outside the lock + AdapterContext->SendQueueRunning = false; + LogVerbose(DRIVER_DEFAULT, "Send Work Item Complete"); + } + + WdfSpinLockRelease(AdapterContext->SendLock); + + return current ? CONTAINING_RECORD(current, SERIAL_SEND_ITEM, Link) : NULL; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +SerialSendData( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, + _In_ PNET_BUFFER_LIST NetBufferList + ) +/*++ +Routine Description: + + SerialSendData encodes and queues up the data to be sent over the serial COM port. + +Arguments: + + AdapterContext - handle to a OTTMP Adapter + + NetBufferLists - a single NET_BUFFER_LIST object, containing a signle NET_BUFFER for + Spinel tunnel commands. + + DispatchLevel - flag indicating if we are running at dispatch or not + +Return Value: + + NTSTATUS - A failure here will indicate we either failed to encode or queue the data. +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_OBJECT_ATTRIBUTES attributes; + WDFMEMORY WdfMemBuffer = NULL; + PSERIAL_SEND_ITEM SendItem = NULL; + PUCHAR DecodedBuffer = NULL; + ULONG DecodedBufferLength = NetBufferList->FirstNetBuffer->DataLength; + ULONG EncodedBufferLength; + + LogFuncEntry(DRIVER_DEFAULT); + + do + { + // Get the decoded buffer from the NBL/NB. We required + // the use of contiguous buffers. + DecodedBuffer = (PUCHAR)NdisGetDataBuffer(NetBufferList->FirstNetBuffer, DecodedBufferLength, NULL, 1, 0); + if (DecodedBuffer == NULL) { + status = STATUS_INVALID_PARAMETER; + break; + } + + LogVerbose(DRIVER_DEFAULT, "Sending %u decoded bytes", DecodedBufferLength); + DumpBuffer(DecodedBuffer, DecodedBufferLength); + + // Calculate the buffer size required + EncodedBufferLength = HdlcComputeEncodedLength(DecodedBuffer, DecodedBufferLength); + + // Allocate the memory + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = AdapterContext->Device; +#pragma warning(push) +#pragma warning(suppress: 28160) // Param 3 could be 0 + status = WdfMemoryCreate( + &attributes, + NonPagedPoolNx, + 0, + SERIAL_SEND_ITEM_SIZE + EncodedBufferLength, + &WdfMemBuffer, + (PVOID*)&SendItem + ); +#pragma warning(pop) + + if (!NT_SUCCESS(status)) { + LogWarning(DRIVER_DEFAULT, "WdfMemoryCreate (%u bytes) failed %!STATUS!", (SERIAL_SEND_ITEM_SIZE + EncodedBufferLength), status); + break; + } + + SendItem->NetBufferList = NetBufferList; + SendItem->WdfMemory = WdfMemBuffer; + SendItem->EncodedBufferLength = EncodedBufferLength; + + // Encode data + if (!HdlcEncodeBuffer(DecodedBuffer, DecodedBufferLength, SendItem->EncodedBuffer, EncodedBufferLength)) { + NT_ASSERT(FALSE); // Should never fail, unless we have a bug in the length computation + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // Queue data to be sent out + if (!SerialPushSend(AdapterContext, SendItem)) { + status = STATUS_DEVICE_NOT_READY; + break; + } + + } while (false); + + if (!NT_SUCCESS(status)) { + if (WdfMemBuffer) { + WdfObjectDelete(WdfMemBuffer); + } + } + + LogFuncExitNT(DRIVER_DEFAULT, status); + + return status; +} + +PAGED +_Function_class_(EVT_WDF_WORKITEM) +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +SerialSendLoop( + _In_ WDFWORKITEM WorkItem + ) +/*++ +Routine Description: + + SerialSendLoop handles the actual sending of data over the serial COM port. + +Arguments: + + WorkItem - handle to a Wdf Device Info object for the Adapter context + +--*/ +{ + POTTMP_ADAPTER_CONTEXT AdapterContext = GetWdfDeviceInfo(WorkItem)->AdapterContext; + WDF_REQUEST_SEND_OPTIONS wrso = { + sizeof(WDF_REQUEST_SEND_OPTIONS), + WDF_REQUEST_SEND_OPTION_TIMEOUT | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, + WDF_REL_TIMEOUT_IN_SEC(1) // Nothing should take more than a second to complete + }; + WDFMEMORY_OFFSET offset = { 0 }; + PSERIAL_SEND_ITEM SendItem = NULL; + WDF_MEMORY_DESCRIPTOR wmd; + NTSTATUS status; + + WDF_OBJECT_ATTRIBUTES Attributes; + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + Attributes.ParentObject = AdapterContext->Device; + + LogFuncEntry(DRIVER_DEFAULT); + + PAGED_CODE(); + +#pragma warning(push) +#pragma warning(suppress: 6387) // Param 2 is NULL + WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&wmd, NULL, &offset); +#pragma warning(pop) + + while (NULL != (SendItem = SerialPopSend(AdapterContext))) + { + LogVerbose(DRIVER_DEFAULT, "Sending %u encoded bytes", SendItem->EncodedBufferLength); + DumpBuffer(SendItem->EncodedBuffer, SendItem->EncodedBufferLength); + + if (SendItem->EncodedBufferLength > 0) + { + offset.BufferLength = SendItem->EncodedBufferLength; + status = + WdfMemoryCreatePreallocated( + &Attributes, + SendItem->EncodedBuffer, + SendItem->EncodedBufferLength, + &wmd.u.HandleType.Memory); + + if (!NT_SUCCESS( status )) { + LogError(DRIVER_DEFAULT, "WdfIoTargetSendWriteSynchronously (%u bytes) failed %!STATUS!", SendItem->EncodedBufferLength, status); + } + else + { + // Send the buffer out + status = WdfIoTargetSendWriteSynchronously(AdapterContext->WdfIoTarget, NULL, &wmd, NULL, &wrso, NULL); + + if (!NT_SUCCESS( status )) { + LogError(DRIVER_DEFAULT, "WdfIoTargetSendWriteSynchronously (%u bytes) failed %!STATUS!", SendItem->EncodedBufferLength, status); + } + + WdfObjectDelete(wmd.u.HandleType.Memory); + } + } + else + { + status = STATUS_INVALID_PARAMETER; + } + + // Complete the NetBufferList + SendItem->NetBufferList->Status = status; +#ifdef OTTMP_LEGACY + NdisMSendNetBufferListsComplete(AdapterContext->Adapter, SendItem->NetBufferList, 0); +#else + NetBufferListsCompleteSend(SendItem->NetBufferList); +#endif + + // Hack to sleep 1 ms per 5 bytes sent + NdisMSleep(1000 * (1 + SendItem->EncodedBufferLength / 5)); + + // Cleanup + WdfObjectDelete(SendItem->WdfMemory); + }; + + LogFuncExit(DRIVER_DEFAULT); +} + +PAGED +_Function_class_(EVT_WDF_WORKITEM) +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +SerialRecvLoop( + _In_ WDFWORKITEM WorkItem + ) +{ + POTTMP_ADAPTER_CONTEXT AdapterContext = GetWdfDeviceInfo(WorkItem)->AdapterContext; + WDFMEMORY mem = { 0 }; + NTSTATUS status; + + LogFuncEntry(DRIVER_DEFAULT); + + PAGED_CODE(); + + do + { + WDFREQUEST & request = AdapterContext->RecvReadRequest; + + WDF_OBJECT_ATTRIBUTES attributes; + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = AdapterContext->Device; + status = + WdfMemoryCreatePreallocated( + &attributes, + AdapterContext->RecvBuffer + AdapterContext->RecvBufferLength, + MAX_SPINEL_COMMAND_LENGTH, + &mem); + + if (!NT_SUCCESS(status)) + { + LogError(DRIVER_DEFAULT, "WdfMemoryCreateFromLookaside failed %!STATUS!", status); + break; + } + + status = WdfIoTargetFormatRequestForRead(AdapterContext->WdfIoTarget, request, mem, NULL, NULL); + + if (!NT_SUCCESS(status)) + { + LogError(DRIVER_DEFAULT, "WdfIoTargetFormatRequestForRead failed %!STATUS!", status); + break; + } + else + { + WdfRequestSetCompletionRoutine(request, SerialRecvComplete, AdapterContext); + if (WdfRequestSend(request, AdapterContext->WdfIoTarget, WDF_NO_SEND_OPTIONS)) + { + // Send succeeded, no cleanup + mem = NULL; + break; + } + + status = WdfRequestGetStatus(request); + if (!NT_SUCCESS(status)) + { + LogError(DRIVER_DEFAULT, "WdfRequestSend failed %!STATUS!", status); + } + + WDF_REQUEST_REUSE_PARAMS reuseParams = { 0 }; + WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS); + + // refresh the request so it is ready to reuse + status = WdfRequestReuse(request, &reuseParams); + if (!NT_SUCCESS(status)) + { + NT_ASSERT(NT_SUCCESS(status)); + LogError(DRIVER_DEFAULT, "WdfRequestReuse failed %!STATUS!", status); + } + } + + } while (false); + + if (mem) { + WdfObjectDelete(mem); + } + + LogFuncExit(DRIVER_DEFAULT); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +_When_(return==0,_At_(*pNetBufferList, __drv_allocatesMem(mem))) +_When_(return==0,_At_((*pNetBufferList)->FirstNetBuffer, __drv_allocatesMem(mem))) +NTSTATUS +SerialAllocateNetBufferList( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, + _In_ ULONG BufferLength, + _Out_ PNET_BUFFER_LIST *pNetBufferList + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PNET_BUFFER_LIST NetBufferList = NULL; + PNET_BUFFER NetBuffer = NULL; + + do + { + +#ifdef OTTMP_LEGACY + // Allocate the NetBufferList + NetBufferList = NdisAllocateNetBufferList(AdapterContext->pGlobals->hNblPool, 0, 0); + + if (NetBufferList == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // Allocate the NetBuffer + NetBufferList->FirstNetBuffer = NdisAllocateNetBufferMdlAndData(AdapterContext->pGlobals->hNbPool); + + if (NetBufferList->FirstNetBuffer == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } +#else + // Grab a NetBufferList from the collection + status = NetBufferListCollectionRetrieveNbls(AdapterContext->ReceiveCollection, 1, &NetBufferList); + + if (!NT_SUCCESS(status)) + { + break; + } +#endif + + NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; + NetBuffer = NetBufferList->FirstNetBuffer; + + // If there is no buffer allocated yet, go ahead and allocate the max + if (NET_BUFFER_DATA_LENGTH(NetBuffer) == 0) + { + // Allocate the max buffer size + ndisStatus = NdisRetreatNetBufferDataStart(NetBuffer, MAX_SPINEL_COMMAND_LENGTH, 0, NULL); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + LogError(DRIVER_DEFAULT, "NdisRetreatNetBufferDataStart failed %!NDIS_STATUS!", ndisStatus); + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + } + + // By this point, we should have a NetBuffer with a contiguous memory block of MAX_SPINEL_COMMAND_LENGTH bytes, + // though it's offset could be anywhere in the buffer, from when it was previously used. + + // Adjust buffer length to fit the requested length + if (NET_BUFFER_DATA_LENGTH(NetBuffer) > BufferLength) + { + NdisAdvanceNetBufferDataStart(NetBuffer, NET_BUFFER_DATA_LENGTH(NetBuffer) - BufferLength, FALSE, NULL); + } + else if (NET_BUFFER_DATA_LENGTH(NetBuffer) < BufferLength) + { + ndisStatus = NdisRetreatNetBufferDataStart(NetBuffer, BufferLength - NET_BUFFER_DATA_LENGTH(NetBuffer), 0, NULL); + NT_ASSERT(ndisStatus == NDIS_STATUS_SUCCESS); + if (ndisStatus != NDIS_STATUS_SUCCESS) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + } + + // Set the output + *pNetBufferList = NetBufferList; + + } while (FALSE); + + if (!NT_SUCCESS(status)) + { +#ifdef OTTMP_LEGACY + if (NetBuffer) NdisFreeNetBuffer(NetBuffer); + if (NetBufferList) NdisFreeNetBufferList(NetBufferList); +#else + if (NetBufferList) NetBufferListsDiscardReceive(NetBufferList); +#endif + } + + return status; +} + +_Function_class_(EVT_WDF_REQUEST_COMPLETION_ROUTINE) +_IRQL_requires_same_ +VOID +SerialRecvComplete( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, + _In_ WDFCONTEXT Context + ) +{ + POTTMP_ADAPTER_CONTEXT AdapterContext = (POTTMP_ADAPTER_CONTEXT)Context; + NTSTATUS status; + + LogFuncEntry(DRIVER_DEFAULT); + + UNREFERENCED_PARAMETER(Target); // Except for an assert + NT_ASSERT((Target == AdapterContext->WdfIoTarget) || (WDF_NO_HANDLE == AdapterContext->WdfIoTarget)); + NT_ASSERT(Request == AdapterContext->RecvReadRequest); + + WDFMEMORY mem = Params->Parameters.Read.Buffer; + NT_ASSERT(mem); + + status = WdfRequestGetStatus(Request); + if (NT_SUCCESS(status)) + { + NT_ASSERT(Params->Type == WdfRequestTypeRead); + NT_ASSERT(Params->Parameters.Read.Offset == 0); + + size_t MemoryLength = 0; + NT_ASSERT(AdapterContext->RecvBuffer + AdapterContext->RecvBufferLength == (PUCHAR)WdfMemoryGetBuffer(mem, &MemoryLength)); + UNREFERENCED_PARAMETER(MemoryLength); + + LogVerbose(DRIVER_DEFAULT, "Received %u encoded bytes", (ULONG)Params->IoStatus.Information); + DumpBuffer(AdapterContext->RecvBuffer + AdapterContext->RecvBufferLength, (ULONG)Params->IoStatus.Information); + + AdapterContext->RecvBufferLength += (ULONG)Params->IoStatus.Information; + + // Decode and receive + ULONG ReadOffset = 0; + while (AdapterContext->RecvBufferLength > ReadOffset) + { + // Parse, validate and compute the decoded buffer size requirements + ULONG UsedEncodedBufferLength = AdapterContext->RecvBufferLength - ReadOffset; + ULONG DecodedBufferLength = 0; + bool HasGoodBuffer = false; + bool HasCompleteBuffer = + HdlcDecodeBuffer( + AdapterContext->RecvBuffer + ReadOffset, + &UsedEncodedBufferLength, + &DecodedBufferLength, + NULL, + &HasGoodBuffer); + + // We should never have used more buffer than available + NT_ASSERT(UsedEncodedBufferLength <= AdapterContext->RecvBufferLength - ReadOffset); + + // Did we have a complete (start and end sequence chars) buffer? + if (!HasCompleteBuffer) + { + AdapterContext->RecvBufferLength -= ReadOffset; + + LogWarning(DRIVER_DEFAULT, "Buffering %u incomplete bytes", AdapterContext->RecvBufferLength); + NT_ASSERT(AdapterContext->RecvBufferLength < MAX_SPINEL_COMMAND_LENGTH); + + memmove_s(AdapterContext->RecvBuffer, sizeof(AdapterContext->RecvBuffer), + AdapterContext->RecvBuffer + ReadOffset, AdapterContext->RecvBufferLength); + break; + } + else + { + // Was the buffer too short or did it's FCS not match? + if (HasGoodBuffer) + { + NT_ASSERT(UsedEncodedBufferLength <= MAX_SPINEL_COMMAND_LENGTH); + + // Allocate the NetBufferList & NetBuffer to decode the data to + PNET_BUFFER_LIST NetBufferList = NULL; + status = SerialAllocateNetBufferList(AdapterContext, DecodedBufferLength, &NetBufferList); + + if (NT_SUCCESS(status)) + { + PNET_BUFFER NetBuffer = NetBufferList->FirstNetBuffer; + NT_ASSERT(DecodedBufferLength == NET_BUFFER_DATA_LENGTH(NetBuffer)); + + // Get pointer to contiguous buffer + PUCHAR DecodedBuffer = (PUCHAR)NdisGetDataBuffer(NetBuffer, DecodedBufferLength, NULL, 1, 0); + NT_ASSERT(DecodedBuffer); + if (DecodedBuffer) + { + HasCompleteBuffer = + HdlcDecodeBuffer( + AdapterContext->RecvBuffer + ReadOffset, + &UsedEncodedBufferLength, + &DecodedBufferLength, + DecodedBuffer, + &HasGoodBuffer); + + NT_ASSERT(HasCompleteBuffer); + NT_ASSERT(HasGoodBuffer); + NT_ASSERT(DecodedBufferLength == NET_BUFFER_DATA_LENGTH(NetBuffer)); + + LogVerbose(DRIVER_DEFAULT, "Received %u decoded bytes", DecodedBufferLength); + DumpBuffer(DecodedBuffer, DecodedBufferLength); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + + if (NT_SUCCESS(status)) + { + // Indicate up the new NBL we just created +#ifdef OTTMP_LEGACY + NdisMIndicateReceiveNetBufferLists( + AdapterContext->Adapter, + NetBufferList, + NDIS_DEFAULT_PORT_NUMBER, + 1, + 0 + ); +#else + NetBufferListsCompleteReceive( + NetBufferList, + NDIS_DEFAULT_PORT_NUMBER, + 0 + ); +#endif + } + else + { +#ifdef OTTMP_LEGACY + NdisFreeNetBuffer(NetBuffer); + NdisFreeNetBufferList(NetBufferList); +#else + NetBufferListsDiscardReceive(NetBufferList); +#endif + } + } + } + else + { + LogWarning(DRIVER_DEFAULT, "Dropping %u bad bytes", UsedEncodedBufferLength); + DumpBuffer(AdapterContext->RecvBuffer + ReadOffset, UsedEncodedBufferLength); + } + + // Skip over used data + ReadOffset += UsedEncodedBufferLength; + } + } + + // We read all the buffer, so reset the length + if (AdapterContext->RecvBufferLength == ReadOffset) + { + AdapterContext->RecvBufferLength = 0; + } + } + else + { + LogError(DRIVER_DEFAULT, "Read request failed %!STATUS!", status); + } + + WdfObjectDelete(mem); + + if (status != STATUS_DELETE_PENDING) + { + WDF_REQUEST_REUSE_PARAMS reuseParams = { 0 }; + WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS); + status = WdfRequestReuse(Request, &reuseParams); + if (!NT_SUCCESS(status)) + { + NT_ASSERT(NT_SUCCESS(status)); + LogError(DRIVER_DEFAULT, "WdfRequestReuse failed %!STATUS!", status); + } + + LogVerbose(DRIVER_DEFAULT, "Starting recv worker"); + WdfWorkItemEnqueue(AdapterContext->RecvWorkItem); + } + + LogFuncExit(DRIVER_DEFAULT); +} + +VOID +DumpLine( + _In_reads_bytes_(aLength) PCUCHAR aBuf, + _In_ size_t aLength + ) +{ + char buf[80] = {0}; + char *cur = buf; + + sprintf_s(cur, sizeof(buf) - (cur - buf), "|"); + cur += 1; + + for (size_t i = 0; i < 16; i++) + { + if (i < aLength) + { + sprintf_s(cur, sizeof(buf) - (cur - buf), " %02X", aBuf[i]); + cur += 3; + } + else + { + sprintf_s(cur, sizeof(buf) - (cur - buf), " .."); + cur += 3; + } + + if (!((i + 1) % 8)) + { + sprintf_s(cur, sizeof(buf) - (cur - buf), " |"); + cur += 2; + } + } + + sprintf_s(cur, sizeof(buf) - (cur - buf), " "); + cur += 1; + + for (size_t i = 0; i < 16; i++) + { + if (i < aLength && isprint(0x7f & aBuf[i])) + { + char c = 0x7f & aBuf[i]; + sprintf_s(cur, sizeof(buf) - (cur - buf), "%c", c); + cur += 1; + } + else + { + sprintf_s(cur, sizeof(buf) - (cur - buf), "."); + cur += 1; + } + } + + LogVerbose(DRIVER_DEFAULT, "%s", buf); +} + +VOID +DumpBuffer( + _In_reads_bytes_(aLength) PCUCHAR aBuf, + _In_ size_t aLength + ) +{ + for (size_t i = 0; i < aLength; i += 16) + { + DumpLine(aBuf + i, (aLength - i) < 16 ? (aLength - i) : 16); + } +} diff --git a/examples/drivers/windows/ottmp/serial.hpp b/examples/drivers/windows/ottmp/serial.hpp new file mode 100644 index 000000000..7d581921e --- /dev/null +++ b/examples/drivers/windows/ottmp/serial.hpp @@ -0,0 +1,122 @@ +/* + * 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. + */ + +#pragma once + +PAGED +_No_competing_thread_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SerialInitialize( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext + ); + +PAGED +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +SerialUninitialize( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext + ); + +PAGED +_No_competing_thread_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SerialInitializeTarget( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, + _In_ PCWSTR TargetName +); + +PAGED +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +SerialUninitializeTarget( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext +); + +PAGED +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +SerialConfigure( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext + ); + +PAGED +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SerialCheckStatus( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, + _In_ bool DataExpected + ); + +PAGED +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SerialFlushAndCheckStatus( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +SerialSendData( + _In_ POTTMP_ADAPTER_CONTEXT AdapterContext, + _In_ PNET_BUFFER_LIST NetBufferLists + ); + +PAGED +_Function_class_(EVT_WDF_WORKITEM) +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +SerialSendLoop( + _In_ WDFWORKITEM WorkItem + ); + +PAGED +_Function_class_(EVT_WDF_WORKITEM) +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +SerialRecvLoop( + _In_ WDFWORKITEM WorkItem + ); + +_Function_class_(EVT_WDF_REQUEST_COMPLETION_ROUTINE) +_IRQL_requires_same_ +VOID +SerialRecvComplete( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, + _In_ WDFCONTEXT Context + ); + +VOID +DumpBuffer( + _In_reads_bytes_(aLength) PCUCHAR aBuf, + _In_ size_t aLength + );