diff --git a/kernel/windows/netuio/netuio.inf b/kernel/windows/netuio/netuio.inf
index b1696cc50..4c4d5e047 100644
--- a/kernel/windows/netuio/netuio.inf
+++ b/kernel/windows/netuio/netuio.inf
@@ -38,7 +38,7 @@
 Signature="$WINDOWS NT$"
 Class=Net
 ClassGuid={4d36e972-e325-11ce-bfc1-08002be10318}
-Provider=%ManufacturerName%
+Provider=%Intel%
 CatalogFile=netuio.cat
 DriverVer=
 
@@ -47,7 +47,8 @@ DriverVer=
 ;*****************************************
 
 [Manufacturer]
-%ManufacturerName%=Standard,NT$ARCH$
+%Intel%=Standard,NT$ARCH$
+%Broadcom%=Broadcom,NT$ARCH$
 
 [Standard.NT$ARCH$]
 %F1583.netuio.Description%=netuio_Device, PCI\VEN_8086&DEV_1583
@@ -56,6 +57,9 @@ DriverVer=
 %F37D0.netuio.Description%=netuio_Device, PCI\VEN_8086&DEV_37D0
 %F153B.netuio.Description%=netuio_Device, PCI\VEN_8086&DEV_153B
 
+[Broadcom.NT$ARCH$]
+%F16D7.netuio.Description%=netuio_Device, PCI\VEN_14E4&DEV_16D7
+
 [netuio_Device.NT]
 CopyFiles=Drivers_Dir
 
@@ -113,7 +117,8 @@ KmdfLibraryVersion = $KMDFVERSION$
 
 [Strings]
 SPSVCINST_ASSOCSERVICE= 0x00000002
-ManufacturerName="Intel"
+Intel = "Intel"
+Broadcom = "Broadcom Corporation"
 ClassName = "Intel(R) DPDK netUIO Driver"
 DiskName = "DPDK netUIO Installation Disk"
 F1583.netuio.Description = "DPDK netUIO for Intel(R) Ethernet Converged Network Adapter XL710-Q2"
@@ -121,5 +126,6 @@ F158A.netuio.Description = "DPDK netUIO for Intel(R) Ethernet Network Adapter XX
 F158B.netuio.Description = "DPDK netUIO for Intel(R) Ethernet Network Adapter XXV710-DA1"
 F37D0.netuio.Description = "DPDK netUIO for Intel(R) Ethernet Connection X722"
 F153B.netuio.Description = "DPDK netUIO for Intel(R) Ethernet Connection I217-V"
+F16D7.netuio.Description = "DPDK netUIO for Broadcom P225p NetXtreme-E Dual-port 10Gb/25Gb Ethernet PCIe Adapter"
 netuio.DeviceDesc = "netuio Device"
 netuio.SVCDESC = "netuio Service"
diff --git a/kernel/windows/netuio/netuio_dev.c b/kernel/windows/netuio/netuio_dev.c
index a1fe447f7..3b5c95e84 100644
--- a/kernel/windows/netuio/netuio_dev.c
+++ b/kernel/windows/netuio/netuio_dev.c
@@ -27,16 +27,28 @@ Return Value:
 NTSTATUS
 netuio_create_device(_Inout_ PWDFDEVICE_INIT DeviceInit)
 {
-    WDF_OBJECT_ATTRIBUTES deviceAttributes;
-    WDFDEVICE device = NULL;
     NTSTATUS status;
+    WDFDEVICE device = NULL;
+
+    WDF_OBJECT_ATTRIBUTES deviceAttributes;
+    WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
+    WDF_FILEOBJECT_CONFIG fileConfig;
 
     PAGED_CODE();
 
-    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, NETUIO_CONTEXT_DATA);
+    // Register PnP power management callbacks
+    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
+    pnpPowerCallbacks.EvtDevicePrepareHardware = netuio_evt_prepare_hw;
+    pnpPowerCallbacks.EvtDeviceReleaseHardware = netuio_evt_release_hw;
+    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
+
+    // Register callbacks for when a HANDLE is opened or closed.
+    WDF_FILEOBJECT_CONFIG_INIT(&fileConfig, WDF_NO_EVENT_CALLBACK, WDF_NO_EVENT_CALLBACK, netuio_evt_file_cleanup);
+    WdfDeviceInitSetFileObjectConfig(DeviceInit, &fileConfig, WDF_NO_OBJECT_ATTRIBUTES);
 
     // Set the device context cleanup callback.
     // This function will be called when the WDF Device Object associated to the current device is destroyed
