mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2024-11-23 08:26:23 -06:00
Take ownership of all USB controllers before probing for devices. (#167)
When two controllers are attached to a physical port (e.g. in the case of EHCI and its companion controllers, problems can occur if the BIOS still has control of one controller when we try to use the other one. So perform a first pass to scan the PCI bus and take ownership of and reset all the controllers we find, and perform a second pass to initialise the controllers and probe for attached devices. As we don't support hot plugging, split the second pass into two, with the first probing the EHCI controllers and handing over any low and full speed devices to the companion controllers, and the second probing the remaining controller types.
This commit is contained in:
parent
ddbee66c85
commit
407fb811c2
@ -486,7 +486,7 @@ static const hcd_methods_t methods = {
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
bool ehci_reset(int bus, int dev, int func, uintptr_t base_addr)
|
||||
{
|
||||
ehci_cap_regs_t *cap_regs = (ehci_cap_regs_t *)base_addr;
|
||||
|
||||
@ -513,6 +513,15 @@ bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
if (!halt_host_controller(op_regs)) return false;
|
||||
if (!reset_host_controller(op_regs)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ehci_probe(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
{
|
||||
ehci_cap_regs_t *cap_regs = (ehci_cap_regs_t *)base_addr;
|
||||
|
||||
ehci_op_regs_t *op_regs = (ehci_op_regs_t *)(base_addr + cap_regs->cap_length);
|
||||
|
||||
// Record the heap state to allow us to free memory.
|
||||
uintptr_t initial_heap_mark = heap_mark(HEAP_TYPE_LM_1);
|
||||
|
||||
|
@ -15,12 +15,35 @@
|
||||
#include "usbhcd.h"
|
||||
|
||||
/**
|
||||
* Initialises the EHCI device identified by bus, dev, func, and base_addr,
|
||||
* scans all the attached USB devices, and configures any HID USB keyboard
|
||||
* devices it finds to generate periodic interrupt transfers that report key
|
||||
* presses. Initialises hcd and returns true if the device was successfully
|
||||
* initialised and one or more keyboards were found.
|
||||
* If necessary, takes ownership of the EHCI device at the specified base
|
||||
* address, then resets it.
|
||||
*
|
||||
* \param bus - the PCI bus number for accessing the device
|
||||
* \param dev - the PCI device number for accessing the device
|
||||
* \param func - the PCI function number for accessing the device
|
||||
* \param base_addr - the base address of the device in virtual memory
|
||||
*
|
||||
* \returns
|
||||
* true if ownership was acquired and the device was successfully reset,
|
||||
* otherwise false.
|
||||
*/
|
||||
bool ehci_init(int bus, int dev, int func, uintptr_t base_addr, usb_hcd_t *hcd);
|
||||
bool ehci_reset(int bus, int dev, int func, uintptr_t base_addr);
|
||||
|
||||
/**
|
||||
* Initialises the EHCI device at the specified base address, probes all
|
||||
* the attached USB devices, and configures any HID USB keyboard devices
|
||||
* it finds to generate periodic interrupt transfers that report key
|
||||
* presses. If successful, initialises the specified host controller
|
||||
* driver object accordingly.
|
||||
*
|
||||
* \param base_addr - the base address of the device in virtual memory
|
||||
* \param hcd - a pointer to a pre-allocated host controller
|
||||
* driver object that can be used for this device
|
||||
*
|
||||
* \returns
|
||||
* true if the device was successfully initialised and one or more
|
||||
* keyboards were found, otherwise false.
|
||||
*/
|
||||
bool ehci_probe(uintptr_t base_addr, usb_hcd_t *hcd);
|
||||
|
||||
#endif // EHCI_H
|
||||
|
@ -243,6 +243,36 @@ typedef struct {
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static bool reset_host_controller(ohci_op_regs_t *op_regs)
|
||||
{
|
||||
// Prepare for host controller setup (see section 5.1.1.3 of the OHCI spec.).
|
||||
switch (read32(&op_regs->control) & OHCI_CTRL_HCFS) {
|
||||
case OHCI_CTRL_HCFS_RST:
|
||||
usleep(50*MILLISEC);
|
||||
break;
|
||||
case OHCI_CTRL_HCFS_SUS:
|
||||
case OHCI_CTRL_HCFS_RES:
|
||||
flush32(&op_regs->control, OHCI_CTRL_HCFS_RES);
|
||||
usleep(20*MILLISEC);
|
||||
break;
|
||||
default: // operational
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset the host controller.
|
||||
write32(&op_regs->command_status, OHCI_CMD_HCR);
|
||||
if (!wait_until_clr(&op_regs->command_status, OHCI_CMD_HCR, 30)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check we are now in SUSPEND state.
|
||||
if ((read32(&op_regs->control) & OHCI_CTRL_HCFS) != OHCI_CTRL_HCFS_SUS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool reset_ohci_port(ohci_op_regs_t *op_regs, int port_idx)
|
||||
{
|
||||
// The OHCI reset lasts for 10ms, but the USB specification calls for 50ms (but not necessarily continuously).
|
||||
@ -406,7 +436,7 @@ static const hcd_methods_t methods = {
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool ohci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
bool ohci_reset(uintptr_t base_addr)
|
||||
{
|
||||
ohci_op_regs_t *op_regs = (ohci_op_regs_t *)base_addr;
|
||||
|
||||
@ -424,6 +454,20 @@ bool ohci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the controller, but preserve the frame interval set by the SMM or BIOS.
|
||||
uint32_t fm_interval = read32(&op_regs->fm_interval);
|
||||
if (!reset_host_controller(op_regs)) {
|
||||
return false;
|
||||
}
|
||||
write32(&op_regs->fm_interval, fm_interval);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ohci_probe(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
{
|
||||
ohci_op_regs_t *op_regs = (ohci_op_regs_t *)base_addr;
|
||||
|
||||
// Preserve the frame interval set by the SMM or BIOS.
|
||||
// If not set, use the default value.
|
||||
uint32_t frame_interval = read32(&op_regs->fm_interval) & 0x3fff;
|
||||
@ -431,28 +475,10 @@ bool ohci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
frame_interval = 0x2edf;
|
||||
}
|
||||
|
||||
// Prepare for host controller setup (see section 5.1.1.3 of the OHCI spec.).
|
||||
switch (read32(&op_regs->control) & OHCI_CTRL_HCFS) {
|
||||
case OHCI_CTRL_HCFS_RST:
|
||||
usleep(50*MILLISEC);
|
||||
break;
|
||||
case OHCI_CTRL_HCFS_SUS:
|
||||
case OHCI_CTRL_HCFS_RES:
|
||||
flush32(&op_regs->control, OHCI_CTRL_HCFS_RES);
|
||||
usleep(10*MILLISEC);
|
||||
break;
|
||||
default: // operational
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset the host controller.
|
||||
write32(&op_regs->command_status, OHCI_CMD_HCR);
|
||||
if (!wait_until_clr(&op_regs->command_status, OHCI_CMD_HCR, 30)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check we are now in SUSPEND state.
|
||||
if ((read32(&op_regs->control) & OHCI_CTRL_HCFS) != OHCI_CTRL_HCFS_SUS) {
|
||||
// We will have already reset the controller, but can't guarantee to get
|
||||
// here within the 2ms time limit for moving directly from suspend state
|
||||
// to operational state. So reset it again.
|
||||
if (!reset_host_controller(op_regs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -15,12 +15,32 @@
|
||||
#include "usbhcd.h"
|
||||
|
||||
/**
|
||||
* Initialises the OHCI device found at base_addr, scans all the attached USB
|
||||
* devices, and configures any HID USB keyboard devices it finds to generate
|
||||
* periodic interrupt transfers that report key presses. Initialises hcd and
|
||||
* returns true if the device was successfully initialised and one or more
|
||||
* keyboards were found.
|
||||
* If necessary, takes ownership of the OHCI device at the specified base
|
||||
* address, then resets it.
|
||||
*
|
||||
* \param base_addr - the base address of the device in virtual memory
|
||||
*
|
||||
* \returns
|
||||
* true if ownership was acquired and the device was successfully reset,
|
||||
* otherwise false.
|
||||
*/
|
||||
bool ohci_init(uintptr_t base_addr, usb_hcd_t *hcd);
|
||||
bool ohci_reset(uintptr_t base_addr);
|
||||
|
||||
/**
|
||||
* Initialises the OHCI device at the specified base address, probes all
|
||||
* the attached USB devices, and configures any HID USB keyboard devices
|
||||
* it finds to generate periodic interrupt transfers that report key
|
||||
* presses. If successful, initialises the specified host controller
|
||||
* driver object accordingly.
|
||||
*
|
||||
* \param base_addr - the base address of the device in virtual memory
|
||||
* \param hcd - a pointer to a pre-allocated host controller
|
||||
* driver object that can be used for this device
|
||||
*
|
||||
* \returns
|
||||
* true if the device was successfully initialised and one or more
|
||||
* keyboards were found, otherwise false.
|
||||
*/
|
||||
bool ohci_probe(uintptr_t base_addr, usb_hcd_t *hcd);
|
||||
|
||||
#endif // OHCI_H
|
||||
|
@ -420,7 +420,7 @@ static const hcd_methods_t methods = {
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool uhci_init(int bus, int dev, int func, uint16_t io_base, usb_hcd_t *hcd)
|
||||
bool uhci_reset(int bus, int dev, int func, uint16_t io_base)
|
||||
{
|
||||
// Disable PCI and SMM interrupts.
|
||||
pci_config_write16(bus, dev, func, UHCI_LEGSUP, UHCI_LEGSUP_CLEAR);
|
||||
@ -429,6 +429,11 @@ bool uhci_init(int bus, int dev, int func, uint16_t io_base, usb_hcd_t *hcd)
|
||||
if (!halt_host_controller(io_base)) return false;
|
||||
if (!reset_host_controller(io_base)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uhci_probe(uint16_t io_base, usb_hcd_t *hcd)
|
||||
{
|
||||
// Record the heap state to allow us to free memory.
|
||||
uintptr_t initial_heap_mark = heap_mark(HEAP_TYPE_LM_1);
|
||||
|
||||
|
@ -15,13 +15,35 @@
|
||||
#include "usbhcd.h"
|
||||
|
||||
/**
|
||||
* Initialises the UHCI device found at bus, dev, func on the PCI bus and
|
||||
* io_base in the I/O address space, scans all the attached USB devices, and
|
||||
* configures any HID USB keyboard devices it finds to generate periodic
|
||||
* interrupt transfers that report key presses. Initialises hcd and returns
|
||||
* true if the device was successfully initialised and one or more keyboards
|
||||
* were found.
|
||||
* If necessary, takes ownership of the UHCI device at the specified base
|
||||
* address, then resets it.
|
||||
*
|
||||
* \param bus - the PCI bus number for accessing the device
|
||||
* \param dev - the PCI device number for accessing the device
|
||||
* \param func - the PCI function number for accessing the device
|
||||
* \param io_base - the base address of the device in I/O space
|
||||
*
|
||||
* \returns
|
||||
* true if ownership was acquired and the device was successfully reset,
|
||||
* otherwise false.
|
||||
*/
|
||||
bool uhci_init(int bus, int dev, int func, uint16_t io_base, usb_hcd_t *hcd);
|
||||
bool uhci_reset(int bus, int dev, int func, uint16_t io_base);
|
||||
|
||||
/**
|
||||
* Initialises the UHCI device at the specified base address, probes all
|
||||
* the attached USB devices, and configures any HID USB keyboard devices
|
||||
* it finds to generate periodic interrupt transfers that report key
|
||||
* presses. If successful, initialises the specified host controller
|
||||
* driver object accordingly.
|
||||
*
|
||||
* \param io_base - the base address of the device in I/O space
|
||||
* \param hcd - a pointer to a pre-allocated host controller
|
||||
* driver object that can be used for this device
|
||||
*
|
||||
* \returns
|
||||
* true if the device was successfully initialised and one or more
|
||||
* keyboards were found, otherwise false.
|
||||
*/
|
||||
bool uhci_probe(uint16_t io_base, usb_hcd_t *hcd);
|
||||
|
||||
#endif // UHCI_H
|
||||
|
214
system/usbhcd.c
214
system/usbhcd.c
@ -22,7 +22,9 @@
|
||||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define MAX_USB_CONTROLLERS 8 // an arbitrary limit - must match the initialisation of usb_controllers
|
||||
#define MAX_HCI 16 // an arbitrary limit - only affects stack usage
|
||||
|
||||
#define MAX_HCD 8 // an arbitrary limit - must match the initialisation of hcd_list
|
||||
|
||||
#define PAUSE_IF_NONE_TIME 10 // seconds
|
||||
|
||||
@ -41,6 +43,14 @@ typedef enum {
|
||||
MAX_HCI_TYPE = 4
|
||||
} hci_type_t;
|
||||
|
||||
typedef struct {
|
||||
hci_type_t type;
|
||||
uint8_t bus;
|
||||
uint8_t dev;
|
||||
uint8_t func;
|
||||
uintptr_t base_addr;
|
||||
} hci_info_t;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Variables
|
||||
//------------------------------------------------------------------------------
|
||||
@ -60,7 +70,7 @@ static const hcd_methods_t methods = {
|
||||
};
|
||||
|
||||
// All entries in this array must be initialised in order to generate the necessary relocation records.
|
||||
static usb_hcd_t usb_controllers[MAX_USB_CONTROLLERS] = {
|
||||
static usb_hcd_t hcd_list[MAX_HCD] = {
|
||||
{ &methods, NULL },
|
||||
{ &methods, NULL },
|
||||
{ &methods, NULL },
|
||||
@ -71,7 +81,7 @@ static usb_hcd_t usb_controllers[MAX_USB_CONTROLLERS] = {
|
||||
{ &methods, NULL }
|
||||
};
|
||||
|
||||
static int num_usb_controllers = 0;
|
||||
static int num_hcd = 0;
|
||||
|
||||
static int print_row = 0;
|
||||
static int print_col = 0;
|
||||
@ -349,8 +359,56 @@ static bool scan_hub_ports(const usb_hcd_t *hcd, const usb_hub_t *hub, int *num_
|
||||
return keyboard_found;
|
||||
}
|
||||
|
||||
static void probe_usb_controller(int bus, int dev, int func, hci_type_t controller_type)
|
||||
static int find_usb_controllers(hci_info_t hci_list[])
|
||||
{
|
||||
int num_hci = 0;
|
||||
for (int bus = 0; bus < PCI_MAX_BUS; bus++) {
|
||||
for (int dev = 0; dev < PCI_MAX_DEV; dev++) {
|
||||
for (int func = 0; func < PCI_MAX_FUNC; func++) {
|
||||
// Test for device/function present.
|
||||
uint16_t vendor_id = pci_config_read16(bus, dev, func, 0x00);
|
||||
uint8_t hdr_type = pci_config_read8 (bus, dev, func, 0x0e);
|
||||
if (vendor_id != 0xffff) {
|
||||
// Test for a USB controller.
|
||||
uint16_t class_code = pci_config_read16(bus, dev, func, 0x0a);
|
||||
if (class_code == 0x0c03) {
|
||||
hci_type_t controller_type = pci_config_read8(bus, dev, func, 0x09) >> 4;
|
||||
if (controller_type < MAX_HCI_TYPE) {
|
||||
hci_list[num_hci].type = controller_type;
|
||||
hci_list[num_hci].bus = bus;
|
||||
hci_list[num_hci].dev = dev;
|
||||
hci_list[num_hci].func = func;
|
||||
num_hci++;
|
||||
// If we've filled the table, abort now.
|
||||
if (num_hci == MAX_HCI) {
|
||||
return num_hci;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Break out if this is a single function device.
|
||||
if (func == 0 && (hdr_type & 0x80) == 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Break out if no device is present.
|
||||
if (func == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return num_hci;
|
||||
}
|
||||
|
||||
static void reset_usb_controller(hci_info_t *hci)
|
||||
{
|
||||
hci_type_t controller_type = hci->type;
|
||||
|
||||
int bus = hci->bus;
|
||||
int dev = hci->dev;
|
||||
int func = hci->func;
|
||||
|
||||
uint16_t vendor_id = pci_config_read16(bus, dev, func, 0x00);
|
||||
uint16_t device_id = pci_config_read16(bus, dev, func, 0x02);
|
||||
uint16_t pci_status = pci_config_read16(bus, dev, func, 0x06);
|
||||
@ -382,22 +440,27 @@ static void probe_usb_controller(int bus, int dev, int func, hci_type_t controll
|
||||
// Restore access to the device and set the bus master flag in case the BIOS hasn't.
|
||||
pci_config_write16(bus, dev, func, 0x04, pci_command | (in_io_space ? 0x0005 : 0x0006));
|
||||
|
||||
hci->base_addr = base_addr;
|
||||
|
||||
print_usb_info("Found %s controller %04x:%04x at %08x size %08x in %s space", hci_name[controller_type],
|
||||
(uintptr_t)vendor_id, (uintptr_t)device_id, base_addr, mmio_size, in_io_space ? "I/O" : "Mem");
|
||||
|
||||
if (in_io_space) {
|
||||
if (controller_type != UHCI) {
|
||||
print_usb_info(" Unsupported address mapping for this controller type");
|
||||
hci->type = NOT_HCI; // mark this controller as unusable
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (controller_type == UHCI) {
|
||||
print_usb_info(" Unsupported address mapping for this controller type");
|
||||
hci->type = NOT_HCI; // mark this controller as unusable
|
||||
return;
|
||||
}
|
||||
base_addr = map_region(base_addr, mmio_size, false);
|
||||
if (base_addr == 0) {
|
||||
print_usb_info(" Failed to map device into virtual memory");
|
||||
hci->type = NOT_HCI; // mark this controller as unusable
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -422,22 +485,53 @@ static void probe_usb_controller(int bus, int dev, int func, hci_type_t controll
|
||||
}
|
||||
}
|
||||
|
||||
// Initialise the device according to its type.
|
||||
// Reset the device according to its type.
|
||||
bool success = false;
|
||||
switch (controller_type) {
|
||||
case UHCI:
|
||||
success = uhci_reset(bus, dev, func, base_addr);
|
||||
break;
|
||||
case OHCI:
|
||||
success = ohci_reset(base_addr);
|
||||
break;
|
||||
case EHCI:
|
||||
success = ehci_reset(bus, dev, func, base_addr);
|
||||
break;
|
||||
case XHCI:
|
||||
success = xhci_reset(base_addr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!success) {
|
||||
hci->type = NOT_HCI; // mark this controller as unusable
|
||||
}
|
||||
}
|
||||
|
||||
static void probe_usb_controller(hci_type_t controller_type, uintptr_t base_addr)
|
||||
{
|
||||
print_usb_info("Probing %s controller at %08x", hci_name[controller_type], base_addr);
|
||||
|
||||
// Probe the device according to its type.
|
||||
bool keyboards_found = false;
|
||||
if (controller_type == UHCI) {
|
||||
keyboards_found = uhci_init(bus, dev, func, base_addr, &usb_controllers[num_usb_controllers]);
|
||||
}
|
||||
if (controller_type == OHCI) {
|
||||
keyboards_found = ohci_init(base_addr, &usb_controllers[num_usb_controllers]);
|
||||
}
|
||||
if (controller_type == EHCI) {
|
||||
keyboards_found = ehci_init(bus, dev, func, base_addr, &usb_controllers[num_usb_controllers]);
|
||||
}
|
||||
if (controller_type == XHCI) {
|
||||
keyboards_found = xhci_init(base_addr, &usb_controllers[num_usb_controllers]);
|
||||
switch (controller_type) {
|
||||
case UHCI:
|
||||
keyboards_found = uhci_probe(base_addr, &hcd_list[num_hcd]);
|
||||
break;
|
||||
case OHCI:
|
||||
keyboards_found = ohci_probe(base_addr, &hcd_list[num_hcd]);
|
||||
break;
|
||||
case EHCI:
|
||||
keyboards_found = ehci_probe(base_addr, &hcd_list[num_hcd]);
|
||||
break;
|
||||
case XHCI:
|
||||
keyboards_found = xhci_probe(base_addr, &hcd_list[num_hcd]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (keyboards_found) {
|
||||
num_usb_controllers++;
|
||||
num_hcd++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -735,66 +829,40 @@ void find_usb_keyboards(bool pause_if_none)
|
||||
clear_screen();
|
||||
print_usb_info("Scanning for USB keyboards...");
|
||||
|
||||
num_usb_controllers = 0;
|
||||
for (int bus = 0; bus < PCI_MAX_BUS; bus++) {
|
||||
for (int dev = 0; dev < PCI_MAX_DEV; dev++) {
|
||||
hci_type_t controller_type[PCI_MAX_FUNC];
|
||||
for (int func = 0; func < PCI_MAX_FUNC; func++) {
|
||||
controller_type[func] = NOT_HCI;
|
||||
}
|
||||
for (int func = 0; func < PCI_MAX_FUNC; func++) {
|
||||
// Test for device/function present.
|
||||
uint16_t vendor_id = pci_config_read16(bus, dev, func, 0x00);
|
||||
uint8_t hdr_type = pci_config_read8 (bus, dev, func, 0x0e);
|
||||
if (vendor_id != 0xffff) {
|
||||
// Test for a USB controller.
|
||||
uint16_t class_code = pci_config_read16(bus, dev, func, 0x0a);
|
||||
if (class_code == 0x0c03) {
|
||||
controller_type[func] = pci_config_read8(bus, dev, func, 0x09) >> 4;
|
||||
if (controller_type[func] >= MAX_HCI_TYPE) {
|
||||
// Unsupported or invalid controller type - ignore it.
|
||||
controller_type[func] = NOT_HCI;
|
||||
}
|
||||
// We need to initialise EHCI controllers before initialising any of their companion
|
||||
// controllers, so do it now.
|
||||
if (controller_type[func] == EHCI) {
|
||||
if (~usb_init_options & USB_IGNORE_EHCI) {
|
||||
probe_usb_controller(bus, dev, func, controller_type[func]);
|
||||
}
|
||||
// If we've filled the controller table, abort now.
|
||||
if (num_usb_controllers == MAX_USB_CONTROLLERS) {
|
||||
return;
|
||||
}
|
||||
controller_type[func] = NOT_HCI; // prevent reprobing
|
||||
}
|
||||
}
|
||||
// Break out if this is a single function device.
|
||||
if (func == 0 && (hdr_type & 0x80) == 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Break out if no device is present.
|
||||
if (func == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int func = 0; func < PCI_MAX_FUNC; func++) {
|
||||
if (controller_type[func] != NOT_HCI) {
|
||||
probe_usb_controller(bus, dev, func, controller_type[func]);
|
||||
// If we've filled the controller table, abort now.
|
||||
if (num_usb_controllers == MAX_USB_CONTROLLERS) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
hci_info_t hci_list[MAX_HCI];
|
||||
|
||||
int num_hci = find_usb_controllers(hci_list);
|
||||
|
||||
// Take ownership of all controllers and reset them.
|
||||
for (int i = 0; i < num_hci; i++) {
|
||||
reset_usb_controller(&hci_list[i]);
|
||||
}
|
||||
|
||||
num_hcd = 0;
|
||||
|
||||
// As we don't support hot plugging, we need to probe EHCI controllers before
|
||||
// probing any of their companion controllers, to ensure any low and full speed
|
||||
// devices are routed to the companion controllers before we probe them.
|
||||
for (int i = 0; i < num_hci && num_hcd < MAX_HCD; i++) {
|
||||
if (hci_list[i].type == EHCI) {
|
||||
if (~usb_init_options & USB_IGNORE_EHCI) {
|
||||
probe_usb_controller(EHCI, hci_list[i].base_addr);
|
||||
}
|
||||
hci_list[i].type = NOT_HCI; // prevent this controller from being scanned again
|
||||
}
|
||||
}
|
||||
|
||||
// Now probe the other controllers.
|
||||
for (int i = 0; i < num_hci && num_hcd < MAX_HCD; i++) {
|
||||
if (hci_list[i].type != NOT_HCI) {
|
||||
probe_usb_controller(hci_list[i].type, hci_list[i].base_addr);
|
||||
}
|
||||
}
|
||||
|
||||
if (usb_init_options & USB_DEBUG) {
|
||||
print_usb_info("Press any key to continue...");
|
||||
while (get_key() == 0) {}
|
||||
} else if (pause_if_none && num_usb_controllers == 0) {
|
||||
} else if (pause_if_none && num_hcd == 0) {
|
||||
for (int i = PAUSE_IF_NONE_TIME; i > 0; i--) {
|
||||
print_usb_info("No USB keyboards found. Continuing in %i second%c ", i, i == 1 ? ' ' : 's');
|
||||
sleep(1);
|
||||
@ -805,10 +873,10 @@ void find_usb_keyboards(bool pause_if_none)
|
||||
|
||||
uint8_t get_usb_keycode(void)
|
||||
{
|
||||
for (int i = 0; i < num_usb_controllers; i++) {
|
||||
const usb_hcd_t *hcd = &usb_controllers[i];
|
||||
for (int i = 0; i < num_hcd; i++) {
|
||||
const usb_hcd_t *hcd = &hcd_list[i];
|
||||
|
||||
usb_controllers[i].methods->poll_keyboards(hcd);
|
||||
hcd->methods->poll_keyboards(hcd);
|
||||
|
||||
int kc_index_o = hcd->ws->kc_index_o;
|
||||
if (kc_index_o != hcd->ws->kc_index_i) {
|
||||
|
@ -910,12 +910,8 @@ static const hcd_methods_t methods = {
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
bool xhci_reset(uintptr_t base_addr)
|
||||
{
|
||||
uint8_t port_type[XHCI_MAX_PORTS];
|
||||
|
||||
memset(port_type, 0, sizeof(port_type));
|
||||
|
||||
xhci_cap_regs_t *cap_regs = (xhci_cap_regs_t *)base_addr;
|
||||
|
||||
#ifdef QEMU_WORKAROUND
|
||||
@ -947,6 +943,44 @@ bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
timer--;
|
||||
}
|
||||
}
|
||||
ext_cap_offs = ext_cap->next_offset;
|
||||
}
|
||||
|
||||
xhci_op_regs_t *op_regs = (xhci_op_regs_t *)(base_addr + cap_regs->cap_length);
|
||||
|
||||
// Ensure the controller is halted and then reset it.
|
||||
if (!halt_host_controller(op_regs)) return false;
|
||||
if (!reset_host_controller(op_regs)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool xhci_probe(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
{
|
||||
uint8_t port_type[XHCI_MAX_PORTS];
|
||||
|
||||
memset(port_type, 0, sizeof(port_type));
|
||||
|
||||
xhci_cap_regs_t *cap_regs = (xhci_cap_regs_t *)base_addr;
|
||||
|
||||
#ifdef QEMU_WORKAROUND
|
||||
xhci_cap_regs_t cap_regs_copy;
|
||||
memcpy32(&cap_regs_copy, cap_regs, sizeof(cap_regs_copy));
|
||||
cap_regs = &cap_regs_copy;
|
||||
#endif
|
||||
|
||||
// Walk the extra capabilities list.
|
||||
uintptr_t ext_cap_base = base_addr;
|
||||
uintptr_t ext_cap_offs = cap_regs->hcc_params1 >> 16;
|
||||
while (ext_cap_offs != 0) {
|
||||
ext_cap_base += ext_cap_offs * sizeof(uint32_t);
|
||||
xhci_ext_cap_t *ext_cap = (xhci_ext_cap_t *)ext_cap_base;
|
||||
|
||||
#ifdef QEMU_WORKAROUND
|
||||
xhci_ext_cap_t ext_cap_copy;
|
||||
memcpy32(&ext_cap_copy, ext_cap, sizeof(ext_cap_copy));
|
||||
ext_cap = &ext_cap_copy;
|
||||
#endif
|
||||
if (ext_cap->id == XHCI_EXT_CAP_SUPPORTED_PROTOCOL) {
|
||||
xhci_supported_protocol_t *protocol = (xhci_supported_protocol_t *)ext_cap_base;
|
||||
|
||||
@ -984,10 +1018,6 @@ bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
xhci_rt_regs_t *rt_regs = (xhci_rt_regs_t *)(base_addr + cap_regs->rts_offset);
|
||||
xhci_db_reg_t *db_regs = (xhci_db_reg_t *)(base_addr + cap_regs->db_offset);
|
||||
|
||||
// Ensure the controller is halted and then reset it.
|
||||
if (!halt_host_controller(op_regs)) return false;
|
||||
if (!reset_host_controller(op_regs)) return false;
|
||||
|
||||
// Record the heap states to allow us to free memory.
|
||||
uintptr_t initial_lm_heap_mark = heap_mark(HEAP_TYPE_LM_1);
|
||||
uintptr_t initial_hm_heap_mark = heap_mark(HEAP_TYPE_HM_1);
|
||||
|
@ -15,12 +15,32 @@
|
||||
#include "usbhcd.h"
|
||||
|
||||
/**
|
||||
* Initialises the XHCI device found at base_addr, scans all the attached USB
|
||||
* devices, and configures any HID USB keyboard devices it finds to generate
|
||||
* periodic interrupt transfers that report key presses. Initialises hcd and
|
||||
* returns true if the device was successfully initialised and one or more
|
||||
* keyboards were found.
|
||||
* If necessary, takes ownership of the XHCI device at the specified base
|
||||
* address, then resets it.
|
||||
*
|
||||
* \param base_addr - the base address of the device in virtual memory
|
||||
*
|
||||
* \returns
|
||||
* true if ownership was acquired and the device was successfully reset,
|
||||
* otherwise false.
|
||||
*/
|
||||
bool xhci_init(uintptr_t base_addr, usb_hcd_t *hcd);
|
||||
bool xhci_reset(uintptr_t base_addr);
|
||||
|
||||
/**
|
||||
* Initialises the XHCI device at the specified base address, probes all
|
||||
* the attached USB devices, and configures any HID USB keyboard devices
|
||||
* it finds to generate periodic interrupt transfers that report key
|
||||
* presses. If successful, initialises the specified host controller
|
||||
* driver object accordingly.
|
||||
*
|
||||
* \param base_addr - the base address of the device in virtual memory
|
||||
* \param hcd - a pointer to a pre-allocated host controller
|
||||
* driver object that can be used for this device
|
||||
*
|
||||
* \returns
|
||||
* true if the device was successfully initialised and one or more
|
||||
* keyboards were found, otherwise false.
|
||||
*/
|
||||
bool xhci_probe(uintptr_t base_addr, usb_hcd_t *hcd);
|
||||
|
||||
#endif // XHCI_H
|
||||
|
Loading…
Reference in New Issue
Block a user