summaryrefslogtreecommitdiff
path: root/network/wwan/cxwmbclass/device.cpp
diff options
context:
space:
mode:
authortaj0207 <[email protected]>2022-11-09 11:31:14 -0800
committerGitHub <[email protected]>2022-11-09 11:31:14 -0800
commit82537c2a1da27908ffbc5d98a7ad77e6af0956c8 (patch)
treeaf74ee90abbbe0eacc15c28873f35a0ca46cec7c /network/wwan/cxwmbclass/device.cpp
parent96e23c8f1172e227122de5c66390e8e5b4028831 (diff)
Add mobile broadband mbbcx client driver sample code (#797)
Diffstat (limited to 'network/wwan/cxwmbclass/device.cpp')
-rw-r--r--network/wwan/cxwmbclass/device.cpp381
1 files changed, 381 insertions, 0 deletions
diff --git a/network/wwan/cxwmbclass/device.cpp b/network/wwan/cxwmbclass/device.cpp
new file mode 100644
index 00000000..0aadfc35
--- /dev/null
+++ b/network/wwan/cxwmbclass/device.cpp
@@ -0,0 +1,381 @@
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+
+#include "precomp.h"
+
+#include "device.h"
+#include "rxqueue.h"
+
+const ULONG MBB_DEFAULT_IDLE_TIMEOUT_HINT_MS = 2u * 1000u; // 2 seconds
+
+_Use_decl_annotations_ VOID EvtMbbDeviceSendMbimFragment(WDFDEVICE Device, MBBREQUEST Fragment)
+{
+ PWMBCLASS_DEVICE_CONTEXT deviceContext = WmbClassGetDeviceContext(Device);
+
+ size_t bufferSize = 0;
+ PVOID buffer = MbbRequestGetBuffer(Fragment, &bufferSize);
+
+ auto completionRoutine = [](MBB_PROTOCOL_HANDLE, MBB_REQUEST_HANDLE RequestHandle, NTSTATUS NtStatus) {
+ MbbRequestComplete((MBBREQUEST)RequestHandle, NtStatus);
+ };
+
+ NTSTATUS sendStatus = MbbBusSendMessageFragment(
+ deviceContext->BusHandle, Fragment, buffer, (ULONG)bufferSize, (LPGUID)MbbRequestGetCorrelationId(Fragment), completionRoutine);
+
+ // Accoding to the documentation of MbbBusSendMessageFragment the completion routine
+ // will be called only when ntStatus is STATUS_PENDING, so we need to call it ourselves
+ // for the other cases
+ if (sendStatus != STATUS_PENDING)
+ {
+ completionRoutine(deviceContext, Fragment, sendStatus);
+ }
+}
+
+VOID MbbNdisResponseFragmentAvailable(__in MBB_PROTOCOL_HANDLE ProtocolHandle)
+{
+ PWMBCLASS_DEVICE_CONTEXT deviceContext = (PWMBCLASS_DEVICE_CONTEXT)ProtocolHandle;
+ MbbDeviceResponseAvailable(deviceContext->WdfDevice);
+}
+
+_Use_decl_annotations_ VOID EvtMbbDeviceReceiveMbimFragment(WDFDEVICE Device, MBBREQUEST Fragment)
+{
+ PWMBCLASS_DEVICE_CONTEXT deviceContext = WmbClassGetDeviceContext(Device);
+
+ size_t bufferSize = 0;
+ PVOID buffer = MbbRequestGetBuffer(Fragment, &bufferSize);
+
+ auto completionRoutine = [](MBB_PROTOCOL_HANDLE, MBB_REQUEST_HANDLE RequestHandle, NTSTATUS Status, ULONG_PTR ReceivedLength) {
+ MbbRequestCompleteWithInformation((MBBREQUEST)RequestHandle, Status, ReceivedLength);
+ };
+
+ // Accoding to the documentation of MbbBusReceiveMessageFragment the completion routine
+ // will be called only when ntStatus is STATUS_PENDING, so we need to call it ourselves
+ // for the other cases
+ NTSTATUS receiveStatus = MbbBusReceiveMessageFragment(
+ deviceContext->BusHandle, Fragment, buffer, (ULONG)bufferSize, (LPGUID)MbbRequestGetCorrelationId(Fragment), completionRoutine);
+
+ if (receiveStatus != STATUS_PENDING)
+ {
+ completionRoutine(deviceContext, Fragment, receiveStatus, 0);
+ }
+}
+
+static NTSTATUS SetupAdapterSpecificPowerSettings(_In_ PWMBCLASS_DEVICE_CONTEXT DeviceContext)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ if (DeviceContext->BusParams.RemoteWakeCapable)
+ {
+ //
+ // Configure USB selective suspend with default timeout
+ //
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleUsbSelectiveSuspend);
+
+ idleSettings.IdleTimeout = MBB_DEFAULT_IDLE_TIMEOUT_HINT_MS;
+ idleSettings.IdleTimeoutType = SystemManagedIdleTimeoutWithHint;
+
+ status = WdfDeviceAssignS0IdleSettings(DeviceContext->WdfDevice, &idleSettings);
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+ }
+Exit:
+ return status;
+}
+
+NTSTATUS
+MbbInitializeHardware(_In_ PWMBCLASS_DEVICE_CONTEXT DeviceContext)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ WDF_OBJECT_ATTRIBUTES attributes;
+ UNICODE_STRING manufacturer;
+ UNICODE_STRING model;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.ParentObject = DeviceContext->WdfDevice;
+ status = WdfLookasideListCreate(
+ &attributes,
+ sizeof(MBB_NDIS_RECEIVE_CONTEXT),
+ NonPagedPoolNx,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ MbbPoolTagMdlReceive,
+ &DeviceContext->ReceiveLookasideList);
+ if (!NT_SUCCESS(status))
+ {
+ goto Cleanup;
+ }
+
+ for (int i = 0; i < MBB_MAX_NUMBER_OF_SESSIONS; i++)
+ {
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.ParentObject = DeviceContext->WdfDevice;
+
+ status = WdfSpinLockCreate(&attributes, &DeviceContext->Sessions[i].WdfRecvSpinLock);
+ if (!NT_SUCCESS(status))
+ {
+ goto Cleanup;
+ }
+ }
+
+ status = MbbBusInitializeByWdf(
+ DeviceContext->WdfDevice,
+ MbbNdisResponseFragmentAvailable,
+ MbbNdisReceiveCallback,
+ nullptr,
+ nullptr,
+ DeviceContext,
+ &DeviceContext->BusHandle,
+ nullptr);
+
+ if (status != STATUS_SUCCESS)
+ {
+ goto Cleanup;
+ }
+
+ //
+ // Initialize the OID/CID handlers before the device is OPENED.
+ // Once the device is opened and the interrupt pipe is connected.
+ // This driver will start receiving unsolicited interrupt message
+ // from the device.
+ //
+ status = MbbBusQueryBusParameters(DeviceContext->BusHandle, &DeviceContext->BusParams);
+
+ if (status != STATUS_SUCCESS)
+ {
+ goto Cleanup;
+ }
+
+ WDFMEMORY sharedPaddingMemory;
+ ULONG sharedPaddingLength = max(DeviceContext->BusParams.NdpOutAlignment, DeviceContext->BusParams.NdpOutDivisor);
+ status = CreateNonPagedWdfMemory(
+ sharedPaddingLength, &sharedPaddingMemory, (PVOID*)&DeviceContext->sharedPaddingBuffer, DeviceContext->WdfDevice, MbbPoolTagNtbSend);
+ if (!NT_SUCCESS(status))
+ {
+ goto Cleanup;
+ }
+
+ RtlInitUnicodeString(&manufacturer, DeviceContext->BusParams.Manufacturer);
+ RtlInitUnicodeString(&model, DeviceContext->BusParams.Model);
+ MBB_DEVICE_MBIM_PARAMETERS mbimParams;
+
+ // 2.0 by default
+ MBB_MBIM_EXTENDED_VERSION mbimExVer = MbbMbimExtendedVersion2Dot0;
+ switch(DeviceContext->BusParams.MbimExtendedVersion)
+ {
+ case 0x0100:
+ mbimExVer = MbbMbimExtendedVersion1Dot0;
+ break;
+ case 0x0200:
+ mbimExVer = MbbMbimExtendedVersion2Dot0;
+ break;
+ case 0x0300:
+ mbimExVer = MbbMbimExtendedVersion3Dot0;
+ break;
+ case 0x0400:
+ mbimExVer = MbbMbimExtendedVersion4Dot0;
+ break;
+ default:
+ status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ MBB_DEVICE_MBIM_PARAMETERS_INIT(
+ &mbimParams,
+ DeviceContext->BusParams.IsErrataDevice ? MbbMbimVersion1Dot0Errata : MbbMbimVersion1Dot0,
+ DeviceContext->BusParams.FragmentSize,
+ mbimExVer);
+
+ MbbDeviceSetMbimParameters(DeviceContext->WdfDevice, &mbimParams);
+
+ MBB_DEVICE_WAKE_CAPABILITIES mbbWakeCapabilities;
+ MBB_DEVICE_WAKE_CAPABILITIES_INIT(&mbbWakeCapabilities);
+
+ mbbWakeCapabilities.PacketState = TRUE;
+ mbbWakeCapabilities.RegisterState = TRUE;
+ mbbWakeCapabilities.SmsReceive = TRUE;
+ mbbWakeCapabilities.UiccChange = TRUE;
+ mbbWakeCapabilities.UssdReceive = TRUE;
+
+ MbbDeviceSetWakeCapabilities(DeviceContext->WdfDevice, &mbbWakeCapabilities);
+
+ status = SetupAdapterSpecificPowerSettings(DeviceContext);
+ if (NT_ERROR(status))
+ {
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ if (status != STATUS_SUCCESS)
+ {
+ if (DeviceContext != NULL)
+ {
+ if (DeviceContext->BusHandle != NULL)
+ {
+ MbbBusStop(DeviceContext->BusHandle);
+ MbbBusCleanup(DeviceContext->BusHandle);
+ DeviceContext->BusHandle = NULL;
+ }
+ }
+
+ }
+
+ return status;
+}
+
+void MbbReleaseHardware(_In_ PWMBCLASS_DEVICE_CONTEXT DeviceContext)
+{
+
+ if (DeviceContext->BusHandle != NULL)
+ {
+ MbbBusCleanup(DeviceContext->BusHandle);
+ DeviceContext->BusHandle = NULL;
+ }
+
+ return;
+}
+
+NTSTATUS
+EvtDevicePrepareHardware(_In_ WDFDEVICE device, _In_ WDFCMRESLIST resourcesRaw, _In_ WDFCMRESLIST resourcesTranslated)
+{
+ UNREFERENCED_PARAMETER(resourcesRaw);
+ UNREFERENCED_PARAMETER(resourcesTranslated);
+
+ PWMBCLASS_DEVICE_CONTEXT deviceContext = WmbClassGetDeviceContext(device);
+
+ NTSTATUS status = MbbInitializeHardware(deviceContext);
+
+ return status;
+}
+
+NTSTATUS
+EvtDeviceReleaseHardware(_In_ WDFDEVICE device, _In_ WDFCMRESLIST resourcesTranslated)
+{
+ UNREFERENCED_PARAMETER(resourcesTranslated);
+ PWMBCLASS_DEVICE_CONTEXT deviceContext = WmbClassGetDeviceContext(device);
+
+ MbbReleaseHardware(deviceContext);
+
+ return STATUS_SUCCESS;
+}
+
+_Use_decl_annotations_ void EvtDeviceSurpriseRemoval(WDFDEVICE device)
+{
+ UNREFERENCED_PARAMETER(device);
+}
+
+NTSTATUS MbbInitAdapterContext(_In_ WDFDEVICE Device, _In_ NETADAPTER NetAdapter)
+{
+ WDF_OBJECT_ATTRIBUTES adapterAttributes;
+ PWMBCLASS_DEVICE_CONTEXT deviceContext = WmbClassGetDeviceContext(Device);
+ PWMBCLASS_NETADAPTER_CONTEXT netAdapterContext = WmbClassGetNetAdapterContext(NetAdapter);
+
+ ULONG ntbSize;
+ ntbSize = FIELD_OFFSET(MBB_NTB_BUILD_CONTEXT, NdpDatagramEntries);
+ ntbSize += (deviceContext->BusParams.MaxOutDatagrams * sizeof(MBB_NDP_HEADER_ENTRY));
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&adapterAttributes);
+ adapterAttributes.ParentObject = NetAdapter;
+
+ NTSTATUS ntStatus = WdfLookasideListCreate(
+ &adapterAttributes, ntbSize, NonPagedPoolNx, WDF_NO_OBJECT_ATTRIBUTES, MbbPoolTagNtbSend, &netAdapterContext->NtbLookasideList);
+ if (!NT_SUCCESS(ntStatus))
+ {
+ return ntStatus;
+ }
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&adapterAttributes);
+ adapterAttributes.ParentObject = NetAdapter;
+ ntStatus = WdfLookasideListCreate(
+ &adapterAttributes,
+ sizeof(MBB_RECEIVE_NDP_CONTEXT),
+ NonPagedPoolNx,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ MbbPoolTagMdlReceive,
+ &netAdapterContext->ReceiveNdpLookasideList);
+ if (!NT_SUCCESS(ntStatus))
+ {
+ return ntStatus;
+ }
+ netAdapterContext->SessionId = MbbAdapterGetSessionId(NetAdapter);
+ WdfSpinLockAcquire(deviceContext->Sessions[netAdapterContext->SessionId].WdfRecvSpinLock);
+ deviceContext->Sessions[netAdapterContext->SessionId].NetAdapterContext = netAdapterContext;
+ // Allow to receive data from this point since connection may happen before RxQueueCreate, if it happens, we allow to cache the recieved NDP
+ netAdapterContext->AllowRxTraffic = TRUE;
+ InitializeListHead(&netAdapterContext->ReceiveNdpList);
+ netAdapterContext->WmbDeviceContext = deviceContext;
+ netAdapterContext->NetAdapter = NetAdapter;
+ WdfSpinLockRelease(deviceContext->Sessions[netAdapterContext->SessionId].WdfRecvSpinLock);
+ return ntStatus;
+}
+
+_Use_decl_annotations_ NTSTATUS EvtMbbDeviceCreateAdapter(WDFDEVICE Device, NETADAPTER_INIT* AdapterInit)
+{
+
+ NET_ADAPTER_DATAPATH_CALLBACKS datapathCallbacks;
+ NET_ADAPTER_DATAPATH_CALLBACKS_INIT(&datapathCallbacks, EvtAdapterCreateTxQueue, EvtAdapterCreateRxQueue);
+
+ NetAdapterInitSetDatapathCallbacks(AdapterInit, &datapathCallbacks);
+
+ WDF_OBJECT_ATTRIBUTES adapterAttributes;
+ WDF_OBJECT_ATTRIBUTES_INIT(&adapterAttributes);
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&adapterAttributes, WMBCLASS_NETADAPTER_CONTEXT);
+ adapterAttributes.EvtCleanupCallback = EvtAdapterCleanup;
+
+ NETADAPTER netAdapter;
+ NTSTATUS ntStatus = NetAdapterCreate(AdapterInit, &adapterAttributes, &netAdapter);
+ if (!NT_SUCCESS(ntStatus))
+ {
+ return ntStatus;
+ }
+
+ ntStatus = MbbAdapterInitialize(netAdapter);
+
+ if (!NT_SUCCESS(ntStatus))
+ {
+ return ntStatus;
+ }
+
+ ntStatus = MbbInitAdapterContext(Device, netAdapter);
+ if (!NT_SUCCESS(ntStatus))
+ {
+ return ntStatus;
+ }
+
+ ntStatus = WmbClassAdapterStart(netAdapter);
+
+ if (!NT_SUCCESS(ntStatus))
+ {
+ return ntStatus;
+ }
+
+ return ntStatus;
+}
+
+_Use_decl_annotations_ VOID EvtAdapterCleanup(_In_ WDFOBJECT NetAdapter)
+{
+ PWMBCLASS_NETADAPTER_CONTEXT netAdapterContext = WmbClassGetNetAdapterContext(NetAdapter);
+ PWMBCLASS_DEVICE_CONTEXT deviceContext = netAdapterContext->WmbDeviceContext;
+ if (deviceContext == NULL || deviceContext->Sessions[netAdapterContext->SessionId].NetAdapterContext == NULL)
+ {
+ return;
+ }
+
+ // Cancel these Ndps which are cached before RxQueueCreate but not cleaned in case RxQueueCreate failed.
+ // NetAdapter should be destoryed immediately if RxQueueCreate failed, or these cached Ndps will stay in memory until release device or delete additional PDP
+ MbbRecvCancelNdps(deviceContext, netAdapterContext->SessionId);
+
+ WdfSpinLockAcquire(deviceContext->Sessions[netAdapterContext->SessionId].WdfRecvSpinLock);
+ deviceContext->Sessions[netAdapterContext->SessionId].NetAdapterContext = NULL;
+ WdfSpinLockRelease(deviceContext->Sessions[netAdapterContext->SessionId].WdfRecvSpinLock);
+
+}
+
+_Use_decl_annotations_ VOID EvtMbbDeviceReset(WDFDEVICE WdfDevice)
+{
+ PWMBCLASS_DEVICE_CONTEXT deviceContext = WmbClassGetDeviceContext(WdfDevice);
+ MbbBusResetDataPipes(deviceContext->BusHandle);
+}