summaryrefslogtreecommitdiff
path: root/bluetooth
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /bluetooth
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'bluetooth')
-rw-r--r--bluetooth/serialhcibus/Fdo.c2165
-rw-r--r--bluetooth/serialhcibus/Io.h251
-rw-r--r--bluetooth/serialhcibus/ReadMe.md59
-rw-r--r--bluetooth/serialhcibus/WDK/Debugdef.h49
-rw-r--r--bluetooth/serialhcibus/WDK/SerialBusWdk.inx88
-rw-r--r--bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj210
-rw-r--r--bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj.Filters51
-rw-r--r--bluetooth/serialhcibus/WDK/device.c257
-rw-r--r--bluetooth/serialhcibus/WDK/device.h56
-rw-r--r--bluetooth/serialhcibus/WDK/driver.rc14
-rw-r--r--bluetooth/serialhcibus/driver.c378
-rw-r--r--bluetooth/serialhcibus/driver.h530
-rw-r--r--bluetooth/serialhcibus/io.c1592
-rw-r--r--bluetooth/serialhcibus/pdo.c972
-rw-r--r--bluetooth/serialhcibus/public.h47
-rw-r--r--bluetooth/serialhcibus/serialhcibus.sln28
16 files changed, 6747 insertions, 0 deletions
diff --git a/bluetooth/serialhcibus/Fdo.c b/bluetooth/serialhcibus/Fdo.c
new file mode 100644
index 00000000..c7fcc4ff
--- /dev/null
+++ b/bluetooth/serialhcibus/Fdo.c
@@ -0,0 +1,2165 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ Fdo.c
+
+Abstract:
+
+ This module contains routines to handle the function driver
+ aspect of the bus driver.
+
+Environment:
+
+ kernel mode only
+
+--*/
+
+#include "driver.h"
+#include <reshub.h>
+#include "fdo.tmh"
+
+#define BTHX_VALID_WRITE_PACKET_TYPE(type) (type == HciPacketCommand || type == HciPacketAclData)
+#define BTHX_VALID_READ_PACKET_TYPE(type) (type == HciPacketEvent || type == HciPacketAclData)
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text (PAGE, FdoCreateOneChildDevice)
+#pragma alloc_text (PAGE, FdoRemoveOneChildDevice)
+#pragma alloc_text (PAGE, FdoCreateAllChildren)
+#pragma alloc_text (PAGE, FdoFindConnectResources)
+#pragma alloc_text (PAGE, FdoDevPrepareHardware)
+#pragma alloc_text (PAGE, FdoDevReleaseHardware)
+#pragma alloc_text (PAGE, FdoDevSelfManagedIoInit)
+#pragma alloc_text (PAGE, FdoDevSelfManagedIoCleanup)
+#pragma alloc_text (PAGE, FdoDevD0Exit)
+#pragma alloc_text (PAGE, HlpInitializeFdoExtension)
+#pragma alloc_text (PAGE, FdoWriteToDeviceSync)
+#endif
+
+//
+// Child device node, PDO(s), could be enumerated statically if number of PDOs are known
+// at driver start, or dynamic enuermation mechanism is used. Both methods are presented
+// in this code, but only one can be chosen using the define macro (see sources file).
+//
+#ifdef DYNAMIC_ENUM
+
+typedef struct _ENABLE_PDO_CONTEXT {
+ WDFDEVICE Fdo;
+} ENABLE_PDO_CONTEXT, *PENABLE_PDO_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(ENABLE_PDO_CONTEXT, GetEnablePdoWorkItemContext)
+
+// Timeout used to delay dev node enuemeration
+ULONG g_WaitToEnablePDO = 20000; // MSec
+
+VOID
+DeviceEnablePDOWorker(
+ _In_ WDFWORKITEM _WorkItem
+ )
+/*++
+Routine Description:
+
+ A work item function to dynamically enuermate a PDO.
+
+Arguments:
+
+ _pWorkItem - work item that contains a context to help carrying out its task
+
+Return Value:
+--*/
+{
+ PENABLE_PDO_CONTEXT Context;
+ LARGE_INTEGER RemoteWakeTimeout;
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP, ("+DeviceEnablePDOWorker"));
+ Context = GetEnablePdoWorkItemContext(_WorkItem);
+
+ RemoteWakeTimeout.QuadPart = WDF_REL_TIMEOUT_IN_MS(g_WaitToEnablePDO);
+ KeDelayExecutionThread(KernelMode, FALSE, &RemoteWakeTimeout);
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP, ("+Complete the wait"));
+
+ Status = FdoCreateOneChildDeviceDynamic(Context->Fdo,
+ BT_PDO_HARDWARE_IDS,
+ sizeof(BT_PDO_HARDWARE_IDS)/sizeof(WCHAR),
+ BLUETOOTH_FUNC_IDS );
+
+ DoTrace(LEVEL_INFO, TFLAG_POWER, ("-DeviceEnablePDOWorker %!STATUS!", Status));
+
+}
+
+NTSTATUS
+FdoEvtDeviceListCreatePdo(
+ WDFCHILDLIST DeviceList,
+ PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription,
+ PWDFDEVICE_INIT ChildInit
+ )
+/*++
+
+Routine Description:
+
+ Called by the framework in response to Query-Device relation when
+ a new PDO for a child device needs to be created.
+
+Arguments:
+
+ DeviceList - Handle to the default WDFCHILDLIST created by the framework as part
+ of FDO.
+
+ IdentificationDescription - Decription of the new child device.
+
+ ChildInit - It's a opaque structure used in collecting device settings
+ and passed in as a parameter to CreateDevice.
+
+Return Value:
+
+ NT Status code.
+
+--*/
+{
+ PPDO_IDENTIFICATION_DESCRIPTION pDesc;
+
+ PAGED_CODE();
+
+ pDesc = CONTAINING_RECORD(IdentificationDescription,
+ PDO_IDENTIFICATION_DESCRIPTION,
+ Header);
+
+ return PdoCreateDynamic(WdfChildListGetDevice(DeviceList),
+ ChildInit,
+ pDesc->HardwareIds,
+ pDesc->SerialNo);
+}
+
+
+NTSTATUS
+FdoCreateOneChildDeviceDynamic(
+ _In_ WDFDEVICE _Device,
+ _In_ PWCHAR _HardwareIds,
+ _In_ size_t _CchHardwareIds,
+ _In_ ULONG _SerialNo
+ )
+
+/*++
+
+Routine Description:
+
+ The trigger event has been signalled that a new device on the bus has arrived.
+
+ We therefore create a description structure in stack, fill in information about
+ the child device and call WdfChildListAddOrUpdateChildDescriptionAsPresent
+ to add the device.
+
+--*/
+
+{
+ PDO_IDENTIFICATION_DESCRIPTION Description;
+ NTSTATUS Status;
+
+ PAGED_CODE ();
+
+ //
+ // Initialize the description with the information about the newly
+ // plugged in device.
+ //
+ WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&Description.Header,
+ sizeof(Description));
+
+ Description.SerialNo = _SerialNo;
+ Description.CchHardwareIds = _CchHardwareIds;
+ Description.HardwareIds = _HardwareIds;
+
+ //
+ // Call the framework to add this child to the childlist. This call
+ // will internaly call our DescriptionCompare callback to check
+ // whether this device is a new device or existing device. If
+ // it's a new device, the framework will call DescriptionDuplicate to create
+ // a copy of this description in nonpaged pool.
+ // The actual creation of the child device will happen when the framework
+ // receives QUERY_DEVICE_RELATION request from the PNP manager in
+ // response to InvalidateDeviceRelations call made as part of adding
+ // a new child.
+ //
+ Status = WdfChildListAddOrUpdateChildDescriptionAsPresent(WdfFdoGetDefaultChildList(_Device),
+ &Description.Header,
+ NULL); // AddressDescription
+
+ if (Status == STATUS_OBJECT_NAME_EXISTS) {
+ //
+ // The description is already present in the list, the serial number is
+ // not unique, return error.
+ //
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ return Status;
+}
+
+#endif // ifdef DYNAMIC_ENUM
+
+
+NTSTATUS
+FdoCreateOneChildDevice(
+ _In_ WDFDEVICE _Device,
+ _In_ PWSTR _HardwareIds,
+ _In_ ULONG _SerialNo
+ )
+/*++
+
+Routine Description:
+
+ Create a new PDO, initialize it, add it to the list of PDOs for this
+ FDO bus.
+
+Arguments:
+
+ _Device - WDF device object
+
+ _HardwareIDs - hardware Id for a device
+
+ _SerialNo - Unique ID for a child DO
+
+Returns:
+
+ Status
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN IsUnique = TRUE;
+ WDFDEVICE ChildDevice;
+ PPDO_EXTENSION PdoExtension;
+ PFDO_EXTENSION FdoExtension;
+
+ PAGED_CODE();
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP, ("+ FdoCreateOneChildDevice() HWID: %S", _HardwareIds));
+
+ //
+ // First make sure that we don't already have another device with the
+ // same serial number.
+ // Framework creates a collection of all the child devices we have
+ // created so far. So acquire the handle to the collection and lock
+ // it before walking the item.
+ //
+ FdoExtension = FdoGetExtension(_Device);
+ ChildDevice = NULL;
+
+ //
+ // We need an additional lock to synchronize addition because
+ // WdfFdoLockStaticChildListForIteration locks against anyone immediately
+ // updating the static child list (the changes are put on a queue until the
+ // list has been unlocked). This type of lock does not enforce our concept
+ // of unique IDs on the bus (ie SerialNo).
+ //
+ // Without our additional lock, 2 threads could execute this function, both
+ // find that the requested SerialNo is not in the list and attempt to add
+ // it. If that were to occur, 2 PDOs would have the same unique SerialNo,
+ // which is incorrect.
+ //
+ // We must use a passive level lock because you can only call WdfDeviceCreate
+ // at PASSIVE_LEVEL.
+ //
+ WdfWaitLockAcquire(FdoExtension->ChildLock, NULL);
+ WdfFdoLockStaticChildListForIteration(_Device);
+
+ while ((ChildDevice = WdfFdoRetrieveNextStaticChild(_Device,
+ ChildDevice,
+ WdfRetrieveAddedChildren)) != NULL) {
+ //
+ // WdfFdoRetrieveNextStaticChild returns reported and to be reported
+ // children (ie children who have been added but not yet reported to PNP).
+ //
+ // A surprise removed child will not be returned in this list.
+ //
+ PdoExtension = PdoGetExtension(ChildDevice);
+
+ //
+ // It's okay to plug in another device with the same serial number
+ // as long as the previous one is in a surprise-removed state. The
+ // previous one would be in that state after the device has been
+ // physically removed, if somebody has an handle open to it.
+ //
+ if (_SerialNo == PdoExtension->SerialNo) {
+ IsUnique = FALSE;
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ if (IsUnique) {
+ //
+ // Create a new child device. It is OK to create and add a child while
+ // the list locked for enumeration. The enumeration lock applies only
+ // to enumeration, not addition or removal.
+ //
+ Status = PdoCreate(_Device, _HardwareIds, _SerialNo);
+ }
+
+ WdfFdoUnlockStaticChildListFromIteration(_Device);
+ WdfWaitLockRelease(FdoExtension->ChildLock);
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP, ("- FdoCreateOneChildDevice() %!STATUS!", Status));
+
+ return Status;
+}
+
+NTSTATUS
+FdoRemoveOneChildDevice(
+ WDFDEVICE _Device,
+ ULONG _SerialNo
+ )
+/*++
+
+Routine Description:
+
+ The application has told us a device has departed from the bus.
+
+ We therefore need to flag the PDO as no longer present
+ and then tell Plug and Play about it.
+
+Arguments:
+
+ _Device - WDF device object
+
+ _SerialNo - Unique ID for a child DO
+
+Returns:
+
+ Status
+
+--*/
+
+{
+ PPDO_EXTENSION PdoExtension;
+ BOOLEAN Found = FALSE;
+ BOOLEAN PlugOutAll;
+ WDFDEVICE ChildDevice;
+ NTSTATUS Status = STATUS_INVALID_PARAMETER;
+
+ PAGED_CODE();
+
+ PlugOutAll = (0 == _SerialNo) ? TRUE : FALSE;
+
+ ChildDevice = NULL;
+
+ WdfFdoLockStaticChildListForIteration(_Device);
+
+ while ((ChildDevice = WdfFdoRetrieveNextStaticChild(_Device,
+ ChildDevice,
+ WdfRetrieveAddedChildren)) != NULL) {
+ if (PlugOutAll) {
+
+ Status = WdfPdoMarkMissing(ChildDevice);
+ if(!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_INFO, TFLAG_PNP, ("WdfPdoMarkMissing failed 0x%x\n", Status));
+ break;
+ }
+
+ Found = TRUE;
+ }
+ else {
+ PdoExtension = PdoGetExtension(ChildDevice);
+
+ if (_SerialNo == PdoExtension->SerialNo) {
+
+ Status = WdfPdoMarkMissing(ChildDevice);
+ if(!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_INFO, TFLAG_PNP, ("WdfPdoMarkMissing failed 0x%x\n", Status));
+ break;
+ }
+
+ Found = TRUE;
+ break;
+ }
+ }
+ }
+
+ WdfFdoUnlockStaticChildListFromIteration(_Device);
+
+ if (Found) {
+ Status = STATUS_SUCCESS;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+FdoCreateAllChildren(
+ _In_ WDFDEVICE _Device
+ )
+/*++
+Routine Description:
+
+ The routine enables you to statically enumerate child device functions
+ during start.
+
+Arguments:
+
+ _Device - WDF device object
+
+Returns:
+
+ Status
+
+--*/
+{
+ NTSTATUS Status;
+ PFDO_EXTENSION FdoExtension;
+
+ PAGED_CODE();
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP, (" + FdoCreateAllChildren"));
+
+ //
+ // Bus driver enumerates all child devnode in this function.
+ // Vendor Specific: retrieve all statically saved devnode info
+ // HWID, COMPATID, etc.
+ //
+
+ //
+ // This sample code only enuemrate the Bluetooth function as the only
+ // child device.
+ //
+ Status = FdoCreateOneChildDevice(_Device,
+ BT_PDO_HARDWARE_IDS,
+ BLUETOOTH_FUNC_IDS);
+
+ FdoExtension = FdoGetExtension(_Device);
+ if (NT_SUCCESS(Status)) {
+ FdoExtension->IsRadioEnabled = TRUE;
+ }
+
+ return Status;
+}
+
+
+
+NTSTATUS
+HlpInitializeFdoExtension(
+ WDFDEVICE _Device
+ )
+/*++
+Routine Description:
+
+ This helper function initialize the device context.
+
+Arguments:
+
+ _Device - WDF Device object
+
+Return Value:
+
+ Status
+
+--*/
+{
+ PFDO_EXTENSION FdoExtension;
+ WDF_OBJECT_ATTRIBUTES Attributes;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("+HlpInitializeFdoExtension"));
+
+ FdoExtension = FdoGetExtension(_Device);
+ FdoExtension->WdfDevice = _Device;
+
+ //
+ // Set Bluetooth (PDO) capabilities
+ // MaxAclTransferInSize - is used by the host to notify the Bluetooth controller
+ // in HCI_Host_Buffer_Size command to set the maximum size of the data portion
+ // of an HCI ACL packet that will be sent from the controller to the host.
+ // BthMini will only send down an HCI read request with this data buffer size.
+ //
+ FdoExtension->BthXCaps.MaxAclTransferInSize = MAX_HCI_ACLDATA_SIZE;
+ FdoExtension->BthXCaps.ScoSupport = ScoSupportHCIBypass; // Only option
+ FdoExtension->BthXCaps.MaxScoChannels = 1; // Limit to 1 HCIBypass channel
+ FdoExtension->BthXCaps.IsDeviceIdleCapable = TRUE; // Disable Idle to S0 and wake
+ FdoExtension->BthXCaps.IsDeviceWakeCapable = FALSE; // Wake from Sx
+
+ //
+ // Preallocate Request
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);
+ Attributes.ParentObject = _Device;
+
+ Status = WdfRequestCreate(&Attributes, FdoExtension->IoTargetSerial, &FdoExtension->RequestIoctlSync);
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfRequestCreate failed %!STATUS!", Status));
+ goto Exit;
+ }
+
+ FdoExtension->HardwareErrorDetected = FALSE;
+
+ Status = WdfRequestCreate(&Attributes, FdoExtension->IoTargetSerial, &FdoExtension->RequestWaitOnError);
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfRequestCreate failed %!STATUS!", Status));
+ goto Exit;
+ }
+
+ Status = WdfMemoryCreatePreallocated(&Attributes,
+ &FdoExtension->SerErrorMask,
+ sizeof(FdoExtension->SerErrorMask),
+ &FdoExtension->WaitMaskMemory);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfMemoryCreatePreallocated failed %!STATUS!", Status));
+ goto Exit;
+ }
+
+ KeInitializeSpinLock(&FdoExtension->QueueAccessLock);
+
+Exit:
+
+ return Status;
+
+}
+
+
+VOID
+FdoEvtDeviceDisarmWake(
+ _In_ WDFDEVICE _Device
+ )
+/*++
+Routine Description:
+
+
+ This function is invoked by the framework after the bus driver determines
+ that an event has awakened the device, and after the bus driver subsequently
+ completes the wait/wake IRP.
+
+ This function perform any hardware operations that are needed to disable
+ the device's ability to trigger a wake signal after the power has been lowered.
+
+Arguments:
+
+ _Device - WDF Device object
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ UNREFERENCED_PARAMETER(_Device);
+ DoTrace(LEVEL_INFO, TFLAG_PNP,(" FdoEvtDeviceDisarmWake"));
+}
+
+
+NTSTATUS
+FdoEvtDeviceArmWake(
+ _In_ WDFDEVICE _Device
+ )
+/*++
+Routine Description:
+
+ This function is invoked while the device is still in the D0 device power state,
+ before the bus driver lowers the device's power state but after the framework
+ has sent a wait/wake IRP on behalf of the driver.
+
+Arguments:
+
+ _Device - WDF Device object
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ UNREFERENCED_PARAMETER(_Device);
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,(" FdoEvtDeviceArmWake"));
+
+ return Status;
+}
+
+NTSTATUS
+FdoFindConnectResources(
+ _In_ WDFDEVICE _Device,
+ _In_ WDFCMRESLIST _ResourcesRaw,
+ _In_ WDFCMRESLIST _ResourcesTranslated
+ )
+/*++
+
+Routine Description:
+
+ This routine enuermates and finds specific connection resources and cache them.
+
+Arguments:
+
+ _Device - Supplies a handle to a framework device object.
+
+ _ResourcesRaw - Supplies a handle to a collection of framework resource
+ objects. This collection identifies the raw (bus-relative) hardware
+ resources that have been assigned to the device.
+
+ _ResourcesTranslated - Supplies a handle to a collection of framework
+ resource objects. This collection identifies the translated
+ (system-physical) hardware resources that have been assigned to the
+ device. The resources appear from the CPU's point of view.
+
+Return Value:
+
+ NT Status code.
+
+--*/
+
+{
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
+ PFDO_EXTENSION FdoExtension;
+ ULONG Index;
+ ULONG ResourceCount = 0;
+ NTSTATUS Status;
+ BOOLEAN UartConnectionIdIsFound = FALSE;
+
+ UNREFERENCED_PARAMETER(_ResourcesRaw);
+
+ PAGED_CODE();
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoFindConnectResources"));
+
+ FdoExtension = FdoGetExtension(_Device);
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Walk through the resource list and find and cache expected resources.
+ //
+
+ ResourceCount = WdfCmResourceListGetCount(_ResourcesTranslated);
+
+ for (Index = 0; Index < ResourceCount; Index++)
+ {
+ Descriptor = WdfCmResourceListGetDescriptor(_ResourcesTranslated, Index);
+
+ switch(Descriptor->Type)
+ {
+ case CmResourceTypeConnection:
+
+ //
+ // Cache connetion ID that this BT Peripheral device is connected to
+ // - UART (must exist)
+ // - GPIO (optional)
+ //
+
+ if ((Descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL) &&
+ (Descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_UART))
+ {
+ NT_ASSERT(UartConnectionIdIsFound == FALSE && L"More than one set of UART connection");
+
+ UartConnectionIdIsFound = TRUE;
+
+ FdoExtension->UARTConnectionId.LowPart = Descriptor->u.Connection.IdLowPart;
+ FdoExtension->UARTConnectionId.HighPart = Descriptor->u.Connection.IdHighPart;
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,(" UART ConnectionID (0x%x, 0x%x)",
+ FdoExtension->UARTConnectionId.HighPart, FdoExtension->UARTConnectionId.LowPart));
+ }
+ else if ((Descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL) &&
+ (Descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C))
+ {
+
+ FdoExtension->I2CConnectionId.LowPart = Descriptor->u.Connection.IdLowPart;
+ FdoExtension->I2CConnectionId.HighPart = Descriptor->u.Connection.IdHighPart;
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,(" I2C ConnectionID (0x%x, 0x%x)",
+ FdoExtension->I2CConnectionId.HighPart, FdoExtension->I2CConnectionId.LowPart));
+ }
+ else if ((Descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_GPIO) &&
+ (Descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_GPIO_IO))
+ {
+
+ FdoExtension->GPIOConnectionId.LowPart = Descriptor->u.Connection.IdLowPart;
+ FdoExtension->GPIOConnectionId.HighPart = Descriptor->u.Connection.IdHighPart;
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,(" GPIO ConnectionID (0x%x, 0x%x)",
+ FdoExtension->GPIOConnectionId.HighPart, FdoExtension->GPIOConnectionId.LowPart));
+ }
+ break;
+
+ case CmResourceTypeInterrupt:
+
+ //
+ // NT Interrupt to support HOST_WAKE for remote wake (TBD)
+ //
+
+ default:
+ DoTrace(LEVEL_INFO, TFLAG_PNP,(" Resource type %d not used.", Descriptor->Type));
+ break;
+ }
+
+ }
+
+
+ //
+ // Expect to find UART controller
+ //
+ if (!UartConnectionIdIsFound)
+ {
+ Status = STATUS_NOT_FOUND;
+ }
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("-FdoFindConnectResources ResourceCount %d, %!STATUS!", ResourceCount, Status));
+
+ return Status;
+}
+
+
+
+NTSTATUS
+FdoOpenDevice(
+ _In_ WDFDEVICE _Device,
+ _Out_ WDFIOTARGET *_pIoTarget
+ )
+/*++
+Routine Description:
+
+ This function search for a serial port and create a remote IO Target object,
+ which will be used to send control and data.
+
+Arguments:
+
+ _Device - WDF Device object
+
+ _pIoTarget - IO Target object to be created in this function
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ WDFIOTARGET IoTargetSerial;
+ PFDO_EXTENSION FdoExtension = NULL;
+ WCHAR TargetDeviceNameBuffer[100];
+ PWSTR SymbolicLinkList = NULL;
+ UNICODE_STRING TargetDeviceName;
+
+ WDF_IO_TARGET_OPEN_PARAMS OpenParams;
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoOpenDevice"));
+
+
+ Status = WdfIoTargetCreate(_Device,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &IoTargetSerial);
+
+ if (!NT_SUCCESS(Status))
+ {
+ goto Exit;
+ }
+
+ FdoExtension = FdoGetExtension(_Device);
+
+ //
+ // On SoC platform, a valid connection ID to a UART is set; if not, the legacy way
+ // of enumerating serial device interface is used.
+ //
+
+ if (ValidConnectionID(FdoExtension->UARTConnectionId))
+ {
+ RtlInitEmptyUnicodeString(&TargetDeviceName,
+ TargetDeviceNameBuffer,
+ sizeof(TargetDeviceNameBuffer));
+
+ Status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&TargetDeviceName,
+ FdoExtension->UARTConnectionId.LowPart,
+ FdoExtension->UARTConnectionId.HighPart);
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_INFO, TFLAG_PNP,(" Failed to construct the open path %!STATUS!", Status));
+ goto Exit;
+ }
+ }
+ else
+ {
+ // 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))
+ {
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("IoGetDeviceInterfaces(): %!STATUS!", Status));
+ goto Exit;
+ }
+
+ // Check for empty list
+ if (*SymbolicLinkList == L'\0')
+ {
+ Status = STATUS_DEVICE_DOES_NOT_EXIST;
+ goto Exit;
+ }
+
+ // A list of devices is returned, we use only the first one.
+ // ACPI component will enuermate us and this step is not necessary.
+ RtlInitUnicodeString(&TargetDeviceName, SymbolicLinkList);
+ }
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP, (" Symbolic Name '%S'", TargetDeviceName.Buffer));
+
+ //
+ // Open the "remote" IO Target (device) using its symbolic link.
+ //
+ WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&OpenParams,
+ &TargetDeviceName,
+ STANDARD_RIGHTS_ALL);
+ OpenParams.ShareAccess = 0; // Explicite: Exclusive access
+
+ //
+ // Open this serial device (Io Target) in order to send IOCTL_SERIAL_* control to it.
+ //
+ Status = WdfIoTargetOpen(IoTargetSerial,
+ &OpenParams);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_INFO, TFLAG_PNP, ( " WdfIoTargetOpen failed %!STATUS!", Status));
+ WdfObjectDelete(IoTargetSerial);
+ goto Exit;
+ }
+
+ *_pIoTarget = IoTargetSerial;
+
+Exit:
+
+ if (SymbolicLinkList)
+ {
+ ExFreePool(SymbolicLinkList);
+ SymbolicLinkList = NULL;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+FdoSetIdleSettings(
+ _In_ WDFDEVICE _Device,
+ _In_ IDLE_CAP_STATE _IdleCapState
+ )
+/*++
+Routine Description:
+
+ This function defines how device idle (Dx) is support while system is in
+ (S0) for the Serial Hci device (not its child node, which is supported
+ in the PDO).
+
+ If its Enuemrator is "ROOT" (in the case of using a Bluetooth dev board),
+ its Idle support is IdleCannotWakeFromS0. Its power capabilities are
+ limited to D0 and D3; it is basically on or off, and there is no Idle
+ while in S0.
+
+ Vendor: If its Enumerator is ACPI, then it might be possible to support
+ idle while in S0. This is vendor specific.
+
+Arguments:
+
+ _Device - WDF Device object
+
+ IDLE_CAP_STATE - The idle capability state to enter
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS IdleSettings;
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN AssignS0IdleSettings = TRUE;
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoSetIdleSettings"));
+
+ switch (_IdleCapState)
+ {
+ case IdleCapActiveOnly:
+
+ //
+ // By default ACPI supports D0 active, and idle to D3 without remote wake.
+ // While in D3, only host (e.g. IO request) can wake the device to D0.
+ //
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&IdleSettings,
+ IdleCannotWakeFromS0);
+
+ // Low Dx state to enter after IdleTimeout has expired and Idle is enabled.
+ IdleSettings.DxState = PowerDeviceD3;
+ IdleSettings.IdleTimeout = IdleTimeoutDefaultValue; // Use default (~5 seconds)
+ IdleSettings.IdleTimeoutType = DriverManagedIdleTimeout; // Driver is in control (typically for out of SoC).
+
+ // Idle to DxState is not initially disable, and do not allow user control to enable it (as this is active only).
+ IdleSettings.UserControlOfIdleSettings = IdleDoNotAllowUserControl;
+ IdleSettings.Enabled = WdfFalse;
+
+ // Do not wake from D3 to D0 due to system wake (Sx to S0); ie only host app can wake.
+ IdleSettings.PowerUpIdleDeviceOnSystemWake = WdfFalse;
+ break;
+
+ case IdleCapCanWake:
+
+ //
+ // If it has a child PDO and there is a controller (GPIO) being configured to support wake,
+ // this state can be supported.
+ //
+ // Vendor: in order to support idle in S0 for this ACPI enumerated device, specify that the device
+ // can wake in S0. For example, if it can wake from D2 in S0, this should be set in its device section:
+ //
+ // Name(_S0W, 0x2)
+ //
+ // Additionally, the wake interrupt, e.g. HOST_WAKE, will need to be known by ACPI (instead of exposing
+ // it directly to this driver as system resource); so that, ACPI will do the arming and wake on this
+ // driver's behalf with Dx state transition.
+ //
+
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&IdleSettings,
+ IdleCanWakeFromS0);
+
+ // Low Dx state to enter after IdleTimeout has expired and Idle is enabled.
+ IdleSettings.DxState = PowerDeviceD2;
+ IdleSettings.IdleTimeout = 0; // May want to enter D2 immediately and invoke arm wake callback.
+ IdleSettings.IdleTimeoutType = DriverManagedIdleTimeout; // Driver is in control (typically for out of SoC).
+
+ // Idle to DxState is initially enable, but allow user control as well (e.g to turn off idle support).
+ IdleSettings.UserControlOfIdleSettings = IdleAllowUserControl;
+ IdleSettings.Enabled = WdfTrue;
+
+ //
+ // Note: wiil invoke EvtDeviceArmWakeFromS0 callback before entering DxState;
+ // Driver can arm for HOST_WAKE interrrupt in the callback.
+ //
+ break;
+
+ case IdleCapCanTurnOff:
+
+ //
+ // If there is no child PDO (e.g. in Radio off mode), in effect the BT radio can be turned off
+ // to enter D3 state. All unused controllers (e.g. GPIO) can be turned off, also
+ // the Bluetooth function block. While in D3 state, only host can wake the device.
+ //
+ // Here is one approach to prevent the FDO from entering DxState while its PDO is in Dx and there is no pending IO:
+ //
+ // The PDO can hold a reference on its parent to prevent the parent from going into DxState. This is done in
+ // PrepareHardware with WdfDeviceStopIdle() and releasing that reference
+ // in the PDO's ReleaseHardware with WdfDeviceResumeIdle(). This applies to the case when the PDO is disabled.
+ // In the resource rebalancing case, the FDO may enter D3 shortly and then resume to D0.
+ //
+
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&IdleSettings,
+ IdleCannotWakeFromS0);
+
+ // Low Dx state to enter after IdleTimeout has expired and Idle is enabled.
+ IdleSettings.DxState = PowerDeviceD3;
+ IdleSettings.IdleTimeout = IdleTimeoutDefaultValue;
+ IdleSettings.IdleTimeoutType = DriverManagedIdleTimeout; // Driver is in control (typically for out of SoC).
+
+ // Idle to DxState is initially enabled, but allow user control as well (e.g. do not turn off).
+ IdleSettings.UserControlOfIdleSettings = IdleAllowUserControl;
+ IdleSettings.Enabled = WdfTrue;
+
+ // Do not wake from D3 to D0 due to system wake (Sx to S0); ie only host app can wake.
+ IdleSettings.PowerUpIdleDeviceOnSystemWake = WdfFalse;
+ break;
+
+ default:
+ AssignS0IdleSettings = FALSE;
+ break;
+ }
+
+ if (AssignS0IdleSettings)
+ {
+ Status = WdfDeviceAssignS0IdleSettings(_Device,
+ &IdleSettings);
+ }
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("-FdoSetIdleSettings %!STATUS!", Status));
+ return Status;
+}
+
+
+NTSTATUS
+FdoDevPrepareHardware(
+ _In_ WDFDEVICE _Device,
+ _In_ WDFCMRESLIST _ResourcesRaw,
+ _In_ WDFCMRESLIST _ResourcesTranslated
+ )
+/*++
+Routine Description:
+
+ This PnP CB function allocate hardware related resource allocation and
+ perform device initialization.
+
+Arguments:
+
+ _Device - WDF Device object
+
+ _ResourcesRaw - (Not referenced)
+
+ _ResourcesTranslated - (Not referenced)
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS Status;
+ PFDO_EXTENSION FdoExtension;
+
+
+ PAGED_CODE();
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoDevPrepareHardware"));
+
+ //
+ // Acquire connection ID of connected controllers (UART and GPIO)
+ //
+ Status = FdoFindConnectResources(_Device,
+ _ResourcesRaw,
+ _ResourcesTranslated);
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, (" Failed to find connection ID of target UART controller %!STATUS!", Status));
+
+ // Log(Informational): no UART Connection ID resource
+
+ // Can still use the legacy approach to find it based on its serial interface GUID.
+ }
+
+
+ FdoExtension = FdoGetExtension(_Device);
+
+ //
+ // Open Bluetooth UART device as a remote IO Target
+ //
+ Status = FdoOpenDevice(_Device, &FdoExtension->IoTargetSerial);
+
+ if (!NT_SUCCESS(Status) || FdoExtension->IoTargetSerial == NULL)
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, (" FdoOpenDevice failed %!STATUS!", Status));
+
+ // Log(Error): Failed to open UART controller
+ goto Exit;
+ }
+
+ //
+ // Initialize content of this device extension
+ //
+ Status = HlpInitializeFdoExtension(_Device);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, (" HlpInitializeFdoExtension failed %!STATUS!", Status));
+ goto Exit;
+ }
+
+ //
+ // Set device's idle configuration if it is capable
+ //
+ Status = FdoSetIdleSettings(_Device,
+ IdleCapCanTurnOff);
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, (" FdoSetIdleSettings failed %!STATUS!", Status));
+ // goto Exit;
+ }
+
+ // Enable serial bus device
+ if (ValidConnectionID(FdoExtension->GPIOConnectionId)) {
+ Status = DeviceEnable(_Device, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP,("DeviceEnable failed %!STATUS!", Status));
+ goto Exit;
+ }
+ }
+
+ // Power On serial bus device
+ if (ValidConnectionID(FdoExtension->I2CConnectionId)) {
+ Status = DevicePowerOn(_Device);
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP,("DevicePowerOn failed %!STATUS!", Status));
+ goto Exit;
+ }
+ }
+
+ //
+ // Configure local UART controller
+ //
+ FdoExtension->DeviceInitialized = DeviceInitialize(FdoExtension,
+ FdoExtension->IoTargetSerial,
+ FdoExtension->RequestIoctlSync,
+ TRUE);
+ if (!IsDeviceInitialized(FdoExtension))
+ {
+ // Can have issue if this UART device cannot be initalized
+ Status = STATUS_DEVICE_NOT_READY;
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, (" DeviceInitialize failed %!STATUS!", Status));
+
+ // Log(Error): Failed to intialize/configure the device
+ goto Exit;
+ }
+
+#ifdef DYNAMIC_ENUM
+ //
+ // This code segment is for testing: spawn a work item to do dynamic enuermation
+ // of a Bluetooth dev node (PDO); the actual implementation could be to query
+ // the peripheral device for what function blocks that it can support, or
+ // to listen for a published interface of its dependent controller driver
+ // to start the enuermation after driver has started.
+ //
+ {
+ WDF_OBJECT_ATTRIBUTES ObjAttributes;
+ WDF_WORKITEM_CONFIG WorkitemConfig;
+ WDFWORKITEM WorkItem;
+ PENABLE_PDO_CONTEXT Context;
+
+ PAGED_CODE();
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP, ("+CreateWorkItem to enable PDO"));
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&ObjAttributes);
+
+ WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&ObjAttributes,
+ ENABLE_PDO_CONTEXT);
+ ObjAttributes.ParentObject = _Device;
+
+ WDF_WORKITEM_CONFIG_INIT(&WorkitemConfig, DeviceEnablePDOWorker);
+ Status = WdfWorkItemCreate(&WorkitemConfig, &ObjAttributes, &WorkItem);
+
+ if (NT_SUCCESS(Status))
+ {
+ // Get and initialize the context
+ Context = GetEnablePdoWorkItemContext(WorkItem);
+ Context->Fdo = _Device;
+
+ // Initialize work item context
+ WdfWorkItemEnqueue(WorkItem);
+ }
+ }
+#else
+ //
+ // Perform static PDO enumertion by reading child device info saved in the registry.
+ // But the info needs to be populated first by acquired supported device for supported
+ // child devices.
+ //
+ Status = FdoCreateAllChildren(_Device);
+#endif
+
+Exit:
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP, ("-FdoDevPrepareHardware %!STATUS!", Status));
+
+ return Status;
+}
+
+
+NTSTATUS
+FdoDevReleaseHardware(
+ _In_ WDFDEVICE _Device,
+ _In_ WDFCMRESLIST _ResourcesTranslated
+ )
+/*++
+Routine Description:
+
+ This PnP CB function free resource allocated in FdoDevPrepareHardware.
+
+Arguments:
+
+ _Device - WDF Device object
+
+ _ResourcesTranslated - (Not referenced)
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ PFDO_EXTENSION FdoExtension;
+
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(_ResourcesTranslated);
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("+PnpReleaseHardware"));
+
+ FdoExtension = FdoGetExtension(_Device);
+
+ if (FdoExtension->IoTargetSerial)
+ {
+ WdfObjectDelete(FdoExtension->IoTargetSerial);
+ FdoExtension->IoTargetSerial = NULL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+
+NTSTATUS
+FdoDevSelfManagedIoInit(
+ _In_ WDFDEVICE _Device
+)
+/*++
+Routine Description:
+
+ This PnP CB function is invoked once and will perform IO related resource allocation
+ and start the read pump.
+
+Arguments:
+
+ _Device - WDF Device object
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS Status;
+ PFDO_EXTENSION FdoExtension;
+
+ PAGED_CODE();
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoDevSelfManagedIoInit"));
+
+ //
+ // Preallocate resources needed to perform read opeations
+ //
+ Status = ReadResourcesAllocate(_Device);
+
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadResourcesAllocate failed %!STATUS!", Status));
+ goto Exit;
+ }
+
+ // Issue pending IO request to prefetch HCI event and data
+ FdoExtension = FdoGetExtension(_Device);
+ FdoExtension->ReadContext.RequestState = REQUEST_COMPLETE;
+
+ // Start the read pump
+ FdoExtension->ReadPumpRunning = TRUE;
+ Status = ReadH4Packet(&FdoExtension->ReadContext,
+ FdoExtension->ReadRequest,
+ FdoExtension->ReadMemory,
+ FdoExtension->ReadBuffer,
+ INITIAL_H4_READ_SIZE);
+
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadH4Packet failed %!STATUS!", Status));
+ goto Exit;
+ }
+
+Exit:
+
+ return Status;
+}
+
+
+VOID
+FdoDevSelfManagedIoCleanup(
+ _In_ WDFDEVICE _Device
+ )
+/*++
+Routine Description:
+
+ This PnP CB function is invoked once and will be used here to free resource
+ that was alocated in its corresponding SelfMagedInit fucntion.
+
+Arguments:
+
+ _Device - WDF Device object
+
+Return Value:
+
+ none
+
+--*/
+{
+ PAGED_CODE();
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoDevSelfManagedIoCleanup"));
+
+ //
+ // Cancel and free resources
+ //
+ ReadResourcesFree(_Device);
+
+ return;
+}
+
+
+NTSTATUS
+FdoDevD0Entry(
+ _In_ WDFDEVICE _Device,
+ _In_ WDF_POWER_DEVICE_STATE _PreviousState
+ )
+/*++
+Routine Description:
+
+ This PnP CB function is invoked after device has enter D0 (working) state. Most
+ of initilization of hardware is already performed in PrepareHardware CB but will
+ be performed again if the device was resume from non-D0 state.
+
+Arguments:
+
+ _Device - WDF Device object
+
+ PreviousState - Next power state it is entering from D0
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ PFDO_EXTENSION FdoExtension = FdoGetExtension(_Device);
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ UNREFERENCED_PARAMETER(_PreviousState);
+
+ DoTrace(LEVEL_INFO, TFLAG_UART, ("+FdoDevD0Entry"));
+
+ // Reset error count upon resume to D0
+ FdoExtension->OutOfSyncErrorCount = 0;
+
+ // Initialize serial port settings if re-enter D0
+ if (!IsDeviceInitialized(FdoExtension)) {
+
+ // Enable serial bus device
+ if (ValidConnectionID(FdoExtension->GPIOConnectionId)) {
+ Status = DeviceEnable(_Device, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP,("DeviceEnable failed %!STATUS!", Status));
+ goto Done;
+ }
+ }
+
+ // Power On serial bus device
+ if (ValidConnectionID(FdoExtension->I2CConnectionId)) {
+ Status = DevicePowerOn(_Device);
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP,("DevicePowerOn failed %!STATUS!", Status));
+ goto Done;
+ }
+ }
+
+ //
+ // The local UART may need to be re-initialized to match the remote UART if its context
+ // was lost, but the assumption here is that the UART controller driver does save and
+ // restore its context.
+ //
+#ifdef REQUIRE_REINITIALIZE
+
+ // Reinitialize serial bus device
+ FdoExtension->DeviceInitialized = DeviceInitialize(FdoExtension,
+ FdoExtension->IoTargetSerial,
+ FdoExtension->RequestIoctlSync,
+ FALSE);
+ if (!IsDeviceInitialized(FdoExtension)) {
+ Status = STATUS_DEVICE_NOT_READY;
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, ("DeviceInitialize failed!"));
+ goto Done;
+ }
+
+#else
+ // Set to TRUE in order to restart the read pump
+ FdoExtension->DeviceInitialized = TRUE;
+#endif
+
+ // Restart the IOTarget to receiving request
+ Status = WdfIoTargetStart(FdoExtension->IoTargetSerial);
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, ("WdfIoTargetStart failed %!STATUS!", Status));
+ goto Done;
+ }
+
+ // Restart read pump
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" Restarting read pump"));
+ Status = ReadH4Packet(&FdoExtension->ReadContext,
+ FdoExtension->ReadRequest,
+ FdoExtension->ReadMemory,
+ FdoExtension->ReadBuffer,
+ INITIAL_H4_READ_SIZE);
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, ("ReadH4Packet [0] failed %!STATUS!", Status));
+ goto Done;
+ }
+ }
+
+Done:
+
+ DoTrace(LEVEL_INFO, TFLAG_UART, ("-FdoDevD0Entry %!STATUS!", Status));
+
+ return Status;
+}
+
+
+NTSTATUS
+FdoDevD0Exit(
+ _In_ WDFDEVICE _Device,
+ _In_ WDF_POWER_DEVICE_STATE _TargetState
+ )
+/*++
+Routine Description:
+
+ This PnP CB function is invoked when device has exited D0 (working) state.
+ It stops the queue and can be restarted later, and mark the device uninitialize
+ and will be initialized again when resumes to D0.
+
+Arguments:
+
+ _Device - WDF Device object
+
+ _TargetState - Next power state it is entering from D0
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ PFDO_EXTENSION FdoExtension = FdoGetExtension(_Device);
+
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(_TargetState);
+
+ DoTrace(LEVEL_INFO, TFLAG_UART, ("+FdoDevD0Exit D0-> D%d", _TargetState-WdfPowerDeviceD0));
+
+ // Cancel IO requests that are already in the IO queue,
+ // wait for their completion before this function is returned.
+ // Can restart this queue at later time.
+ WdfIoTargetStop(FdoExtension->IoTargetSerial, WdfIoTargetCancelSentIo);
+
+ // Delete GPIO IoTarget to disable the device and this will
+ // require device to be re-initialized when it re-enters D0.
+ if (FdoExtension->IoTargetGPIO)
+ {
+ WdfObjectDelete(FdoExtension->IoTargetGPIO);
+ FdoExtension->IoTargetGPIO = NULL;
+ }
+ FdoExtension->DeviceInitialized = FALSE;
+
+ //
+ // Note: Do not delete the UART's IoTarget.
+ //
+
+ DoTrace(LEVEL_INFO, TFLAG_UART, ("-FdoDevD0Exit"));
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+HCIContextValidate(
+ ULONG _Index,
+ PBTHX_HCI_READ_WRITE_CONTEXT _HCIContext
+ )
+/*++
+Routine Description:
+
+ This function validate the incoming data context and print out (WPP) trace.
+
+Arguments:
+
+ _Index - count number of HCI command/event/data that has been completed (0 based).
+ _HCIContext - Context to be valdiated
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS or STATUS_INVALID_PARAMETER
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG Index;
+
+ DoTrace(LEVEL_INFO, TFLAG_HCI,("+HCIContextValidate"));
+
+ switch ((BTHX_HCI_PACKET_TYPE) _HCIContext->Type)
+ {
+ case HciPacketCommand:
+ {
+ PHCI_COMMAND_PACKET HciCommand = (PHCI_COMMAND_PACKET) _HCIContext->Data;
+ DoTrace(LEVEL_INFO, TFLAG_HCI, (" -> HCI Command [%d] OpCode: 0x%x, nParams: %d ---->",
+ _Index,
+ HciCommand->Opcode,
+ HciCommand->ParamsCount));
+
+ for (Index = 0; Index < MinToPrint((ULONG) HciCommand->ParamsCount, MAX_COMMAND_PARAMS_TO_DISPLAY); Index++)
+ {
+ DoTrace(LEVEL_INFO, TFLAG_HCI, (" [%d] 0x%.2x",
+ Index, HciCommand->Params[Index]));
+ }
+
+ if (!WithinRange(MIN_HCI_CMD_SIZE, _HCIContext->DataLen, MAX_HCI_CMD_SIZE))
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ DoTrace(LEVEL_ERROR, TFLAG_HCI,(" HciPacketCommand %!STATUS!", Status));
+ break;
+ }
+ }
+ break;
+
+ case HciPacketEvent:
+ {
+ PHCI_EVENT_PACKET HciEvent = (PHCI_EVENT_PACKET) _HCIContext->Data;
+ DoTrace(LEVEL_INFO, TFLAG_HCI, (" <- HCI Event [%d] EventCode: 0x%x (%S), nParams: %d",
+ _Index,
+ HciEvent->EventCode,
+ HciEvent->EventCode == CommandComplete ? L"Complete" :
+ HciEvent->EventCode == CommandStatus ? L"Status(Async)!!" : L"??",
+ HciEvent->ParamsCount));
+
+ // Note if CommandStatus is returned, there will be another event to complete this command.
+
+ for (Index = 0; Index < MinToPrint((ULONG) HciEvent->ParamsCount, MAX_EVENT_PARAMS_TO_DISPLAY); Index++)
+ {
+ DoTrace(LEVEL_VERBOSE, TFLAG_HCI, (" [%d] 0x%.2x",
+ Index, HciEvent->Params[Index]));
+ }
+
+ if (!WithinRange(MIN_HCI_EVENT_SIZE, _HCIContext->DataLen, MAX_HCI_EVENT_SIZE))
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ DoTrace(LEVEL_ERROR, TFLAG_HCI,(" HciPacketEvent %!STATUS!", Status));
+ break;
+ }
+ }
+ break;
+
+ case HciPacketAclData:
+ {
+ PHCI_ACLDATA_PACKET AclData = (PHCI_ACLDATA_PACKET) _HCIContext->Data;
+ DoTrace(LEVEL_INFO, TFLAG_HCI, (" HCI Data [%d] (Handle:0x%x, PB:%x, BC:%x, Length:%d)",
+ _Index,
+ AclData->ConnectionHandle,
+ AclData->PBFlag,
+ AclData->BCFlag,
+ AclData->DataLength));
+
+ for (Index = 0; Index < (ULONG) (AclData->DataLength > 8 ? 8 : AclData->DataLength); Index++)
+ {
+ DoTrace(LEVEL_VERBOSE, TFLAG_HCI, (" [%d] 0x%.2x",
+ Index, AclData->Data[Index]));
+ }
+
+ if (!WithinRange(1, AclData->DataLength, MAX_HCI_ACLDATA_SIZE))
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ DoTrace(LEVEL_ERROR, TFLAG_HCI,(" HciPacketAclData data (%d) exceeds its max %d, %!STATUS!",
+ AclData->DataLength, MAX_HCI_ACLDATA_SIZE, Status));
+ break;
+ }
+ }
+ break;
+
+ default:
+ DoTrace(LEVEL_ERROR, TFLAG_HCI, (" Packet type %d unexpected!", _HCIContext->Type));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ NT_ASSERT(NT_SUCCESS(Status) && L"Invlaid data is detected!");
+
+ DoTrace(LEVEL_INFO, TFLAG_HCI,("-HCIContextValidate %!STATUS!", Status));
+
+ return Status;
+}
+
+
+
+
+NTSTATUS
+FdoWriteDeviceIO(
+ _In_ WDFREQUEST _RequestFromBthport,
+ _In_ WDFDEVICE _Device,
+ _In_ PFDO_EXTENSION _FdoExtension,
+ _In_ PBTHX_HCI_READ_WRITE_CONTEXT _HCIContext
+ )
+/*++
+
+Routine Description:
+
+ This function send an HCI packet to target device.
+
+Arguments:
+
+ _RequestFromBthport - Request from upper layer that initiate this transfer
+ _Device - WDF Device Object
+ _FdoExtension - Device's context
+ _HCIContext - Context used to process this HCI
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ WDF_OBJECT_ATTRIBUTES ObjAttributes;
+ NTSTATUS Status;
+ WDFREQUEST RequestToUART;
+ PUART_WRITE_CONTEXT TransferContext = NULL;
+ ULONG DataLength;
+ PVOID Data = NULL;
+
+ DoTrace(LEVEL_INFO, TFLAG_DATA,("+FdoWriteDeviceIO"));
+
+
+ if (!IsDeviceInitialized(_FdoExtension))
+ {
+ Status = STATUS_DEVICE_NOT_READY;
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" FdoWriteDeviceIO: cannot attach IO %!STATUS!", Status));
+ goto Done;
+ }
+
+ //
+ // Add a context to this existing WDFREQUEST for cancellation purpose
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&ObjAttributes,
+ UART_WRITE_CONTEXT);
+
+ Status = WdfObjectAllocateContext(_RequestFromBthport,
+ &ObjAttributes,
+ &TransferContext);
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfObjectAllocateContext failed %!STATUS!", Status));
+ goto Done;
+ }
+
+ Status = HLP_AllocateResourceForWrite(
+ _Device,
+ _FdoExtension->IoTargetSerial,
+ &RequestToUART);
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_IO,(" HLP_WriteDeviceIO %!STATUS!", Status));
+ goto Done;
+ }
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&ObjAttributes);
+ ObjAttributes.ParentObject = _Device;
+
+ // Reuse the data buffer coming from upper layer; UART's HCI packet starts with
+ // packet type, and then follows by the actual HCI packet.
+ Data = (PVOID) &_HCIContext->Type;
+ DataLength = (ULONG) sizeof(_HCIContext->Type) + _HCIContext->DataLen;
+
+ _Analysis_assume_(DataLength > 0);
+ Status = WdfMemoryCreatePreallocated(&ObjAttributes,
+ Data,
+ DataLength,
+ &TransferContext->Memory);
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfMemoryCreatePreallocated failed %!STATUS!", Status));
+ goto Done;
+ }
+
+ Status = WdfIoTargetFormatRequestForWrite(_FdoExtension->IoTargetSerial,
+ RequestToUART,
+ TransferContext->Memory,
+ NULL,
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoTargetFormatRequestForRead failed %!STATUS!", Status));
+ goto Done;
+ }
+
+ // Setup transfer context
+ TransferContext->FdoExtension = _FdoExtension;
+ TransferContext->HCIContext = _HCIContext;
+ TransferContext->RequestFromBthport = _RequestFromBthport;
+ TransferContext->RequestCompletePath = REQUEST_PATH_NONE;
+ TransferContext->RequestToUART = RequestToUART;
+ TransferContext->HCIPacket = Data;
+ TransferContext->HCIPacketLen = DataLength;
+
+ //
+ // Both Requests are typically accessed by the completion routine, and in rare case also
+ // by the cancellation callback. Take a reference on them to ensure they stays valid in both cases.
+ //
+ WdfObjectReference(RequestToUART);
+ WdfObjectReference(_RequestFromBthport);
+
+
+ // Mark cancellable of the Request in our possession from upper layer
+ // Cannot mark the request that we will forward to lower driver cancellable.
+ // Only if the Request from upper layer is cancelled, we will then cancel the
+ // Request that is sent to lower driver.
+ WdfRequestMarkCancelable(_RequestFromBthport, CB_RequestFromBthportCancel);
+
+ WdfRequestSetCompletionRoutine(RequestToUART, CR_WriteDeviceIO, TransferContext);
+
+ // This request will be delivered to its IoTarget asynchronously (the default option). It should return
+ // STATUS_PENDING unless there is an error in its delivery to its IoTarget. After it has been delivered
+ // successfully, its completion function will be called for any outcome - success, failure, or cancellation.
+ if (!WdfRequestSend(RequestToUART, _FdoExtension->IoTargetSerial, WDF_NO_SEND_OPTIONS))
+ {
+ NTSTATUS StatusTemp;
+
+ // Get failure status, and this request will be completed by its caller of this function with this status.
+ Status = WdfRequestGetStatus(RequestToUART);
+
+ // Unmark cancellable before it is completed.
+ StatusTemp = WdfRequestUnmarkCancelable(_RequestFromBthport);
+
+ // Balance the reference count for both Requests due to failure.
+ WdfObjectDereference(RequestToUART);
+ WdfObjectDereference(_RequestFromBthport);
+
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestSend failed %!STATUS! and UnmarkCancelable %!STATUS!", Status, StatusTemp));
+ goto Done;
+ }
+ else
+ {
+ // Request has been delivered to UART driver asychronously. It will be completed in its completion function
+ // after IoTarget (UART driver) completes its delivery to the BT controller.
+ }
+
+Done:
+
+ if (!NT_SUCCESS(Status))
+ {
+ HLP_FreeResourceForWrite(TransferContext);
+ }
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, ("-FdoWriteDeviceIO %!STATUS!", Status));
+
+ return Status;
+}
+
+
+NTSTATUS
+FdoWriteToDeviceSync(
+ _In_ WDFIOTARGET _IoTargetSerial,
+ _In_ WDFREQUEST _RequestWriteSync,
+ _In_ ULONG _IoControlCode,
+ _In_opt_ ULONG _InBufferSize,
+ _In_opt_ PVOID _InBuffer,
+ _Out_ PULONG_PTR _BytesWritten
+)
+/*++
+Routine Description:
+
+ This helper function send a synchronous write or Ioctl Request to device with
+ timeout (to prevent hang).
+
+Arguments:
+
+ _IoTargetSerial - Serial port IO Target where to issue this request to
+ _RequestWriteSync - caller allocated WDF Request
+ _IoControlCode - IOCTL control code; if 0, it is a Write request.
+ _InBufferSize - Input buffer size
+ _InBuffer - (optional) Input buffer
+ _BytesWritten - Bytes written to device; this is driver dependent; a write
+ could be successfully (and fully) written with 0 BytesWritten.
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS or Status from issuing this request
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ WDF_REQUEST_REUSE_PARAMS RequestReuseParams;
+ WDF_REQUEST_SEND_OPTIONS Options;
+ WDF_MEMORY_DESCRIPTOR MemoryDescriptor;
+ ULONG_PTR BytesWritten = 0;
+ BOOLEAN HasInputParam = FALSE;
+
+ PAGED_CODE();
+
+ DoTrace(LEVEL_INFO, TFLAG_IO,("+FdoWriteToDeviceSync"));
+
+
+ WDF_REQUEST_REUSE_PARAMS_INIT(&RequestReuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS);
+ Status = WdfRequestReuse(_RequestWriteSync, &RequestReuseParams);
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestReuse failed %!STATUS!", Status));
+ goto Done;
+ }
+
+ if (_InBuffer && _InBufferSize) {
+ HasInputParam = TRUE;
+ WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&MemoryDescriptor,
+ _InBuffer,
+ _InBufferSize);
+ }
+
+ WDF_REQUEST_SEND_OPTIONS_INIT(&Options, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);
+ WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&Options, WDF_REL_TIMEOUT_IN_SEC(MAX_WRITE_TIMEOUT_IN_SEC));
+
+ if (_IoControlCode)
+ {
+ Status = WdfIoTargetSendIoctlSynchronously(_IoTargetSerial,
+ NULL,
+ _IoControlCode,
+ HasInputParam ? &MemoryDescriptor : NULL, // InputBuffer
+ NULL, // OutputBuffer
+ &Options, // RequestOptions
+ &BytesWritten // BytesReturned
+ );
+ }
+ else
+ {
+ Status = WdfIoTargetSendWriteSynchronously(_IoTargetSerial,
+ NULL,
+ HasInputParam ? &MemoryDescriptor : NULL, // InputBuffer
+ NULL, // DeviceOffset
+ &Options, // RequestOptions
+ &BytesWritten // BytesReturned
+ );
+ }
+
+ DoTrace(LEVEL_INFO, TFLAG_IO,("-FdoWriteToDeviceSync: %d BytesWritten %!STATUS!", (ULONG) BytesWritten, Status));
+
+ if (NT_SUCCESS(Status))
+ {
+ *_BytesWritten = BytesWritten;
+ }
+
+Done:
+ return Status;
+}
+
+
+VOID
+FdoIoQuDeviceControl(
+ _In_ WDFQUEUE _Queue,
+ _In_ WDFREQUEST _Request,
+ _In_ size_t _OutputBufferLength,
+ _In_ size_t _InputBufferLength,
+ _In_ ULONG _IoControlCode
+ )
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for device control requests.
+
+Arguments:
+
+ _Queue - Handle to the framework queue object that is associated
+ with the I/O request.
+ _Request - Handle to a framework request object.
+
+ _OutputBufferLength - length of the request's output buffer,
+ if an output buffer is available.
+ _InputBufferLength - length of the request's input buffer,
+ if an input buffer is available.
+
+ _IoControlCode - the driver-defined or system-defined I/O control code
+ (IOCTL) that is associated with the request.
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ WDFMEMORY ReqInMemory = NULL, ReqOutMemory = NULL;
+ PVOID InBuffer = NULL, OutBuffer = NULL;
+ size_t InBufferSize = 0, OutBufferSize = 0;
+ PFDO_EXTENSION FdoExtension;
+ NTSTATUS Status = STATUS_SUCCESS;
+ WDFDEVICE Device;
+ BOOLEAN CompleteRequest = FALSE;
+ ULONG ControlCode = (_IoControlCode & 0x00003ffc) >> 2;
+ KIRQL Irql;
+ BTHX_HCI_PACKET_TYPE PacketType;
+ PBTHX_HCI_READ_WRITE_CONTEXT HCIContext;
+
+
+ DoTrace(LEVEL_INFO, TFLAG_IOCTL,("+IoDeviceControl - InBufLen:%d, OutBufLen:%d",
+ (ULONG) _InputBufferLength, (ULONG) _OutputBufferLength));
+
+ Device = WdfIoQueueGetDevice(_Queue);
+
+ FdoExtension = FdoGetExtension(Device);
+
+ if (_InputBufferLength)
+ {
+ Status = WdfRequestRetrieveInputMemory(_Request, &ReqInMemory);
+ if (NT_SUCCESS(Status))
+ {
+ InBuffer = WdfMemoryGetBuffer(ReqInMemory, &InBufferSize);
+ }
+ }
+
+ if (_OutputBufferLength)
+ {
+ Status = WdfRequestRetrieveOutputMemory(_Request, &ReqOutMemory);
+ if (NT_SUCCESS(Status))
+ {
+ OutBuffer = WdfMemoryGetBuffer(ReqOutMemory, &OutBufferSize);
+ }
+ }
+
+ switch (_IoControlCode)
+ {
+ case IOCTL_BTHX_WRITE_HCI:
+ DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" IOCTL_BTHX_WRITE_HCI ---------->"));
+ // Validate input and output parameters
+ if (!InBuffer || InBufferSize < sizeof(BTHX_HCI_READ_WRITE_CONTEXT) ||
+ !OutBuffer || OutBufferSize != sizeof(BTHX_HCI_PACKET_TYPE))
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" IOCTL_BTHX_WRITE_HCI %!STATUS!", Status));
+ break;
+ }
+
+ HCIContext = (PBTHX_HCI_READ_WRITE_CONTEXT) InBuffer;
+
+ PacketType = (BTHX_HCI_PACKET_TYPE) HCIContext->Type;
+
+ if (!BTHX_VALID_WRITE_PACKET_TYPE(PacketType))
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" Mismach Write HCI packet type and IOCTL %!STATUS!", Status));
+ break;
+ }
+
+ if (PacketType == HciPacketCommand)
+ {
+ InterlockedIncrement(&FdoExtension->CntCommandReq);
+ }
+ else
+ {
+ InterlockedIncrement(&FdoExtension->CntWriteDataReq);
+ }
+
+ Status = FdoWriteDeviceIO(_Request,
+ Device,
+ FdoExtension,
+ HCIContext);
+ break;
+
+ case IOCTL_BTHX_READ_HCI:
+ DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" IOCTL_BTHX_READ_HCI <----------"));
+ // Validate input and output parameters
+ if (!InBuffer || InBufferSize != sizeof(BTHX_HCI_PACKET_TYPE) ||
+ !OutBuffer || OutBufferSize < sizeof(BTHX_HCI_READ_WRITE_CONTEXT))
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" IOCTL_BTHX_READ_HCI %!STATUS!", Status));
+ break;
+ }
+
+ PacketType = *((BTHX_HCI_PACKET_TYPE *) InBuffer);
+
+ if (!BTHX_VALID_READ_PACKET_TYPE(PacketType))
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" IOCTL_BTHX_READ_HCI %!STATUS!", Status));
+ break;
+ }
+
+ if (PacketType == HciPacketEvent)
+ {
+ KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql);
+ // Queue the new request to preserve sequential order
+ Status = WdfRequestForwardToIoQueue(_Request, FdoExtension->ReadEventQueue);
+ if (NT_SUCCESS(Status))
+ {
+ InterlockedIncrement(&FdoExtension->EventQueueCount);
+ InterlockedIncrement(&FdoExtension->CntEventReq);
+ }
+ KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql);
+
+ if (NT_SUCCESS(Status))
+ {
+ Status = ReadRequestComplete(FdoExtension,
+ HciPacketEvent,
+ 0, NULL,
+ FdoExtension->ReadEventQueue,
+ &FdoExtension->EventQueueCount,
+ &FdoExtension->ReadEventList,
+ &FdoExtension->EventListCount);
+ }
+
+ }
+ else if (PacketType == HciPacketAclData)
+ {
+ KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql);
+ // Queue the new request to preserve sequential order
+ Status = WdfRequestForwardToIoQueue(_Request, FdoExtension->ReadDataQueue);
+ if (NT_SUCCESS(Status))
+ {
+ InterlockedIncrement(&FdoExtension->DataQueueCount);
+ InterlockedIncrement(&FdoExtension->CntReadDataReq);
+ }
+ KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql);
+
+ if (NT_SUCCESS(Status))
+ {
+ Status = ReadRequestComplete(FdoExtension,
+ HciPacketAclData,
+ 0, NULL,
+ FdoExtension->ReadDataQueue,
+ &FdoExtension->DataQueueCount,
+ &FdoExtension->ReadDataList,
+ &FdoExtension->DataListCount);
+ }
+ }
+ else
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" IOCTL_BTHX_READ_HCI %!STATUS!", Status));
+ break;
+ }
+ break;
+
+ case IOCTL_BTHX_GET_VERSION:
+ CompleteRequest = TRUE;
+ DoTrace(LEVEL_INFO, TFLAG_IOCTL,("IOCTL_BTHX_GET_VERSION"));
+
+ if (OutBuffer && OutBufferSize >= sizeof(BTHX_VERSION))
+ {
+ RtlCopyMemory(OutBuffer, &Microsoft_BTHX_DDI_Version, sizeof(BTHX_VERSION));
+ WdfRequestCompleteWithInformation(_Request, Status, sizeof(BTHX_VERSION));
+ return;
+ }
+ else
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+
+ case IOCTL_BTHX_SET_VERSION:
+ CompleteRequest = TRUE;
+ DoTrace(LEVEL_INFO, TFLAG_IOCTL,("IOCTL_BTHX_SET_VERSION"));
+
+ if (InBuffer && InBufferSize >= sizeof(BTHX_VERSION))
+ {
+ BTHX_VERSION SupportedVersion = *((BTHX_VERSION *)InBuffer);
+
+ DoTrace(LEVEL_INFO, TFLAG_IOCTL,("IOCTL_BTHX_SET_VERSION 0x%x", SupportedVersion.Version));
+
+ WdfRequestComplete(_Request, Status);
+ return;
+ }
+ else
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+
+ case IOCTL_BTHX_QUERY_CAPABILITIES:
+ CompleteRequest = TRUE;
+ DoTrace(LEVEL_INFO, TFLAG_IOCTL,("IOCTL_BTHX_QUERY_CAPABILITIES"));
+
+ if (OutBuffer && OutBufferSize >= sizeof(BTHX_CAPABILITIES))
+ {
+ BTHX_CAPABILITIES *pCaps = (BTHX_CAPABILITIES *) OutBuffer;
+
+ RtlCopyMemory(pCaps, &FdoExtension->BthXCaps, sizeof(BTHX_CAPABILITIES));
+ WdfRequestCompleteWithInformation(_Request, Status, sizeof(BTHX_CAPABILITIES));
+ return;
+ }
+ else
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+
+ //
+ // This IOCTL is used to support radio on/off feature by doing the following
+ // 1. Power up/down the Bluetooth radio function, and
+ // 2. Add/remove a PDO for Bluetooth devnode;
+ //
+ case IOCTL_BUSENUM_SET_RADIO_ONOFF_VENDOR_SPECFIC:
+ CompleteRequest = TRUE;
+ DoTrace(LEVEL_INFO, TFLAG_IOCTL,("IOCTL_BUSENUM_SET_RADIO_ONOFF_VENDOR_SPECFIC"));
+ if (InBuffer && InBufferSize >= sizeof(BOOLEAN)) {
+ BOOLEAN IsRadioEnabled = *((BOOLEAN *) InBuffer);
+
+ if (IsRadioEnabled) {
+ if (!FdoExtension->IsRadioEnabled) {
+
+ //
+ // 1. Power up the Bluetooth function of this device;
+ //
+ Status = DevicePowerOn(Device);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // 2. Create a PDO for the Bluetooth devnode;
+ //
+ Status = FdoCreateOneChildDevice(Device,
+ BT_PDO_HARDWARE_IDS,
+ BLUETOOTH_FUNC_IDS);
+ if (NT_SUCCESS(Status)) {
+ FdoExtension->IsRadioEnabled = TRUE;
+ }
+ }
+ DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" EnableRadio %!STATUS!", Status));
+ }
+ else {
+ Status = STATUS_SUCCESS;
+ DoTrace(LEVEL_WARNING, TFLAG_IOCTL,(" Already enabled!"));
+ }
+ }
+ else {
+ if (FdoExtension->IsRadioEnabled) {
+
+ //
+ // 1. Remove the PDO for the Bluetooth devnode;
+ //
+ Status = FdoRemoveOneChildDevice(Device,
+ BLUETOOTH_FUNC_IDS);
+ if (NT_SUCCESS(Status)) {
+ FdoExtension->IsRadioEnabled = FALSE;
+
+ //
+ // 2. Power down the Bluetooth function (at least the antenna) of this device;
+ //
+ Status = DevicePowerOff(Device);
+ }
+
+ DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" DisableRadio %!STATUS!", Status));
+ }
+ else {
+ Status = STATUS_SUCCESS;
+ DoTrace(LEVEL_WARNING, TFLAG_IOCTL,(" Already disabled!"));
+ }
+ }
+ }
+ else {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+
+ default:
+ DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" IOCTL_(0x%x, Func %d)", _IoControlCode, ControlCode));
+ Status = STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ if (!NT_SUCCESS(Status) || CompleteRequest)
+ {
+ WdfRequestComplete(_Request, Status);
+ }
+
+ return;
+}
diff --git a/bluetooth/serialhcibus/Io.h b/bluetooth/serialhcibus/Io.h
new file mode 100644
index 00000000..76abb82a
--- /dev/null
+++ b/bluetooth/serialhcibus/Io.h
@@ -0,0 +1,251 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ io.h
+
+Abstract:
+
+ Common header definitions and structs for read and write (IO) operation
+
+Author:
+
+Environment:
+
+ Kernel mode only
+
+
+Revision History:
+
+--*/
+
+#ifndef __IO_H__
+#define __IO_H__
+
+//
+// 255 bytes of data + 3 bytes for HCI cmd hdr (2-byte opcode + 1-byte Parameter).
+//
+#define MIN_HCI_CMD_SIZE (3)
+#define MAX_HCI_CMD_SIZE (258)
+
+//
+// 255 bytes of data + 2 byte hdr (1-byte event code + 1-byte parameter).
+//
+#define MIN_HCI_EVENT_SIZE (2)
+#define HCI_EVENT_HEADER_SIZE (2)
+#define MAX_HCI_EVENT_SIZE (257)
+
+//
+// Can be variable but usually 1021-byte (largest 3-DH5 ACL packet size)
+//
+#define HCI_ACL_HEADER_SIZE (4)
+#define HCI_MAX_ACL_PAYLOAD_SIZE (1021)
+#define MIN_HCI_ACLDATA_SIZE HCI_ACL_HEADER_SIZE
+#define MAX_HCI_ACLDATA_SIZE (HCI_ACL_HEADER_SIZE + HCI_MAX_ACL_PAYLOAD_SIZE)
+
+#define INITIAL_H4_READ_SIZE (1+HCI_EVENT_HEADER_SIZE)
+#define MAX_H4_HCI_PACKET_SIZE (1+HCI_ACL_HEADER_SIZE + HCI_MAX_ACL_PAYLOAD_SIZE) // include packet type
+
+#define BUFFER_AND_SIZE_ADJUSTED(Buffer, Size, SegmentCount, Increment) {Buffer += Increment; Size -= Increment; SegmentCount += Increment;}
+
+#include <PSHPACK1.H>
+
+//
+// Standard HCI packet structs for Command, Event and ACL Data
+//
+typedef struct _HCI_COMMAND_PACKET {
+ UINT16 Opcode;
+ UCHAR ParamsCount; // 0..255
+ UCHAR Params[1];
+} HCI_COMMAND_PACKET, *PHCI_COMMAND_PACKET;
+#define HCI_COMMAND_HEADER_LEN FIELD_OFFSET(HCI_COMMAND_PACKET, Params)
+
+typedef struct _HCI_EVENT_PACKET {
+ UCHAR EventCode;
+ UCHAR ParamsCount; // 0..255
+ UCHAR Params[1];
+} HCI_EVENT_PACKET, *PHCI_EVENT_PACKET;
+#define HCI_EVENT_HEADER_LEN FIELD_OFFSET(HCI_EVENT_PACKET, Params)
+
+typedef struct _HCI_ACLDATA_PACKET {
+ UINT16 ConnectionHandle : 12;
+ UINT16 PBFlag : 2;
+ UINT16 BCFlag : 2;
+ UINT16 DataLength; // 0..65535
+ UCHAR Data[1];
+} HCI_ACLDATA_PACKET, *PHCI_ACLDATA_PACKET;
+#define HCI_ACLDATA_HEADER_LEN FIELD_OFFSET(HCI_ACLDATA_PACKET, Data)
+
+//
+// UART packet that has a leading packet type over standard HCI packet
+//
+
+typedef struct _H4_PACKET {
+ UCHAR Type;
+ union {
+ HCI_COMMAND_PACKET Command;
+ HCI_EVENT_PACKET Event;
+ HCI_ACLDATA_PACKET AclData;
+ UCHAR Raw[MAX_HCI_ACLDATA_SIZE];
+ } Packet;
+} H4_PACKET, *PH4_PACKET;
+
+typedef struct _UART_COMMAND_PACKET {
+ UCHAR Type;
+ HCI_COMMAND_PACKET Packet;
+} UART_COMMAND_PACKET, *PUART_COMMAND_PACKET;
+
+typedef struct _UART_EVENT_PACKET {
+ UCHAR Type;
+ HCI_EVENT_PACKET Packet;
+} UART_EVENT_PACKET, *PUART_EVENT_PACKET;
+
+typedef struct _UART_ACLDATA_PACKET {
+ UCHAR Type;
+ HCI_ACLDATA_PACKET Packet;
+} UART_ACLDATA_PACKET, *PUART_ACLDATA_PACKET;
+
+#include <POPPACK.H>
+
+
+typedef struct _FDO_EXTENSION *PFDO_EXTENSION;
+
+
+#define REQUEST_PATH_NONE 0x00000000
+#define REQUEST_PATH_CANCELLATION 0x00000001
+#define REQUEST_PATH_COMPLETION 0x00000002
+
+
+//
+// Context used for data transfer to device (write)
+//
+typedef struct _UART_WRITE_CONTEXT {
+
+ //
+ // Back pointer to the FDO's extension
+ //
+ PFDO_EXTENSION FdoExtension;
+
+ //
+ // Request from BthPort upper driver
+ //
+ WDFREQUEST RequestFromBthport;
+
+ //
+ // Flag(Bit) to determine ownership for completing RequestFromBthport
+ //
+ LONG RequestCompletePath;
+
+ //
+ // Request to perform this transfer to UART device
+ //
+ WDFREQUEST RequestToUART;
+
+ //
+ // Memory object for data
+ //
+ WDFMEMORY Memory;
+
+ //
+ // The caller's transfer context.
+ //
+ PBTHX_HCI_READ_WRITE_CONTEXT HCIContext;
+
+ //
+ // Pointer to the data buffer from client's incoming data; not a copy.
+ //
+ PVOID HCIPacket;
+
+ //
+ // Packet length, including packet type.
+ //
+ ULONG HCIPacketLen;
+
+} UART_WRITE_CONTEXT, *PUART_WRITE_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(UART_WRITE_CONTEXT, GetWriteRequestContext)
+
+
+//
+// State machine used for reading incoming data streaming to form HCI event or data packet
+//
+typedef enum {
+ GET_PKT_TYPE = 1, // For UART/H4, UCHAR of packet type (Event or Data)
+ GET_PKT_HEADER = 2, // Get length to determine remaining payload
+ GET_PKT_PAYLOAD = 3 // Data payload
+} UART_READ_STATE;
+
+
+//
+// A list to store prefetched (read) HCI packets utill they are retrieved.
+//
+typedef struct _HCI_PACKET_ENTRY {
+ LIST_ENTRY DataEntry;
+ ULONG PacketLen;
+ _Field_size_bytes_(PacketLen) UCHAR Packet[1];
+} HCI_PACKET_ENTRY, *PHCI_PACKET_ENTRY;
+
+//
+// Use to track request completion path
+//
+typedef enum _READ_REQUEST_STATE {
+ REQUEST_SENT = 1, // Request is being sent
+ REQUEST_PENDING = 2, // Request is pending first - asynchronous completion
+ REQUEST_COMPLETE = 3 // Request has completedly first - synchronous completion
+} READ_REQUEST_STATE;
+
+
+//
+// Context used for reading UART operation to form HCI data or event packet
+//
+typedef struct _UART_READ_CONTEXT {
+
+ //
+ // Status of this request
+ //
+ NTSTATUS Status;
+
+ //
+ // Back pointer to the device extension
+ //
+ PFDO_EXTENSION FdoExtension;
+
+ //
+ // State machine for the read Request
+ //
+ READ_REQUEST_STATE RequestState;
+
+ //
+ // State machine of repeat read (read pump) to complete an HCI packet
+ //
+ UART_READ_STATE ReadSegmentState;
+
+ //
+ // Bytes read for each Segment (Type, Header, and Paylaod) of a partial H4 packet below
+ //
+ ULONG BytesReadNextSegment;
+
+ //
+ // Bytes to read in order to have a full packet (only meaningful in GET_PKT_PAYLOAD state.
+ //
+ ULONG BytesToRead4FullPacket;
+
+ //
+ // A union of H4 packet
+ //
+ H4_PACKET H4Packet;
+
+} UART_READ_CONTEXT, *PUART_READ_CONTEXT;
+
+#define MAX_HARDWARE_ERROR_COUNT 0 // Do not allow any error this time
+
+
+// Timeout value for synchronous read and write requests
+#define MAX_WRITE_TIMEOUT_IN_SEC 1 // unit = second
+#define MAX_READ_TIMEOUT_IN_SEC 1
+
+
+#endif
+
diff --git a/bluetooth/serialhcibus/ReadMe.md b/bluetooth/serialhcibus/ReadMe.md
new file mode 100644
index 00000000..871bb9e1
--- /dev/null
+++ b/bluetooth/serialhcibus/ReadMe.md
@@ -0,0 +1,59 @@
+Bluetooth Serial HCI Bus Driver
+===============================
+
+The purpose of this sample is to demonstrate how to implement a basic bus driver to support the new [Bluetooth Extensibility transport DDIs](http://msdn.microsoft.com/en-us/library/windows/hardware/ff536585) over the UART transport. Such a serial bus driver can support a multi-radio device over the UART transport and utilize a common Bluetooth HCI packet for communication. The lower edge of this driver interfaces with a UART controller following the Bluetooth SIG’s UART (H4) transport protocol.
+
+## Universal Compliant
+This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core.
+
+**Note**: This sample driver is generic i.e. it is not designed for a specific device and allows for a vendor to adopt and enhance it for supporting Bluetooth
+
+This sample driver, as is, may not properly function for a device until all vendor-specific device requirements (e.g. device initialization) have been incorporated
+
+It is recommended to use WDK whose version matches the target Windows build version or newer for the development of the serial bus driver.
+
+**FILE MANIFEST**
+
+**WDK header file**
+
+BthXDDI.h – this has the constants, struct, and IOCTL definitions for the Bluetooth extensibility transport. This header file is included in WDK.
+
+**Common code section**
+
+driver.c – driver initialization
+
+driver.h – common header file for driver.c and includes other header files
+
+Fdo.c – functions for function device object (FDO) and BTHX DDI processing
+
+io.c – functions that perform IO read pump via UART controller
+
+Io.h – header for io.c
+
+pdo.c – PDO (Bluetooth function) enumeration and IOCTL processing
+
+public.h – header to share with application to support Radio On/Off (“Airplane mode”)
+
+Note: The goal is to keep the common code section the same, so the vendor will only need to update those code sections in the device specific directory.
+
+**Device-specific code section**
+
+Debugdef.h – WPP trace GUID; user should use a new GUID (unique per driver)
+
+device.c – device specific functions to implement:
+
+--DeviceInitialize() – to perform UART and Bluetooth device initialization;
+
+--DeviceEnable() – (optional) to bring serial bus device out of disable/reset state.
+
+--DevicePowerOn() – (optional) to power on the device.
+
+--DeviceEnableWakeControl() – (optional) to arm for device wake signal
+
+--DeviceDisableWakeControl() – (optional) to disarm for device wake signal
+
+device.h – header file for device.c
+
+driver.rc – driver version and name
+
+SerialBusWdk.inx – device specific INF file to install this driver. The vendor will need to add the hardware ID to match the “\_HID” for the Serial Bus Device (Bluetooth) in the DSDT.asl file. For example, in SerialBusWDK.inx, the hardware ID is “ACPI\\\<Vendor\>\_BTH0” where “\<vendor\>” could be a 4 digit vendor name
diff --git a/bluetooth/serialhcibus/WDK/Debugdef.h b/bluetooth/serialhcibus/WDK/Debugdef.h
new file mode 100644
index 00000000..96958c58
--- /dev/null
+++ b/bluetooth/serialhcibus/WDK/Debugdef.h
@@ -0,0 +1,49 @@
+#ifndef __DEBUGDEF_H__
+#define __DEBUGDEF_H__
+
+//
+// SerialBusWdk's control Guid
+// {a2bf23a4-bc00-4903-81f2-6fbd82ad2d13}
+//
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID(serialbuswdk,(a2bf23a4,bc00,4903,81f2,6fbd82ad2d13), \
+ WPP_DEFINE_BIT(TFLAG_PNP) \
+ WPP_DEFINE_BIT(TFLAG_POWER) \
+ WPP_DEFINE_BIT(TFLAG_UART) \
+ WPP_DEFINE_BIT(TFLAG_IOCTL) \
+ WPP_DEFINE_BIT(TFLAG_IO) \
+ WPP_DEFINE_BIT(TFLAG_DATA) \
+ WPP_DEFINE_BIT(TFLAG_HCI))
+
+//
+// Define shorter versions of the ETW trace levels
+//
+#define LEVEL_CRITICAL TRACE_LEVEL_CRITICAL
+#define LEVEL_ERROR TRACE_LEVEL_ERROR
+#define LEVEL_WARNING TRACE_LEVEL_WARNING
+#define LEVEL_INFO TRACE_LEVEL_INFORMATION
+#define LEVEL_VERBOSE TRACE_LEVEL_VERBOSE
+
+#define WPP_LEVEL_FLAG_ENABLED(lvl, component) \
+ (WPP_LEVEL_ENABLED(component) && WPP_CONTROL(WPP_BIT_ ## component).Level >=lvl)
+
+#define WPP_LEVEL_FLAG_LOGGER(lvl, component) \
+ WPP_LEVEL_LOGGER(component)
+
+//
+// IFR enable macros
+//
+#define WPP_RECORDER_LEVEL_FLAG_ARGS(lvl, component) \
+ WPP_CONTROL(WPP_BIT_ ## component).AutoLogContext, 0, WPP_BIT_ ## component
+#define WPP_RECORDER_LEVEL_FLAG_FILTER(lvl, component) \
+ (lvl < TRACE_LEVEL_VERBOSE || WPP_CONTROL(WPP_BIT_ ## component).AutoLogVerboseEnabled)
+
+//
+// Use for WPP trace
+//
+#define WithinRange(min, value, max) (min <= value && value <= max)
+#define MinToPrint(val1, val2) (val1 < val2 ? val1 : val2)
+#define MAX_EVENT_PARAMS_TO_DISPLAY 8 // maximun number of event parameter to be printed (WPP)
+#define MAX_COMMAND_PARAMS_TO_DISPLAY 8 // maximun number of vommand parameter to be printed (WPP)
+
+#endif // __DEBUGDEF_H__
diff --git a/bluetooth/serialhcibus/WDK/SerialBusWdk.inx b/bluetooth/serialhcibus/WDK/SerialBusWdk.inx
new file mode 100644
index 00000000..b6e81096
--- /dev/null
+++ b/bluetooth/serialhcibus/WDK/SerialBusWdk.inx
@@ -0,0 +1,88 @@
+;/*++
+;
+;Copyright (c) 2010 Microsoft Corporation All rights Reserved
+;
+;Module Name:
+;
+; SerialBusWdk.INF
+;
+;Abstract:
+; INF file for installing a serial bus driver over UART transport
+;
+;Installation Notes:
+; For testing purpose, use Devcon.exe to install
+;
+; "devcon install SerialBusWdk.inf SerialBusWdk_RootEnum"
+;
+;--*/
+
+[Version]
+Signature="$WINDOWS NT$"
+Class=System
+ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318}
+Provider=%MSFTSAMPLE%
+DriverVer=06/21/2006,6.2.7923.0
+CatalogFile=SerialBusWdk.cat
+
+[DestinationDirs]
+DefaultDestDir = 12
+
+
+[SourceDisksNames]
+1 = %DiskId1%,,,""
+
+[SourceDisksFiles]
+SerialBusWdk.sys = 1,,
+
+
+[ControlFlags]
+ExcludeFromSelect=*
+
+[Manufacturer]
+%StdMfg%=VendorModels,NT$ARCH$
+
+;*****************************************
+; Serial bus driver install Section
+;*****************************************
+
+[VendorModels.NT$ARCH$]
+%Bus.DeviceDesc%=Bus_Vendor, SerialBusWdk_RootEnum
+
+[Bus_Vendor.NT]
+CopyFiles=Bus.CopyFiles
+
+[Bus.CopyFiles]
+SerialBusWdk.sys
+
+[Bus_Vendor.NT.HW]
+AddReg=Bus_Vendor.NT.AddReg
+
+[Bus_Vendor.NT.AddReg]
+; Reference: http://msdn.microsoft.com/en-us/library/ff546320.aspx
+HKR,,DeviceCharacteristics,0x10001,0x0100 ; Use same security checks on relative opens
+; Reference: http://msdn.microsoft.com/en-us/library/windows/hardware/ff563667(v=vs.85).aspx
+HKR,,Security,,"D:P(A;;GA;;;BA)(A;;GA;;;SY)(A;;GA;;;LS)" ; DACL Protected to allow generic-all access to built-in Admin group, system, and Local Service.
+
+;-------------- Service installation
+[Bus_Vendor.NT.Services]
+AddService = SerialBusWdk,%SPSVCINST_ASSOCSERVICE%, Bus_Service_Inst
+
+; -------------- statbus driver install sections
+[Bus_Service_Inst]
+DisplayName = %Bus.SVCDESC%
+ServiceType = 1 ; SERVICE_KERNEL_DRIVER
+StartType = 3 ; SERVICE_DEMAND_START
+ErrorControl = 1 ; SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\SerialBusWdk.sys
+
+[Strings]
+;Non-Localizable
+
+SPSVCINST_ASSOCSERVICE= 0x00000002
+
+;Localizable
+MSFTSAMPLE = "Microsoft Sample"
+StdMfg = "(Standard system devices)"
+DiskId1 = "Serial Bus Driver over UART Installation Disk #1"
+Bus.DeviceDesc = "Serial Bus Driver over UART Bus Enumerator"
+Bus.SVCDESC = "Serial Bus Driver over UART Bus Enumerator" \ No newline at end of file
diff --git a/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj b/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj
new file mode 100644
index 00000000..770cb2e7
--- /dev/null
+++ b/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj
@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{B126E3A5-91B5-41B5-9B40-719D474AA513}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{0929A3E4-68D7-482B-93C5-539571CF51D7}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems">
+ <ClCompile Include="..\fdo.c; device.c; ..\io.c; ..\pdo.c; ..\driver.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppScanConfigurationData>debugdef.h</WppScanConfigurationData>
+ <WppTraceFunction>DoTrace(LEVEL,FLAG,(MSG,...))</WppTraceFunction>
+ </ClCompile>
+ <Inf Include=".\SerialBusWdk.inx">
+ <Architecture>$(InfArch)</Architecture>
+ <SpecifyArchitecture>true</SpecifyArchitecture>
+ <CopyOutput>.\$(IntDir)\SerialBusWdk.inf</CopyOutput>
+ </Inf>
+ <OtherWpp Include="driver.rc">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppScanConfigurationData>debugdef.h</WppScanConfigurationData>
+ <WppTraceFunction>DoTrace(LEVEL,FLAG,(MSG,...))</WppTraceFunction>
+ </OtherWpp>
+ </ItemGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>SerialBusWdk</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>SerialBusWdk</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>SerialBusWdk</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>SerialBusWdk</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES</PreprocessorDefinitions>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES</PreprocessorDefinitions>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\Ntstrsafe.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES</PreprocessorDefinitions>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES</PreprocessorDefinitions>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\Ntstrsafe.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES</PreprocessorDefinitions>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES</PreprocessorDefinitions>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\Ntstrsafe.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES</PreprocessorDefinitions>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES</PreprocessorDefinitions>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\Ntstrsafe.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ResourceCompile Include="driver.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj.Filters b/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj.Filters
new file mode 100644
index 00000000..83b788e4
--- /dev/null
+++ b/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj.Filters
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{4614A6EB-0DDA-4E7D-B6B7-1AC9BF5F6AAA}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{A95E3765-07D5-45E1-B82E-8D9EF61CD4BC}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{4E3D8A20-8B42-428D-AAEB-A5BA3C5C33C5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{C6A2211E-78CA-4EFF-85FD-A921F4F8EB29}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\driver.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\fdo.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\io.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\pdo.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="device.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <FilesToPackage Include=".\Debug\\SerialBusWdk.inf">
+ <Filter>Driver Files</Filter>
+ </FilesToPackage>
+ <Inf Include=".\SerialBusWdk.inx">
+ <Filter>Driver Files</Filter>
+ </Inf>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="driver.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/bluetooth/serialhcibus/WDK/device.c b/bluetooth/serialhcibus/WDK/device.c
new file mode 100644
index 00000000..11859e9f
--- /dev/null
+++ b/bluetooth/serialhcibus/WDK/device.c
@@ -0,0 +1,257 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ Device.c
+
+Abstract:
+
+ This file handles device specific operations.
+
+Environment:
+
+ Kernel mode only
+
+--*/
+
+#include "driver.h"
+#include "Device.tmh"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, DeviceQueryDeviceParameters)
+#endif
+
+#define STR_BAUDRATE L"BaudRateIndex"
+
+VOID
+DeviceQueryDeviceParameters(
+ _In_ WDFDRIVER _Driver
+)
+/*++
+Routine Description:
+
+ Query driver's registry location for device specific parameter, such as baudrate.
+
+ HLM\system\CCS\Services\serialhcibus\Parameters\
+
+ KeyName/Type/value
+
+Arguments:
+
+ _Driver - WDF Driver object
+
+Return Value:
+
+ None
+
+--*/
+{
+ WDFKEY Key;
+ NTSTATUS Status;
+ UNICODE_STRING ValueName;
+ ULONG Value = 0;
+
+ PAGED_CODE();
+
+ Status = WdfDriverOpenParametersRegistryKey(_Driver,
+ GENERIC_READ,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &Key
+ );
+ if (NT_SUCCESS(Status)) {
+
+ RtlInitUnicodeString(&ValueName, STR_BAUDRATE);
+ Status = WdfRegistryQueryULong(Key, &ValueName, &Value);
+
+ if (NT_SUCCESS(Status)) {
+ // Vendor: can cache and use this values.
+ }
+
+ WdfRegistryClose(Key);
+ }
+
+}
+
+
+NTSTATUS
+DeviceEnableWakeControl(
+ _In_ WDFDEVICE _Device,
+ _In_ SYSTEM_POWER_STATE _PowerState
+ )
+/*++
+
+Routine Description:
+
+ Vendor: This is a device specific function, and it arms the wake mechanism
+ for this driver to receive the wake signal. This could be using an
+ HOST_WAKE GPIO interrupt, or inband CTS/RTS mechanism.
+
+Arguments:
+
+ _Device - WDF Device object
+ _PowerState - Context used for reading data from target UART device
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ UNREFERENCED_PARAMETER(_Device);
+ UNREFERENCED_PARAMETER(_PowerState);
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+DeviceDisableWakeControl(
+ WDFDEVICE _Device
+ )
+/*++
+
+Routine Description:
+
+ Vendor: This is a device specific function, and it disarms the wake mechanism
+ for this driver to receive the wake signal.
+
+Arguments:
+
+ _Device - WDF Device object
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ UNREFERENCED_PARAMETER(_Device);
+
+ return;
+}
+
+BOOLEAN
+DeviceInitialize(
+ _In_ PFDO_EXTENSION _FdoExtension,
+ _In_ WDFIOTARGET _IoTargetSerial,
+ _In_ WDFREQUEST _RequestSync,
+ _In_ BOOLEAN _IsUartReset
+ )
+/*++
+Routine Description:
+
+ This function perform device specific operations to intialize in order
+ to bring the device to operational state.
+
+Arguments:
+
+ _FdoExtension - Function device object extension
+
+ _IoTargetSerial - IO Target to issue request to serial port
+
+ _RequestSync - A reuseable WDF Request to issue serial control
+
+ -IsUartReset - Is UART Reset is required
+
+Return Value:
+
+ TRUE if initialization is completed and successful; FALSE otherwise.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(_FdoExtension);
+ UNREFERENCED_PARAMETER(_IoTargetSerial);
+ UNREFERENCED_PARAMETER(_RequestSync);
+ UNREFERENCED_PARAMETER(_IsUartReset);
+
+ //
+ // Vendor specifc operation;
+ //
+
+ return TRUE;
+}
+
+NTSTATUS
+DeviceEnable(
+ _In_ WDFDEVICE _Device,
+ _In_ BOOLEAN _IsEnabled
+ )
+
+/*++
+
+Routine Description:
+
+ This function enable/wake serial bus device.
+
+Arguments:
+
+ _Device - Supplies a handle to the framework device object.
+
+ _IsEnabled - Boolean to enable or disable the BT device.
+
+
+Return Value:
+
+ NTSTATUS code.
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(_Device);
+ UNREFERENCED_PARAMETER(_IsEnabled);
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+DevicePowerOn(
+ _In_ WDFDEVICE _Device
+)
+/*++
+
+Routine Description:
+
+ This routine powers on the serial bus device
+
+Arguments:
+
+ _Device - Supplies a handle to the framework device object.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(_Device);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+DevicePowerOff(
+ _In_ WDFDEVICE _Device
+)
+/*++
+
+Routine Description:
+
+ This routine powers off the serial bus device
+
+Arguments:
+
+ _Device - Supplies a handle to the framework device object.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(_Device);
+
+ return STATUS_SUCCESS;
+}
+
diff --git a/bluetooth/serialhcibus/WDK/device.h b/bluetooth/serialhcibus/WDK/device.h
new file mode 100644
index 00000000..1827493d
--- /dev/null
+++ b/bluetooth/serialhcibus/WDK/device.h
@@ -0,0 +1,56 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ device.h
+
+Abstract:
+
+ Header definitions and structs that are device specific
+
+Author:
+
+Environment:
+
+ Kernel mode only
+
+
+Revision History:
+
+--*/
+
+#ifndef __DEVICE_H__
+#define __DEVICE_H__
+
+#pragma warning(disable:4214) // bit field types other than int
+
+
+#define BT_PDO_HARDWARE_IDS L"SerialBusWdk\\UART_H4"
+#define BT_PDO_COMPATIBLE_IDS L"MS_BTHX_BTHMINI"
+#define BT_PDO_DEVICE_LOCATION L"Serial HCI Bus - Bluetooth Function"
+
+
+//
+// 255 bytes of data + 3 bytes for HCI cmd hdr (2-byte opcode + 1-byte Parameter).
+//
+#define MIN_HCI_CMD_SIZE (3)
+#define MAX_HCI_CMD_SIZE (258)
+
+//
+// 255 bytes of data + 2 byte hdr (1-byte event code + 1-byte parameter).
+//
+#define MIN_HCI_EVENT_SIZE (2)
+#define MAX_HCI_EVENT_SIZE (257)
+
+//
+// Can be variable but usually 1021-byte (largest 3-DH5 ACL packet size)
+//
+#define HCI_ACL_HEADER_SIZE (4)
+#define HCI_MAX_ACL_PAYLOAD_SIZE (1021)
+#define MIN_HCI_ACLDATA_SIZE HCI_ACL_HEADER_SIZE
+#define MAX_HCI_ACLDATA_SIZE (HCI_ACL_HEADER_SIZE + HCI_MAX_ACL_PAYLOAD_SIZE)
+
+#endif
+
diff --git a/bluetooth/serialhcibus/WDK/driver.rc b/bluetooth/serialhcibus/WDK/driver.rc
new file mode 100644
index 00000000..1d3dd7a9
--- /dev/null
+++ b/bluetooth/serialhcibus/WDK/driver.rc
@@ -0,0 +1,14 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Serial bus driver over UART using a common HCI packet"
+#define VER_INTERNALNAME_STR "SerialBusWdk.sys"
+#define VER_ORIGINALFILENAME_STR "SerialBusWdk.sys"
+
+#include "common.ver"
+
+//MofResourceName MOFDATA driver.bmf
+
diff --git a/bluetooth/serialhcibus/driver.c b/bluetooth/serialhcibus/driver.c
new file mode 100644
index 00000000..8bb5b6aa
--- /dev/null
+++ b/bluetooth/serialhcibus/driver.c
@@ -0,0 +1,378 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ driver.C
+
+Abstract:
+
+ This module contains routines to handle the function driver
+ aspect of the bus driver.
+
+Environment:
+
+ kernel mode only
+
+--*/
+
+#include "driver.h"
+#include "driver.tmh"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text (PAGE, DriverCleanup)
+#pragma alloc_text (PAGE, DriverSetDeviceCallbackEvents)
+#pragma alloc_text (PAGE, DriverDeviceAdd)
+#pragma alloc_text (INIT, DriverEntry)
+#endif
+
+VOID
+DriverCleanup(
+ _In_ WDFOBJECT _Object
+ )
+/*++
+
+Routine Description:
+
+ This callback function performs operations that must take place before the
+ driver is unloaded. Free all the resources allocated in DriverEntry.
+
+Arguments:
+
+ _Object - handle to a WDF Driver object.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("+DriverCleanup"));
+
+ WPP_CLEANUP( WdfDriverWdmGetDriverObject( _Object ));
+}
+
+
+VOID
+DriverSetDeviceCallbackEvents(
+ _In_ PWDFDEVICE_INIT _DeviceInit
+ )
+// Initialize device callback events
+{
+ WDF_POWER_POLICY_EVENT_CALLBACKS PowerPolicyCallbacks;
+ WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks;
+
+ PAGED_CODE();
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("+DriverSetDeviceCallbackEvents"));
+
+ //
+ // Set event callbacks
+ // 1. Pnp & Power events
+ // 2. Power Policy events
+ //
+
+ WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks);
+
+ //
+ // Register PnP callback
+ //
+ PnpPowerCallbacks.EvtDevicePrepareHardware = FdoDevPrepareHardware;
+ PnpPowerCallbacks.EvtDeviceReleaseHardware = FdoDevReleaseHardware;
+
+ //
+ // Register Power callback
+ //
+ PnpPowerCallbacks.EvtDeviceD0Entry = FdoDevD0Entry;
+ PnpPowerCallbacks.EvtDeviceD0Exit = FdoDevD0Exit;
+ PnpPowerCallbacks.EvtDeviceSelfManagedIoInit = FdoDevSelfManagedIoInit;
+ PnpPowerCallbacks.EvtDeviceSelfManagedIoCleanup = FdoDevSelfManagedIoCleanup;
+
+ WdfDeviceInitSetPnpPowerEventCallbacks(_DeviceInit,
+ &PnpPowerCallbacks);
+
+ //
+ // This driver can manage arm and disarm wake signal to support
+ // idle while S0/Sx.
+ //
+ WDF_POWER_POLICY_EVENT_CALLBACKS_INIT(&PowerPolicyCallbacks);
+
+ //
+ // Register power policy callback. This is device specific. The ArmWake
+ // callback function can enable/disable external event that triggers a
+ // wake signal.
+ // These functions are invoked only if Idle capability is also set; that is,
+ // IdleSettings.IdleCaps == IdleCanWakeFromS0
+ //
+ PowerPolicyCallbacks.EvtDeviceArmWakeFromS0 = FdoEvtDeviceArmWake;
+ PowerPolicyCallbacks.EvtDeviceDisarmWakeFromS0 = FdoEvtDeviceDisarmWake;
+
+ WdfDeviceInitSetPowerPolicyEventCallbacks(_DeviceInit,
+ &PowerPolicyCallbacks);
+}
+
+
+NTSTATUS
+DriverDeviceAdd(
+ IN WDFDRIVER _Driver,
+ IN PWDFDEVICE_INIT _DeviceInit
+ )
+/*++
+Routine Description:
+
+ DriverDeviceAdd is called by the framework in response to AddDevice
+ call from the PnP manager. We create and initialize a device object to
+ represent a new instance of toaster bus.
+
+Arguments:
+
+ _Driver - Handle to a framework driver object created in DriverEntry
+
+ _DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ WDF_IO_QUEUE_CONFIG QueueConfig;
+ WDF_OBJECT_ATTRIBUTES Attributes;
+ NTSTATUS Status;
+ WDFDEVICE Device;
+ PFDO_EXTENSION FdoExtension;
+ WDFQUEUE Queue;
+ PNP_BUS_INFORMATION BusInfo;
+ WDF_DEVICE_STATE DeviceState;
+#ifdef DYNAMIC_ENUM
+ WDF_CHILD_LIST_CONFIG Config;
+#endif
+
+
+ PAGED_CODE();
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP, ("+DriverDeviceAdd: 0x%p", _Driver));
+
+ //
+ // Get device specific parameters, such as baudrate
+ //
+ DeviceQueryDeviceParameters(_Driver);
+
+ //
+ // Set PnP, Power and Power Policy event callback
+ //
+ DriverSetDeviceCallbackEvents(_DeviceInit);
+
+ //
+ // Initialize all the properties specific to the device.
+ // Framework has default values for the one that are not
+ // set explicitly here. So please read the doc and make sure
+ // you are okay with the defaults.
+ //
+ WdfDeviceInitSetDeviceType(_DeviceInit, FILE_DEVICE_BUS_EXTENDER);
+
+#ifdef DYNAMIC_ENUM
+
+ //
+ // WDF_ DEVICE_LIST_CONFIG describes how the framework should handle
+ // dynamic child enumeration on behalf of the driver writer.
+ // Since we are a bus driver, we need to specify identification description
+ // for our child devices. This description will serve as the identity of our
+ // child device. Since the description is opaque to the framework, we
+ // have to provide bunch of callbacks to compare, copy, or free
+ // any other resources associated with the description.
+ //
+ WDF_CHILD_LIST_CONFIG_INIT(&Config,
+ sizeof(PDO_IDENTIFICATION_DESCRIPTION),
+ FdoEvtDeviceListCreatePdo // callback to create a child device.
+ );
+
+ // Do not register function pointers and use default option unless customization is
+ // required. Consult MSDN or other WDK documentation for their usage.
+
+ //
+ // Tell the framework to use the built-in childlist to track the state
+ // of the device based on the configuration we just created.
+ //
+ WdfFdoInitSetDefaultChildListConfig(_DeviceInit,
+ &Config,
+ WDF_NO_OBJECT_ATTRIBUTES);
+#endif
+
+ //
+ // Initialize Attributes structure to specify size and accessor function
+ // for storing device context.
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, FDO_EXTENSION);
+
+ //
+ // Create a framework device object to represent FDO of this bus driver. In response
+ // to this call, framework creates a WDM deviceobject.
+ // Can no longer access the WDFDEVICE_INIT structure after this call.
+ //
+ Status = WdfDeviceCreate(&_DeviceInit,
+ &Attributes,
+ &Device);
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfDeveiceCreate failed %!STATUS!", Status));
+ return Status;
+ }
+
+
+ //
+ // Allow serial bus driver to be disabled
+ //
+ WDF_DEVICE_STATE_INIT(&DeviceState);
+ DeviceState.NotDisableable = WdfFalse;
+ WdfDeviceSetDeviceState(Device, &DeviceState);
+
+
+ //
+ // Get the device context.
+ //
+ FdoExtension = FdoGetExtension(Device);
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);
+ Attributes.ParentObject = Device;
+
+ //
+ // Purpose of this lock is documented in FdoCreateOneChildDevice routine.
+ //
+ Status = WdfWaitLockCreate(&Attributes, &FdoExtension->ChildLock);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ //
+ // Create a power-managed IO Queue
+ //
+ // Configure a default queue so that requests that are not
+ // configure-forwarded using WdfDeviceConfigureRequestDispatching to go to
+ // other queues get dispatched here.
+ //
+ WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig,
+ WdfIoQueueDispatchParallel);
+ QueueConfig.PowerManaged = WdfTrue;
+ // Queue's callback event
+ QueueConfig.EvtIoDeviceControl = FdoIoQuDeviceControl;
+
+ //
+ // By default, Static Driver Verifier (SDV) displays a warning if it
+ // doesn't find the EvtIoStop callback on a power-managed queue.
+ // The 'assume' below causes SDV to suppress this warning.
+ //
+ // No need to handle EvtIoStop/Resume:
+ //
+ // Condition: When there is a device state change from D0 to Dx, it is processed as a
+ // device stop event, and the caller (BthMini) will cancel all pending IOs.
+ //
+ // 1. Write command/data Requests are marked cancellable by serial bus driver, and
+ // the cancellation routine will handle the cancellation.
+ //
+ // 2. Read event/data Requests have their separate queues (with manual dispatch);
+ // when a request is in the queue, WDF owns the requests and can cancel the request
+ // in response to a cancel reqeust (IoCancelIrp) from the caller (BthMini).
+ //
+
+ __analysis_assume(QueueConfig.EvtIoStop != 0);
+ Status = WdfIoQueueCreate(Device,
+ &QueueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &Queue);
+ __analysis_assume(QueueConfig.EvtIoStop == 0);
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfIoQueueCreate failed %!STATUS!", Status));
+ return Status;
+ }
+
+ //
+ // Create device interface for this device. The interface will be
+ // enabled by the framework when we return from StartDevice successfully.
+ // Use this interface to support Bluetooth Radio on/off scenario
+ //
+ Status = WdfDeviceCreateDeviceInterface(Device,
+ &GUID_DEVINTERFACE_BLUETOOTH_RADIO_ONOFF_VENDOR_SPECIFIC,
+ NULL /* No Reference String */ );
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfDeviceCreateDeviceInterface failed %!STATUS!", Status));
+ return Status;
+ }
+
+ //
+ // This value is used in responding to the IRP_MN_QUERY_BUS_INFORMATION
+ // for the child devices. This is an optional information provided to
+ // uniquely idenitfy the bus the device is connected.
+ //
+ BusInfo.BusTypeGuid = GUID_SERENUM_BUS_ENUMERATOR;
+ BusInfo.LegacyBusType = PNPBus;
+ BusInfo.BusNumber = 0;
+
+ WdfDeviceSetBusInformationForChildren(Device, &BusInfo);
+
+ //
+ // Note: Do static PDO enumeration in FdoDevPrepareHardware PnP callback
+ //
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP, ("-DriverDeviceAdd: exit %!STATUS!", Status));
+
+ return Status;
+}
+
+
+NTSTATUS
+DriverEntry(
+ _In_ PDRIVER_OBJECT _DriverObject,
+ _In_ PUNICODE_STRING _RegistryPath
+ )
+/*++
+Routine Description:
+
+ Initialize the call backs structure of Driver Framework.
+
+Arguments:
+
+ _DriverObject - pointer to the driver object
+
+ _RegistryPath - pointer to a unicode string representing the path,
+ to driver-specific key in the registry.
+
+Return Value:
+
+ NT Status Code
+
+--*/
+{
+ WDF_DRIVER_CONFIG Config;
+ NTSTATUS Status;
+ WDF_OBJECT_ATTRIBUTES Attributes;
+
+
+ WDF_DRIVER_CONFIG_INIT(&Config, DriverDeviceAdd);
+ Config.DriverPoolTag = POOLTAG_UARTHCIBUSSAMPLE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);
+ Attributes.EvtCleanupCallback = DriverCleanup;
+
+ //
+ // Create a framework driver object to represent our driver.
+ //
+ Status = WdfDriverCreate(_DriverObject,
+ _RegistryPath,
+ &Attributes,
+ &Config,
+ WDF_NO_HANDLE);
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, ("WdfDriverCreate failed %!STATUS!", Status));
+ return Status;
+ }
+
+ WPP_INIT_TRACING(_DriverObject, _RegistryPath);
+
+ return Status;
+
+}
+
diff --git a/bluetooth/serialhcibus/driver.h b/bluetooth/serialhcibus/driver.h
new file mode 100644
index 00000000..d82f1f55
--- /dev/null
+++ b/bluetooth/serialhcibus/driver.h
@@ -0,0 +1,530 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ driver.h
+
+Abstract:
+
+ This module contains the common private declarations for
+ for the Serial HCI bus driver.
+
+Environment:
+
+ kernel mode only
+
+--*/
+
+#ifndef DRIVER_H
+#define DRIVER_H
+
+#include <ntddk.h>
+#include <wdf.h>
+
+#define NTSTRSAFE_LIB
+#include <ntstrsafe.h>
+
+#define INITGUID
+#include <guiddef.h>
+#include <ntddser.h> // Constants and types for access Serial device
+
+#include <BthXDDI.h> // BT Extensible Transport DDI
+
+#include "device.h" // Device specific
+#include "io.h" // Read pump
+#include "debugdef.h" // WPP trace
+#include "public.h" // Share between driver and application
+
+#ifdef DEFINE_GUID
+
+//
+// Container ID for internally connected device
+//
+DEFINE_GUID(GUID_CONTAINERID_INTERNALLY_CONNECTED_DEVICE,
+ 0x00000000, 0x0000, 0x0000, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
+
+//{00000000-0000-0000-ffff-ffffffffffff}
+
+#endif // #ifdef DEFINE_GUID
+
+//
+// Define HCI event code
+//
+#ifndef CommandComplete
+#define CommandComplete 0x0e
+#endif
+#ifndef CommandStatus
+#define CommandStatus 0x0f
+#endif
+
+#define POOLTAG_UARTHCIBUSSAMPLE 'SBHS' // 'S'erial 'H'ci 'B'us 'S'ample
+#undef ExAllocatePool
+#define ExAllocatePool(type, size) \
+ ExAllocatePoolWithTag(type, size, POOLTAG_UARTHCIBUSSAMPLE)
+
+//
+// An ID used to uniquely identify Bluetooth function from other function
+// of this multifunction device.
+//
+#define BLUETOOTH_FUNC_IDS 0x1001
+
+
+
+//
+// Device's idle state capability
+//
+typedef enum _IDLE_CAP_STATE {
+ IdleCapActiveOnly = 1, // Support active only (cannot idle)
+ IdleCapCanWake = 2, // Can enter D2 (idle) and remote wake to save power while in idle state.
+ IdleCapCanTurnOff = 3 // Can enter D3 (off) and not remote wake to save max power while device is off.
+} IDLE_CAP_STATE;
+
+
+#ifdef DYNAMIC_ENUM
+//
+// The goal of the identification and address description abstractions is that enough
+// information is stored for a discovered device so that when it appears on the bus,
+// the framework (with the help of the driver writer) can determine if it is a new or
+// existing device. The identification and address descriptions are opaque structures
+// to the framework, they are private to the driver writer. The only thing the framework
+// knows about these descriptions is what their size is.
+// The identification contains the bus specific information required to recognize
+// an instance of a device on its the bus. The identification information usually
+// contains device IDs along with any serial or slot numbers.
+// For some buses (like USB and PCI), the identification of the device is sufficient to
+// address the device on the bus; in these instances there is no need for a separate
+// address description. Once reported, the identification description remains static
+// for the lifetime of the device. For example, the identification description that the
+// PCI bus driver would use for a child would contain the vendor ID, device ID,
+// subsystem ID, revision, and class for the device. This sample uses only identification
+// description.
+// On other busses (like 1394 and auto LUN SCSI), the device is assigned a dynamic
+// address by the hardware (which may reassigned and updated periodically); in these
+// instances the driver will use the address description to encapsulate this dynamic piece
+// of data. For example in a 1394 driver, the address description would contain the
+// device's current generation count while the identification description would contain
+// vendor name, model name, unit spec ID, and unit software version.
+//
+typedef struct _PDO_IDENTIFICATION_DESCRIPTION
+{
+ WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER Header; // should contain this header
+
+ //
+ // Unique serail number of the device on the bus
+ //
+ ULONG SerialNo;
+
+ size_t CchHardwareIds;
+
+ _Field_size_bytes_(CchHardwareIds) PWCHAR HardwareIds;
+
+} PDO_IDENTIFICATION_DESCRIPTION, *PPDO_IDENTIFICATION_DESCRIPTION;
+#endif // #ifdef DYNAMIC_ENUM
+
+
+typedef struct _UART_READ_CONTEXT *PUART_READ_CONTEXT;
+
+//
+// Bus driver's FDO (Function Device Object) extension structure used to maintain device
+// properties and state.
+//
+
+
+typedef struct _FDO_EXTENSION
+{
+ WDFWAITLOCK ChildLock;
+
+ //
+ // Radio On/Off state
+ //
+ BOOLEAN IsRadioEnabled;
+
+ //
+ // WDF Device handle
+ //
+ WDFDEVICE WdfDevice;
+
+ //
+ // Serial port IO Target where we send IOCTL/READ/WRITE reuquest to
+ //
+ WDFIOTARGET IoTargetSerial;
+
+ //
+ // (optional) GPIO IO Target to enable serial bus device
+ //
+ WDFIOTARGET IoTargetGPIO;
+
+ //
+ // Bluetooth child dev node (PDO) capabilities
+ //
+ BTHX_CAPABILITIES BthXCaps;
+
+ //
+ // Indicator if UART is properly initialize; may require re-inialization
+ // when tranistion from exiting D0 to resume D0.
+ //
+ BOOLEAN DeviceInitialized;
+
+ //
+ // Cached UART controller connection IDs
+ //
+ LARGE_INTEGER UARTConnectionId;
+
+ //
+ // Cached I2C controller connection IDs
+ //
+ LARGE_INTEGER I2CConnectionId;
+
+ //
+ // Cached GPIO controller connection IDs
+ //
+ LARGE_INTEGER GPIOConnectionId;
+
+ //
+ // Preallocate WDF Requests for synchronous operation like serial port settings
+ //
+ WDFREQUEST RequestIoctlSync;
+
+ //
+ // Preallocate WDF Requests to wait on serial error event
+ //
+ WDFREQUEST RequestWaitOnError;
+
+ //
+ // Data return from serial event wait mask IOCTL
+ //
+ ULONG SerErrorMask;
+
+ //
+ // WDM memory use for Wait Mask event
+ //
+ WDFMEMORY WaitMaskMemory;
+
+ //
+ // Set if a hardware error (e.g. data overrun in UART FIFO) is detected
+ //
+ BOOLEAN HardwareErrorDetected;
+
+ //
+ // Indication the state of the read pump (TRUE = active)
+ //
+ BOOLEAN ReadPumpRunning;
+
+ //
+ // Track number of out-of-sync error that has been detected
+ //
+ ULONG OutOfSyncErrorCount;
+
+ //
+ // Locks for synchronization for list and queue
+ //
+ KSPIN_LOCK QueueAccessLock;
+
+ //
+ // Track next packet read (one and only one)
+ //
+ UART_READ_CONTEXT ReadContext;
+
+ //
+ // Preallocated local WDF requested and memory object that is reused to
+ // implement read pump
+ //
+ WDFREQUEST ReadRequest;
+ WDFMEMORY ReadMemory;
+ UCHAR ReadBuffer[MAX_H4_HCI_PACKET_SIZE];
+
+#if DBG
+ //
+ // Track last completed HCI packet
+ //
+ UCHAR LastPacket[MAX_H4_HCI_PACKET_SIZE];
+ ULONG LastPacketLength;
+#endif
+ //
+ // WDF Queue for HCI event Request and total number of such request recevied
+ //
+ WDFQUEUE ReadEventQueue;
+ LONG EventQueueCount;
+
+ //
+ // List to store (prefetched) incoming HCI events and number of entries
+ //
+ LIST_ENTRY ReadEventList;
+ LONG EventListCount;
+
+
+ //
+ // WDF Queue for HCI read data Request and total number of such request recevied
+ //
+ WDFQUEUE ReadDataQueue;
+ LONG DataQueueCount;
+
+ //
+ // List to store (prefetched) incoming HCI data and number of entries
+ //
+ LIST_ENTRY ReadDataList;
+ LONG DataListCount;
+
+
+ //
+ // Counts used to track HCI requests received and completed for various packet types
+ //
+ LONG CntCommandReq; // Track total number of HCI command Requests
+ LONG CntCommandCompleted; // Number of HCI Command completed
+
+ LONG CntEventReq; // Track total number of HCI Event Requests
+ LONG CntEventCompleted; // Number of HCI Command completed
+
+ LONG CntWriteDataReq; // Track total number of HCI Write Data requests
+ LONG CntWriteDataCompleted; // Number of HCI (write) Data completed
+
+ LONG CntReadDataReq; // Track total number of HCI Read Data Requests
+ LONG CntReadDataCompleted; // Number of HCI (Read) Data completed
+} FDO_EXTENSION, *PFDO_EXTENSION;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_EXTENSION, FdoGetExtension)
+
+
+//
+// Can send IO only if the device (UART) is in the initialized state.
+//
+#define IsDeviceInitialized(FdoExtension) (FdoExtension->DeviceInitialized)
+
+
+#define ValidConnectionID(ConnectionId) (ConnectionId.QuadPart != 0)
+
+
+//
+// Bus driver's child PDO (Physical Device Object) extension structure used to maintain this
+// PDO's device properties and state.
+//
+
+typedef struct _PDO_EXTENSION
+{
+ //
+ // Back pointer to FDO_EXTENSION
+ //
+ PFDO_EXTENSION FdoExtension;
+
+ //
+ // Unique serial number of the device on the bus
+ //
+ ULONG SerialNo;
+
+} PDO_EXTENSION, *PPDO_EXTENSION;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PDO_EXTENSION, PdoGetExtension)
+
+
+//
+// Prototypes of functions
+//
+
+
+//
+// Driver.c
+//
+
+EVT_WDF_OBJECT_CONTEXT_CLEANUP DriverCleanup;
+
+VOID
+DriverSetDeviceCallbackEvents(
+ _In_ PWDFDEVICE_INIT _DeviceInit
+ );
+
+EVT_WDF_DRIVER_DEVICE_ADD DriverDeviceAdd;
+
+DRIVER_INITIALIZE DriverEntry;
+
+
+//
+// FDO.c
+//
+
+NTSTATUS
+HlpInitializeFdoExtension(WDFDEVICE _Device);
+
+NTSTATUS
+FdoWriteDeviceIO(_In_ WDFREQUEST _RequestFromBthport,
+ _In_ WDFDEVICE _Device,
+ _In_ PFDO_EXTENSION _FdoExtension,
+ _In_ PBTHX_HCI_READ_WRITE_CONTEXT _HCIContext);
+
+NTSTATUS
+FdoWriteToDeviceSync(_In_ WDFIOTARGET _IoTargetSerial,
+ _In_ WDFREQUEST _RequestWriteSync,
+ _In_ ULONG _IoControlCode,
+ _In_opt_ ULONG _InBufferSize,
+ _In_opt_ PVOID _InBuffer,
+ _Out_ PULONG_PTR _BytesWritten);
+
+NTSTATUS
+DeviceConfigWaitOnError(_In_ WDFIOTARGET _IoTargetSerial,
+ _In_ WDFREQUEST _RequestWaitOnError,
+ _In_ WDFMEMORY _WaitMaskMemory,
+ _In_ PULONG _ErrorResult,
+ _In_ PFDO_EXTENSION _FdoExtension);
+
+
+NTSTATUS
+HCIContextValidate(ULONG Index,
+ PBTHX_HCI_READ_WRITE_CONTEXT _HCIContext);
+
+
+// Power policy events
+EVT_WDF_DEVICE_ARM_WAKE_FROM_S0 FdoEvtDeviceArmWake;
+EVT_WDF_DEVICE_DISARM_WAKE_FROM_S0 FdoEvtDeviceDisarmWake;
+
+EVT_WDF_DEVICE_ARM_WAKE_FROM_SX FdoEvtDeviceArmWake;
+EVT_WDF_DEVICE_DISARM_WAKE_FROM_SX FdoEvtDeviceDisarmWake;
+
+// PnP events
+EVT_WDF_DEVICE_PREPARE_HARDWARE FdoDevPrepareHardware;
+EVT_WDF_DEVICE_RELEASE_HARDWARE FdoDevReleaseHardware;
+
+// Power events
+EVT_WDF_DEVICE_D0_ENTRY FdoDevD0Entry;
+EVT_WDF_DEVICE_D0_EXIT FdoDevD0Exit;
+
+EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT FdoDevSelfManagedIoInit;
+EVT_WDF_DEVICE_SELF_MANAGED_IO_CLEANUP FdoDevSelfManagedIoCleanup;
+
+// Queue
+EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL FdoIoQuDeviceControl;
+
+// PDO creation
+
+#ifdef DYNAMIC_ENUM
+EVT_WDF_CHILD_LIST_CREATE_DEVICE FdoEvtDeviceListCreatePdo;
+
+NTSTATUS
+PdoCreateDynamic(_In_ WDFDEVICE Device,
+ _In_ PWDFDEVICE_INIT DeviceInit,
+ _In_ PWCHAR HardwareIds,
+ _In_ ULONG SerialNo);
+
+NTSTATUS
+FdoCreateOneChildDeviceDynamic(_In_ WDFDEVICE _Device,
+ _In_ PWCHAR _HardwareIds,
+ _In_ size_t _CchHardwareIds,
+ _In_ ULONG _SerialNo);
+#endif
+
+EVT_WDF_DEVICE_DISABLE_WAKE_AT_BUS PdoDevDisableWakeAtBus;
+EVT_WDF_DEVICE_ENABLE_WAKE_AT_BUS PdoDevEnableWakeAtBus;
+
+NTSTATUS
+FdoCreateOneChildDevice(_In_ WDFDEVICE _Device,
+ _In_ PWCHAR _HardwareIds,
+ _In_ ULONG _SerialNo);
+
+NTSTATUS
+FdoCreateAllChildren(_In_ WDFDEVICE _Device);
+
+NTSTATUS
+FdoRemoveOneChildDevice(WDFDEVICE _Device,
+ ULONG _SerialNo);
+
+NTSTATUS
+FdoFindConnectResources(_In_ WDFDEVICE _Device,
+ _In_ WDFCMRESLIST _ResourcesRaw,
+ _In_ WDFCMRESLIST _ResourcesTranslated);
+
+
+//
+// Pdo.c
+//
+
+EVT_WDF_DEVICE_PREPARE_HARDWARE PdoDevPrepareHardware;
+EVT_WDF_DEVICE_RELEASE_HARDWARE PdoDevReleaseHardware;
+
+EVT_WDF_DEVICE_D0_ENTRY PdoDevD0Entry;
+EVT_WDF_DEVICE_D0_EXIT PdoDevD0Exit;
+
+EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL PdoIoQuDeviceControl;
+
+NTSTATUS
+PdoCreate(_In_ WDFDEVICE _Device,
+ _In_ PWSTR _HardwareIds,
+ _In_ ULONG _SerialNo);
+
+VOID
+PdoDevDisableWakeAtBus(_In_ WDFDEVICE _Device);
+
+NTSTATUS
+PdoDevEnableWakeAtBus(_In_ WDFDEVICE _Device,
+ _In_ SYSTEM_POWER_STATE _PowerState);
+
+//
+// Define in io.c
+//
+NTSTATUS ReadResourcesAllocate(_In_ WDFDEVICE _Device);
+VOID ReadResourcesFree(_In_ WDFDEVICE _Device);
+
+NTSTATUS
+HLP_AllocateResourceForWrite(_In_ WDFDEVICE _Device,
+ _In_ WDFIOTARGET _IoTargetSerial,
+ _Out_ WDFREQUEST *_pRequest);
+
+VOID
+HLP_FreeResourceForWrite(PUART_WRITE_CONTEXT _TransferContext);
+
+EVT_WDF_REQUEST_CANCEL CB_RequestFromBthportCancel;
+
+EVT_WDF_REQUEST_COMPLETION_ROUTINE CR_WriteDeviceIO;
+
+NTSTATUS
+ReadRequestComplete(_In_ PFDO_EXTENSION _FdoExtension,
+ _In_ UCHAR _Type,
+ _In_ ULONG _PacketLength,
+ _In_reads_bytes_opt_(_PacketLength) PUCHAR _Packet,
+ _Inout_ WDFQUEUE _Queue,
+ _Inout_ PLONG _QueueCount,
+ _Inout_ PLIST_ENTRY _ListHead,
+ _Inout_ PLONG _ListCount);
+
+EVT_WDF_REQUEST_COMPLETION_ROUTINE ReadH4PacketCompletionRoutine;
+
+NTSTATUS
+ReadH4Packet(_In_ PUART_READ_CONTEXT _ReadContext,
+ _In_ WDFREQUEST _WdfRequest,
+ _In_ WDFMEMORY _WdfMemory,
+ _Pre_notnull_ _Pre_writable_byte_size_(_BufferLen) PVOID _Buffer,
+ _In_ ULONG _BufferLen);
+
+
+//
+// Device.c
+//
+
+VOID
+DeviceQueryDeviceParameters(_In_ WDFDRIVER _Driver);
+
+BOOLEAN
+DeviceInitialize(_In_ PFDO_EXTENSION _FdoExtension,
+ _In_ WDFIOTARGET _IoTargetSerial,
+ _In_ WDFREQUEST _RequestSync,
+ _In_ BOOLEAN _ResetUart);
+
+NTSTATUS
+DeviceEnableWakeControl(_In_ WDFDEVICE _Device,
+ _In_ SYSTEM_POWER_STATE _PowerState);
+void
+DeviceDisableWakeControl(WDFDEVICE _Device);
+
+NTSTATUS
+DeviceEnable(_In_ WDFDEVICE _Device,
+ _In_ BOOLEAN _Enabled);
+
+NTSTATUS
+DevicePowerOn(_In_ WDFDEVICE _Device);
+
+NTSTATUS
+DevicePowerOff(_In_ WDFDEVICE _Device);
+
+#endif
+
+
+
diff --git a/bluetooth/serialhcibus/io.c b/bluetooth/serialhcibus/io.c
new file mode 100644
index 00000000..0b4d64ac
--- /dev/null
+++ b/bluetooth/serialhcibus/io.c
@@ -0,0 +1,1592 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ IO.c
+
+Abstract:
+
+ This module contains routines that perform read/write IO operations.
+
+Environment:
+
+ Kernel mode only
+
+Revision History:
+
+--*/
+
+#include "driver.h"
+#include "IO.tmh"
+
+#pragma warning(disable:4127) // conditional expression is constant
+
+#ifdef ALLOC_PRAGMA
+#endif
+
+VOID
+CB_RequestFromBthportCancel(
+ _In_ WDFREQUEST _RequestFromUpper
+ )
+/*++
+
+Routine Description:
+
+ Request from upper layer that driver owns is being canceled. Its associated
+ Request to lower (UART) driver will be canceled and then this Request will
+ be completed with STATUS_CANCELLED.
+
+ There are different paths for the Request from upper layer:
+
+ 1. Completion routine is invoked without cancellation (typical path)
+ 2. Cancellation routine is invoked while lower Request is pending. The lower
+ request could be completed either
+ a. Synchronously - completion routine is invoked before
+ WdfRequestCancelSentRequest() is returned in the cancellation routine; or
+ b. Asynchronously - completion routine is invoked at later time after
+ WdfRequestCancelSentRequest has returned.
+ 3. Race conditions when both the cancelation and completion routine have independently started
+ a. Cancellation routine is ahead and the request is completed with cancellation status.
+ b. Completion routine is ahead and the request is completed with the status from the lower request.
+
+Arguments:
+
+ _RequestFromUpper - WDF Request to be cancelled
+
+Return Value:
+
+ none
+
+--*/
+{
+ PUART_WRITE_CONTEXT TransferContext;
+ WDFREQUEST RequestToUART;
+ WDFMEMORY Memory;
+ BOOLEAN CancelSuccess;
+ LONG CompletePath = REQUEST_PATH_NONE;
+
+
+ DoTrace(LEVEL_WARNING, TFLAG_IO, ("+CB_RequestFromBthportCancel: Request(%p) from upper driver", _RequestFromUpper));
+
+ TransferContext = GetWriteRequestContext(_RequestFromUpper);
+ NT_ASSERT(TransferContext && L"TransferContext is not valid!");
+
+ // Cancel the write Request that was previously submitted to its I/O target
+ RequestToUART = TransferContext->RequestToUART;
+ Memory = TransferContext->Memory;
+
+
+ //
+ // The below operation can return one of the following values.
+ // REQUEST_PATH_NONE
+ // This value was returned due to one of the following conditions
+ // 1. The completion routine was not yet run.
+ // 2. The completion routine was run and it relinquished the control of completing the request from bthport to the cancel routine.
+ //
+ // No matter what causes this value to be returned, this function is now responsible for completing the request from bthport.
+ //
+ // REQUEST_PATH_COMPLETION
+ // The completion routine was already called.
+ // The completion routine has not yet had a chance to relinquish control of completing the request from bthport.
+ //
+ // This function does not have the control to complete the request from bthport.
+ //
+ CompletePath = InterlockedOr(&TransferContext->RequestCompletePath, REQUEST_PATH_CANCELLATION);
+
+ if (REQUEST_PATH_NONE == CompletePath) {
+
+ DoTrace(LEVEL_WARNING, TFLAG_IO, (" >CancelSentRequest(%p) to IO Target", RequestToUART));
+ CancelSuccess = WdfRequestCancelSentRequest(RequestToUART);
+ DoTrace(LEVEL_WARNING, TFLAG_IO, (" <CancelSentRequest: %S", CancelSuccess ? L"Cancelled" : L"Failed"));
+
+ // Done sending the cancel. It can be dereferenced.
+ WdfObjectDereference(RequestToUART);
+
+ // No need to access this memory object in the cancellation code path in the completion function.
+ WdfObjectDelete(Memory);
+
+ // Cannot access this request, including WdfRequestUnmarkCancelable(), after it has been completed.
+ WdfRequestComplete(_RequestFromUpper, STATUS_CANCELLED);
+ }
+
+}
+
+
+NTSTATUS
+HLP_AllocateResourceForWrite(
+ _In_ WDFDEVICE _Device,
+ _In_ WDFIOTARGET _IoTargetSerial,
+ _Out_ WDFREQUEST *_PRequest
+ )
+/*++
+
+Routine Description:
+
+ This helper function allocate resource to perform a write request
+
+Arguments:
+
+ _Device - WDF Device object
+
+ _IoTargetSerial - WDF IO Target
+
+ _PRequest - WDF Request to allocate in this function
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS Status;
+ WDF_OBJECT_ATTRIBUTES ObjAttributes;
+
+ DoTrace(LEVEL_INFO, TFLAG_IO,("+HLP_AllocateResourceForWrite"));
+
+ // Create a WDF Request that will allocate a context(UART_WRITE_CONTEXT)
+ WDF_OBJECT_ATTRIBUTES_INIT(&ObjAttributes);
+ ObjAttributes.ParentObject = _Device;
+
+ Status = WdfRequestCreate(&ObjAttributes,
+ _IoTargetSerial,
+ _PRequest);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestCreate() failed %!STATUS!", Status));
+ goto Done;
+ }
+
+Done:
+
+ return Status;
+}
+
+
+VOID
+HLP_FreeResourceForWrite(
+ PUART_WRITE_CONTEXT _TransferContext
+ )
+/*++
+
+Routine Description:
+
+ This helper function free resource allocated to perform a write request
+
+Arguments:
+
+ _TransferContext - Transfer context used to perform write operation
+
+Return Value:
+
+ none
+
+--*/
+{
+ DoTrace(LEVEL_INFO, TFLAG_IO,("+HLP_FreeResourceForWrite"));
+
+ if (_TransferContext)
+ {
+ if (_TransferContext->Memory)
+ {
+ WdfObjectDelete(_TransferContext->Memory);
+ _TransferContext->Memory = NULL;
+ }
+
+ if (_TransferContext->RequestToUART)
+ {
+ WdfObjectDelete(_TransferContext->RequestToUART);
+ _TransferContext->RequestToUART = NULL;
+
+ }
+ }
+}
+
+
+VOID
+CR_WriteDeviceIO(
+ _In_ WDFREQUEST _Request,
+ _In_ WDFIOTARGET _Target,
+ _In_ PWDF_REQUEST_COMPLETION_PARAMS _Params,
+ _In_ WDFCONTEXT _Context
+ )
+/*++
+
+Routine Description:
+
+ This is the completion function for sending HCI packet to the lower layer.
+ This function can also complete the request from the upper layer; see the
+ description in the cancellation function for detail on the handling of possible
+ race conditions.
+
+ A RequestCompletionPath flag in the write Context is used with atomic Interlocked function
+ to ensure deterministic operation in both the cancellation and this completion functions.
+
+ If the cancellation function has been called, the WdfRequestUnmarkCancelable in the completion function will return STATUS_CANCELLED.
+ This return code is used to determine to handle the processing either as a typical completion, or as a cancellation and be in sync
+ with the cancellation function.
+
+ Here are what are performed in either situations:
+
+ 1. Typical completion (completion function only)
+ - WdfRequestUnmarkCancelable() returns not STATUS_CANCELLED
+ Exercise its typical completion code path
+ - Retrieve data transfer information for success case
+ - Dereference(RequestUART) - will not be accessed by cancellation function
+ - Complete(RequestFromUpper) & Delete(its Memory Object)
+
+ - Delete(RequestUART)
+ - Dereference(RequestFromUpper)
+
+ 2. Cancellation (both functions)
+ A: Cancellation Function
+ WdfRequestCancelSentRequest(RequestToUART) to cancel RequestToUART
+ - Dereference(RequestToUART) after cancel is sent
+ - Complete(RequestFromUpper) & Delete(its Memory Object)
+
+ B: Completion function
+ WdfRequestUnmarkCancelable() returns STATUS_CANCELLED
+ Exercise its cancellation code path
+ - Delete(RequestToUART)
+ - Dereference(RequestFromUpper)
+
+ Note: Code path A & B have no synchronization object to ensure their order of execution, but reference is taken on the Requests to ensure
+ that they stay valid until last access.
+
+ RequestToUART - take a reference to protect against being used by the cancellation function; it is de-referenced by the
+ - completion function - in its typical completion code path, or
+ - cancellation function - after finishing accessing it (to sent cancel)
+
+ RequestFromBthport - take a reference to protect against being completed by the cancellation function and then its context
+ is later accessed by the completion function; this can happen if the completion function is completed
+ asynchronously after WdfRequestCancelSentRequest() is returned; it is de-referenced by the
+ - completion function - right before it exits.
+
+Arguments:
+
+ _Request - WDF Request allocated by this driver
+ _Target - WDF IO Target
+ _Params - Completion parameters
+ _Context - Context used to process this request
+
+Return Value:
+
+ none
+
+--*/
+{
+ NTSTATUS Status;
+ PUART_WRITE_CONTEXT TransferContext;
+ PFDO_EXTENSION FdoExtension;
+ WDFREQUEST RequestFromBthport;
+ ULONG BytesDataWritten = 0;
+ LONG CompletePath = REQUEST_PATH_NONE;
+
+ UNREFERENCED_PARAMETER(_Target);
+
+ Status = _Params->IoStatus.Status;
+ TransferContext = (PUART_WRITE_CONTEXT) _Context;
+
+ DoTrace(LEVEL_INFO, TFLAG_DATA,("+CR_WriteDeviceIO: %!STATUS!, Request %p, Context %p",
+ Status, _Request, _Context));
+
+ NT_ASSERT( (Status == STATUS_SUCCESS || Status == STATUS_CANCELLED) && L"WriteHCI request failed!");
+
+ //
+ // Request to be completed to upper layer.
+ //
+ RequestFromBthport = TransferContext->RequestFromBthport;
+
+ //
+ // The below operation can return one of the following values.
+ // REQUEST_PATH_NONE
+ // This value was returned either because
+ // 1. This is the normal operation for this function and the request from bthport has to be completed.
+ // 2. The request from bthport has already been cancelled, but the cancellation routine has not yet been called (race condition).
+ //
+ // No matter what causes this value to be returned, it is safe to call WdfRequestUnmarkCancelable on the request from bthport
+ //
+ // REQUEST_PATH_CANCELLATION
+ // The cancellation routine was already called.
+ //
+ // This function does not have the control to complete the request from bthport.
+ //
+ CompletePath = InterlockedOr(&TransferContext->RequestCompletePath, REQUEST_PATH_COMPLETION);
+
+ // Mark RequestFromBthPort not cancellable as it is about to be completed.
+ if (REQUEST_PATH_NONE != CompletePath)
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_IO,(" Request %p is in the process of being cancelled", RequestFromBthport));
+ }
+ else
+ {
+ //
+ // Call WdfRequestUnmarkCancelable() to check whether this request has already been cancelled.
+ //
+ if (STATUS_CANCELLED == WdfRequestUnmarkCancelable(RequestFromBthport)) {
+ //
+ // The request from bthport has already been cancelled.
+ // Try to relinquish control of completing the request from bthport to the cancellation routine. It is possible that the cancellation routine
+ // has already been executed. In this case, this routine will have to complete the request from bthport.
+ //
+ // The below operation can return one of the following values.
+ // REQUEST_PATH_CANCELLATION | REQUEST_PATH_COMPLETION
+ // The cancellation routine was called. The cancellation will not complete the request, so this function will have to complete it.
+ //
+ // REQUEST_PATH_COMPLETION
+ // The cancellation routine has not yet been called.
+ // The InterlockedCompareExchange successfully masked the REQUEST_PATH_COMPLETE bit and so the completin routine
+ // will complete this request.
+ //
+ CompletePath = InterlockedCompareExchange(&TransferContext->RequestCompletePath,
+ REQUEST_PATH_NONE,
+ REQUEST_PATH_COMPLETION);
+
+ //
+ // Since the cancellation was already called and it will not complete the request, reset the value of complete to
+ // REQUEST_PATH_NONE so that the request from bthport will be completed.
+ //
+ if (CompletePath & REQUEST_PATH_CANCELLATION) {
+ CompletePath = REQUEST_PATH_NONE;
+ }
+ }
+
+ if (REQUEST_PATH_NONE == CompletePath) {
+
+ // Dereference this request as cancellation function is not invoked to access it.
+ WdfObjectDereference(_Request);
+
+ //
+ // Return data transfer information to caller for success Status
+ //
+ if (NT_SUCCESS(Status))
+ {
+ WDFMEMORY ReqOutMemory = NULL;
+ ULONG BytesWritten;
+ PULONG OutBuffer = NULL;
+ size_t OutBufferSize = 0;
+
+ BytesWritten = (ULONG) _Params->Parameters.Write.Length;
+
+ DoTrace(LEVEL_INFO, TFLAG_DATA,(" Packet: Type %d, DataLen %d, BytesWritten %d",
+ TransferContext->HCIContext->Type,
+ TransferContext->HCIContext->DataLen,
+ BytesWritten));
+
+ NT_ASSERT(BytesWritten == TransferContext->HCIPacketLen && "Unexpected incomplete HCI Write!");
+
+ if (BytesWritten != TransferContext->HCIPacketLen)
+ {
+ // return a generic failure for an incomplete transfer
+ Status = STATUS_UNSUCCESSFUL;
+ goto Done;
+ }
+
+ //
+ // return data bytes written in the OutputParameter
+ //
+ Status = WdfRequestRetrieveOutputMemory(RequestFromBthport, &ReqOutMemory);
+ if (NT_SUCCESS(Status))
+ {
+ OutBuffer = (PULONG) WdfMemoryGetBuffer(ReqOutMemory, &OutBufferSize);
+ if (OutBufferSize >= sizeof(ULONG))
+ {
+ // Set OutputParameter value and its size
+ *OutBuffer = TransferContext->HCIContext->DataLen;
+ BytesDataWritten = sizeof(ULONG);
+ }
+ }
+ }
+ else
+ {
+ // Return the status as is.
+ }
+ }
+ }
+
+Done:
+
+ if (REQUEST_PATH_NONE == CompletePath)
+ {
+ // Increment the completion count based on packet type.
+ FdoExtension = TransferContext->FdoExtension;
+
+ if (TransferContext->HCIContext->Type == (UCHAR) HciPacketCommand)
+ {
+ InterlockedIncrement(&FdoExtension->CntCommandCompleted);
+ }
+ else if (TransferContext->HCIContext->Type == (UCHAR) HciPacketAclData)
+ {
+ InterlockedIncrement(&FdoExtension->CntWriteDataCompleted);
+ }
+
+ DoTrace(LEVEL_INFO, TFLAG_IO,(" WriteDeviceIO: Request %p complete with %!STATUS! and %d BytesDataWritten",
+ RequestFromBthport, Status, BytesDataWritten));
+
+ // Delete this memory object that is no longer needed.
+ WdfObjectDelete(TransferContext->Memory);
+
+ // Cannot access this Request and its context after it is completed.
+ WdfRequestCompleteWithInformation(RequestFromBthport, Status, BytesDataWritten);
+
+ }
+
+ // Delete this request in its completion function.
+ WdfObjectDelete(_Request);
+
+ // Done accessing it in this function. This request is either completed in this function for the typical completion situation or in the cancellation function.
+ WdfObjectDereference(RequestFromBthport);
+
+ DoTrace(LEVEL_INFO, TFLAG_IO,("-CR_WriteDeviceIO"));
+}
+
+
+
+VOID
+ReadSegmentStateSet(
+ PUART_READ_CONTEXT _ReadContext,
+ UART_READ_STATE _NewState
+ )
+/*++
+
+Routine Description:
+
+ This helper centralize the setting of read state. It can be used to detect
+ possible incorrect state transition.
+
+Arguments:
+
+ _ReadContext - read context which has existing state
+ _NewState - new read state
+
+Return Value:
+
+ none
+
+--*/
+{
+ UART_READ_STATE OldState = _ReadContext->ReadSegmentState;
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, ("+<<<< -- %s to %s state -- >>>>",
+ OldState == GET_PKT_TYPE ? "Type" :
+ OldState == GET_PKT_HEADER ? "Header" :
+ OldState == GET_PKT_PAYLOAD ? "Payload" : "Unknown",
+ _NewState == GET_PKT_TYPE ? "Type" :
+ _NewState == GET_PKT_HEADER ? "Header" :
+ _NewState == GET_PKT_PAYLOAD ? "Payload" : "Unknown" ));
+
+ // Validate the state transition
+ switch (_NewState)
+ {
+ case GET_PKT_TYPE:
+ // Intialize the context for a new packet
+ _ReadContext->BytesReadNextSegment = 0;
+ _ReadContext->H4Packet.Type = 0;
+ _ReadContext->BytesToRead4FullPacket = 0;
+ RtlZeroMemory(_ReadContext->H4Packet.Packet.Raw, HCI_ACLDATA_HEADER_LEN);
+ break;
+ case GET_PKT_HEADER:
+ case GET_PKT_PAYLOAD:
+ // Reset segment count
+ _ReadContext->BytesReadNextSegment = 0;
+ break;
+ }
+
+ _ReadContext->ReadSegmentState = _NewState;
+}
+
+ // Full packet: match to a Request and complete it.
+NTSTATUS
+ReadH4PacketComplete(
+ PFDO_EXTENSION _FdoExtension,
+ UCHAR _Type,
+ _In_reads_bytes_(_BufferLength) PUCHAR _Buffer,
+ ULONG _BufferLength
+ )
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadH4PacketComplete %S Packet Length %d",
+ _Type == (UCHAR) HciPacketEvent ? L"Event" : L"AclData", _BufferLength ));
+
+#if DBG
+ // Tracking last completed packet
+ RtlCopyMemory(_FdoExtension->LastPacket, _Buffer, _BufferLength);
+ _FdoExtension->LastPacketLength = _BufferLength;
+#endif
+
+ if (_Type == (UCHAR) HciPacketEvent)
+ {
+ ReadRequestComplete(_FdoExtension,
+ HciPacketEvent,
+ _BufferLength,
+ _Buffer,
+ _FdoExtension->ReadEventQueue,
+ &_FdoExtension->EventQueueCount,
+ &_FdoExtension->ReadEventList,
+ &_FdoExtension->EventListCount);
+ }
+ else
+ {
+ ReadRequestComplete(_FdoExtension,
+ HciPacketAclData,
+ _BufferLength,
+ _Buffer,
+ _FdoExtension->ReadDataQueue,
+ &_FdoExtension->DataQueueCount,
+ &_FdoExtension->ReadDataList,
+ &_FdoExtension->DataListCount);
+ }
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, ("-ReadH4PacketComplete %!STATUS!", Status));
+
+ return Status;
+}
+
+
+NTSTATUS
+ReadH4PacketReassemble(
+ _Inout_ PUART_READ_CONTEXT _ReadContext,
+ _In_ ULONG _BytesRead,
+ _In_reads_bytes_(_BytesRead) PUCHAR _Buffer
+ )
+/*++
+
+Routine Description:
+
+ A function enforce a state machine to process reading data to form a
+ complete HCI packet.
+
+Arguments:
+
+ _ReadContext - read context
+ _BytesRead - bytes of data read and is in the output buffer
+ _OutBuffer - Buffer that contain the data
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG BytesRemained = _BytesRead;
+ PUCHAR Buffer = _Buffer;
+ PFDO_EXTENSION FdoExtension = _ReadContext->FdoExtension;
+ PH4_PACKET H4Packet;
+ ULONG PacketLen;
+ ULONG BytesToRead;
+
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadH4PacketReassemble: %d _BytesRead, ReadSegmentState %d",
+ _BytesRead, _ReadContext->ReadSegmentState));
+
+ //
+ // By design, it will take two reads to complete an H4 packets.
+ //
+ // First Read (5 bytes = 1 + 4 = Type + Larger of (ACLDataHeader:4, EvetnHeader:2))
+ //
+ // - Event
+ // Complete (1 + 2 ), this is an Event packet without any param.
+ // Complete (1 + 2 + 1), event with 1 param
+ // * These two outcome requires interval timeout to complete the read (ask for 5).
+ // Complete (1 + 2 + 2), event with 2 params
+ // * if completed with one read, do the First read again.
+ //
+ // Partial (1 + 2 + 2 + ParamCount-2), this will complete in next read
+ // BytesToRead = ParamCount - 2
+ //
+ // - ACL Data
+ // Partial (1 + 4 + DataLength), this packet will be complete in next read
+ // ByteToRead = DataLength
+ // Second read
+ // - Event/AclData
+ // Complete (5 + BytesToRead)
+ //
+
+ while (NT_SUCCESS(Status) && BytesRemained > 0) {
+
+ // Process read buffer based on its read state
+ switch (_ReadContext->ReadSegmentState) {
+ case GET_PKT_TYPE:
+ H4Packet = (PH4_PACKET) Buffer;
+ BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1);
+
+ if (H4Packet->Type == (UCHAR) HciPacketEvent) {
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] ---------- "));
+ _ReadContext->BytesToRead4FullPacket = HCI_EVENT_HEADER_SIZE;
+ }
+ else if (H4Packet->Type == (UCHAR) HciPacketAclData) {
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] ---------- "));
+ _ReadContext->BytesToRead4FullPacket = HCI_ACL_HEADER_SIZE;
+ }
+ else {
+ //
+ // Abort the read operation here but can consider to traverse the data
+ // until a valid packet type is found.
+ //
+ Status = STATUS_INVALID_PARAMETER; // discard and read again
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" Unexpected PacketType %d", H4Packet->Type));
+ NT_ASSERT(FALSE && L"Detected unknown packet type");
+ goto OutOfSync;
+ }
+
+ // Proceed to read packet header
+ _ReadContext->H4Packet.Type = H4Packet->Type; // Valid packet type is cached.
+ ReadSegmentStateSet(_ReadContext, GET_PKT_HEADER);
+ break;
+
+ case GET_PKT_HEADER:
+ if (_ReadContext->H4Packet.Type == (UCHAR) HciPacketEvent) {
+ if (_ReadContext->BytesReadNextSegment == 0 && BytesRemained) {
+ _ReadContext->H4Packet.Packet.Event.EventCode = *Buffer;
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] Code 0x%x", _ReadContext->H4Packet.Packet.Event.EventCode));
+ BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1);
+ _ReadContext->BytesToRead4FullPacket = 1; // Read the ParamsCount if needed
+ }
+
+ if (_ReadContext->BytesReadNextSegment == 1 && BytesRemained) {
+ _ReadContext->H4Packet.Packet.Event.ParamsCount = *Buffer;
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] ParamsCount 0x%x", _ReadContext->H4Packet.Packet.Event.ParamsCount));
+ BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1);
+
+ if (_ReadContext->H4Packet.Packet.Event.ParamsCount == 0) {
+ // Full packet: match to a Request and complete it.
+ PacketLen = HCI_EVENT_HEADER_LEN + _ReadContext->H4Packet.Packet.Event.ParamsCount;
+ DoTrace(LEVEL_INFO, TFLAG_DATA, (" [Event completed] PacketLen %d", PacketLen));
+ Status = ReadH4PacketComplete(FdoExtension,
+ _ReadContext->H4Packet.Type,
+ (PUCHAR) &_ReadContext->H4Packet.Packet.Event,
+ PacketLen);
+ // Read next packet
+ ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE);
+ }
+ // Read the remainder of a full (Event) packet
+ else {
+ if (BytesRemained < _ReadContext->H4Packet.Packet.Event.ParamsCount) {
+ _ReadContext->BytesToRead4FullPacket =
+ _ReadContext->H4Packet.Packet.Event.ParamsCount - BytesRemained;
+ }
+
+ // Process to read packet payload
+ ReadSegmentStateSet(_ReadContext, GET_PKT_PAYLOAD);
+ }
+ }
+ }
+ else {
+
+ if (_ReadContext->BytesReadNextSegment == 0 && BytesRemained) {
+ _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment] = *Buffer;
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Header[0] 0x%x", _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment]));
+ BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1);
+ _ReadContext->BytesToRead4FullPacket = 3; // Read the remaining Dta header if needed
+ }
+
+ if (_ReadContext->BytesReadNextSegment == 1 && BytesRemained) {
+ _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment] = *Buffer;
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Header[1] 0x%x", _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment]));
+ BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1);
+ _ReadContext->BytesToRead4FullPacket = 2; // Read the remaining Dta header if needed
+ }
+
+ if (_ReadContext->BytesReadNextSegment == 2 && BytesRemained) {
+ _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment] = *Buffer;
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Header[2] 0x%x", _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment]));
+ BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1);
+ _ReadContext->BytesToRead4FullPacket = 1; // Read the remaining Dta header if needed
+ }
+
+ if (_ReadContext->BytesReadNextSegment == 3 && BytesRemained) {
+ _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment] = *Buffer;
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Header[3] 0x%x", _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment]));
+ BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1);
+
+ // Read the reamainder of a full (Data) packet
+ if (BytesRemained < _ReadContext->H4Packet.Packet.AclData.DataLength) {
+ _ReadContext->BytesToRead4FullPacket =
+ _ReadContext->H4Packet.Packet.AclData.DataLength - BytesRemained;
+ }
+
+ // Process to read packet payload
+ ReadSegmentStateSet(_ReadContext, GET_PKT_PAYLOAD);
+ }
+ }
+ break;
+
+ case GET_PKT_PAYLOAD:
+ if (_ReadContext->H4Packet.Type == (UCHAR) HciPacketEvent) {
+
+ BytesToRead = _ReadContext->H4Packet.Packet.Event.ParamsCount - _ReadContext->BytesReadNextSegment;
+
+ if (BytesRemained >= BytesToRead) {
+ // Full packet
+ RtlCopyMemory(&_ReadContext->H4Packet.Packet.Event.Params[_ReadContext->BytesReadNextSegment],
+ Buffer,
+ BytesToRead);
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] Payload[%d + %d] = FULL",
+ _ReadContext->BytesReadNextSegment,
+ BytesToRead));
+ BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesToRead);
+
+ // Full packet: match to a Request and complete it.
+ PacketLen = HCI_EVENT_HEADER_LEN + _ReadContext->H4Packet.Packet.Event.ParamsCount;
+ Status = ReadH4PacketComplete(FdoExtension,
+ _ReadContext->H4Packet.Type,
+ (PUCHAR) &_ReadContext->H4Packet.Packet.Event,
+ PacketLen);
+ // Read next packet
+ ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE);
+ }
+ else {
+ // Partial packet
+ RtlCopyMemory(&_ReadContext->H4Packet.Packet.Event.Params[_ReadContext->BytesReadNextSegment],
+ Buffer,
+ BytesRemained);
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] Payload[%d + %d] = Partial; %d to read",
+ _ReadContext->BytesReadNextSegment,
+ BytesRemained,
+ BytesToRead - BytesRemained));
+ _ReadContext->BytesReadNextSegment += BytesRemained;
+ BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesRemained);
+
+ // Remaining event params to read
+ _ReadContext->BytesToRead4FullPacket =
+ _ReadContext->H4Packet.Packet.Event.ParamsCount - _ReadContext->BytesReadNextSegment;
+ }
+ }
+ else {
+
+ if (_ReadContext->H4Packet.Packet.AclData.DataLength > HCI_MAX_ACL_PAYLOAD_SIZE) {
+ Status = STATUS_INVALID_PARAMETER; // discard and read again
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" Unexpected ACL DataLength %d > Presetted maximum size %d",
+ _ReadContext->H4Packet.Packet.AclData.DataLength,
+ HCI_MAX_ACL_PAYLOAD_SIZE));
+ NT_ASSERT(FALSE && L"Max ACL DataLength exceeded the presetted Max");
+ goto OutOfSync;
+ }
+
+ BytesToRead = _ReadContext->H4Packet.Packet.AclData.DataLength - _ReadContext->BytesReadNextSegment;
+
+ if (BytesRemained >= BytesToRead) {
+ // Process full packet
+ RtlCopyMemory(&_ReadContext->H4Packet.Packet.AclData.Data[_ReadContext->BytesReadNextSegment],
+ Buffer,
+ BytesToRead);
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Payload[%d + %d] = FULL",
+ _ReadContext->BytesReadNextSegment,
+ BytesToRead));
+ BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesToRead);
+
+ // Full packet: try match to a Request in queue (if any) and complete it.
+ PacketLen = HCI_ACLDATA_HEADER_LEN + _ReadContext->H4Packet.Packet.AclData.DataLength;
+ Status = ReadH4PacketComplete(FdoExtension,
+ _ReadContext->H4Packet.Type,
+ (PUCHAR) &_ReadContext->H4Packet.Packet.AclData,
+ PacketLen);
+ // Next packet
+ ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE);
+ }
+ else {
+ // Process partial packet
+ RtlCopyMemory(&_ReadContext->H4Packet.Packet.AclData.Data[_ReadContext->BytesReadNextSegment],
+ Buffer,
+ BytesRemained);
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Payload[%d + %d] = Partial; %d to read",
+ _ReadContext->BytesReadNextSegment,
+ BytesRemained,
+ BytesToRead - BytesRemained));
+ _ReadContext->BytesReadNextSegment += BytesRemained;
+ BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesRemained);
+
+ // Remaining data to read
+ _ReadContext->BytesToRead4FullPacket =
+ _ReadContext->H4Packet.Packet.AclData.DataLength - _ReadContext->BytesReadNextSegment;
+ }
+ }
+ break;
+
+ default:
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" Unknown ReadSegmentState"));
+ break;
+ }
+ }
+
+ return Status;
+
+OutOfSync:
+
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" Out-of-sync error detected in ProcessReadBuffer() %!STATUS!", Status));
+
+ return Status;
+}
+
+
+VOID
+ReadH4PacketCompletionRoutine(
+ _In_ WDFREQUEST _Request,
+ _In_ WDFIOTARGET _Target,
+ _In_ PWDF_REQUEST_COMPLETION_PARAMS _Params,
+ _In_ WDFCONTEXT _Context
+ )
+/*++
+
+Routine Description:
+
+ This is CR function for reading data from device. It process the data read and
+ send down another request unless there is an error or the request is being
+ canceled.
+
+Arguments:
+
+ _Request - a caller allocated WDF Request
+ _Target - WDF IO Target
+ _Params - Completion parameters
+ _Context - Context of this request
+
+Return Value:
+
+ none
+
+--*/
+{
+ NTSTATUS Status;
+ PUART_READ_CONTEXT ReadContext;
+ PFDO_EXTENSION FdoExtension;
+ ULONG BytesRead;
+ WDFMEMORY ReadMemory;
+ PUCHAR OutBuffer;
+ size_t OutBufferSize;
+ READ_REQUEST_STATE PreviousState;
+
+
+ UNREFERENCED_PARAMETER(_Request);
+ UNREFERENCED_PARAMETER(_Target);
+
+ // Operation result
+ Status = _Params->IoStatus.Status;
+ BytesRead = (ULONG) _Params->Parameters.Read.Length;
+
+ ReadContext = (PUART_READ_CONTEXT) _Context;
+ ReadContext->Status = Status;
+
+ // Set to REQUEST_COMPLETE if skip REQUEST_PENDING state.
+ PreviousState = InterlockedCompareExchange((PLONG)&ReadContext->RequestState,
+ REQUEST_COMPLETE,
+ REQUEST_SENT);
+
+ DoTrace(LEVEL_WARNING, TFLAG_DATA, ("+ReadH4PacketCompletionRoutine %!STATUS! %d BytesRead %S)",
+ Status, BytesRead, PreviousState == REQUEST_PENDING ? L"Async" : L"*Sync*"));
+
+ FdoExtension = (PFDO_EXTENSION) ReadContext->FdoExtension;
+
+ //
+ // The return status can either be
+ // - successful (buffer completely filled),
+ // - timeout (buffer not completed filled prior to interval timeout expired
+ // - cancellation
+ // - failure
+ //
+ if (NT_SUCCESS(Status) || Status == STATUS_IO_TIMEOUT || Status == STATUS_TIMEOUT) {
+ // Continue to process
+ }
+ else {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadH4PacketCompletionRoutine failed %!STATUS!", Status));
+ if (Status == STATUS_CANCELLED) {
+ //
+ // Under regualr operational state, IO Target will only cancel a request
+ // when it is ready to abort (e.g. device removal).
+ //
+ }
+
+ goto Exit;
+ }
+
+ ReadMemory = _Params->Parameters.Read.Buffer;
+ OutBuffer = (PUCHAR) WdfMemoryGetBuffer(ReadMemory, &OutBufferSize);
+ NT_ASSERT(OutBufferSize >= BytesRead);
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" ReadH4PacketCompletionRoutine %d BytesRead pBuffer %p", BytesRead, OutBuffer));
+
+ //
+ // Process a read buffer if there is data
+ //
+ if (OutBuffer && BytesRead)
+ {
+ //
+ // Process the incoming data to form partial or full H4 packet
+ //
+ Status = ReadH4PacketReassemble(ReadContext,
+ BytesRead,
+ OutBuffer);
+
+ // If data stream error, ignore the packet and start over.
+ if (!NT_SUCCESS(Status))
+ {
+ FdoExtension->OutOfSyncErrorCount++;
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" ====> [%d] 0x%x <=====",
+ FdoExtension->OutOfSyncErrorCount,
+ *OutBuffer));
+ NT_ASSERT(NT_SUCCESS(Status) && L"Encountered an out-of-sync condition!");
+
+ // Prepare to read next data packet, starting with packet type.
+ ReadSegmentStateSet(ReadContext, GET_PKT_TYPE);
+
+ // Log(Error): log statistic of the read pump until this error
+
+ //
+ // If there is a (knonw) hardware error or if we have exceeded maximun hardware count,
+ // the link is no longer reliable. Need to report to the upper layer via a read request.
+ //
+ if (FdoExtension->HardwareErrorDetected && FdoExtension->OutOfSyncErrorCount > MAX_HARDWARE_ERROR_COUNT)
+ {
+ //
+ // Complete an event or read data request with STATUS_DEVICE_DATA_ERROR error to trigger
+ // BthMini/BthPort to handle the situation. IT can perform HCI_RESET to restore the
+ // data channel.
+ //
+#ifdef REPORT_HARDWARE_ERROR
+ WDFREQUEST Request;
+
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" ++++ Report a hardware error; OutOfSyncCount %d", FdoExtension->OutOfSyncErrorCount));
+
+ KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql);
+ // Complete a read (event or data) request with a specific error to indicate hardware error.
+ Status = WdfIoQueueRetrieveNextRequest(FdoExtension->ReadEventQueue, &Request);
+
+ // if there is no event request, find a read data request.
+ if (Status == STATUS_NO_MORE_ENTRIES)
+ {
+ Status = WdfIoQueueRetrieveNextRequest(FdoExtension->ReadDataQueue, &Request);
+ }
+ KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql);
+
+
+ if (NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" Complete a request with STATUS_DEVICE_DATA_ERROR"));
+ WdfRequestComplete(Request, STATUS_DEVICE_DATA_ERROR);
+ }
+#endif // REPORT_HARDWARE_ERROR
+ Status = STATUS_DEVICE_DATA_ERROR;
+
+ // abort and stop read pump
+ goto Exit;
+
+ }
+ else
+ {
+
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" Detect out-of-sync error but read ahead..."));
+
+ // Reset hardware error.
+ FdoExtension->HardwareErrorDetected = FALSE;
+
+ // try next
+ goto ReadNext;
+ }
+ }
+ }
+ else
+ {
+ NT_ASSERT(Status == STATUS_TIMEOUT);
+ }
+
+ReadNext:
+
+ if (PreviousState == REQUEST_PENDING)
+ {
+ ULONG BytesToRead;
+
+ //
+ // Determine what is the size of the buffer to send down.
+ //
+ BytesToRead = (ReadContext->ReadSegmentState == GET_PKT_TYPE ? INITIAL_H4_READ_SIZE :
+ ReadContext->BytesToRead4FullPacket ? ReadContext->BytesToRead4FullPacket :
+ sizeof(FdoExtension->ReadBuffer));
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" ReadH4Packet(Read Buffer Size %d bytes)", BytesToRead));
+
+ // Issue next read here since this request was complete asychronously
+ // i.e. pending first and then this completion routein is invoked.
+ ReadH4Packet(ReadContext,
+ FdoExtension->ReadRequest,
+ FdoExtension->ReadMemory,
+ FdoExtension->ReadBuffer,
+ BytesToRead);
+ }
+ else
+ {
+ // Fall through and leave this fucntion if this request was completed synchronously;
+ // i.e. this function is invoked first and then return to the RequestSent function.
+ }
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, ("-CR_ReadReadIO (fall though)"));
+
+ return;
+
+Exit:
+
+ if (!NT_SUCCESS(Status))
+ {
+ NT_ASSERT(Status == STATUS_CANCELLED);
+ FdoExtension->ReadPumpRunning = FALSE;
+ DoTrace(LEVEL_WARNING, TFLAG_IO, (" Pump has stopped!"));
+ }
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, ("-CR_ReadReadIO (error)"));
+}
+
+
+NTSTATUS
+ReadH4Packet(
+ _In_ PUART_READ_CONTEXT _ReadContext,
+ _In_ WDFREQUEST _WdfRequest,
+ _In_ WDFMEMORY _WdfMemory,
+ _Pre_notnull_ _Pre_writable_byte_size_ (_BufferLen) PVOID _Buffer,
+ _In_ ULONG _BufferLen
+ )
+/*++
+
+Routine Description:
+
+ Initiate the reading of an HCI packet (event or data) by sending down a read request.
+
+Arguments:
+
+ _ReadContext - Context used for reading data from target UART device
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ PFDO_EXTENSION FdoExtension;
+ WDF_REQUEST_REUSE_PARAMS RequestReuseParams;
+ NTSTATUS Status;
+
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadH4Packet"));
+
+ FdoExtension = _ReadContext->FdoExtension;
+
+ if (0 == _BufferLen) {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadH4Packet: _BufferLen cannot be 0"));
+ Status = STATUS_INVALID_PARAMETER;
+ goto Done;
+ }
+
+ while (TRUE) {
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" ReadH4Packet - <start>"));
+ NT_ASSERT(_ReadContext->RequestState != REQUEST_SENT);
+
+ if (!IsDeviceInitialized(FdoExtension)) {
+ Status = STATUS_DEVICE_NOT_READY;
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadH4Packet: cannot attach IO %!STATUS!", Status));
+ goto Done;
+ }
+
+ //
+ // Issue a read event request
+ //
+ WDF_REQUEST_REUSE_PARAMS_INIT(&RequestReuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS);
+ Status = WdfRequestReuse(_WdfRequest, &RequestReuseParams);
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestReuse failed %!STATUS!", Status));
+ goto Done;
+ }
+
+ Status = WdfMemoryAssignBuffer(_WdfMemory, _Buffer, _BufferLen);
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfMemoryAssignBuffer failed %!STATUS!", Status));
+ goto Done;
+ }
+
+ Status = WdfIoTargetFormatRequestForRead(FdoExtension->IoTargetSerial,
+ _WdfRequest,
+ _WdfMemory,
+ NULL, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoTargetFormatRequestForRead failed %!STATUS!", Status));
+ goto Done;
+ }
+
+ // Note: This request is sent to UART driver so it cannot be marked cancellable.
+ // But it can be canceled by issuing WdfRequestCancelSentRequest().
+
+ WdfRequestSetCompletionRoutine(_WdfRequest,
+ ReadH4PacketCompletionRoutine,
+ _ReadContext);
+
+ InterlockedExchange((PLONG)&_ReadContext->RequestState, REQUEST_SENT);
+
+ if (FALSE == WdfRequestSend(_WdfRequest,
+ FdoExtension->IoTargetSerial,
+ WDF_NO_SEND_OPTIONS))
+ {
+ Status = WdfRequestGetStatus(_WdfRequest);
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestSend failed %!STATUS!", Status));
+
+ // Not much we can do if cannot send this request; data pump will be stopped!
+ goto Done;
+ }
+ else
+ {
+ READ_REQUEST_STATE PreviousState;
+
+ // Set to REQUEST_PENDING if it is in the REQUEST_SENT state.
+ PreviousState = InterlockedCompareExchange((PLONG) &_ReadContext->RequestState,
+ REQUEST_PENDING,
+ REQUEST_SENT);
+
+ DoTrace(LEVEL_WARNING, TFLAG_IO, (" WdfRequestSend ReqState: %d -> %d",
+ PreviousState, _ReadContext->RequestState));
+
+ if (PreviousState == REQUEST_SENT)
+ {
+ // Request is still pending, and will be completed asychronously in the
+ // completion routine where it can issue next read.
+ Status = STATUS_PENDING;
+ break;
+ }
+ else
+ {
+ Status = FdoExtension->ReadContext.Status;
+ if (NT_SUCCESS(Status))
+ {
+ // Previous request has been complete synchronously in the
+ // completion routine; do next read in this function.
+ }
+ else
+ {
+ // No tolerance for error
+ break;
+ }
+ }
+ }
+ }
+
+Done:
+
+ if (!NT_SUCCESS(Status))
+ {
+ NT_ASSERT(Status == STATUS_CANCELLED);
+ FdoExtension->ReadPumpRunning = FALSE;
+ }
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, ("-ReadH4Packet %!STATUS!", Status));
+
+ return Status;
+}
+
+__inline
+PHCI_PACKET_ENTRY
+HLP_CreatePacketEntry(
+ _In_ ULONG _PacketLength,
+ _In_reads_bytes_(_PacketLength) PUCHAR _Packet
+ )
+{
+ PHCI_PACKET_ENTRY PacketEntry = NULL;
+
+ PacketEntry = (PHCI_PACKET_ENTRY)ExAllocatePool(NonPagedPoolNx, sizeof(HCI_PACKET_ENTRY) + _PacketLength);
+ if (PacketEntry != NULL) {
+ InitializeListHead(&PacketEntry->DataEntry);
+ RtlCopyMemory(PacketEntry->Packet, _Packet, _PacketLength);
+ PacketEntry->PacketLen = _PacketLength;
+ }
+
+ return PacketEntry;
+}
+
+NTSTATUS
+ReadRequestComplete(
+ _In_ PFDO_EXTENSION _FdoExtension,
+ _In_ UCHAR _PacketType,
+ _In_ ULONG _PacketLength,
+ _In_reads_bytes_opt_(_PacketLength) PUCHAR _Packet,
+ _Inout_ WDFQUEUE _Queue,
+ _Inout_ PLONG _QueueCount,
+ _Inout_ PLIST_ENTRY _ListHead,
+ _Inout_ PLONG _ListCount
+ )
+/*++
+Routine Description:
+
+ This helper function processes both complete HCI Data packet from the device to find
+ a pending Request, or find a completed HCI packet in a list to complete a Request.
+
+Arguments:
+
+ _FdoExtension - Device context
+ _PacketType - HCI packet type (either Event or Data for incoming data)
+ _ListHead - List where to retrieve completed HCI packet
+ _Request - Request that is used to complete a read if a corresponding HCI packet is available.
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS Or STATUS_INSUFFICIENT_RESOURCE
+
+--*/
+{
+ KIRQL Irql;
+ WDFREQUEST Request = NULL;
+ NTSTATUS Status = STATUS_SUCCESS;
+ PHCI_PACKET_ENTRY PacketEntry = NULL;
+ WDFMEMORY ReqOutMemory;
+ size_t BufferSize = 0, BytesToReturn;
+ PBTHX_HCI_READ_WRITE_CONTEXT HCIContext;
+ BOOLEAN CompleteRequest = FALSE;
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadRequestComplete"));
+
+ //
+ // (ReqQueue, PktList)
+ // C0. ( empty, empty) -> Add packet to list
+ // C1. ( empty, !empty) -> Add packet to list
+ // C2. (!empty, empty) -> DequeueAndCompletRequest(Packet)
+ // C3. (!empty, !empty) -> Error! Cannot both empty at this function entry.
+ //
+
+ KeAcquireSpinLock(&_FdoExtension->QueueAccessLock, &Irql);
+
+ if (_Packet) {
+
+ Status = WdfIoQueueRetrieveNextRequest(_Queue, &Request);
+ if (Status == STATUS_SUCCESS) {
+ // Case 2: Typical code path
+ InterlockedDecrement(_QueueCount);
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" (C2) Complete a request %p, _Packet %p, _PacketLength %d",
+ Request, _Packet, _PacketLength));
+
+ CompleteRequest = TRUE;
+
+ // Case 3: An error condition if List is not empty
+ NT_ASSERT(IsListEmpty(_ListHead));
+ }
+ else {
+ // Case 0:
+ PacketEntry = HLP_CreatePacketEntry(_PacketLength, _Packet);
+ if (PacketEntry == NULL) {
+ // Error condition
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" (C0/Error) Could not allocate HCI_PACKET_ENTRY %!STATUS!", Status));
+ // This packet will be dropped; but nothing we can do as system resource is depleted!
+ }
+ else {
+ // Cache this packet to Packet List
+ InsertTailList(_ListHead, &PacketEntry->DataEntry);
+ InterlockedIncrement(_ListCount);
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" (C0) Queuing packet with list count %d", *_ListCount));
+ }
+ }
+ }
+ else {
+ if (!IsListEmpty(_ListHead)) {
+ Status = WdfIoQueueRetrieveNextRequest(_Queue, &Request);
+ if (Status == STATUS_SUCCESS) {
+ // Case 2: Has Packet in the list while a new request arrives
+ InterlockedDecrement(_QueueCount);
+
+ PacketEntry = (PHCI_PACKET_ENTRY) RemoveHeadList(_ListHead);
+ _Packet = PacketEntry->Packet;
+ _PacketLength = PacketEntry->PacketLen;
+ InterlockedDecrement(_ListCount);
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" (C2) Complete a request %p, _Packet %p, _PacketLength %d",
+ Request, _Packet, _PacketLength));
+
+ CompleteRequest = TRUE;
+ }
+ else {
+ NT_ASSERT(FALSE && L"Failed to retrieve a request just queued!");
+ }
+ }
+ else {
+ // Case 1: Request is pre-pening and queued.
+ Status = STATUS_PENDING;
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" (C1) Read request is queued"));
+ }
+ }
+
+ KeReleaseSpinLock(&_FdoExtension->QueueAccessLock, Irql);
+
+ if (!CompleteRequest) {
+ goto Done;
+ }
+
+ // Complete this request
+ Status = WdfRequestRetrieveOutputMemory(Request, &ReqOutMemory);
+ if (Status != STATUS_SUCCESS) {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" Could not retrieve output buffer"));
+ WdfRequestCompleteWithInformation(Request, Status, (ULONG_PTR)0);
+ goto Done;
+ }
+
+ HCIContext = WdfMemoryGetBuffer(ReqOutMemory, &BufferSize);
+ BytesToReturn = FIELD_OFFSET(BTHX_HCI_READ_WRITE_CONTEXT, Data) + _PacketLength;
+
+ // This should not happen because BthMini should have sent down largest buffer according to device's capability.
+ NT_ASSERT(BytesToReturn <= BufferSize);
+
+ // Transfer data to Request's output buffer
+ HCIContext->Type = _PacketType;
+ HCIContext->DataLen = _PacketLength;
+ if (BytesToReturn <= BufferSize) {
+ RtlCopyMemory(&HCIContext->Data, _Packet, _PacketLength);
+ }
+ else {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ BytesToReturn = 0;
+ }
+
+ // Validate and print out (WPP) HCI packet info
+ HCIContextValidate(HCIContext->Type == (UCHAR) HciPacketEvent ?
+ _FdoExtension->CntEventCompleted : _FdoExtension->CntReadDataCompleted,
+ HCIContext);
+
+ //
+ // Release memory allocated for a completed packet entry; it was not removed from the packet list.
+ //
+ if (PacketEntry) {
+ ExFreePool(PacketEntry);
+ }
+
+ if (HCIContext->Type == (UCHAR) HciPacketEvent) {
+ InterlockedIncrement(&_FdoExtension->CntEventCompleted);
+ DoTrace(LEVEL_INFO, TFLAG_DATA, (" [%d] HciPacketEvent completing %!STATUS!, %d BytesToReturn",
+ _FdoExtension->CntEventCompleted, Status, (ULONG) BytesToReturn));
+ }
+ else if (HCIContext->Type == (UCHAR) HciPacketAclData) {
+ InterlockedIncrement(&_FdoExtension->CntReadDataCompleted);
+ DoTrace(LEVEL_INFO, TFLAG_DATA, (" [%d] HciPacketAclData completing %!STATUS!, %d BytesToReturn",
+ _FdoExtension->CntReadDataCompleted, Status, (ULONG) BytesToReturn));
+ }
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, (" Completing Request(%p) %!STATUS!, %d BytesToReturn",
+ Request, Status, (ULONG) BytesToReturn));
+
+ //
+ // return only the actual data read, not including BTHX_HCI_READ_WRITE_CONTEXT
+ //
+ WdfRequestCompleteWithInformation(Request, Status, BytesToReturn);
+
+Done:
+
+ DoTrace(LEVEL_INFO, TFLAG_IO, ("-ReadRequestComplete: %!STATUS!", Status));
+
+ return Status;
+}
+
+
+VOID
+ReadResourcesFree(
+ _In_ WDFDEVICE _Device
+)
+/*++
+Routine Description:
+
+ This helper function free resource allocated in its corresponding allocation
+ function.
+
+Arguments:
+
+ _Device - WDF Device object
+
+Return
+
+ VOID
+
+--*/
+{
+ PFDO_EXTENSION FdoExtension;
+ KIRQL Irql;
+
+ DoTrace(LEVEL_INFO, TFLAG_IO,("+ReadResourcesFree"));
+
+ FdoExtension = FdoGetExtension(_Device);
+
+ //
+ // Note: The Request(s) in WDFQUEUE (Event and ReadData) WDFQUEUEs
+ // are managed by WDF, which will dequeue and cancel them for us.
+ // WdfIoQueueRetrieveNextRequest() returns STATUS_WDF_PAUSED since this
+ // function is invoked after entered D0.
+ //
+
+ //
+ // Free resources allocated earlier
+ //
+
+ while(!IsListEmpty(&FdoExtension->ReadEventList))
+ {
+ PHCI_PACKET_ENTRY PacketEntry;
+
+ KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql);
+ PacketEntry = (PHCI_PACKET_ENTRY)RemoveHeadList(&FdoExtension->ReadEventList);
+ InterlockedDecrement(&FdoExtension->EventListCount);
+ KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql);
+
+ if (PacketEntry)
+ {
+ ExFreePool(PacketEntry);
+ PacketEntry = NULL;
+ }
+ }
+ NT_ASSERT(FdoExtension->EventListCount == 0);
+
+ while(!IsListEmpty(&FdoExtension->ReadDataList))
+ {
+ PHCI_PACKET_ENTRY PacketEntry;
+
+ KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql);
+ PacketEntry = (PHCI_PACKET_ENTRY)RemoveHeadList(&FdoExtension->ReadDataList);
+ InterlockedDecrement(&FdoExtension->DataListCount);
+ KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql);
+
+ if (PacketEntry)
+ {
+ ExFreePool(PacketEntry);
+ PacketEntry = NULL;
+ }
+ }
+ NT_ASSERT(FdoExtension->DataListCount == 0);
+
+ if (FdoExtension->ReadRequest)
+ {
+ WdfObjectDelete(FdoExtension->ReadRequest);
+ FdoExtension->ReadRequest = NULL;
+ }
+}
+
+
+NTSTATUS
+ReadResourcesAllocate(
+ _In_ WDFDEVICE _Device
+)
+/*++
+Routine Description:
+
+ This helper function allocates resource (queues and lists) for managing read IOs
+ Request from upper layer or for data pump with the device.
+
+Arguments:
+
+ _Device - WDF Device object
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS Or STATUS_INSUFFICIENT_RESOURCE
+
+--*/
+{
+ NTSTATUS Status;
+ PFDO_EXTENSION FdoExtension;
+ WDF_IO_QUEUE_CONFIG QueueConfig;
+ WDF_OBJECT_ATTRIBUTES ObjAttributes;
+
+ DoTrace(LEVEL_INFO, TFLAG_IO,("+ReadResourcesAllocate"));
+
+ FdoExtension = FdoGetExtension(_Device);
+
+
+ // HCI_EVENT
+ // Create WDF Queue for pending Read Event Request(s), and
+ // Initialize a List for pre-fetched Event
+ WDF_IO_QUEUE_CONFIG_INIT(&QueueConfig,
+ WdfIoQueueDispatchManual);
+
+ Status = WdfIoQueueCreate(_Device,
+ &QueueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &FdoExtension->ReadEventQueue);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoQueueCreate(Event) %!STATUS!", Status));
+ goto Done;
+ }
+
+ InitializeListHead(&FdoExtension->ReadEventList);
+
+ FdoExtension->EventListCount = 0;
+ FdoExtension->EventQueueCount = 0;
+
+
+ // HCI_DATA
+ // Create WDF Queue for pending Read Data Request(s), and
+ // Initialize a List for pre-fetched Data
+ Status = WdfIoQueueCreate(_Device,
+ &QueueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &FdoExtension->ReadDataQueue);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoQueueCreate(Data) %!STATUS!", Status));
+ goto Done;
+ }
+
+ InitializeListHead(&FdoExtension->ReadDataList);
+
+ FdoExtension->DataListCount = 0;
+ FdoExtension->DataQueueCount = 0;
+
+
+ // Track request from top and HCI packets from device
+ FdoExtension->CntCommandReq = 0;
+ FdoExtension->CntCommandCompleted = 0;
+
+ FdoExtension->CntEventReq = 0;
+ FdoExtension->CntEventCompleted = 0;
+
+ FdoExtension->CntWriteDataReq = 0;
+ FdoExtension->CntWriteDataCompleted = 0;
+
+ FdoExtension->CntReadDataReq = 0;
+ FdoExtension->CntReadDataCompleted = 0;
+
+
+ // Create a WDF Request
+ WDF_OBJECT_ATTRIBUTES_INIT(&ObjAttributes);
+ ObjAttributes.ParentObject = _Device;
+
+ Status = WdfRequestCreate(&ObjAttributes,
+ FdoExtension->IoTargetSerial,
+ &FdoExtension->ReadRequest);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestCreate(ReadRequest) failed %!STATUS!", Status));
+ goto Done;
+ }
+
+ // Initialize the ReadContext and its initial ReadSegmentState
+ RtlZeroMemory(&FdoExtension->ReadContext, sizeof(UART_READ_CONTEXT));
+ FdoExtension->ReadContext.FdoExtension = FdoExtension;
+ ReadSegmentStateSet(&FdoExtension->ReadContext, GET_PKT_TYPE);
+
+ Status = WdfMemoryCreatePreallocated(&ObjAttributes,
+ &FdoExtension->ReadBuffer,
+ sizeof(FdoExtension->ReadBuffer),
+ &FdoExtension->ReadMemory);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfMemoryCreatePreallocated(ReadMemory) failed %!STATUS!", Status));
+ goto Done;
+ }
+
+Done:
+
+ DoTrace(LEVEL_INFO, TFLAG_IO,("-ReadResourcesAllocate %!STATUS!", Status));
+ if (!NT_SUCCESS(Status))
+ {
+ ReadResourcesFree(_Device);
+ }
+
+ return Status;
+}
+
+
diff --git a/bluetooth/serialhcibus/pdo.c b/bluetooth/serialhcibus/pdo.c
new file mode 100644
index 00000000..74ee6350
--- /dev/null
+++ b/bluetooth/serialhcibus/pdo.c
@@ -0,0 +1,972 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ Pdo.c
+
+Abstract:
+
+ This module create a PDO and handles plug & play calls for the child device (PDO).
+
+Environment:
+
+ kernel mode only
+
+--*/
+
+#include "driver.h"
+#include "pdo.tmh"
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, PdoCreate)
+#pragma alloc_text(PAGE, PdoDevD0Exit)
+#pragma alloc_text(PAGE, PdoDevD0Entry)
+#pragma alloc_text(PAGE, PdoDevPrepareHardware)
+#pragma alloc_text(PAGE, PdoDevReleaseHardware)
+#endif
+
+#define MAX_ID_LEN 80
+
+
+
+#ifdef DYNAMIC_ENUM
+
+
+NTSTATUS
+PdoCreateDynamic(
+ _In_ WDFDEVICE _Device,
+ _In_ PWDFDEVICE_INIT _DeviceInit,
+ _In_ PWCHAR _HardwareIds,
+ _In_ ULONG _SerialNo
+ )
+/*++
+
+Routine Description:
+
+ This routine creates and initialize a PDO.
+
+Arguments:
+
+Return Value:
+
+ NT Status code.
+
+--*/
+{
+ NTSTATUS Status;
+ PPDO_EXTENSION PdoExtension = NULL;
+ WDFDEVICE ChildDevice = NULL;
+ WDF_OBJECT_ATTRIBUTES pdoAttributes;
+ WDF_DEVICE_PNP_CAPABILITIES pnpCaps;
+ WDF_DEVICE_POWER_CAPABILITIES powerCaps;
+ DECLARE_CONST_UNICODE_STRING(compatId, BT_PDO_COMPATIBLE_IDS);
+ DECLARE_CONST_UNICODE_STRING(deviceLocation, L"Serial HCI Bus - Bluetooth Function");
+ DECLARE_UNICODE_STRING_SIZE(buffer, MAX_ID_LEN);
+ DECLARE_UNICODE_STRING_SIZE(deviceId, MAX_ID_LEN);
+
+ WDF_IO_QUEUE_CONFIG QueueConfig;
+ WDFQUEUE Queue;
+
+
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(_Device);
+
+ KdPrint(("Entered PdoCreateDynamic\n"));
+
+ //
+ // Set DeviceType
+ //
+ WdfDeviceInitSetDeviceType(_DeviceInit, FILE_DEVICE_BUS_EXTENDER);
+
+ //
+ // Provide DeviceID, HardwareIDs, CompatibleIDs and InstanceId
+ //
+ RtlInitUnicodeString(&deviceId, _HardwareIds);
+
+ Status = WdfPdoInitAssignDeviceID(_DeviceInit, &deviceId);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ //
+ // NOTE: same string is used to initialize hardware id too
+ //
+ Status = WdfPdoInitAddHardwareID(_DeviceInit, &deviceId);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ Status = WdfPdoInitAddCompatibleID(_DeviceInit, &compatId );
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ Status = RtlUnicodeStringPrintf(&buffer, L"%02d", _SerialNo);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ Status = WdfPdoInitAssignInstanceID(_DeviceInit, &buffer);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ //
+ // Provide a description about the device. This text is usually read from
+ // the device. In the case of USB device, this text comes from the string
+ // descriptor. This text is displayed momentarily by the PnP manager while
+ // it's looking for a matching INF. If it finds one, it uses the Device
+ // Description from the INF file or the friendly name created by
+ // coinstallers to display in the device manager. FriendlyName takes
+ // precedence over the DeviceDesc from the INF file.
+ //
+ Status = RtlUnicodeStringPrintf( &buffer,
+ L"SerialHciBus_%02d",
+ _SerialNo );
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ //
+ // You can call WdfPdoInitAddDeviceText multiple times, adding device
+ // text for multiple locales. When the system displays the text, it
+ // chooses the text that matches the current locale, if available.
+ // Otherwise it will use the string for the default locale.
+ // The driver can specify the driver's default locale by calling
+ // WdfPdoInitSetDefaultLocale.
+ //
+ Status = WdfPdoInitAddDeviceText(_DeviceInit,
+ &buffer,
+ &deviceLocation,
+ 0x409 );
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ WdfPdoInitSetDefaultLocale(_DeviceInit, 0x409);
+
+ //
+ // Initialize the attributes to specify the size of PDO device extension.
+ // All the state information private to the PDO will be tracked here.
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, PDO_EXTENSION);
+
+ //
+ // Allow to forward requests to its FDO of this bus driver by using
+ // WdfRequestForwardToParentDeviceIoQueue()in the DeviceIoControl callback.
+ //
+ WdfPdoInitAllowForwardingRequestToParent(_DeviceInit);
+
+ //
+ // Create a framework device object to represent PDO of this bus driver. In response
+ // to this call, framework creates a WDM deviceobject.
+ //
+ Status = WdfDeviceCreate(&_DeviceInit,
+ &pdoAttributes,
+ &ChildDevice);
+
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+
+ //
+ // Note: Once the device is created successfully, framework frees the
+ // _DeviceInit memory and sets the _DeviceInit to NULL. So don't
+ // call any WdfDeviceInit functions after that.
+ //
+
+ //
+ // Initalize the PDO extension
+ //
+ PdoExtension = PdoGetExtension(ChildDevice);
+
+ RtlZeroMemory(PdoExtension, sizeof(PDO_EXTENSION));
+
+ PdoExtension->FdoExtension = FdoGetExtension(_Device);
+
+ PdoExtension->SerialNo = _SerialNo;
+
+
+
+ //
+ // Set some properties for the child device.
+ //
+ WDF_DEVICE_PNP_CAPABILITIES_INIT(&PnpCaps); // Zeros this structure (note: WdfFalse is 0)
+
+ //
+ // Bus driver sets this value to WdfFalse for this embedded device, which cannot
+ // be physically removed; its FDO must not set/override this.
+ //
+ PnpCaps.Removable = WdfFalse;
+
+ //
+ // Bus driver sets this value to WdfTrue. FDO can override this value (when this irp is on its
+ // way up) if it determines that this device cannot be safely surprise (not orderly) removed
+ // without data loss.
+ //
+ PnpCaps.SurpriseRemovalOK = WdfTrue;
+
+ pnpCaps.Address = _SerialNo;
+ pnpCaps.UINumber = _SerialNo;
+
+ WdfDeviceSetPnpCapabilities(ChildDevice, &pnpCaps);
+
+ WDF_DEVICE_POWER_CAPABILITIES_INIT(&powerCaps);
+
+ powerCaps.DeviceD1 = WdfFalse;
+ powerCaps.DeviceD2 = WdfTrue;
+
+ powerCaps.WakeFromD0 = WdfFalse;
+ powerCaps.WakeFromD1 = WdfFalse;
+ powerCaps.WakeFromD2 = WdfTrue;
+ powerCaps.WakeFromD3 = WdfTrue;
+
+ powerCaps.DeviceWake = PowerDeviceD2;
+
+ powerCaps.DeviceState[PowerSystemWorking] = PowerDeviceD0;
+ powerCaps.DeviceState[PowerSystemSleeping1] = PowerDeviceD2;
+ powerCaps.DeviceState[PowerSystemSleeping2] = PowerDeviceD2;
+ powerCaps.DeviceState[PowerSystemSleeping3] = PowerDeviceD2;
+ powerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3;
+ powerCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
+
+ WdfDeviceSetPowerCapabilities(ChildDevice, &powerCaps);
+
+
+ //
+ // Configure a default queue so that requests that are not
+ // configure-forwarded using WdfDeviceConfigureRequestDispatching to goto
+ // other queues get dispatched here.
+ //
+
+ WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig, WdfIoQueueDispatchParallel);
+
+ //
+ // Cannot be power managed queue (dispatch only at D0) as
+ // BthMini issues BthX DDI to get version and capabilities
+ // before enter D0. A deadlock occurs if this is power managed.
+ //
+ QueueConfig.PowerManaged = WdfFalse;
+
+ QueueConfig.EvtIoDeviceControl = PdoIoQuDeviceControl;
+
+ Status = WdfIoQueueCreate(ChildDevice,
+ &QueueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &Queue);
+ DoTrace(LEVEL_INFO, TFLAG_PNP, (" WdfIoQueueCreate (%!STATUS!)", Status));
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+Cleanup:
+
+
+ //
+ // Call WdfDeviceInitFree if you encounter an error before the
+ // device is created. Once the device is created, framework
+ // NULLs the DeviceInit value.
+ //
+ if (!NT_SUCCESS(Status)) {
+
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, (" -PdoCreateDynamic: exit %!STATUS!", Status));
+
+ if(ChildDevice) {
+ WdfObjectDelete(ChildDevice);
+ }
+ }
+ else
+ {
+ DoTrace(LEVEL_INFO, TFLAG_PNP, (" -PdoCreateDynamic: exit %!STATUS!", Status));
+ }
+
+
+ return Status;
+}
+
+#endif
+
+
+
+NTSTATUS
+PdoCreate(
+ _In_ WDFDEVICE _Device,
+ _In_ PWSTR _HardwareIds,
+ _In_ ULONG _SerialNo
+)
+/*++
+
+Routine Description:
+
+ This routine creates and initialize a PDO to service a Bluetooth function.
+
+Arguments:
+
+ _Device - A framework device object
+
+ _HardwareIds - a hardware ID for this device
+
+ -SerialNo - serial number of the child DO
+
+Return Value:
+
+ NT Status code.
+
+--*/
+{
+ NTSTATUS Status;
+ PWDFDEVICE_INIT DeviceInit = NULL;
+ WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks;
+ PPDO_EXTENSION PdoExtension = NULL;
+ WDFDEVICE ChildDevice = NULL;
+ WDF_OBJECT_ATTRIBUTES Attributes;
+ WDF_DEVICE_PNP_CAPABILITIES PnpCaps;
+ WDF_DEVICE_POWER_CAPABILITIES PowerCaps;
+ UNICODE_STRING StaticString = {0};
+ UNICODE_STRING DeviceId;
+ DECLARE_UNICODE_STRING_SIZE(Buffer, MAX_ID_LEN);
+ UNICODE_STRING ContainerID = {0};
+ WDF_PDO_EVENT_CALLBACKS Callbacks;
+ WDF_IO_QUEUE_CONFIG QueueConfig;
+ WDFQUEUE Queue;
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP, (" +PdoCreate: HWID(%S), compatID(%S)", _HardwareIds, BT_PDO_COMPATIBLE_IDS));
+
+ PAGED_CODE();
+
+ //
+ // Allocate a WDFDEVICE_INIT structure and set the properties
+ // so that we can create a device object for the child.
+ //
+ DeviceInit = WdfPdoInitAllocate(_Device);
+ if (DeviceInit == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ //
+ // Set DeviceType
+ //
+ WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER);
+
+ //
+ // Provide DeviceID, HardwareIDs, CompatibleIDs and InstanceId
+ //
+ RtlInitUnicodeString(&DeviceId, _HardwareIds);
+ Status = WdfPdoInitAssignDeviceID(DeviceInit, &DeviceId);
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Note: same string is used to initialize hardware id
+ //
+ Status = WdfPdoInitAddHardwareID(DeviceInit, &DeviceId);
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ RtlInitUnicodeString(&StaticString, BT_PDO_COMPATIBLE_IDS);
+ Status = WdfPdoInitAddCompatibleID(DeviceInit, &StaticString);
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = RtlUnicodeStringPrintf(&Buffer, L"%02d", _SerialNo);
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ Status = WdfPdoInitAssignInstanceID(DeviceInit, &Buffer);
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Assign the containerID for an internally connected device
+ //
+ Status = RtlStringFromGUID(&GUID_CONTAINERID_INTERNALLY_CONNECTED_DEVICE, &ContainerID);
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, ("Failed to generate the ContainerID, %!STATUS!", Status));;
+ goto Cleanup;
+ }
+
+ Status = WdfPdoInitAssignContainerID(DeviceInit, &ContainerID);
+ if (!NT_SUCCESS(Status)) {
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, ("Failed to assign the ContainerID, %!STATUS!", Status));
+ goto Cleanup;
+ }
+
+ //
+ // Provide a description about the device. This text is usually read from
+ // the device. This text is displayed momentarily by the PnP manager while
+ // it's looking for a matching INF. If it finds one, it uses the Device
+ // Description from the INF file or the friendly name created by
+ // coinstallers to display in the device manager. FriendlyName takes
+ // precedence over the DeviceDesc from the INF file.
+ //
+ Status = RtlUnicodeStringPrintf(&Buffer, L"SerialHciBus_%02d", _SerialNo );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // You can call WdfPdoInitAddDeviceText multiple times, adding device
+ // text for multiple locales. When the system displays the text, it
+ // chooses the text that matches the current locale, if available.
+ // Otherwise it will use the string for the default locale.
+ // The driver can specify the driver's default locale by calling
+ // WdfPdoInitSetDefaultLocale.
+ //
+ RtlInitUnicodeString(&StaticString, BT_PDO_DEVICE_LOCATION);
+ Status = WdfPdoInitAddDeviceText(DeviceInit,
+ &Buffer,
+ &StaticString,
+ 0x409);
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ WdfPdoInitSetDefaultLocale(DeviceInit, 0x409);
+
+ //
+ // Initialize the attributes to specify the size of PDO device extension.
+ // All the state information private to the PDO will be tracked here.
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, PDO_EXTENSION);
+
+ //
+ // Set power callbacks to handle idle/active transition of the Bluetooth function
+ //
+ WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks);
+
+ //
+ // Register PnP callback
+ //
+ PnpPowerCallbacks.EvtDevicePrepareHardware = PdoDevPrepareHardware;
+ PnpPowerCallbacks.EvtDeviceReleaseHardware = PdoDevReleaseHardware;
+
+ //
+ // Register for Power callback
+ //
+ PnpPowerCallbacks.EvtDeviceD0Entry = PdoDevD0Entry;
+ PnpPowerCallbacks.EvtDeviceD0Exit = PdoDevD0Exit;
+
+ WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit,
+ &PnpPowerCallbacks);
+
+ //
+ // Allow to forward requests to its FDO of this bus driver by using
+ // WdfRequestForwardToParentDeviceIoQueue()in the DeviceIoControl callback.
+ //
+ WdfPdoInitAllowForwardingRequestToParent(DeviceInit);
+
+ //
+ // Register to handle bus level power management (arm for wake?)
+ //
+ WDF_PDO_EVENT_CALLBACKS_INIT(&Callbacks);
+
+ //
+ // Arm the device for wake:
+ //
+ // When the device is powered down, the framework calls the bus driver's
+ // EvtDeviceEnableWakeAtBus callback function at the beginning of the shutdown
+ // sequence, while the child device is still in the D0 state. In this callback
+ // function, the bus driver must do whatever is required at the bus level to
+ // enable the wake signal.
+ //
+ Callbacks.EvtDeviceEnableWakeAtBus = PdoDevEnableWakeAtBus;
+
+ //
+ // Disarm the device for wake:
+ //
+ // If the child device triggered a wake signal, the system and the framework
+ // return the device to D0. The framework calls the bus driver's
+ // EvtDeviceDisableWakeAtBus callback function during startup of the child
+ // device. In this callback function, the bus driver should do whatever is
+ // required at the bus level to disable the wake signal, so that the device
+ // can no longer trigger it. Thus, EvtDeviceDisableWakeAtBus reverses the
+ // actions of EvtDeviceEnableWakeAtBus.
+ //
+ Callbacks.EvtDeviceDisableWakeAtBus = PdoDevDisableWakeAtBus;
+
+ WdfPdoInitSetEventCallbacks(DeviceInit, &Callbacks);
+
+ //
+ // Create a framework device object to represent PDO of this bus driver. In response
+ // to this call, framework creates a WDM deviceobject.
+ //
+ Status = WdfDeviceCreate(&DeviceInit,
+ &Attributes,
+ &ChildDevice);
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Note: Once the device is created successfully, framework frees the
+ // DeviceInit memory and sets the DeviceInit to NULL. So don't
+ // call any WdfDeviceInit functions after that.
+ //
+
+ //
+ // Initalize the PDO extension
+ //
+ PdoExtension = PdoGetExtension(ChildDevice);
+
+ RtlZeroMemory(PdoExtension, sizeof(PDO_EXTENSION));
+
+ PdoExtension->FdoExtension = FdoGetExtension(_Device);
+
+ PdoExtension->SerialNo = _SerialNo;
+
+
+ //
+ // Set PnP and Power capabilities for this child device.
+ //
+ WDF_DEVICE_PNP_CAPABILITIES_INIT(&PnpCaps); // Zeros this structure (note: WdfFalse is 0)
+
+ //
+ // Bus driver sets this value to WdfFalse for this embedded device, which cannot
+ // be physically removed; its FDO must not set/override this.
+ //
+ PnpCaps.Removable = WdfFalse;
+
+ //
+ // Bus driver sets this value to WdfTrue. FDO can override this value (when this irp is on its
+ // way up) if it determines that this device cannot be safely surprise (not orderly) removed
+ // without data loss.
+ //
+ PnpCaps.SurpriseRemovalOK = WdfTrue;
+
+ PnpCaps.Address = _SerialNo;
+ PnpCaps.UINumber = _SerialNo;
+
+ WdfDeviceSetPnpCapabilities(ChildDevice, &PnpCaps);
+
+ WDF_DEVICE_POWER_CAPABILITIES_INIT(&PowerCaps);
+
+ PowerCaps.DeviceD1 = WdfFalse;
+ PowerCaps.DeviceD2 = WdfTrue;
+
+ PowerCaps.WakeFromD0 = WdfFalse;
+ PowerCaps.WakeFromD1 = WdfFalse;
+ PowerCaps.WakeFromD2 = WdfTrue;
+ PowerCaps.WakeFromD3 = WdfTrue;
+
+ PowerCaps.DeviceState[PowerSystemWorking] = PowerDeviceD0;
+ PowerCaps.DeviceState[PowerSystemSleeping1] = PowerDeviceD2;
+ PowerCaps.DeviceState[PowerSystemSleeping2] = PowerDeviceD2;
+ PowerCaps.DeviceState[PowerSystemSleeping3] = PowerDeviceD2;
+ PowerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3;
+ PowerCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
+
+ PowerCaps.DeviceWake = PowerDeviceD2; // Lowest-powered Dx state to send wake signal to system
+
+ WdfDeviceSetPowerCapabilities(ChildDevice, &PowerCaps);
+
+ //
+ // Configure a default queue so that requests that are not
+ // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto
+ // other queues get dispatched here.
+ //
+
+ WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig, WdfIoQueueDispatchParallel);
+
+ //
+ // Cannot be power managed queue (dispatch only at D0) as
+ // BthMini issues BthX DDI to get version and capabilities
+ // before enter D0. A deadlock occurs if this is power managed.
+ //
+ QueueConfig.PowerManaged = WdfFalse;
+
+ QueueConfig.EvtIoDeviceControl = PdoIoQuDeviceControl;
+
+ Status = WdfIoQueueCreate(ChildDevice,
+ &QueueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &Queue);
+ DoTrace(LEVEL_INFO, TFLAG_PNP, (" WdfIoQueueCreate (%!STATUS!)", Status));
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Add this device to the FDO's collection of children.
+ // After the child device is added to the static collection successfully,
+ // driver must call WdfPdoMarkMissing to get the device deleted. It
+ // shouldn't delete the child device directly by calling WdfObjectDelete.
+ //
+ Status = WdfFdoAddStaticChild(_Device, ChildDevice);
+ DoTrace(LEVEL_INFO, TFLAG_PNP, (" WdfFdoAddStaticChild (%!STATUS!)", Status));
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ //
+ // Call WdfDeviceInitFree if you encounter an error before the
+ // device is created. Once the device is created, framework
+ // NULLs the DeviceInit value.
+ //
+ if (!NT_SUCCESS(Status)) {
+
+ DoTrace(LEVEL_ERROR, TFLAG_PNP, (" -PdoCreate: exit %!STATUS!", Status));
+
+ if (DeviceInit != NULL) {
+ WdfDeviceInitFree(DeviceInit);
+ }
+
+ if(ChildDevice) {
+ WdfObjectDelete(ChildDevice);
+ }
+ }
+ else
+ {
+ DoTrace(LEVEL_INFO, TFLAG_PNP, (" -PdoCreate: exit %!STATUS!", Status));
+ }
+
+ if (NULL != ContainerID.Buffer) {
+ RtlFreeUnicodeString(&ContainerID);
+ }
+
+ return Status;
+}
+
+NTSTATUS
+PdoDevPrepareHardware(
+ _In_ WDFDEVICE _Device,
+ _In_ WDFCMRESLIST _ResourcesRaw,
+ _In_ WDFCMRESLIST _ResourcesTranslated
+ )
+/*++
+Routine Description:
+
+ This PnP CB function take a refernce of its parent so it will not enter DxState while in S0Idle.
+
+Arguments:
+
+ _Device - WDF Device object
+
+ _ResourcesRaw - (Not referenced)
+
+ _ResourcesTranslated - (Not referenced)
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ WDFDEVICE ParentDevice;
+
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(_ResourcesRaw);
+ UNREFERENCED_PARAMETER(_ResourcesTranslated);
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("+PdoDevPrepareHardware"));
+
+ ParentDevice = WdfPdoGetParent(_Device);
+
+ //
+ // Take a reference to avoid FDO to enter DxState in IdleS0
+ //
+ Status = WdfDeviceStopIdle(ParentDevice, FALSE);
+ DoTrace(LEVEL_INFO, TFLAG_PNP, ("WdfDeviceStopIdle %!STATUS!", Status));
+
+ //
+ // Any failure Status code should be invesigated to ensure that the reference count is balanced.
+ //
+ NT_ASSERT(NT_SUCCESS(Status));
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP, ("-PdoDevPrepareHardware"));
+
+ return Status;
+}
+
+
+NTSTATUS
+PdoDevReleaseHardware(
+ _In_ WDFDEVICE _Device,
+ _In_ WDFCMRESLIST _ResourcesTranslated
+ )
+/*++
+Routine Description:
+
+ This PnP CB function release a refcount of its parent so it can enter DxState in S0Idle.
+
+Arguments:
+
+ _Device - WDF Device object
+
+ _ResourcesTranslated - (Not referenced)
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ WDFDEVICE ParentDevice;
+
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(_ResourcesTranslated);
+
+ DoTrace(LEVEL_INFO, TFLAG_PNP,("+PdoDevReleaseHardware"));
+
+ ParentDevice = WdfPdoGetParent(_Device);
+
+ //
+ // Release a reference to allow FDO to enter DxState in IdleS0
+ //
+ WdfDeviceResumeIdle(ParentDevice);
+
+ return STATUS_SUCCESS;
+}
+
+
+
+NTSTATUS
+PdoDevD0Entry(
+ _In_ WDFDEVICE _Device,
+ _In_ WDF_POWER_DEVICE_STATE _PreviousState
+ )
+/*++
+Routine Description:
+
+ This PnPPower CB function is invoked after device has entered D0 (working) state.
+
+Arguments:
+
+ _Device - WDF Device object
+
+ PreviousState - Previous device power state
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(_Device);
+ UNREFERENCED_PARAMETER(_PreviousState);
+
+ DoTrace(LEVEL_INFO, TFLAG_UART, ("+PdoDevD0Entry"));
+
+ //
+ // Can bring the Bluetooth function back to active state
+ //
+
+
+ DoTrace(LEVEL_INFO, TFLAG_UART, ("-PdoDevD0Entry %!STATUS!", Status));
+
+ return Status;
+}
+
+
+NTSTATUS
+PdoDevD0Exit(
+ _In_ WDFDEVICE _Device,
+ _In_ WDF_POWER_DEVICE_STATE _TargetState
+ )
+/*++
+Routine Description:
+
+ This PnP CB function is invoked when device has exited D0 (working) state.
+
+Arguments:
+
+ _Device - WDF Device object
+
+ _TargetState - Next device power state that it is about to enter
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(_Device);
+ UNREFERENCED_PARAMETER(_TargetState);
+
+ DoTrace(LEVEL_INFO, TFLAG_UART, ("+PdoDevD0Exit: D0 -> D%d", _TargetState-WdfPowerDeviceD0));
+
+ //
+ // Can prepare the Bluetooth function to enter lower power device state
+ //
+
+
+ DoTrace(LEVEL_INFO, TFLAG_UART, ("-PdoDevD0Exit"));
+
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+PdoDevDisableWakeAtBus(
+ _In_ WDFDEVICE _Device
+ )
+/*++
+
+Routine Description:
+
+ This framework callback routine performs bus-level operations that disable
+ the ability of one of the bus's devices to trigger a wake-up signal.
+
+Arguments:
+
+ _Device - Framework device object
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ //
+ // Do not mark this function pageable to potentially reduce power up time.
+ //
+
+ DoTrace(LEVEL_INFO, TFLAG_POWER,("<==(D)== PdoDevDisableWakeAtBus"));
+
+ //
+ // Device specific implementation to disarm for wake
+ //
+ DeviceDisableWakeControl(_Device);
+}
+
+NTSTATUS
+PdoDevEnableWakeAtBus(
+ _In_ WDFDEVICE _Device,
+ _In_ SYSTEM_POWER_STATE _PowerState
+ )
+/*++
+
+Routine Description:
+
+ This framework callback routine performs bus-level operations that enable
+ one of the bus's devices to trigger a wake-up signal.
+
+Arguments:
+
+ _Device - Framework device object
+
+ _PowerState - identifies the system power state that the system or device will wake from.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ //
+ // Do not mark this function pageable to potentially reduce power up time.
+ //
+
+ DoTrace(LEVEL_INFO, TFLAG_POWER,("==(E)==> PdoDevEnableWakeAtBus from %S",
+ _PowerState == PowerSystemWorking ? L"S0" : L"Sx"));
+
+ //
+ // Device specific implementation to arm for wake
+ //
+ return DeviceEnableWakeControl(_Device, _PowerState);
+}
+
+VOID
+PdoIoQuDeviceControl(
+ _In_ WDFQUEUE _Queue,
+ _In_ WDFREQUEST _Request,
+ _In_ size_t _OutputBufferLength,
+ _In_ size_t _InputBufferLength,
+ _In_ ULONG _IoControlCode
+ )
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for device control requests. This routine can be invoke
+ at the DISPATCH level from BthPort/mini.
+
+Arguments:
+
+ _Queue - Handle to the framework queue object that is associated
+ with the I/O request.
+ _Request - Handle to a framework request object.
+
+ _OutputBufferLength - length of the request's output buffer,
+ if an output buffer is available.
+ _InputBufferLength - length of the request's input buffer,
+ if an input buffer is available.
+
+ _IoControlCode - the driver-defined or system-defined I/O control code
+ (IOCTL) that is associated with the request.
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ WDFDEVICE Device = NULL;
+ NTSTATUS Status = STATUS_INVALID_PARAMETER;
+ WDF_REQUEST_FORWARD_OPTIONS ForwardOptions;
+ WDFDEVICE ParentDevice;
+ ULONG ControlCode = (_IoControlCode & 0x00003ffc) >> 2;
+
+ UNREFERENCED_PARAMETER(_OutputBufferLength);
+ UNREFERENCED_PARAMETER(_InputBufferLength);
+
+ DoTrace(LEVEL_INFO, TFLAG_IOCTL,("+IoDeviceControl - InBufLen:%d, OutBufLen:%d",
+ (ULONG) _InputBufferLength, (ULONG) _OutputBufferLength));
+
+ switch (_IoControlCode) {
+ case IOCTL_BTHX_GET_VERSION:
+ case IOCTL_BTHX_SET_VERSION:
+ case IOCTL_BTHX_QUERY_CAPABILITIES:
+ case IOCTL_BTHX_WRITE_HCI:
+ case IOCTL_BTHX_READ_HCI:
+ Device = WdfIoQueueGetDevice(_Queue);
+ WDF_REQUEST_FORWARD_OPTIONS_INIT(&ForwardOptions);
+ ForwardOptions.Flags = WDF_REQUEST_FORWARD_OPTION_SEND_AND_FORGET;
+ ParentDevice = WdfPdoGetParent(Device);
+
+ //
+ // Forward known IOCTLs to FDO to process
+ //
+ Status = WdfRequestForwardToParentDeviceIoQueue(_Request,
+ WdfDeviceGetDefaultQueue(ParentDevice),
+ &ForwardOptions);
+ break;
+
+ default:
+ //
+ // Complete this unexptected IOCTL with default STATUS_INVALID_PARAMETER.
+ //
+ DoTrace(LEVEL_ERROR, TFLAG_IOCTL,("Unexpected IOCTL_(0x%x, Func %d)", _IoControlCode, ControlCode));
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)){
+ DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" IOCTL_(0x%x, Func %d) failed %!STATUS!", _IoControlCode, ControlCode, Status));
+ WdfRequestComplete(_Request, Status);
+ return;
+ }
+
+ return;
+}
+
+
diff --git a/bluetooth/serialhcibus/public.h b/bluetooth/serialhcibus/public.h
new file mode 100644
index 00000000..5ef495d0
--- /dev/null
+++ b/bluetooth/serialhcibus/public.h
@@ -0,0 +1,47 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ public.h
+
+Abstract:
+
+ This module contains the common declarations shared by driver
+ and user applications.
+
+Environment:
+
+ user and kernel
+
+--*/
+
+#ifndef __PUBLIC_H
+#define __PUBLIC_H
+
+#ifdef DEFINE_GUID
+
+//
+// Vendor: Define a device interface GUID for Bluetooth Radion On/off.
+// Need to use uuidgen to create your own GUID instead of reusing this one.
+//
+DEFINE_GUID(GUID_DEVINTERFACE_BLUETOOTH_RADIO_ONOFF_VENDOR_SPECIFIC,
+ 0x98899865, 0x63de, 0x427b, 0x84, 0x77, 0x6c, 0xb7, 0x4d, 0x9f, 0xf2, 0xa7);
+//{98899865-63de-427b-8477-6cb74d9ff2a7}
+
+#endif // #ifdef DEFINE_GUID
+
+
+//
+// IOCTL definitions to support Radio on/off
+//
+#define FILE_DEVICE_BUSENUM FILE_DEVICE_BUS_EXTENDER
+#define BUSENUM_IOCTL(id, access) CTL_CODE(FILE_DEVICE_BUSENUM, \
+ (id), \
+ METHOD_BUFFERED, \
+ access)
+
+#define IOCTL_BUSENUM_SET_RADIO_ONOFF_VENDOR_SPECFIC BUSENUM_IOCTL(0x1, FILE_WRITE_DATA)
+
+#endif
diff --git a/bluetooth/serialhcibus/serialhcibus.sln b/bluetooth/serialhcibus/serialhcibus.sln
new file mode 100644
index 00000000..06bf3320
--- /dev/null
+++ b/bluetooth/serialhcibus/serialhcibus.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0
+MinimumVisualStudioVersion = 12.0
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SerialBusWdk", "WDK\SerialBusWdk.vcxproj", "{B126E3A5-91B5-41B5-9B40-719D474AA513}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B126E3A5-91B5-41B5-9B40-719D474AA513}.Debug|Win32.ActiveCfg = Debug|Win32
+ {B126E3A5-91B5-41B5-9B40-719D474AA513}.Debug|Win32.Build.0 = Debug|Win32
+ {B126E3A5-91B5-41B5-9B40-719D474AA513}.Release|Win32.ActiveCfg = Release|Win32
+ {B126E3A5-91B5-41B5-9B40-719D474AA513}.Release|Win32.Build.0 = Release|Win32
+ {B126E3A5-91B5-41B5-9B40-719D474AA513}.Debug|x64.ActiveCfg = Debug|x64
+ {B126E3A5-91B5-41B5-9B40-719D474AA513}.Debug|x64.Build.0 = Debug|x64
+ {B126E3A5-91B5-41B5-9B40-719D474AA513}.Release|x64.ActiveCfg = Release|x64
+ {B126E3A5-91B5-41B5-9B40-719D474AA513}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal