Added skeleton XHCI driver by Jian Chiang, based on the specs and the EHCI driver.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41142 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
c152c70b46
commit
18451a0c36
@ -25,3 +25,10 @@ KernelAddon <usb>ehci :
|
||||
: libusb.a
|
||||
: ehci.rdef
|
||||
;
|
||||
|
||||
KernelAddon <usb>xhci :
|
||||
xhci.cpp
|
||||
: libusb.a
|
||||
: xhci.rdef
|
||||
;
|
||||
|
||||
|
453
src/add-ons/kernel/busses/usb/xhci.cpp
Normal file
453
src/add-ons/kernel/busses/usb/xhci.cpp
Normal file
@ -0,0 +1,453 @@
|
||||
/*
|
||||
* Copyright 2006-2011, Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Some code borrowed from the Haiku EHCI driver
|
||||
*
|
||||
* Authors:
|
||||
* Michael Lotz <mmlr@mlotz.ch>
|
||||
* Jian Chiang <j.jian.chiang@gmail.com>
|
||||
*/
|
||||
|
||||
|
||||
#include <module.h>
|
||||
#include <PCI.h>
|
||||
#include <USB3.h>
|
||||
#include <KernelExport.h>
|
||||
|
||||
#define TRACE_USB
|
||||
#include "xhci.h"
|
||||
|
||||
#define USB_MODULE_NAME "xhci"
|
||||
|
||||
pci_module_info *XHCI::sPCIModule = NULL;
|
||||
|
||||
|
||||
static int32
|
||||
xhci_std_ops(int32 op, ...)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
TRACE_MODULE("xhci init module\n");
|
||||
return B_OK;
|
||||
case B_MODULE_UNINIT:
|
||||
TRACE_MODULE("xhci uninit module\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
|
||||
usb_host_controller_info xhci_module = {
|
||||
{
|
||||
"busses/usb/xhci",
|
||||
0,
|
||||
xhci_std_ops
|
||||
},
|
||||
NULL,
|
||||
XHCI::AddTo
|
||||
};
|
||||
|
||||
|
||||
module_info *modules[] = {
|
||||
(module_info *)&xhci_module,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
XHCI::XHCI(pci_info *info, Stack *stack)
|
||||
: BusManager(stack),
|
||||
fCapabilityRegisters(NULL),
|
||||
fOperationalRegisters(NULL),
|
||||
fRegisterArea(-1),
|
||||
fPCIInfo(info),
|
||||
fStack(stack),
|
||||
fPortCount(0)
|
||||
{
|
||||
if (BusManager::InitCheck() < B_OK) {
|
||||
TRACE_ERROR("bus manager failed to init\n");
|
||||
return;
|
||||
}
|
||||
|
||||
TRACE("constructing new XHCI host controller driver\n");
|
||||
fInitOK = false;
|
||||
|
||||
// enable busmaster and memory mapped access
|
||||
uint16 command = sPCIModule->read_pci_config(fPCIInfo->bus,
|
||||
fPCIInfo->device, fPCIInfo->function, PCI_command, 2);
|
||||
command &= ~PCI_command_io;
|
||||
command |= PCI_command_master | PCI_command_memory;
|
||||
|
||||
sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device,
|
||||
fPCIInfo->function, PCI_command, 2, command);
|
||||
|
||||
// map the registers
|
||||
uint32 offset = fPCIInfo->u.h0.base_registers[0] & (B_PAGE_SIZE - 1);
|
||||
addr_t physicalAddress = fPCIInfo->u.h0.base_registers[0] - offset;
|
||||
size_t mapSize = (fPCIInfo->u.h0.base_register_sizes[0] + offset
|
||||
+ B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
|
||||
|
||||
TRACE("map physical memory 0x%08lx (base: 0x%08lx; offset: %lx); size: %ld\n",
|
||||
fPCIInfo->u.h0.base_registers[0], physicalAddress, offset,
|
||||
fPCIInfo->u.h0.base_register_sizes[0]);
|
||||
|
||||
fRegisterArea = map_physical_memory("EHCI memory mapped registers",
|
||||
physicalAddress, mapSize, B_ANY_KERNEL_BLOCK_ADDRESS,
|
||||
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA,
|
||||
(void **)&fCapabilityRegisters);
|
||||
if (fRegisterArea < B_OK) {
|
||||
TRACE("failed to map register memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fCapabilityRegisters += offset;
|
||||
fOperationalRegisters = fCapabilityRegisters + ReadCapReg8(XHCI_CAPLENGTH);
|
||||
fRuntimeRegisters = fCapabilityRegisters + ReadCapReg8(XHCI_RTSOFF);
|
||||
TRACE("mapped capability registers: 0x%08lx\n", (uint32)fCapabilityRegisters);
|
||||
TRACE("mapped operational registers: 0x%08lx\n", (uint32)fOperationalRegisters);
|
||||
TRACE("mapped rumtime registers: 0x%08lx\n", (uint32)fRuntimeRegisters);
|
||||
|
||||
TRACE("structural parameters1: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS1));
|
||||
TRACE("structural parameters2: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS2));
|
||||
TRACE("structural parameters3: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS3));
|
||||
TRACE("capability parameters: 0x%08lx\n", ReadCapReg32(XHCI_HCCPARAMS));
|
||||
|
||||
// read port count from capability register
|
||||
fPortCount = (ReadCapReg32(XHCI_HCSPARAMS1) >> 24) & 0x7f;
|
||||
|
||||
uint32 extendedCapPointer = ((ReadCapReg32(XHCI_HCCPARAMS) >> 16) & 0xffff)
|
||||
<< 2;
|
||||
if (extendedCapPointer > 0) {
|
||||
TRACE("extended capabilities register at %ld\n", extendedCapPointer);
|
||||
|
||||
uint32 legacySupport = ReadCapReg32(extendedCapPointer);
|
||||
if ((legacySupport & XHCI_LEGSUP_CAPID_MASK) == XHCI_LEGSUP_CAPID) {
|
||||
if ((legacySupport & XHCI_LEGSUP_BIOSOWNED) != 0) {
|
||||
TRACE_ALWAYS("the host controller is bios owned, claiming"
|
||||
" ownership\n");
|
||||
WriteCapReg32(extendedCapPointer, legacySupport
|
||||
| XHCI_LEGSUP_OSOWNED);
|
||||
for (int32 i = 0; i < 20; i++) {
|
||||
legacySupport = ReadCapReg32(extendedCapPointer);
|
||||
|
||||
if ((legacySupport & XHCI_LEGSUP_BIOSOWNED) == 0)
|
||||
break;
|
||||
|
||||
TRACE_ALWAYS("controller is still bios owned, waiting\n");
|
||||
snooze(50000);
|
||||
}
|
||||
}
|
||||
|
||||
if (legacySupport & XHCI_LEGSUP_BIOSOWNED) {
|
||||
TRACE_ERROR("bios won't give up control over the host "
|
||||
"controller (ignoring)\n");
|
||||
} else if (legacySupport & XHCI_LEGSUP_OSOWNED) {
|
||||
TRACE_ALWAYS("successfully took ownership of the host "
|
||||
"controller\n");
|
||||
}
|
||||
|
||||
// Force off the BIOS owned flag, and clear all SMIs. Some BIOSes
|
||||
// do indicate a successful handover but do not remove their SMIs
|
||||
// and then freeze the system when interrupts are generated.
|
||||
WriteCapReg32(extendedCapPointer, legacySupport & ~XHCI_LEGSUP_BIOSOWNED);
|
||||
WriteCapReg32(extendedCapPointer + XHCI_LEGCTLSTS,
|
||||
XHCI_LEGCTLSTS_DISABLE_SMI);
|
||||
} else {
|
||||
TRACE("extended capability is not a legacy support register\n");
|
||||
}
|
||||
} else {
|
||||
TRACE("no extended capabilities register\n");
|
||||
}
|
||||
|
||||
// halt the host controller
|
||||
if (ControllerHalt() < B_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
// reset the host controller
|
||||
if (ControllerReset() < B_OK) {
|
||||
TRACE_ERROR("host controller failed to reset\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fInitOK = true;
|
||||
TRACE("XHCI host controller driver constructed\n");
|
||||
}
|
||||
|
||||
|
||||
XHCI::~XHCI()
|
||||
{
|
||||
TRACE("tear down XHCI host controller driver\n");
|
||||
|
||||
WriteOpReg(XHCI_CMD, 0);
|
||||
|
||||
delete_area(fRegisterArea);
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::Start()
|
||||
{
|
||||
TRACE("starting XHCI host controller\n");
|
||||
TRACE("usbcmd: 0x%08lx; usbsts: 0x%08lx\n", ReadOpReg(XHCI_CMD),
|
||||
ReadOpReg(XHCI_STS));
|
||||
|
||||
TRACE_ALWAYS("successfully started the controller\n");
|
||||
return BusManager::Start();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::SubmitTransfer(Transfer *transfer)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::NotifyPipeChange(Pipe *pipe, usb_change change)
|
||||
{
|
||||
TRACE("pipe change %d for pipe %p\n", change, pipe);
|
||||
switch (change) {
|
||||
case USB_CHANGE_CREATED:
|
||||
case USB_CHANGE_DESTROYED: {
|
||||
// ToDo: we should create and keep a single queue head
|
||||
// for all transfers to/from this pipe
|
||||
break;
|
||||
}
|
||||
|
||||
case USB_CHANGE_PIPE_POLICY_CHANGED: {
|
||||
// ToDo: for isochronous pipes we might need to adapt to new
|
||||
// pipe policy settings here
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::AddTo(Stack *stack)
|
||||
{
|
||||
#ifdef TRACE_USB
|
||||
set_dprintf_enabled(true);
|
||||
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
|
||||
load_driver_symbols("xhci");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!sPCIModule) {
|
||||
status_t status = get_module(B_PCI_MODULE_NAME,
|
||||
(module_info **)&sPCIModule);
|
||||
if (status < B_OK) {
|
||||
TRACE_MODULE_ERROR("getting pci module failed! 0x%08lx\n", status);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_MODULE("searching devices\n");
|
||||
bool found = false;
|
||||
pci_info *item = new(std::nothrow) pci_info;
|
||||
if (!item) {
|
||||
sPCIModule = NULL;
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (int32 i = 0; sPCIModule->get_nth_pci_info(i, item) >= B_OK; i++) {
|
||||
if (item->class_base == PCI_serial_bus && item->class_sub == PCI_usb
|
||||
&& item->class_api == PCI_usb_xhci) {
|
||||
if (item->u.h0.interrupt_line == 0
|
||||
|| item->u.h0.interrupt_line == 0xFF) {
|
||||
TRACE_MODULE_ERROR("found device with invalid IRQ - check IRQ "
|
||||
"assignment\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
TRACE_MODULE("found device at IRQ %u\n",
|
||||
item->u.h0.interrupt_line);
|
||||
XHCI *bus = new(std::nothrow) XHCI(item, stack);
|
||||
if (!bus) {
|
||||
delete item;
|
||||
sPCIModule = NULL;
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (bus->InitCheck() < B_OK) {
|
||||
TRACE_MODULE_ERROR("bus failed init check\n");
|
||||
delete bus;
|
||||
continue;
|
||||
}
|
||||
|
||||
// the bus took it away
|
||||
item = new(std::nothrow) pci_info;
|
||||
|
||||
bus->Start();
|
||||
stack->AddBusManager(bus);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
TRACE_MODULE_ERROR("no devices found\n");
|
||||
delete item;
|
||||
sPCIModule = NULL;
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
delete item;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::GetPortStatus(uint8 index, usb_port_status *status)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::SetPortFeature(uint8 index, uint16 feature)
|
||||
{
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::ClearPortFeature(uint8 index, uint16 feature)
|
||||
{
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::ResetPort(uint8 index)
|
||||
{
|
||||
TRACE("reset port %d\n", index);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::SuspendPort(uint8 index)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::ControllerHalt()
|
||||
{
|
||||
WriteOpReg(XHCI_CMD, 0);
|
||||
|
||||
int32 tries = 100;
|
||||
while ((ReadOpReg(XHCI_STS) & STS_HCH) == 0) {
|
||||
snooze(1000);
|
||||
if (tries-- < 0)
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::ControllerReset()
|
||||
{
|
||||
WriteOpReg(XHCI_CMD, CMD_HCRST);
|
||||
|
||||
int32 tries = 100;
|
||||
while (ReadOpReg(XHCI_CMD) & CMD_HCRST) {
|
||||
snooze(1000);
|
||||
if (tries-- < 0)
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
tries = 100;
|
||||
while (ReadOpReg(XHCI_STS) & STS_CNR) {
|
||||
snooze(1000);
|
||||
if (tries-- < 0)
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::LightReset()
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
XHCI::InterruptHandler(void *data)
|
||||
{
|
||||
return ((XHCI *)data)->Interrupt();
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
XHCI::Interrupt()
|
||||
{
|
||||
int32 result = B_HANDLED_INTERRUPT;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void
|
||||
XHCI::WriteOpReg(uint32 reg, uint32 value)
|
||||
{
|
||||
*(volatile uint32 *)(fOperationalRegisters + reg) = value;
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
XHCI::ReadOpReg(uint32 reg)
|
||||
{
|
||||
return *(volatile uint32 *)(fOperationalRegisters + reg);
|
||||
}
|
||||
|
||||
|
||||
inline uint8
|
||||
XHCI::ReadCapReg8(uint32 reg)
|
||||
{
|
||||
return *(volatile uint8 *)(fCapabilityRegisters + reg);
|
||||
}
|
||||
|
||||
|
||||
inline uint16
|
||||
XHCI::ReadCapReg16(uint32 reg)
|
||||
{
|
||||
return *(volatile uint16 *)(fCapabilityRegisters + reg);
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
XHCI::ReadCapReg32(uint32 reg)
|
||||
{
|
||||
return *(volatile uint32 *)(fCapabilityRegisters + reg);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
XHCI::WriteCapReg32(uint32 reg, uint32 value)
|
||||
{
|
||||
*(volatile uint32 *)(fCapabilityRegisters + reg) = value;
|
||||
}
|
||||
|
83
src/add-ons/kernel/busses/usb/xhci.h
Normal file
83
src/add-ons/kernel/busses/usb/xhci.h
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2006-2011, Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Michael Lotz <mmlr@mlotz.ch>
|
||||
* Jian Chiang <j.jian.chiang@gmail.com>
|
||||
*/
|
||||
#ifndef XHCI_H
|
||||
#define XHCI_H
|
||||
|
||||
|
||||
#include "usb_private.h"
|
||||
#include "xhci_hardware.h"
|
||||
|
||||
|
||||
struct pci_info;
|
||||
struct pci_module_info;
|
||||
|
||||
|
||||
class XHCI : public BusManager {
|
||||
public:
|
||||
XHCI(pci_info *info, Stack *stack);
|
||||
~XHCI();
|
||||
|
||||
status_t Start();
|
||||
virtual status_t SubmitTransfer(Transfer *transfer);
|
||||
virtual status_t CancelQueuedTransfers(Pipe *pipe, bool force);
|
||||
|
||||
virtual status_t NotifyPipeChange(Pipe *pipe,
|
||||
usb_change change);
|
||||
|
||||
static status_t AddTo(Stack *stack);
|
||||
|
||||
// Port operations for root hub
|
||||
uint8 PortCount() { return fPortCount; };
|
||||
status_t GetPortStatus(uint8 index, usb_port_status *status);
|
||||
status_t SetPortFeature(uint8 index, uint16 feature);
|
||||
status_t ClearPortFeature(uint8 index, uint16 feature);
|
||||
|
||||
status_t ResetPort(uint8 index);
|
||||
status_t SuspendPort(uint8 index);
|
||||
|
||||
virtual const char * TypeName() const { return "xhci"; };
|
||||
|
||||
private:
|
||||
// Controller resets
|
||||
status_t ControllerReset();
|
||||
status_t ControllerHalt();
|
||||
status_t LightReset();
|
||||
|
||||
// Interrupt functions
|
||||
static int32 InterruptHandler(void *data);
|
||||
int32 Interrupt();
|
||||
|
||||
|
||||
// Operational register functions
|
||||
inline void WriteOpReg(uint32 reg, uint32 value);
|
||||
inline uint32 ReadOpReg(uint32 reg);
|
||||
|
||||
// Capability register functions
|
||||
inline uint8 ReadCapReg8(uint32 reg);
|
||||
inline uint16 ReadCapReg16(uint32 reg);
|
||||
inline uint32 ReadCapReg32(uint32 reg);
|
||||
inline void WriteCapReg32(uint32 reg, uint32 value);
|
||||
|
||||
static pci_module_info * sPCIModule;
|
||||
|
||||
uint8 * fCapabilityRegisters;
|
||||
uint8 * fOperationalRegisters;
|
||||
uint8 * fRuntimeRegisters;
|
||||
area_id fRegisterArea;
|
||||
pci_info * fPCIInfo;
|
||||
Stack * fStack;
|
||||
|
||||
// Root Hub
|
||||
|
||||
// Port management
|
||||
uint8 fPortCount;
|
||||
};
|
||||
|
||||
|
||||
#endif // !XHCI_H
|
15
src/add-ons/kernel/busses/usb/xhci.rdef
Normal file
15
src/add-ons/kernel/busses/usb/xhci.rdef
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* xhci.rdef
|
||||
*/
|
||||
|
||||
resource app_signature "application/x-vnd.haiku-xhci";
|
||||
|
||||
resource app_version {
|
||||
major = 1,
|
||||
middle = 0,
|
||||
minor = 0,
|
||||
variety = 0,
|
||||
internal = 0,
|
||||
short_info = "XHCI host controller driver",
|
||||
long_info = "Haiku XHCI HCD - Copyright 2011, Haiku Inc."
|
||||
};
|
50
src/add-ons/kernel/busses/usb/xhci_hardware.h
Normal file
50
src/add-ons/kernel/busses/usb/xhci_hardware.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2011, Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Jian Chiang <j.jian.chiang@gmail.com>
|
||||
*/
|
||||
#ifndef XHCI_HARDWARE_H
|
||||
#define XHCI_HARDWARE_H
|
||||
|
||||
|
||||
// Host Controller Capability Registers
|
||||
#define XHCI_CAPLENGTH 0x00 // Capability Register Length
|
||||
#define XHCI_HCIVERSION 0x02 // Interface Version Number
|
||||
#define XHCI_HCSPARAMS1 0x04 // Structural Parameters 1
|
||||
#define XHCI_HCSPARAMS2 0x08 // Structural Parameters 2
|
||||
#define XHCI_HCSPARAMS3 0x0C // Structural Parameters 3
|
||||
#define XHCI_HCCPARAMS 0x10 // Capability Parameters
|
||||
#define XHCI_RTSOFF 0x18 // Runtime Register Space offset
|
||||
|
||||
|
||||
// Host Controller Operational Registers
|
||||
#define XHCI_CMD 0x00 // USB Command
|
||||
#define XHCI_STS 0x04 // USB Status
|
||||
|
||||
|
||||
// USB Command Register
|
||||
#define CMD_HCRST (1 << 1) // Host Controller Reset
|
||||
|
||||
|
||||
// USB Status Register
|
||||
#define STS_HCH (1<<0)
|
||||
#define STS_CNR (1<<11)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Extended Capabilities
|
||||
#define XHCI_LEGSUP_CAPID_MASK 0xff
|
||||
#define XHCI_LEGSUP_CAPID 0x01
|
||||
#define XHCI_LEGSUP_OSOWNED (1 << 24) // OS Owned Semaphore
|
||||
#define XHCI_LEGSUP_BIOSOWNED (1 << 16) // BIOS Owned Semaphore
|
||||
|
||||
#define XHCI_LEGCTLSTS 0x04
|
||||
#define XHCI_LEGCTLSTS_DISABLE_SMI ((0x3 << 1) + (0xff << 5) + (0x7 << 17))
|
||||
|
||||
|
||||
#endif // !XHCI_HARDWARE_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user