diff options
| author | taj0207 <[email protected]> | 2022-11-09 11:31:14 -0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-11-09 11:31:14 -0800 |
| commit | 82537c2a1da27908ffbc5d98a7ad77e6af0956c8 (patch) | |
| tree | af74ee90abbbe0eacc15c28873f35a0ca46cec7c /network/wwan/cxwmbclass/device.cpp | |
| parent | 96e23c8f1172e227122de5c66390e8e5b4028831 (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.cpp | 381 |
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); +} |