+    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, NETUIO_CONTEXT_DATA);
     deviceAttributes.EvtCleanupCallback = netuio_evt_device_context_cleanup;
 
     status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
@@ -88,63 +100,112 @@ netuio_map_hw_resources(_In_ WDFDEVICE Device, _In_ WDFCMRESLIST Resources, _In_
 {
     UNREFERENCED_PARAMETER(Resources);
 
-    NTSTATUS status = STATUS_SUCCESS;
+    NTSTATUS status;
 
     PNETUIO_CONTEXT_DATA  netuio_contextdata;
     netuio_contextdata = netuio_get_context_data(Device);
 
-    if (!netuio_contextdata)
+    if (!netuio_contextdata) {
         return STATUS_UNSUCCESSFUL;
+    }
 
-    PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor;
-    UINT8 bar_index = 0;
+    PCI_COMMON_HEADER pci_config = {0};
+    ULONG bytes_returned;
 
-    // Collect device BAR resources from the ResourcesTranslated object
-    for (ULONG idx = 0; idx < WdfCmResourceListGetCount(ResourcesTranslated); idx++) {
-        descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, idx);
-        if (!descriptor) {
-            status = STATUS_DEVICE_CONFIGURATION_ERROR;
-            goto end;
+    // Read PCI configuration space
+    bytes_returned = netuio_contextdata->bus_interface.GetBusData(
+        netuio_contextdata->bus_interface.Context,
+        PCI_WHICHSPACE_CONFIG,
+        &pci_config,
+        0,
+        sizeof(pci_config));
+
+    if (bytes_returned != sizeof(pci_config)) {
+        status = STATUS_NOT_SUPPORTED;
+        goto end;
+    }
+
+    // Device type is implictly enforced by .inf
+    ASSERT(PCI_CONFIGURATION_TYPE(&pci_config) == PCI_DEVICE_TYPE);
+
+    PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor;
+    ULONG next_descriptor = 0;
+    ULONGLONG bar_addr = 0;
+    ULONG curr_bar = 0;
+    ULONG prev_bar = 0;
+
+    for (INT bar_index = 0; bar_index < PCI_MAX_BAR; bar_index++) {
+        prev_bar = curr_bar;
+        curr_bar = pci_config.u.type0.BaseAddresses[bar_index];
+        if (curr_bar == 0 || (prev_bar & PCI_TYPE_64BIT)) {
+            // Skip this bar
+            netuio_contextdata->bar[bar_index].base_addr.QuadPart = 0;
+            netuio_contextdata->bar[bar_index].size = 0;
+            netuio_contextdata->bar[bar_index].virt_addr = 0;
+
+            netuio_contextdata->dpdk_hw[bar_index].mdl = NULL;
+            netuio_contextdata->dpdk_hw[bar_index].mem.size = 0;
+
+            continue;
         }
 
-        switch (descriptor->Type) {
-        case CmResourceTypeMemory:
-            // Retrieve and map the BARs
-            netuio_contextdata->bar[bar_index].base_addr.QuadPart = descriptor->u.Memory.Start.QuadPart;
-            netuio_contextdata->bar[bar_index].size = descriptor->u.Memory.Length;
-            netuio_contextdata->bar[bar_index].virt_addr =
-                MmMapIoSpace(descriptor->u.Memory.Start, descriptor->u.Memory.Length, MmNonCached);
+        // Find next CmResourceTypeMemory
+        do {
+            descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, next_descriptor);
+            next_descriptor++;
 
-            if (netuio_contextdata->bar[bar_index].virt_addr == NULL) {
-                status = STATUS_UNSUCCESSFUL;
+            if (descriptor == NULL) {
+                status = STATUS_DEVICE_CONFIGURATION_ERROR;
                 goto end;
             }
+        } while (descriptor->Type != CmResourceTypeMemory);
 
-            bar_index++;
-            break;
+        // Assert that we have the correct descriptor
+        ASSERT((descriptor->Flags & CM_RESOURCE_MEMORY_BAR) != 0);
 
-            // Don't handle any other resource type
-            // This could be device-private type added by the PCI bus driver.
-        case CmResourceTypeInterrupt:
-        default:
-            break;
+        if (curr_bar & PCI_TYPE_64BIT) {
+            ASSERT(bar_index != PCI_TYPE0_ADDRESSES - 1);
+            bar_addr = ((ULONGLONG)pci_config.u.type0.BaseAddresses[bar_index + 1] << 32) | (curr_bar & PCI_ADDRESS_MEMORY_ADDRESS_MASK);
+        }
+        else
+        {
+            bar_addr = curr_bar & PCI_ADDRESS_MEMORY_ADDRESS_MASK;
         }
-    }
 
-    // Allocate an MDL for the device BAR, so that we can map it to the user's process context later...
-    if (status == STATUS_SUCCESS) {
-        // Bar 0 is typically the HW BAR
-        if (netuio_contextdata->bar[0].virt_addr) {
-            netuio_contextdata->dpdk_hw.mdl = IoAllocateMdl(netuio_contextdata->bar[0].virt_addr, (ULONG)netuio_contextdata->bar[0].size, FALSE, FALSE, NULL);
-            if (!netuio_contextdata->dpdk_hw.mdl) {
-                status = STATUS_INSUFFICIENT_RESOURCES;
-                goto end;
-            }
-            netuio_contextdata->dpdk_hw.mem.size = netuio_contextdata->bar[0].size;
+        ASSERT((ULONGLONG)descriptor->u.Memory.Start.QuadPart == bar_addr);
+
+        // Retrieve and map the BARs
+        netuio_contextdata->bar[bar_index].base_addr.QuadPart = descriptor->u.Memory.Start.QuadPart;
+        netuio_contextdata->bar[bar_index].size = descriptor->u.Memory.Length;
+        netuio_contextdata->bar[bar_index].virt_addr = MmMapIoSpace(descriptor->u.Memory.Start,
+                                                                    descriptor->u.Memory.Length,
+                                                                    MmNonCached);
+        if (netuio_contextdata->bar[bar_index].virt_addr == NULL) {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
         }
-    }
+
+        // Allocate an MDL for the device BAR, so we can map it to the user's process context later.
+        netuio_contextdata->dpdk_hw[bar_index].mdl = IoAllocateMdl(netuio_contextdata->bar[bar_index].virt_addr,
+                                                                   (ULONG)netuio_contextdata->bar[bar_index].size,
+                                                                   FALSE,
+                                                                   FALSE,
+                                                                   NULL);
+        if (!netuio_contextdata->dpdk_hw[bar_index].mdl) {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
+        }
+
+        netuio_contextdata->dpdk_hw[bar_index].mem.size = netuio_contextdata->bar[bar_index].size;
+    } // for bar_index
+
+    status = STATUS_SUCCESS;
 
 end:
+    if (status != STATUS_SUCCESS) {
+        netuio_free_hw_resources(Device);
+    }
+
     return status;
 }
 
@@ -155,15 +216,21 @@ netuio_free_hw_resources(_In_ WDFDEVICE Device)
     netuio_contextdata = netuio_get_context_data(Device);
 
     if (netuio_contextdata) {
-        // Free the allocated MDL
-        if (netuio_contextdata->dpdk_hw.mdl)
-            IoFreeMdl(netuio_contextdata->dpdk_hw.mdl);
-
-        // Unmap all the BAR regions previously mapped
         for (UINT8 bar_index = 0; bar_index < PCI_MAX_BAR; bar_index++) {
-            if (netuio_contextdata->bar[bar_index].virt_addr)
+
+            // Free the allocated MDLs
+            if (netuio_contextdata->dpdk_hw[bar_index].mdl) {
+                IoFreeMdl(netuio_contextdata->dpdk_hw[bar_index].mdl);
+            }
+
+            // Unmap all the BAR regions previously mapped
+            if (netuio_contextdata->bar[bar_index].virt_addr) {
                 MmUnmapIoSpace(netuio_contextdata->bar[bar_index].virt_addr, netuio_contextdata->bar[bar_index].size);
+            }
         }
+
+        RtlZeroMemory(netuio_contextdata->dpdk_hw, sizeof(netuio_contextdata->dpdk_hw));
+        RtlZeroMemory(netuio_contextdata->bar, sizeof(netuio_contextdata->bar));
     }
 }
 
@@ -246,13 +313,16 @@ create_device_specific_symbolic_link(_In_ WDFOBJECT device)
 static NTSTATUS
 allocate_usermemory_segment(_In_ WDFOBJECT device)
 {
-    NTSTATUS status = STATUS_SUCCESS;
+    NTSTATUS status;
 
     PNETUIO_CONTEXT_DATA  netuio_contextdata;
     netuio_contextdata = netuio_get_context_data(device);
 
     if (!netuio_contextdata)
-        return STATUS_UNSUCCESSFUL;
+    {
+        status = STATUS_INVALID_DEVICE_STATE;
+        goto end;
+    }
 
     PHYSICAL_ADDRESS lowest_acceptable_address;
     PHYSICAL_ADDRESS highest_acceptable_address;
@@ -287,7 +357,14 @@ allocate_usermemory_segment(_In_ WDFOBJECT device)
     // Store the region's physical address
     netuio_contextdata->dpdk_seg.mem.phys_addr = MmGetPhysicalAddress(netuio_contextdata->dpdk_seg.mem.virt_addr);
 
+    status = STATUS_SUCCESS;
+
 end:
+    if (status != STATUS_SUCCESS)
+    {
+        free_usermemory_segment(device);
+    }
+
     return status;
 }
 
@@ -299,9 +376,15 @@ free_usermemory_segment(_In_ WDFOBJECT device)
 
     if (netuio_contextdata) {
         if (netuio_contextdata->dpdk_seg.mdl)
+        {
             IoFreeMdl(netuio_contextdata->dpdk_seg.mdl);
+            netuio_contextdata->dpdk_seg.mdl = NULL;
+        }
 
         if (netuio_contextdata->dpdk_seg.mem.virt_addr)
+        {
             MmFreeContiguousMemory(netuio_contextdata->dpdk_seg.mem.virt_addr);
+            netuio_contextdata->dpdk_seg.mem.virt_addr = NULL;
+        }
     }
 }
diff --git a/kernel/windows/netuio/netuio_dev.h b/kernel/windows/netuio/netuio_dev.h
index a19a4fb42..59956720e 100644
--- a/kernel/windows/netuio/netuio_dev.h
+++ b/kernel/windows/netuio/netuio_dev.h
@@ -12,7 +12,6 @@ EXTERN_C_START
 #include "netuio_interface.h"
 
 // Constants
-#define PCI_MAX_BAR              3
 #define USER_MEMORY_SEGMENT_SIZE (256ULL * 1024ULL * 1024ULL)   // 256MB
 
 struct pci_bar {
@@ -29,13 +28,13 @@ struct mem_map_region {
 // The device context performs the same job as a WDM device extension in the driver frameworks
 typedef struct _NETUIO_CONTEXT_DATA
 {
-    WDFDEVICE               wdf_device;        // WDF device handle to the FDO
-    BUS_INTERFACE_STANDARD  bus_interface;     // Bus interface for config space access
-    struct pci_bar          bar[PCI_MAX_BAR];  // device BARs
-    struct dev_addr         addr;              // B:D:F details of device
-    USHORT                  dev_numa_node;     // The NUMA node of the device
-    struct mem_map_region   dpdk_hw;           // mapped region for the device's register space
-    struct mem_map_region   dpdk_seg;          // mapped region allocated for DPDK process use
+    WDFDEVICE               wdf_device;             // WDF device handle to the FDO
+    BUS_INTERFACE_STANDARD  bus_interface;          // Bus interface for config space access
+    struct pci_bar          bar[PCI_MAX_BAR];       // device BARs
+    struct dev_addr         addr;                   // B:D:F details of device
+    USHORT                  dev_numa_node;          // The NUMA node of the device
+    struct mem_map_region   dpdk_hw[PCI_MAX_BAR];   // mapped region for the device's register space
+    struct mem_map_region   dpdk_seg;               // mapped region allocated for DPDK process use
 } NETUIO_CONTEXT_DATA, *PNETUIO_CONTEXT_DATA;
 
 
diff --git a/kernel/windows/netuio/netuio_drv.c b/kernel/windows/netuio/netuio_drv.c
index d45a9ec4f..554629c98 100644
--- a/kernel/windows/netuio/netuio_drv.c
+++ b/kernel/windows/netuio/netuio_drv.c
@@ -62,25 +62,8 @@ Return Value:
 NTSTATUS
 netuio_evt_device_add(_In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit)
 {
-    NTSTATUS status;
-    WDF_PNPPOWER_EVENT_CALLBACKS    pnpPowerCallbacks;
-
     UNREFERENCED_PARAMETER(Driver);
-
-    PAGED_CODE();
-
-    // Zero out the PnpPowerCallbacks structure
-    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
-
-    // Register Plug-aNd-Play and power management callbacks
-    pnpPowerCallbacks.EvtDevicePrepareHardware = netuio_evt_prepare_hw;
-    pnpPowerCallbacks.EvtDeviceReleaseHardware = netuio_evt_release_hw;
-
-    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
-
-    status = netuio_create_device(DeviceInit);
-
-    return status;
+    return netuio_create_device(DeviceInit);
 }
 
 /*
@@ -140,3 +123,27 @@ netuio_evt_driver_context_cleanup(_In_ WDFOBJECT DriverObject)
     DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_NETUIO_INFO_LEVEL, "netUIO Driver unloaded.\n");
     PAGED_CODE();
 }
+
+/*
+Routine Description :
+    EVT_WDF_FILE_CLEANUP callback, called when user process handle is closed.
+
+    Undoes IOCTL_NETUIO_MAP_HW_INTO_USERMODE.
+
+Return value :
+    None
+-*/
+VOID
+netuio_evt_file_cleanup(_In_ WDFFILEOBJECT FileObject)
+{
+    WDFDEVICE device;
+    PNETUIO_CONTEXT_DATA netuio_contextdata;
+
+    device = WdfFileObjectGetDevice(FileObject);
+    netuio_contextdata = netuio_get_context_data(device);
+
+    if (netuio_contextdata)
+    {
+        netuio_unmap_address_from_user_process(netuio_contextdata);
+    }
+}
diff --git a/kernel/windows/netuio/netuio_drv.h b/kernel/windows/netuio/netuio_drv.h
index 39d7f301e..5dc778625 100644
--- a/kernel/windows/netuio/netuio_drv.h
+++ b/kernel/windows/netuio/netuio_drv.h
@@ -27,6 +27,7 @@ EVT_WDF_DRIVER_DEVICE_ADD       netuio_evt_device_add;
 EVT_WDF_OBJECT_CONTEXT_CLEANUP  netuio_evt_driver_context_cleanup;
 EVT_WDF_DEVICE_PREPARE_HARDWARE netuio_evt_prepare_hw;
 EVT_WDF_DEVICE_RELEASE_HARDWARE netuio_evt_release_hw;
+EVT_WDF_FILE_CLOSE              netuio_evt_file_cleanup;
 
 EXTERN_C_END
 
diff --git a/kernel/windows/netuio/netuio_interface.h b/kernel/windows/netuio/netuio_interface.h
index f84ee895c..82fb1cb50 100644
--- a/kernel/windows/netuio/netuio_interface.h
+++ b/kernel/windows/netuio/netuio_interface.h
@@ -22,8 +22,8 @@ DEFINE_GUID (GUID_DEVINTERFACE_netUIO, 0x08336f60,0x0679,0x4c6c,0x85,0xd2,0xae,0
 #define NETUIO_DRIVER_NAME  _T("netuio")
 
 // IOCTL code definitions
-#define IOCTL_NETUIO_GET_HW_DATA      CTL_CODE(FILE_DEVICE_NETWORK, 51, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
-#define IOCTL_NETUIO_PCI_CONFIG_IO    CTL_CODE(FILE_DEVICE_NETWORK, 52, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#define IOCTL_NETUIO_MAP_HW_INTO_USERMODE CTL_CODE(FILE_DEVICE_NETWORK, 51, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#define IOCTL_NETUIO_PCI_CONFIG_IO        CTL_CODE(FILE_DEVICE_NETWORK, 52, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
 
 struct mem_region {
     UINT64           size;       // memory region size
@@ -43,13 +43,13 @@ enum pci_io {
     PCI_IO_WRITE = 1
 };
 
+#define PCI_MAX_BAR 6
+
 struct dpdk_private_info
 {
-    struct mem_region   hw;
+    struct mem_region   hw[PCI_MAX_BAR];
     struct mem_region   ms;
     struct dev_addr     dev_addr;
-    struct mem_region	bar1;
-//  struct mem_region	bar2;
     UINT16              dev_id;
     UINT16              sub_dev_id;
     USHORT              dev_numa_node;
diff --git a/kernel/windows/netuio/netuio_queue.c b/kernel/windows/netuio/netuio_queue.c
index 760b4a7e5..9c7ff7d06 100644
--- a/kernel/windows/netuio/netuio_queue.c
+++ b/kernel/windows/netuio/netuio_queue.c
@@ -11,54 +11,113 @@
 #pragma alloc_text (PAGE, netuio_queue_initialize)
 #endif
 
-VOID netuio_read_PCI_config(PNETUIO_CONTEXT_DATA netuio_contextdata, ULONG offset, UINT32 access_size, _Out_ UINT64 *output)
-{
-    *output = 0;
-    netuio_contextdata->bus_interface.GetBusData(netuio_contextdata->bus_interface.Context,
-                                                 PCI_WHICHSPACE_CONFIG,
-                                                 output,
-                                                 offset,
-                                                 access_size);
-}
-
-VOID netuio_write_PCI_config(PNETUIO_CONTEXT_DATA netuio_contextdata, ULONG offset, UINT32 access_size, union dpdk_pci_config_io_data const *input)
-{
-    netuio_contextdata->bus_interface.SetBusData(netuio_contextdata->bus_interface.Context,
-                                                 PCI_WHICHSPACE_CONFIG,
-                                                 (PVOID)input,
-                                                 offset,
-                                                 access_size);
-}
-
-static NTSTATUS
+static void
 netuio_handle_get_hw_data_request(_In_ WDFREQUEST Request, _In_ PNETUIO_CONTEXT_DATA netuio_contextdata,
                                    _In_ PVOID outputBuf, _In_ size_t outputBufSize)
 {
-    NTSTATUS status = STATUS_SUCCESS;
-
     WDF_REQUEST_PARAMETERS params;
     WDF_REQUEST_PARAMETERS_INIT(&params);
     WdfRequestGetParameters(Request, &params);
 
-    if (!netuio_contextdata || (outputBufSize != sizeof(struct dpdk_private_info))) {
-        status = STATUS_INVALID_PARAMETER;
-        goto end;
-    }
+    ASSERT(outputBufSize == sizeof(struct dpdk_private_info));
 
     struct dpdk_private_info *dpdk_pvt_info = (struct dpdk_private_info *)outputBuf;
     RtlZeroMemory(dpdk_pvt_info, outputBufSize);
 
-    dpdk_pvt_info->hw.phys_addr.QuadPart = netuio_contextdata->bar[0].base_addr.QuadPart;
-    dpdk_pvt_info->hw.user_mapped_virt_addr = netuio_contextdata->dpdk_hw.mem.user_mapped_virt_addr;
-    dpdk_pvt_info->hw.size = netuio_contextdata->bar[0].size;
+    for (ULONG idx = 0; idx < PCI_MAX_BAR; idx++) {
+        dpdk_pvt_info->hw[idx].phys_addr.QuadPart = netuio_contextdata->bar[idx].base_addr.QuadPart;
+        dpdk_pvt_info->hw[idx].user_mapped_virt_addr = netuio_contextdata->dpdk_hw[idx].mem.user_mapped_virt_addr;
+        dpdk_pvt_info->hw[idx].size = netuio_contextdata->bar[idx].size;
+    }
 
     dpdk_pvt_info->ms.phys_addr.QuadPart = netuio_contextdata->dpdk_seg.mem.phys_addr.QuadPart;
     dpdk_pvt_info->ms.user_mapped_virt_addr = netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr;
     dpdk_pvt_info->ms.size = netuio_contextdata->dpdk_seg.mem.size;
+}
+
+/*
+Routine Description:
+    Maps address ranges into the usermode process's address space.  The following
+    ranges are mapped:
+
+        * Any PCI BARs that our device was assigned
+        * The scratch buffer of contiguous pages
+
+Return Value:
+    NTSTATUS
+*/
+static NTSTATUS
+netuio_map_address_into_user_process(_In_ PNETUIO_CONTEXT_DATA netuio_contextdata)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    // Map the scratch memory regions to the user's process context
+    MmBuildMdlForNonPagedPool(netuio_contextdata->dpdk_seg.mdl);
+    netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr =
+        MmMapLockedPagesSpecifyCache(
+            netuio_contextdata->dpdk_seg.mdl, UserMode, MmCached,
+            NULL, FALSE, (NormalPagePriority | MdlMappingNoExecute));
+
+    if (netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr == NULL) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto end;
+    }
+
+    // Map any device BAR(s) to the user's process context
+    for (INT idx = 0; idx < PCI_MAX_BAR; idx++) {
+        if (netuio_contextdata->dpdk_hw[idx].mdl == NULL) {
+            continue;
+        }
+
+        MmBuildMdlForNonPagedPool(netuio_contextdata->dpdk_hw[idx].mdl);
+        netuio_contextdata->dpdk_hw[idx].mem.user_mapped_virt_addr =
+            MmMapLockedPagesSpecifyCache(
+                netuio_contextdata->dpdk_hw[idx].mdl, UserMode, MmCached,
+                NULL, FALSE, (NormalPagePriority | MdlMappingNoExecute));
+
+        if (netuio_contextdata->dpdk_hw[idx].mem.user_mapped_virt_addr == NULL) {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
+        }
+    }
+
 end:
+    if (status != STATUS_SUCCESS) {
+		netuio_unmap_address_from_user_process(netuio_contextdata);
+    }
+
     return status;
 }
 
+/*
+Routine Description:
+    Unmaps all address ranges from the usermode process address space.
+    MUST be called in the context of the same process which created
+    the mapping.
+
+Return Value:
+    None
+ */
+VOID
+netuio_unmap_address_from_user_process(_In_ PNETUIO_CONTEXT_DATA netuio_contextdata)
+{
+    if (netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr != NULL) {
+        MmUnmapLockedPages(
+            netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr,
+            netuio_contextdata->dpdk_seg.mdl);
+        netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr = NULL;
+    }
+
+    for (INT idx = 0; idx < PCI_MAX_BAR; idx++) {
+        if (netuio_contextdata->dpdk_hw[idx].mem.user_mapped_virt_addr != NULL) {
+            MmUnmapLockedPages(
+                netuio_contextdata->dpdk_hw[idx].mem.user_mapped_virt_addr,
+                netuio_contextdata->dpdk_hw[idx].mdl);
+            netuio_contextdata->dpdk_hw[idx].mem.user_mapped_virt_addr = NULL;
+        }
+    }
+}
+
 /*
 Routine Description:
     The I/O dispatch callbacks for the frameworks device object are configured here.
@@ -123,7 +182,7 @@ netuio_evt_IO_device_control(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request,
     netuio_contextdata = netuio_get_context_data(device);
 
     switch (IoControlCode) {
-    case IOCTL_NETUIO_GET_HW_DATA:
+    case IOCTL_NETUIO_MAP_HW_INTO_USERMODE:
         // First retrieve the input buffer and see if it matches our device
         status = WdfRequestRetrieveInputBuffer(Request, sizeof(struct dpdk_private_info), &input_buf, &input_buf_size);
         if (!NT_SUCCESS(status)) {
@@ -140,35 +199,29 @@ netuio_evt_IO_device_control(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request,
             break;
         }
 
-        // Map the previously allocated/defined memory regions to the user's process context
-        MmBuildMdlForNonPagedPool(netuio_contextdata->dpdk_hw.mdl);
-        netuio_contextdata->dpdk_hw.mem.user_mapped_virt_addr =
-                            MmMapLockedPagesSpecifyCache(netuio_contextdata->dpdk_hw.mdl, UserMode, MmCached,
-        NULL, FALSE, (NormalPagePriority | MdlMappingNoExecute));
-
-        MmBuildMdlForNonPagedPool(netuio_contextdata->dpdk_seg.mdl);
-        netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr =
-                            MmMapLockedPagesSpecifyCache(netuio_contextdata->dpdk_seg.mdl, UserMode, MmCached,
-        NULL, FALSE, (NormalPagePriority | MdlMappingNoExecute));
-
-        if (!netuio_contextdata->dpdk_hw.mem.user_mapped_virt_addr && !netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr) {
-            status = STATUS_INSUFFICIENT_RESOURCES;
+        if (netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr != NULL) {
+            status = STATUS_ALREADY_COMMITTED;
             break;
         }
 
-		// Zero out the physically contiguous block
-		RtlZeroMemory(netuio_contextdata->dpdk_seg.mem.virt_addr, netuio_contextdata->dpdk_seg.mem.size);
-
         // Return relevant data to the caller
         status = WdfRequestRetrieveOutputBuffer(Request, sizeof(struct dpdk_private_info), &output_buf, &output_buf_size);
         if (!NT_SUCCESS(status)) {
             status = STATUS_INVALID_BUFFER_SIZE;
             break;
         }
+
+        // Zero out the physically contiguous block
+        RtlZeroMemory(netuio_contextdata->dpdk_seg.mem.virt_addr, netuio_contextdata->dpdk_seg.mem.size);
+
+        status = netuio_map_address_into_user_process(netuio_contextdata);
+        if (status != STATUS_SUCCESS) {
+            break;
+        }
+
         ASSERT(output_buf_size == OutputBufferLength);
-        status = netuio_handle_get_hw_data_request(Request, netuio_contextdata, output_buf, output_buf_size);
-        if (NT_SUCCESS(status))
-            bytes_returned = output_buf_size;
+        netuio_handle_get_hw_data_request(Request, netuio_contextdata, output_buf, output_buf_size);
+        bytes_returned = output_buf_size;
 
         break;
 
@@ -206,19 +259,22 @@ netuio_evt_IO_device_control(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request,
         ASSERT(output_buf_size == OutputBufferLength);
 
         if (dpdk_pci_io_input->op == PCI_IO_READ) {
-            netuio_read_PCI_config(netuio_contextdata,
-                                   dpdk_pci_io_input->offset,
-                                   dpdk_pci_io_input->access_size,
-                                   (UINT64*)output_buf);
-            
-            bytes_returned = sizeof(UINT64);
+            *(UINT64 *)output_buf = 0;
+            bytes_returned = netuio_contextdata->bus_interface.GetBusData(
+                netuio_contextdata->bus_interface.Context,
+                PCI_WHICHSPACE_CONFIG,
+                output_buf,
+                dpdk_pci_io_input->offset,
+                dpdk_pci_io_input->access_size);
         }
         else if (dpdk_pci_io_input->op == PCI_IO_WRITE) {
-            netuio_write_PCI_config(netuio_contextdata,
-                                    dpdk_pci_io_input->offset,
-                                    dpdk_pci_io_input->access_size,
-                                    &dpdk_pci_io_input->data);
-            bytes_returned = 0;
+            // returns bytes written
+            bytes_returned = netuio_contextdata->bus_interface.SetBusData(
+                netuio_contextdata->bus_interface.Context,
+                PCI_WHICHSPACE_CONFIG,
+                (PVOID)&dpdk_pci_io_input->data,
+                dpdk_pci_io_input->offset,
+                dpdk_pci_io_input->access_size);
         }
         else {
             status = STATUS_INVALID_PARAMETER;
diff --git a/kernel/windows/netuio/netuio_queue.h b/kernel/windows/netuio/netuio_queue.h
index 6a0306516..90fdb4008 100644
--- a/kernel/windows/netuio/netuio_queue.h
+++ b/kernel/windows/netuio/netuio_queue.h
@@ -17,6 +17,8 @@ typedef struct _QUEUE_CONTEXT {
 
 WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT, QueueGetContext)
 
+VOID
+netuio_unmap_address_from_user_process(_In_ PNETUIO_CONTEXT_DATA netuio_contextdata);
 
 NTSTATUS
 netuio_queue_initialize(_In_ WDFDEVICE hDevice);
