* Copyright 2011, Michael Lotz mmlr@mlotz.ch.
* Copyright 2009, Clemens Zeidler haiku@clemens-zeidler.de.
* All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#include "acpi_irq_routing_table.h"
#include "acpi.h"
#include <interrupts.h>
#include <PCI.h>
#ifdef TRACE_PRT
# define TRACE(x...) dprintf("IRQRoutingTable: " x)
#else
# define TRACE(x...)
#endif
const char* kACPIPciRootName = "PNP0A03";
const char* kACPIPciExpressRootName = "PNP0A08";
static const uint8 kMaxPCIFunctionCount = 8;
static const uint8 kMaxPCIDeviceCount = 32;
#if defined(__i386__) || defined(__x86_64__)
static const uint8 kMaxISAInterrupts = 16;
#else
static const uint8 kMaxISAInterrupts = 0;
#endif
irq_descriptor::irq_descriptor()
:
irq(0),
shareable(false),
polarity(B_HIGH_ACTIVE_POLARITY),
trigger_mode(B_EDGE_TRIGGERED)
{
}
void
print_irq_descriptor(const irq_descriptor& descriptor)
{
const char* activeHighString = "active high";
const char* activeLowString = " active low";
const char* levelTriggeredString = "level triggered";
const char* edgeTriggeredString = "edge triggered";
dprintf("irq: %u, shareable: %u, polarity: %s, trigger_mode: %s\n",
descriptor.irq, descriptor.shareable,
descriptor.polarity == B_HIGH_ACTIVE_POLARITY ? activeHighString
: activeLowString,
descriptor.trigger_mode == B_LEVEL_TRIGGERED ? levelTriggeredString
: edgeTriggeredString);
}
static void
print_irq_routing_entry(const irq_routing_entry& entry)
{
dprintf("address 0x%04" B_PRIx64 "; pin %u;", entry.device_address,
entry.pin);
if (entry.source_index != 0)
dprintf(" GSI %" B_PRIu32 ";", entry.source_index);
else
dprintf(" source %p %" B_PRIu32 ";", entry.source, entry.source_index);
dprintf(" pci %u:%u pin %u func mask %" B_PRIx32 "; bios irq: %u; gsi %u;"
" config 0x%02x\n", entry.pci_bus, entry.pci_device, entry.pin + 1,
entry.pci_function_mask, entry.bios_irq, entry.irq,
entry.polarity | entry.trigger_mode);
}
void
print_irq_routing_table(const IRQRoutingTable& table)
{
dprintf("IRQ routing table with %i entries\n", (int)table.Count());
for (int i = 0; i < table.Count(); i++)
print_irq_routing_entry(table.ElementAt(i));
}
static status_t
update_pci_info_for_entry(pci_module_info* pci, const irq_routing_entry& entry)
{
uint32 updateCount = 0;
for (uint8 function = 0; function < kMaxPCIFunctionCount; function++) {
if ((entry.pci_function_mask & (1 << function)) == 0)
continue;
if (pci->update_interrupt_line(entry.pci_bus, entry.pci_device,
function, entry.irq) == B_OK) {
updateCount++;
}
}
return updateCount > 0 ? B_OK : B_ENTRY_NOT_FOUND;
}
static status_t
fill_pci_info_for_entry(pci_module_info* pci, irq_routing_entry& entry)
{
uint8 headerType = pci->read_pci_config(entry.pci_bus, entry.pci_device, 0,
PCI_header_type, 1);
if (headerType == 0xff) {
TRACE("PCI %" B_PRIu8 ":%" B_PRIu8 " entry not found\n",
entry.pci_bus, entry.pci_device);
return B_ENTRY_NOT_FOUND;
}
uint8 functionCount = 1;
if ((headerType & PCI_multifunction) != 0)
functionCount = kMaxPCIFunctionCount;
for (uint8 function = 0; function < functionCount; function++) {
uint16 vendorId = pci->read_pci_config(entry.pci_bus, entry.pci_device,
function, PCI_vendor_id, 2);
if (vendorId == 0xffff) {
TRACE("PCI %" B_PRIu8 ":%" B_PRIu8 ":%" B_PRIu8 " vendor 0xffff\n",
entry.pci_bus, entry.pci_device, function);
continue;
}
uint8 interruptPin = pci->read_pci_config(entry.pci_bus,
entry.pci_device, function, PCI_interrupt_pin, 1);
if (interruptPin != entry.pin + 1) {
TRACE("PCI %" B_PRIu8 ":%" B_PRIu8 ":%" B_PRIu8 " IRQ Pin %" B_PRIu8
" != %" B_PRIu8 "\n", entry.pci_bus, entry.pci_device, function,
interruptPin, entry.pin + 1);
continue;
}
if (entry.bios_irq == 0) {
entry.bios_irq = pci->read_pci_config(entry.pci_bus,
entry.pci_device, function, PCI_interrupt_line, 1);
}
entry.pci_function_mask |= 1 << function;
}
return entry.pci_function_mask != 0 ? B_OK : B_ENTRY_NOT_FOUND;
}
static status_t
choose_link_device_configurations(acpi_module_info* acpi,
IRQRoutingTable& routingTable,
interrupt_available_check_function checkFunction)
{
Before configuring the link devices we have to take a few things into
consideration:
* Multiple PCI devices / functions may link to the same PCI link
device, so we must ensure that we don't try to configure different
IRQs for each device, overwriting the previous config of the
respective link device.
* If we can't use non-ISA IRQs then we must ensure that we don't
configure any IRQs that overlaps with ISA devices (as they use
different triggering modes and polarity they aren't compatible).
Since the ISA bus isn't enumerable we don't have any clues as to
where an ISA device might be connected. The only safe assumption
therefore is to only use IRQs that are known to be usable for PCI
devices. In our case we can use all the previously assigned PCI
interrupt_line IRQs as stored in the bios_irq field.
*/
uint16 validForPCI = 0;
uint16 irqUsage[256];
memset(irqUsage, 0, sizeof(irqUsage));
Vector<link_device*> links;
for (int i = 0; i < routingTable.Count(); i++) {
irq_routing_entry& irqEntry = routingTable.ElementAt(i);
if (irqEntry.bios_irq != 0 && irqEntry.bios_irq != 255) {
if (irqEntry.bios_irq < kMaxISAInterrupts)
validForPCI |= (1 << irqEntry.bios_irq);
}
if (irqEntry.source == NULL) {
irqUsage[irqEntry.irq]++;
if (irqEntry.irq < kMaxISAInterrupts)
validForPCI |= (1 << irqEntry.irq);
continue;
}
link_device* link = NULL;
for (int j = 0; j < links.Count(); j++) {
link_device* existing = links.ElementAt(j);
if (existing->handle == irqEntry.source) {
link = existing;
break;
}
}
if (link != NULL) {
link->used_by.PushBack(&irqEntry);
continue;
}
link = new(std::nothrow) link_device;
if (link == NULL) {
panic("ran out of memory while configuring irq link devices");
return B_NO_MEMORY;
}
link->handle = irqEntry.source;
status_t status = read_possible_irqs(acpi, link->handle,
link->possible_irqs);
if (status != B_OK) {
panic("failed to read possible irqs of link device");
delete link;
return status;
}
status = read_current_irq(acpi, link->handle, link->current_irq);
if (status != B_OK) {
panic("failed to read current irq of link device");
delete link;
return status;
}
if (link->current_irq.irq < kMaxISAInterrupts)
validForPCI |= (1 << link->current_irq.irq);
link->used_by.PushBack(&irqEntry);
links.PushBack(link);
}
for (int i = 0; i < links.Count(); i++) {
link_device* link = links.ElementAt(i);
int bestIRQIndex = 0;
uint16 bestIRQUsage = UINT16_MAX;
for (int j = 0; j < link->possible_irqs.Count(); j++) {
irq_descriptor& possibleIRQ = link->possible_irqs.ElementAt(j);
if (!checkFunction(possibleIRQ.irq)) {
continue;
}
if (possibleIRQ.irq < kMaxISAInterrupts
&& (validForPCI & (1 << possibleIRQ.irq)) == 0) {
continue;
}
if (irqUsage[possibleIRQ.irq] < bestIRQUsage) {
bestIRQIndex = j;
bestIRQUsage = irqUsage[possibleIRQ.irq];
}
}
irq_descriptor& chosenDescriptor
= link->possible_irqs.ElementAt(bestIRQIndex);
if (!checkFunction(chosenDescriptor.irq)) {
dprintf("chosen irq %u is not addressable\n", chosenDescriptor.irq);
return B_ERROR;
}
irqUsage[chosenDescriptor.irq] += link->used_by.Count();
for (int j = 0; j < link->used_by.Count(); j++) {
irq_routing_entry* irqEntry = link->used_by.ElementAt(j);
irqEntry->needs_configuration = j == 0;
irqEntry->irq = chosenDescriptor.irq;
irqEntry->polarity = chosenDescriptor.polarity;
irqEntry->trigger_mode = chosenDescriptor.trigger_mode;
}
delete link;
}
return B_OK;
}
static status_t
configure_link_devices(acpi_module_info* acpi, IRQRoutingTable& routingTable)
{
for (int i = 0; i < routingTable.Count(); i++) {
irq_routing_entry& irqEntry = routingTable.ElementAt(i);
if (!irqEntry.needs_configuration)
continue;
irq_descriptor configuration;
configuration.irq = irqEntry.irq;
configuration.polarity = irqEntry.polarity;
configuration.trigger_mode = irqEntry.trigger_mode;
status_t status = set_current_irq(acpi, irqEntry.source, configuration);
if (status != B_OK) {
dprintf("failed to set irq on link device, keeping current\n");
print_irq_descriptor(configuration);
read_current_irq(acpi, irqEntry.source, configuration);
for (int j = i; j < routingTable.Count(); j++) {
irq_routing_entry& other = routingTable.ElementAt(j);
if (other.source == irqEntry.source) {
other.irq = configuration.irq;
other.polarity = configuration.polarity;
other.trigger_mode = configuration.trigger_mode;
}
}
}
irqEntry.needs_configuration = false;
}
return B_OK;
}
static status_t
evaluate_integer(acpi_module_info* acpi, acpi_handle handle,
const char* method, uint64& value)
{
acpi_object_type result;
acpi_data resultBuffer;
resultBuffer.pointer = &result;
resultBuffer.length = sizeof(result);
status_t status = acpi->evaluate_method(handle, method, NULL,
&resultBuffer);
if (status != B_OK)
return status;
if (result.object_type != ACPI_TYPE_INTEGER)
return B_BAD_TYPE;
value = result.integer.integer;
return B_OK;
}
static status_t
handle_routing_table_entry(acpi_module_info* acpi, pci_module_info* pci,
acpi_handle parent, const acpi_pci_routing_table* acpiTable,
uint8 currentBus, irq_routing_entry& irqEntry)
{
bool noSource = acpiTable->Source[0] == '\0';
noSource = acpiTable->SourceIndex != 0;
status_t status;
acpi_handle source;
if (!noSource) {
status = acpi->get_handle(parent, acpiTable->Source, &source);
if (status != B_OK) {
dprintf("failed to get handle to link device\n");
return status;
}
}
memset(&irqEntry, 0, sizeof(irq_routing_entry));
irqEntry.device_address = acpiTable->Address;
irqEntry.pin = acpiTable->Pin;
irqEntry.source = noSource ? NULL : source;
irqEntry.source_index = acpiTable->SourceIndex;
irqEntry.pci_bus = currentBus;
irqEntry.pci_device = (uint8)(acpiTable->Address >> 16);
status = fill_pci_info_for_entry(pci, irqEntry);
if (status != B_OK) {
#ifdef TRACE_PRT
dprintf("no matching PCI device for irq entry: ");
print_irq_routing_entry(irqEntry);
#endif
} else {
#ifdef TRACE_PRT
dprintf("found matching PCI device for irq entry: ");
print_irq_routing_entry(irqEntry);
#endif
}
if (noSource) {
irqEntry.needs_configuration = false;
irqEntry.irq = irqEntry.source_index;
irqEntry.polarity = B_LOW_ACTIVE_POLARITY;
irqEntry.trigger_mode = B_LEVEL_TRIGGERED;
}
return B_OK;
}
irq_routing_entry*
find_routing_table_entry(IRQRoutingTable& table, uint8 bus, uint8 device,
uint8 pin)
{
for (int i = 0; i < table.Count(); i++) {
irq_routing_entry& irqEntry = table.ElementAt(i);
if (irqEntry.pci_bus != bus || irqEntry.pci_device != device)
continue;
if (irqEntry.pin + 1 == pin)
return &irqEntry;
}
return NULL;
}
static status_t
ensure_all_functions_matched(pci_module_info* pci, uint8 bus,
IRQRoutingTable& matchedTable, IRQRoutingTable& unmatchedTable,
Vector<pci_address>& parents)
{
for (uint8 device = 0; device < kMaxPCIDeviceCount; device++) {
if (pci->read_pci_config(bus, device, 0, PCI_vendor_id, 2) == 0xffff) {
TRACE("PCI bus %" B_PRIu8 ":%" B_PRIu8 " not present.\n",
bus, device);
continue;
}
uint8 headerType = pci->read_pci_config(bus, device, 0,
PCI_header_type, 1);
uint8 functionCount = 1;
if ((headerType & PCI_multifunction) != 0)
functionCount = kMaxPCIFunctionCount;
for (uint8 function = 0; function < functionCount; function++) {
if (pci->read_pci_config(bus, device, function, PCI_vendor_id, 2)
== 0xffff) {
TRACE("PCI %" B_PRIu8 ":%" B_PRIu8 ":%" B_PRIu8 " "
"not present.\n", bus, device, function);
continue;
}
if (function > 0) {
headerType = pci->read_pci_config(bus, device, function,
PCI_header_type, 1);
}
if ((headerType & PCI_header_type_mask)
== PCI_header_type_PCI_to_PCI_bridge) {
pci_address pciAddress;
pciAddress.segment = 0;
pciAddress.bus = bus;
pciAddress.device = device;
pciAddress.function = function;
parents.PushBack(pciAddress);
uint8 secondaryBus = pci->read_pci_config(bus, device, function,
PCI_secondary_bus, 1);
if (secondaryBus != 0xff) {
ensure_all_functions_matched(pci, secondaryBus,
matchedTable, unmatchedTable, parents);
}
parents.PopBack();
}
uint8 interruptPin = pci->read_pci_config(bus, device, function,
PCI_interrupt_pin, 1);
if (interruptPin == 0 || interruptPin > 4) {
TRACE("PCI %" B_PRIu8 ":%" B_PRIu8 ":%" B_PRIu8 " "
"not routed.\n", bus, device, function);
continue;
}
irq_routing_entry* irqEntry = find_routing_table_entry(matchedTable,
bus, device, interruptPin);
if (irqEntry != NULL) {
TRACE("PCI %" B_PRIu8 ":%" B_PRIu8 ":%" B_PRIu8 " "
"already matched. Will mask.\n", bus, device, function);
irqEntry->pci_function_mask |= 1 << function;
continue;
}
TRACE("PCI %" B_PRIu8 ":%" B_PRIu8 ":%" B_PRIu8 " has %" B_PRIu8 " "
"parents, searching them...\n", bus, device, function,
parents.Count());
bool matched = false;
uint8 parentPin = ((device + interruptPin - 1) % 4) + 1;
for (int i = parents.Count() - 1; i >= 0; i--) {
pci_address& parent = parents.ElementAt(i);
irqEntry = find_routing_table_entry(matchedTable, parent.bus,
parent.device, parentPin);
if (irqEntry == NULL) {
TRACE("PCI %" B_PRIu8 ":%" B_PRIu8 ":%" B_PRIu8 " "
"no matchedTable entry.\n", bus, device, function);
irqEntry = find_routing_table_entry(unmatchedTable,
parent.bus, parent.device, parentPin);
}
if (irqEntry == NULL) {
parentPin = ((parent.device + parentPin - 1) % 4) + 1;
TRACE("PCI %" B_PRIu8 ":%" B_PRIu8 ":%" B_PRIu8 " "
"no unmatchedTable entry, looking at parent pin %"
B_PRIu8 "...\n", bus, device, function, parentPin);
continue;
}
irq_routing_entry newEntry = *irqEntry;
newEntry.device_address = (device << 16) | 0xffff;
newEntry.pin = interruptPin - 1;
newEntry.pci_bus = bus;
newEntry.pci_device = device;
newEntry.pci_function_mask = 1 << function;
uint8 biosIRQ = pci->read_pci_config(bus, device, function,
PCI_interrupt_line, 1);
if (biosIRQ != 0 && biosIRQ != 255) {
if (newEntry.bios_irq != 0 && newEntry.bios_irq != 255
&& newEntry.bios_irq != biosIRQ) {
panic("calculated irq routing doesn't match bios for "
"PCI %u:%u:%u", bus, device, function);
return B_ERROR;
}
newEntry.bios_irq = biosIRQ;
}
dprintf("calculated irq routing entry: ");
print_irq_routing_entry(newEntry);
matchedTable.PushBack(newEntry);
matched = true;
break;
}
if (!matched) {
uint32 interrupt_line = pci->read_pci_config(bus, device,
function, PCI_interrupt_line, 1);
if (interrupt_line == 0 || interrupt_line == 255) {
dprintf("assuming no interrupt use on PCI device"
" %u:%u:%u (bios irq 0, interrupt line %" B_PRId32 ")\n",
bus, device, function, interrupt_line);
continue;
}
dprintf("WARNING: unable to find irq routing for PCI "
"%" B_PRIu8 ":%" B_PRIu8 ":%" B_PRIu8 ". Device may be "
"unstable / broken.\n", bus, device, function);
return B_ERROR;
}
}
}
return B_OK;
}
static status_t
read_irq_routing_table_recursive(acpi_module_info* acpi, pci_module_info* pci,
acpi_handle parent, acpi_handle device, uint8 currentBus,
IRQRoutingTable& table, IRQRoutingTable& unmatchedTable, bool rootBridge,
interrupt_available_check_function checkFunction)
{
if (!rootBridge) {
uint64 value;
pci_address pciAddress;
pciAddress.bus = currentBus;
if (evaluate_integer(acpi, device, "_ADR", value) == B_OK) {
pciAddress.device = (uint8)(value >> 16);
pciAddress.function = (uint8)value;
} else {
pciAddress.device = 0;
pciAddress.function = 0;
}
if (pciAddress.device >= kMaxPCIDeviceCount
|| pciAddress.function >= kMaxPCIFunctionCount) {
return B_OK;
}
uint16 deviceID = pci->read_pci_config(pciAddress.bus,
pciAddress.device, pciAddress.function, PCI_device_id, 2);
if (deviceID == 0xffff) {
TRACE("device not present\n");
return B_OK;
}
uint8 baseClass = pci->read_pci_config(pciAddress.bus,
pciAddress.device, pciAddress.function, PCI_class_base, 1);
uint8 subClass = pci->read_pci_config(pciAddress.bus,
pciAddress.device, pciAddress.function, PCI_class_sub, 1);
if (baseClass != PCI_bridge || subClass != PCI_pci) {
TRACE("not a PCI bridge\n");
return B_OK;
}
uint8 headerType = pci->read_pci_config(pciAddress.bus,
pciAddress.device, pciAddress.function, PCI_header_type, 1);
switch (headerType & PCI_header_type_mask) {
case PCI_header_type_PCI_to_PCI_bridge:
case PCI_header_type_cardbus:
TRACE("found a PCI bridge (0x%02x)\n", headerType);
break;
default:
TRACE("unsupported header type (0x%02x)\n", headerType);
return B_OK;
}
uint8 secondaryBus = pci->read_pci_config(pciAddress.bus,
pciAddress.device, pciAddress.function, PCI_secondary_bus, 1);
if (secondaryBus == 255) {
TRACE("secondary bus is inactive\n");
return B_OK;
}
if (secondaryBus == currentBus) {
dprintf("invalid secondary bus %u on primary bus %u,"
" can't configure irq routing of devices below\n",
secondaryBus, currentBus);
return B_ERROR;
}
TRACE("now scanning bus %u\n", secondaryBus);
currentBus = secondaryBus;
}
acpi_data buffer;
buffer.pointer = NULL;
buffer.length = ACPI_ALLOCATE_BUFFER;
status_t status = acpi->get_irq_routing_table(device, &buffer);
if (status == B_OK) {
TRACE("found irq routing table\n");
acpi_pci_routing_table* acpiTable
= (acpi_pci_routing_table*)buffer.pointer;
while (acpiTable->Length) {
irq_routing_entry irqEntry;
status = handle_routing_table_entry(acpi, pci, parent, acpiTable,
currentBus, irqEntry);
if (status == B_OK) {
if (irqEntry.source == NULL && !checkFunction(irqEntry.irq)) {
dprintf("hardwired irq %u not addressable\n", irqEntry.irq);
free(buffer.pointer);
return B_ERROR;
}
if (irqEntry.pci_function_mask != 0)
table.PushBack(irqEntry);
else
unmatchedTable.PushBack(irqEntry);
}
acpiTable = (acpi_pci_routing_table*)((uint8*)acpiTable
+ acpiTable->Length);
}
free(buffer.pointer);
} else {
TRACE("no irq routing table present\n");
}
acpi_data pathBuffer;
pathBuffer.pointer = NULL;
pathBuffer.length = ACPI_ALLOCATE_BUFFER;
status = acpi->ns_handle_to_pathname(device, &pathBuffer);
if (status != B_OK) {
dprintf("failed to resolve handle to path\n");
return status;
}
char childName[255];
void* counter = NULL;
while (acpi->get_next_entry(ACPI_TYPE_DEVICE, (char*)pathBuffer.pointer,
childName, sizeof(childName), &counter) == B_OK) {
acpi_handle childHandle;
status = acpi->get_handle(NULL, childName, &childHandle);
if (status != B_OK) {
dprintf("failed to get handle to child \"%s\"\n", childName);
break;
}
TRACE("recursing down to child \"%s\"\n", childName);
status = read_irq_routing_table_recursive(acpi, pci, device, childHandle,
currentBus, table, unmatchedTable, false, checkFunction);
if (status != B_OK)
break;
}
free(pathBuffer.pointer);
return status;
}
static status_t
read_irq_routing_table(acpi_module_info* acpi, IRQRoutingTable& table,
interrupt_available_check_function checkFunction)
{
char rootPciName[255];
acpi_handle rootPciHandle;
rootPciName[0] = 0;
status_t status = acpi->get_device(kACPIPciRootName, 0, rootPciName, 255);
if (status != B_OK)
return status;
status = acpi->get_handle(NULL, rootPciName, &rootPciHandle);
if (status != B_OK)
return status;
uint8 rootBus = 0;
uint64 value;
if (evaluate_integer(acpi, rootPciHandle, "_BBN", value) == B_OK)
rootBus = (uint8)value;
#if 0
if (evaluate_integer(acpi, rootPciHandle, "_SEG", value) == B_OK)
rootPciAddress.segment = (uint8)value;
#endif
pci_module_info* pci;
status = get_module(B_PCI_MODULE_NAME, (module_info**)&pci);
if (status != B_OK) {
dprintf("failed to get PCI module!\n");
return status;
}
IRQRoutingTable unmatchedTable;
status = read_irq_routing_table_recursive(acpi, pci, ACPI_ROOT_OBJECT,
rootPciHandle, rootBus, table, unmatchedTable, true, checkFunction);
if (status != B_OK) {
put_module(B_PCI_MODULE_NAME);
return status;
}
if (table.Count() == 0) {
put_module(B_PCI_MODULE_NAME);
return B_ERROR;
}
Vector<pci_address> parents;
status = ensure_all_functions_matched(pci, rootBus, table, unmatchedTable,
parents);
put_module(B_PCI_MODULE_NAME);
return status;
}
status_t
prepare_irq_routing(acpi_module_info* acpi, IRQRoutingTable& routingTable,
interrupt_available_check_function checkFunction)
{
status_t status = read_irq_routing_table(acpi, routingTable, checkFunction);
if (status != B_OK)
return status;
return choose_link_device_configurations(acpi, routingTable, checkFunction);
}
status_t
enable_irq_routing(acpi_module_info* acpi, IRQRoutingTable& routingTable)
{
status_t status = configure_link_devices(acpi, routingTable);
if (status != B_OK) {
panic("failed to configure link devices");
return status;
}
pci_module_info* pci;
status = get_module(B_PCI_MODULE_NAME, (module_info**)&pci);
if (status != B_OK) {
dprintf("failed to get PCI module!\n");
return status;
}
for (int i = 0; i < routingTable.Count(); i++) {
irq_routing_entry& irqEntry = routingTable.ElementAt(i);
status = update_pci_info_for_entry(pci, irqEntry);
if (status != B_OK) {
dprintf("failed to update interrupt_line for PCI %u:%u mask %"
B_PRIx32 "\n", irqEntry.pci_bus, irqEntry.pci_device,
irqEntry.pci_function_mask);
}
}
put_module(B_PCI_MODULE_NAME);
return B_OK;
}
static status_t
read_irq_descriptor(acpi_module_info* acpi, acpi_handle device,
bool readCurrent, irq_descriptor* _descriptor,
irq_descriptor_list* descriptorList)
{
acpi_data buffer;
buffer.pointer = NULL;
buffer.length = ACPI_ALLOCATE_BUFFER;
status_t status;
if (readCurrent)
status = acpi->get_current_resources(device, &buffer);
else
status = acpi->get_possible_resources(device, &buffer);
if (status != B_OK) {
dprintf("failed to read %s resources for irq\n",
readCurrent ? "current" : "possible");
free(buffer.pointer);
return status;
}
irq_descriptor descriptor;
descriptor.irq = 255;
acpi_resource* resource = (acpi_resource*)buffer.pointer;
while (resource->Type != ACPI_RESOURCE_TYPE_END_TAG) {
switch (resource->Type) {
case ACPI_RESOURCE_TYPE_IRQ:
{
acpi_resource_irq& irq = resource->Data.Irq;
if (irq.InterruptCount < 1) {
dprintf("acpi irq resource with no interrupts\n");
break;
}
descriptor.shareable = irq.Shareable != 0;
descriptor.trigger_mode = irq.Triggering == 0
? B_LEVEL_TRIGGERED : B_EDGE_TRIGGERED;
descriptor.polarity = irq.Polarity == 0
? B_HIGH_ACTIVE_POLARITY : B_LOW_ACTIVE_POLARITY;
if (readCurrent)
descriptor.irq = irq.Interrupts[0];
else {
for (uint16 i = 0; i < irq.InterruptCount; i++) {
descriptor.irq = irq.Interrupts[i];
descriptorList->PushBack(descriptor);
}
}
#ifdef TRACE_PRT
dprintf("acpi irq resource (%s):\n",
readCurrent ? "current" : "possible");
dprintf("\ttriggering: %s\n",
irq.Triggering == 0 ? "level" : "edge");
dprintf("\tpolarity: %s active\n",
irq.Polarity == 0 ? "high" : "low");
dprintf("\tshareable: %s\n", irq.Shareable != 0 ? "yes" : "no");
dprintf("\tcount: %u\n", irq.InterruptCount);
if (irq.InterruptCount > 0) {
dprintf("\tinterrupts:");
for (uint16 i = 0; i < irq.InterruptCount; i++)
dprintf(" %u", irq.Interrupts[i]);
dprintf("\n");
}
#endif
break;
}
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
{
acpi_resource_extended_irq& irq = resource->Data.ExtendedIrq;
if (irq.InterruptCount < 1) {
dprintf("acpi extended irq resource with no interrupts\n");
break;
}
descriptor.shareable = irq.Shareable != 0;
descriptor.trigger_mode = irq.Triggering == 0
? B_LEVEL_TRIGGERED : B_EDGE_TRIGGERED;
descriptor.polarity = irq.Polarity == 0
? B_HIGH_ACTIVE_POLARITY : B_LOW_ACTIVE_POLARITY;
if (readCurrent)
descriptor.irq = irq.Interrupts[0];
else {
for (uint16 i = 0; i < irq.InterruptCount; i++) {
descriptor.irq = irq.Interrupts[i];
descriptorList->PushBack(descriptor);
}
}
#ifdef TRACE_PRT
dprintf("acpi extended irq resource (%s):\n",
readCurrent ? "current" : "possible");
dprintf("\tproducer: %s\n",
irq.ProducerConsumer ? "yes" : "no");
dprintf("\ttriggering: %s\n",
irq.Triggering == 0 ? "level" : "edge");
dprintf("\tpolarity: %s active\n",
irq.Polarity == 0 ? "high" : "low");
dprintf("\tshareable: %s\n", irq.Shareable != 0 ? "yes" : "no");
dprintf("\tcount: %u\n", irq.InterruptCount);
if (irq.InterruptCount > 0) {
dprintf("\tinterrupts:");
for (uint16 i = 0; i < irq.InterruptCount; i++)
dprintf(" %u", irq.Interrupts[i]);
dprintf("\n");
}
#endif
break;
}
}
if (descriptor.irq != 255)
break;
resource = (acpi_resource*)((uint8*)resource + resource->Length);
}
free(buffer.pointer);
if (descriptor.irq == 255)
return B_ERROR;
if (readCurrent)
*_descriptor = descriptor;
return B_OK;
}
status_t
read_current_irq(acpi_module_info* acpi, acpi_handle device,
irq_descriptor& descriptor)
{
return read_irq_descriptor(acpi, device, true, &descriptor, NULL);
}
status_t
read_possible_irqs(acpi_module_info* acpi, acpi_handle device,
irq_descriptor_list& descriptorList)
{
return read_irq_descriptor(acpi, device, false, NULL, &descriptorList);
}
status_t
set_current_irq(acpi_module_info* acpi, acpi_handle device,
const irq_descriptor& descriptor)
{
acpi_data buffer;
buffer.pointer = NULL;
buffer.length = ACPI_ALLOCATE_BUFFER;
status_t status = acpi->get_current_resources(device, &buffer);
if (status != B_OK) {
dprintf("failed to read current resources for irq\n");
return status;
}
bool irqWritten = false;
acpi_resource* resource = (acpi_resource*)buffer.pointer;
while (resource->Type != ACPI_RESOURCE_TYPE_END_TAG) {
switch (resource->Type) {
case ACPI_RESOURCE_TYPE_IRQ:
{
acpi_resource_irq& irq = resource->Data.Irq;
if (irq.InterruptCount < 1) {
dprintf("acpi irq resource with no interrupts\n");
break;
}
irq.Triggering
= descriptor.trigger_mode == B_LEVEL_TRIGGERED ? 0 : 1;
irq.Polarity
= descriptor.polarity == B_HIGH_ACTIVE_POLARITY ? 0 : 1;
irq.Shareable = descriptor.shareable ? 0 : 1;
irq.InterruptCount = 1;
irq.Interrupts[0] = descriptor.irq;
irqWritten = true;
break;
}
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
{
acpi_resource_extended_irq& irq = resource->Data.ExtendedIrq;
if (irq.InterruptCount < 1) {
dprintf("acpi extended irq resource with no interrupts\n");
break;
}
irq.Triggering
= descriptor.trigger_mode == B_LEVEL_TRIGGERED ? 0 : 1;
irq.Polarity
= descriptor.polarity == B_HIGH_ACTIVE_POLARITY ? 0 : 1;
irq.Shareable = descriptor.shareable ? 0 : 1;
irq.InterruptCount = 1;
irq.Interrupts[0] = descriptor.irq;
irqWritten = true;
break;
}
}
if (irqWritten)
break;
resource = (acpi_resource*)((uint8*)resource + resource->Length);
}
if (irqWritten) {
status = acpi->set_current_resources(device, &buffer);
if (status != B_OK)
dprintf("failed to set irq resources\n");
} else {
dprintf("failed to write requested irq into resources\n");
status = B_ERROR;
}
free(buffer.pointer);
return status;
}