Comparar commits

...

2 Commits

Autor SHA1 Mensagem Data
Christopher Anderson 5c64a1c8d4 wip
Change-Id: I7c3a5ff2fdc5779df0cbbdf279ccaec6be29bf88
2017-08-30 18:43:50 -07:00
Christopher Anderson 360ed32e18 [pci] Add config and capability protocol methods
These protocol methods abstract out config access for
the device dependent region of PCI config space in both
port IO and memory io spaces.

For data in the pci config header region drivers will be
expected to use get_device info.

Change-Id: I5d9877e0e05a37e2e719f732e2ff5abc0332af1d
2017-08-29 18:11:32 -07:00
7 arquivos alterados com 242 adições e 112 exclusões
+46
Ver Arquivo
@@ -330,6 +330,42 @@ mx_status_t sys_pci_get_nth_device(mx_handle_t hrsrc,
return MX_OK;
}
mx_status_t sys_pci_config_read(mx_handle_t handle, uint16_t offset, size_t width,
user_ptr<uint32_t> out_val) {
mxtl::RefPtr<PciDeviceDispatcher> pci_device;
mxtl::RefPtr<Dispatcher> dispatcher;
if (handle == MX_HANDLE_INVALID) {
return MX_ERR_BAD_HANDLE;
}
if (out_val.get() == nullptr) {
return MX_ERR_INVALID_ARGS;
}
// Get the PciDeviceDispatcher from the handle passed in via the pci protocol
auto up = ProcessDispatcher::GetCurrent();
mx_status_t status = up->GetDispatcherWithRights(handle, MX_RIGHT_READ | MX_RIGHT_WRITE,
&pci_device);
if (status != MX_OK) {
return status;
}
// Based on the width passed in we can use the type safety of the PciConfig layer
// to ensure we're getting correctly sized data back and return errors in the PIO
// cases.
auto config = pci_device->device()->config();
switch(width) {
case 8u: return out_val.copy_to_user(static_cast<uint32_t>(config->Read(PciReg8(offset))));
case 16u: return out_val.copy_to_user(static_cast<uint32_t>(config->Read(PciReg16(offset))));
case 32u: return out_val.copy_to_user(config->Read(PciReg32(offset)));
default: return MX_ERR_INVALID_ARGS;
}
// If we reached this point then the width was invalid.
return MX_ERR_INVALID_ARGS;
}
/* This is a transitional method to bootstrap legacy PIO access before
* PCI moves to userspace.
*/
@@ -713,6 +749,16 @@ mx_status_t sys_pci_add_subtract_io_range(mx_handle_t handle, bool mmio, uint64_
return MX_ERR_NOT_SUPPORTED;
}
mx_status_t sys_pci_config_read(mx_handle_t handle, uint16_t offset, size_t width,
user_ptr<uint32_t> out_val) {
return MX_ERR_NOT_SUPPORTED;
}
mx_status_t sys_pci_cfg_pio_rw(mx_handle_t handle, uint8_t bus, uint8_t dev, uint8_t func,
uint8_t offset, user_ptr<uint32_t> val, size_t width, bool write) {
return MX_ERR_NOT_SUPPORTED;
}
mx_status_t sys_pci_get_nth_device(mx_handle_t, uint32_t, user_ptr<mx_pcie_device_info_t>,
user_ptr<mx_handle_t>) {
return MX_ERR_NOT_SUPPORTED;
+49 -2
Ver Arquivo
@@ -6,6 +6,7 @@
#include <assert.h>
#include <limits.h>
#include <magenta/assert.h>
#include <magenta/process.h>
#include "kpci-private.h"
@@ -51,10 +52,53 @@ static mx_status_t do_resource_bookkeeping(mx_pci_resource_t* res) {
return status;
}
static mx_status_t pci_get_resource(void* ctx, uint32_t res_id, mx_pci_resource_t* out_res) {
// These reads are proxied directly over to the device's PciConfig object so the validity of the
// widths and offsets will be validated on that end and then trickle back to this level of the
// protocol.
//
// In the case of config and capability reads/writes, failure is a catastrophic occurrence
// along the lines of hardware failure or a device being removed from the bus. Due to this,
// those statuses will be asserted upon rather than forcing callers to add additional checks
// every time they wish to do a config read / write.
static uint32_t kpci_config_read(void* ctx, uint8_t offset, size_t width) {
MX_DEBUG_ASSERT(ctx);
kpci_device_t* device = ctx;
uint32_t val;
mx_status_t status = mx_pci_config_read(device->handle, offset, width, &val);
MX_DEBUG_ASSERT_MSG(status == MX_OK, "pci_config_read: %d\n", status);
return val;
}
static uint8_t kpci_get_next_capability(void* ctx, uint8_t offset, uint8_t type) {
uint8_t cap_offset = (uint8_t)kpci_config_read(ctx, offset + 1, 8);
uint8_t limit = 64;
// Walk the capability list looking for the type requested, starting at the offset
// passed in. limit acts as a barrier in case of an invalid capability pointer list
// that causes us to iterate forever otherwise.
while (cap_offset != 0 && limit--) {
uint8_t type_id = (uint8_t)kpci_config_read(ctx, cap_offset, 8);
printf("kpci_get_next_capability loop offset %u, type %u\n", cap_offset, type_id);
if (type_id == type) {
printf("kpci_get_next_capability: returning %u\n", cap_offset);
return cap_offset;
}
// We didn't find the right type, move on
cap_offset = (uint8_t)kpci_config_read(ctx, cap_offset + 1, 8);
}
// No more entries are in the list
return 0;
}
static mx_status_t kpci_get_resource(void* ctx, uint32_t res_id, mx_pci_resource_t* out_res) {
mx_status_t status = MX_OK;
if (!out_res || res_id >= PCI_RESOURCE_COUNT) {
printf("????\n");
return MX_ERR_INVALID_ARGS;
}
@@ -98,7 +142,7 @@ static mx_status_t kpci_map_resource(void* ctx,
}
mx_pci_resource_t resource;
mx_status_t status = pci_get_resource(ctx, res_id, &resource);
mx_status_t status = kpci_get_resource(ctx, res_id, &resource);
if (status != MX_OK) {
return status;
}
@@ -188,9 +232,12 @@ static pci_protocol_ops_t _pci_protocol = {
.enable_bus_master = kpci_enable_bus_master,
.enable_pio = kpci_enable_pio,
.reset_device = kpci_reset_device,
.get_resource = kpci_get_resource,
.map_resource = kpci_map_resource,
.map_interrupt = kpci_map_interrupt,
.query_irq_mode_caps = kpci_query_irq_mode_caps,
.set_irq_mode = kpci_set_irq_mode,
.get_device_info = kpci_get_device_info,
.config_read = kpci_config_read,
.get_next_capability = kpci_get_next_capability,
};
+70 -93
Ver Arquivo
@@ -7,6 +7,7 @@
#include <assert.h>
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
@@ -17,7 +18,7 @@
#include "trace.h"
#define LOCAL_TRACE 0
#define LOCAL_TRACE 1
namespace virtio {
@@ -31,15 +32,31 @@ Device::~Device() {
LTRACE_ENTRY;
}
static void ReadVirtioCap(pci_protocol_t* pci, uint8_t offset, virtio_pci_cap& cap) {
cap.cap_vndr = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, cap_vndr)));
cap.cap_next = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, cap_next)));
cap.cap_len = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, cap_len)));
cap.cfg_type = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, cfg_type)));
cap.bar = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, bar)));
cap.padding[0] = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, padding)));
cap.padding[1] = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, padding) + 1));
cap.padding[2] = pci_config_read8(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, padding) + 2));
cap.offset = pci_config_read32(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, offset)));
cap.length = pci_config_read32(pci, static_cast<uint8_t>(offset + offsetof(virtio_pci_cap, length)));
}
mx_status_t Device::MapBar(uint8_t i) {
LTRACEF("bar %u\n", i);
if (bar_[i].mmio_handle != MX_HANDLE_INVALID)
return MX_OK;
uint64_t sz;
mx_handle_t tmp_handle;
TRACE;
mx_status_t r = pci_map_resource(&pci_, PCI_RESOURCE_BAR_0 + i, MX_CACHE_POLICY_UNCACHED_DEVICE,
(void**)&bar_[i].mmio_base, &sz, &tmp_handle);
TRACE;
if (r != MX_OK) {
VIRTIO_ERROR("cannot map io %d\n", bar_[i].mmio_handle.get());
return r;
@@ -50,8 +67,7 @@ mx_status_t Device::MapBar(uint8_t i) {
return MX_OK;
}
mx_status_t Device::Bind(pci_protocol_t* pci,
mx_handle_t pci_config_handle, const pci_config_t* pci_config) {
mx_status_t Device::Bind(pci_protocol_t* pci, mx_pcie_device_info_t info) {
LTRACE_ENTRY;
mxtl::AutoLock lock(&lock_);
@@ -59,8 +75,7 @@ mx_status_t Device::Bind(pci_protocol_t* pci,
// save off handles to things
memcpy(&pci_, pci, sizeof(pci_protocol_t));
pci_config_handle_.reset(pci_config_handle);
pci_config_ = pci_config;
info_ = info;
// enable bus mastering
mx_status_t r;
@@ -89,101 +104,63 @@ mx_status_t Device::Bind(pci_protocol_t* pci,
LTRACEF("irq handle %u\n", irq_handle_.get());
// try to parse capabilities
if (pci_config_->status & PCI_STATUS_NEW_CAPS) {
LTRACEF("pci config capabilities_ptr 0x%x\n", pci_config_->capabilities_ptr);
for (uint8_t off = pci_get_first_capability(&pci_, kPciCapIdVendor);
off != 0;
off = pci_get_next_capability(&pci_, off, kPciCapIdVendor)) {
virtio_pci_cap cap;
size_t off = pci_config_->capabilities_ptr;
for (int i = 0; i < 64; i++) { // only loop so many times in case things out of whack
virtio_pci_cap *cap;
if (off > PAGE_SIZE) {
VIRTIO_ERROR("capability pointer is out of whack %zu\n", off);
return MX_ERR_INVALID_ARGS;
}
cap = (virtio_pci_cap *)(((uintptr_t)pci_config_) + off);
LTRACEF("cap %p: type %#hhx next %#hhx len %#hhx cfg_type %#hhx bar %#hhx offset %#x length %#x\n",
cap, cap->cfg_type, cap->cap_next, cap->cap_len, cap->cfg_type, cap->bar, cap->offset, cap->length);
if (cap->cap_vndr == 0x9) { // vendor specific capability
switch (cap->cfg_type) {
case VIRTIO_PCI_CAP_COMMON_CFG: {
MapBar(cap->bar);
mmio_regs_.common_config = (volatile virtio_pci_common_cfg*)((uintptr_t)bar_[cap->bar].mmio_base + cap->offset);
LTRACEF("common_config %p\n", mmio_regs_.common_config);
break;
}
case VIRTIO_PCI_CAP_NOTIFY_CFG: {
MapBar(cap->bar);
mmio_regs_.notify_base = (volatile uint16_t*)((uintptr_t)bar_[cap->bar].mmio_base + cap->offset);
LTRACEF("notify_base %p\n", mmio_regs_.notify_base);
mmio_regs_.notify_mul = ((virtio_pci_notify_cap *) cap)->notify_off_multiplier;
LTRACEF("notify_mul %x\n", mmio_regs_.notify_mul);
break;
}
case VIRTIO_PCI_CAP_ISR_CFG: {
MapBar(cap->bar);
mmio_regs_.isr_status = (volatile uint32_t*)((uintptr_t)bar_[cap->bar].mmio_base + cap->offset);
LTRACEF("isr_status %p\n", mmio_regs_.isr_status);
break;
}
case VIRTIO_PCI_CAP_DEVICE_CFG: {
MapBar(cap->bar);
mmio_regs_.device_config = (volatile void*)((uintptr_t)bar_[cap->bar].mmio_base + cap->offset);
LTRACEF("device_config %p\n", mmio_regs_.device_config);
break;
}
case VIRTIO_PCI_CAP_PCI_CFG: {
// will be pointing at bar0, which we'll map below anyway
break;
}
}
}
off = cap->cap_next;
if (cap->cap_next == 0)
ReadVirtioCap(&pci_, off, cap);
LTRACEF("cap type %#hhx next %#hhx len %#hhx cfg_type %#hhx bar %#hhx "
"offset %#x length %#x\n", cap.cap_vndr, cap.cap_next, cap.cap_len, cap.cfg_type, cap.bar, cap.offset, cap.length);
switch (cap.cfg_type) {
case VIRTIO_PCI_CAP_COMMON_CFG: {
MapBar(cap.bar);
mmio_regs_.common_config = (volatile virtio_pci_common_cfg*)((uintptr_t)bar_[cap.bar].mmio_base + cap.offset);
LTRACEF("common_config %p\n", mmio_regs_.common_config);
break;
}
case VIRTIO_PCI_CAP_NOTIFY_CFG: {
MapBar(cap.bar);
mmio_regs_.notify_base = (volatile uint16_t*)((uintptr_t)bar_[cap.bar].mmio_base + cap.offset);
uint8_t notify_mul_off = static_cast<uint8_t>(off + offsetof(virtio_pci_notify_cap, notify_off_multiplier));
mmio_regs_.notify_mul = pci_config_read32(&pci_, notify_mul_off);
LTRACEF("notify_base %p\n", mmio_regs_.notify_base);
LTRACEF("notify_mul %x\n", mmio_regs_.notify_mul);
break;
}
case VIRTIO_PCI_CAP_ISR_CFG: {
MapBar(cap.bar);
mmio_regs_.isr_status = (volatile uint32_t*)((uintptr_t)bar_[cap.bar].mmio_base + cap.offset);
LTRACEF("isr_status %p\n", mmio_regs_.isr_status);
break;
}
case VIRTIO_PCI_CAP_DEVICE_CFG: {
MapBar(cap.bar);
mmio_regs_.device_config = (volatile void*)((uintptr_t)bar_[cap.bar].mmio_base + cap.offset);
LTRACEF("device_config %p\n", mmio_regs_.device_config);
break;
}
}
}
// if we've found mmio pointers to everything from the capability structure, then skip mapping bar0, since we don't
// need legacy pio access from BAR0
if (!(mmio_regs_.common_config && mmio_regs_.notify_base && mmio_regs_.isr_status && mmio_regs_.device_config)) {
// transitional devices have a single PIO window at BAR0
if (pci_config_->base_addresses[0] & 0x1) {
// look at BAR0, which should be a PIO memory window
bar0_pio_base_ = pci_config->base_addresses[0];
LTRACEF("BAR0 address %#x\n", bar0_pio_base_);
if ((bar0_pio_base_ & 0x1) == 0) {
VIRTIO_ERROR("bar 0 does not appear to be PIO (address %#x, aborting\n", bar0_pio_base_);
return -1;
}
bar0_pio_base_ &= ~1;
if (bar0_pio_base_ > 0xffff) {
bar0_pio_base_ = 0;
r = MapBar(0);
if (r != MX_OK) {
VIRTIO_ERROR("cannot mmap io %d\n", r);
return r;
}
LTRACEF("bar_[0].mmio_base %p\n", bar_[0].mmio_base);
} else {
// this is probably PIO
r = mx_mmap_device_io(get_root_resource(), bar0_pio_base_, bar0_size_);
if (r != MX_OK) {
VIRTIO_ERROR("failed to access PIO range %#x, length %#xw\n", bar0_pio_base_, bar0_size_);
return r;
}
}
// enable pio access
if ((r = pci_enable_pio(&pci_, true)) < 0) {
VIRTIO_ERROR("cannot enable PIO %d\n", r);
// if we've found mmio pointers to everything from the capability structure,
// then skip mapping bar0, since we don't need legacy pio access from BAR0
if (!(mmio_regs_.common_config && mmio_regs_.notify_base &&
mmio_regs_.isr_status && mmio_regs_.device_config)) {
mx_pci_resource_t bar0;
r = MapBar(0);
if (r == MX_OK) {
LTRACEF("bar_[0].mmio_base %p\n", bar_[0].mmio_base);
} else if (r == MX_ERR_NOT_FOUND) {
r = pci_get_resource(&pci_, PCI_RESOURCE_BAR_0, &bar0);
if (r != MX_OK || bar0.type != PCI_RESOURCE_TYPE_PIO) {
VIRTIO_ERROR("failed to get PIO BAR0: %d\n", r);
return -1;
}
bar0_pio_base_ = static_cast<uint32_t>(bar0.pio_addr);
LTRACEF("Using PIO bar0, base: %d\n", bar0_pio_base_);
} else {
LTRACEF("Failed to do the bar0 thing\n");
}
}
+2 -3
Ver Arquivo
@@ -23,7 +23,7 @@ public:
mx_device_t* bus_device() { return bus_device_; }
mx_device_t* device() { return device_; }
virtual mx_status_t Bind(pci_protocol_t*, mx_handle_t pci_config_handle, const pci_config_t*);
virtual mx_status_t Bind(pci_protocol_t*, mx_pcie_device_info_t info);
virtual mx_status_t Init() = 0;
virtual void Unbind();
virtual void Release();
@@ -60,9 +60,8 @@ protected:
// handles to pci bits
pci_protocol_t pci_ = { nullptr, nullptr };
mx::handle pci_config_handle_ = {};
const pci_config_t* pci_config_ = nullptr;
mx::handle irq_handle_ = {};
mx_pcie_device_info_t info_;
// bar0 memory map or PIO
uint32_t bar0_pio_base_ = 0;
+7 -14
Ver Arquivo
@@ -10,18 +10,15 @@
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/protocol/pci.h>
#include <mxtl/alloc_checker.h>
#include <mxtl/unique_ptr.h>
#include <magenta/compiler.h>
#include <magenta/types.h>
#include "block.h"
#include "device.h"
#include "trace.h"
#include "ethernet.h"
#include "gpu.h"
#include "trace.h"
#define LOCAL_TRACE 0
@@ -38,22 +35,18 @@ extern "C" mx_status_t virtio_bind(void* ctx, mx_device_t* device, void** cookie
return -1;
}
const pci_config_t* config;
size_t config_size;
mx_handle_t config_handle = MX_HANDLE_INVALID;
status = pci_map_resource(&pci, PCI_RESOURCE_CONFIG, MX_CACHE_POLICY_UNCACHED_DEVICE,
(void**)&config, &config_size, &config_handle);
mx_pcie_device_info_t info;
status = pci_get_device_info(&pci, &info);
if (status != MX_OK) {
TRACEF("failed to grab config handle\n");
return status;
}
LTRACEF("pci %p\n", &pci);
LTRACEF("0x%x:0x%x\n", config->vendor_id, config->device_id);
LTRACEF("0x%x:0x%x\n", info.vendor_id, info.device_id);
// TODO: Make symbols for these constants and reuse in the BIND protocol.
// XXX TODO: Make symbols for these constants and reuse in the BIND protocol.
mxtl::unique_ptr<virtio::Device> vd = nullptr;
switch (config->device_id) {
switch (info.device_id) {
case 0x1000:
LTRACEF("found net device\n");
vd.reset(new virtio::EthernetDevice(device));
@@ -73,7 +66,7 @@ extern "C" mx_status_t virtio_bind(void* ctx, mx_device_t* device, void** cookie
}
LTRACEF("calling Bind on driver\n");
status = vd->Bind(&pci, config_handle, config);
status = vd->Bind(&pci, info);
if (status != MX_OK)
return status;
+4
Ver Arquivo
@@ -566,6 +566,10 @@ syscall pci_reset_device
(handle: mx_handle_t)
returns (mx_status_t);
syscall pci_config_read
(handle: mx_handle_t, offset: uint16_t, width: size_t, out_val: uint32_t[1] OUT)
returns (mx_status_t);
syscall pci_cfg_pio_rw
(handle: mx_handle_t, bus: uint8_t, dev: uint8_t, func: uint8_t, offset: uint8_t,
val: uint32_t[1] OUT, width: size_t, write: bool)
+64
Ver Arquivo
@@ -28,7 +28,41 @@ enum pci_resource_ids {
PCI_RESOURCE_COUNT,
};
enum pci_header_fields {
kPciCfgVendorId = 0x00,
kPciCfgDeviceId = 0x02,
kPciCfgRevisionId = 0x08,
kPciCfgClassCode = 0x09,
kPciCfgSubsystemVendorId = 0x2C,
kPciCfgSubsystemId = 0x2E,
kPciCfgCapabilitiesPtr = 0x34,
};
enum pci_cap_types {
kPciCapIdNull = 0x00,
kPciCapIdPciPwrMgmt = 0x01,
kPciCapIdAgp = 0x02,
kPciCapIdVpd = 0x03,
kPciCapIdMsi = 0x05,
kPciCapIdPcix = 0x07,
kPciCapIdHypertransport = 0x08,
kPciCapIdVendor = 0x09,
kPciCapIdDebugPort = 0x0A,
kPciCapIdCompactPciCrc = 0x0B,
kPciCapIdPciHotplug = 0x0C,
kPciCapIdPciBridgeSubsystemVid = 0x0D,
kPciCapIdAgp8x = 0x0E,
kPciCapIdSecureDevice = 0x0F,
kPciCapIdPciExpress = 0x10,
kPciCapIdMsix = 0x11,
kPciCapIdSataDataNdxCfg = 0x12,
kPciCapIdAdvancedFeatures = 0x13,
PciCapIdEnhancedAllocation = 0x14,
};
typedef struct pci_protocol_ops {
mx_status_t (*get_resource)(void* ctx, uint32_t res_id, mx_pci_resource_t* out_res);
mx_status_t (*map_resource)(void* ctx, uint32_t res_id, uint32_t cache_policy,
void** vaddr, size_t* size, mx_handle_t* out_handle);
mx_status_t (*enable_bus_master)(void* ctx, bool enable);
@@ -40,6 +74,8 @@ typedef struct pci_protocol_ops {
mx_status_t (*set_irq_mode)(void* ctx, mx_pci_irq_mode_t mode,
uint32_t requested_irq_count);
mx_status_t (*get_device_info)(void* ctx, mx_pcie_device_info_t* out_info);
uint32_t (*config_read)(void* ctx, uint8_t offset, size_t width);
uint8_t (*get_next_capability)(void* ctx, uint8_t type, uint8_t offset);
} pci_protocol_ops_t;
typedef struct pci_protocol {
@@ -47,6 +83,11 @@ typedef struct pci_protocol {
void* ctx;
} pci_protocol_t;
static inline mx_status_t pci_get_resource(pci_protocol_t* pci, uint32_t res_id,
mx_pci_resource_t* out_info) {
return pci->ops->get_resource(pci->ctx, res_id, out_info);
}
static inline mx_status_t pci_map_resource(pci_protocol_t* pci, uint32_t res_id,
uint32_t cache_policy, void** vaddr, size_t* size,
mx_handle_t* out_handle) {
@@ -85,4 +126,27 @@ static inline mx_status_t pci_get_device_info(pci_protocol_t* pci,
return pci->ops->get_device_info(pci->ctx, out_info);
}
static inline uint8_t pci_config_read8(pci_protocol_t* pci, uint8_t offset) {
return (uint8_t)(pci->ops->config_read(pci->ctx, offset, 8u) & 0XFF);
}
static inline uint16_t pci_config_read16(pci_protocol_t* pci, uint8_t offset) {
return (uint16_t)(pci->ops->config_read(pci->ctx, offset, 16u) & 0xFFFF);
}
static inline uint32_t pci_config_read32(pci_protocol_t* pci, uint8_t offset) {
return pci->ops->config_read(pci->ctx, offset, 32u);
}
static uint8_t pci_get_next_capability(pci_protocol_t* pci, uint8_t type, uint8_t offset) {
return pci->ops->get_next_capability(pci->ctx, type, offset);
}
static uint8_t pci_get_first_capability(pci_protocol_t* pci, uint8_t type) {
// the next_capability method will always look at the second byte next
// pointer to fetch the next capability. By offsetting the CapPtr field
// by -1 we can pretend we're working with a normal capability entry
return pci_get_next_capability(pci, kPciCfgCapabilitiesPtr - 1u, type);
}
__END_CDECLS;