diff --git a/src/libs/compat/freebsd_network/bus.c b/src/libs/compat/freebsd_network/bus.c index 09e40a45bf..48200d809d 100644 --- a/src/libs/compat/freebsd_network/bus.c +++ b/src/libs/compat/freebsd_network/bus.c @@ -22,6 +22,8 @@ // private kernel header to get B_NO_HANDLED_INFO #include +#include + //#define DEBUG_BUS_SPACE_RW #ifdef DEBUG_BUS_SPACE_RW @@ -48,6 +50,7 @@ struct internal_intr { void *arg; int irq; uint32 flags; + bool is_msi; thread_id thread; sem_id sem; @@ -138,9 +141,21 @@ bus_alloc_resource(device_t dev, int type, int *rid, unsigned long start, if (res == NULL) return NULL; - if (type == SYS_RES_IRQ) - result = bus_alloc_irq_resource(dev, res); - else if (type == SYS_RES_MEMORY) + if (type == SYS_RES_IRQ) { + if (*rid == 0) { + // pinned interrupt + result = bus_alloc_irq_resource(dev, res); + } else { + // msi or msi-x interrupt at index *rid - 1 + pci_info *info; + info = &((struct root_device_softc *)dev->root->softc)->pci_info; + res->r_bustag = 1; + res->r_bushandle = info->u.h0.interrupt_line + *rid - 1; + result = 0; + + // TODO: msi-x interrupts + } + } else if (type == SYS_RES_MEMORY) result = bus_alloc_mem_resource(dev, res, *rid); else if (type == SYS_RES_IOPORT) result = bus_alloc_ioport_resource(dev, res, *rid); @@ -309,6 +324,7 @@ bus_setup_intr(device_t dev, struct resource *res, int flags, intr->arg = arg; intr->irq = res->r_bushandle; intr->flags = flags; + intr->is_msi = false; intr->sem = -1; intr->thread = -1; @@ -341,6 +357,20 @@ bus_setup_intr(device_t dev, struct resource *res, int flags, intr_wrapper, intr, B_NO_HANDLED_INFO); } + if (status == B_OK && res->r_bustag == 1 && gPCIx86 != NULL) { + // this is an msi, enable it + pci_info *info + = &((struct root_device_softc *)dev->root->softc)->pci_info; + if (gPCIx86->enable_msi(info->bus, info->device, + info->function) != B_OK) { + device_printf(dev, "enabling msi failed\n"); + bus_teardown_intr(dev, res, intr); + return ENODEV; + } + + intr->is_msi = true; + } + if (status < B_OK) { free_internal_intr(intr); return status; @@ -358,6 +388,13 @@ bus_teardown_intr(device_t dev, struct resource *res, void *arg) { struct internal_intr *intr = arg; + if (intr->is_msi && gPCIx86 != NULL) { + // disable msi generation + pci_info *info + = &((struct root_device_softc *)dev->root->softc)->pci_info; + gPCIx86->disable_msi(info->bus, info->device, info->function); + } + if (intr->filter != NULL) { remove_io_interrupt_handler(intr->irq, (interrupt_handler)intr->filter, intr->arg); @@ -659,21 +696,45 @@ pci_find_extcap(device_t child, int capability, int *_capabilityRegister) int pci_msi_count(device_t dev) { - return 0; + pci_info *info; + if (gPCIx86 == NULL) + return 0; + + info = &((struct root_device_softc *)dev->root->softc)->pci_info; + return gPCIx86->get_msi_count(info->bus, info->device, info->function); } int pci_alloc_msi(device_t dev, int *count) { - return ENODEV; + pci_info *info; + uint8 startVector = 0; + if (gPCIx86 == NULL) + return ENODEV; + + info = &((struct root_device_softc *)dev->root->softc)->pci_info; + + if (gPCIx86->configure_msi(info->bus, info->device, info->function, *count, + &startVector) != B_OK) { + return ENODEV; + } + + info->u.h0.interrupt_line = startVector; + return EOK; } int pci_release_msi(device_t dev) { - return ENODEV; + pci_info *info; + if (gPCIx86 == NULL) + return ENODEV; + + info = &((struct root_device_softc *)dev->root->softc)->pci_info; + gPCIx86->unconfigure_msi(info->bus, info->device, info->function); + return EOK; } diff --git a/src/libs/compat/freebsd_network/compat.c b/src/libs/compat/freebsd_network/compat.c index 3e66aebe8e..d2e780daa2 100644 --- a/src/libs/compat/freebsd_network/compat.c +++ b/src/libs/compat/freebsd_network/compat.c @@ -27,6 +27,7 @@ spinlock __haiku_intr_spinlock; struct net_stack_module_info *gStack; pci_module_info *gPci; +struct pci_x86_module_info *gPCIx86; static struct list sRootDevices; static int sNextUnit; diff --git a/src/libs/compat/freebsd_network/device.h b/src/libs/compat/freebsd_network/device.h index 78e1d59520..0a6349f809 100644 --- a/src/libs/compat/freebsd_network/device.h +++ b/src/libs/compat/freebsd_network/device.h @@ -43,6 +43,7 @@ enum { extern struct net_stack_module_info *gStack; extern pci_module_info *gPci; +extern struct pci_x86_module_info *gPCIx86; static inline void diff --git a/src/libs/compat/freebsd_network/driver.c b/src/libs/compat/freebsd_network/driver.c index e37ce4960e..1679a44fc7 100644 --- a/src/libs/compat/freebsd_network/driver.c +++ b/src/libs/compat/freebsd_network/driver.c @@ -19,6 +19,7 @@ #include #include +#include #include @@ -149,6 +150,10 @@ _fbsd_init_driver(driver_t *driver) if (status < B_OK) return status; + // if it fails we just don't support x86 specific features (like MSIs) + if (get_module(B_PCI_X86_MODULE_NAME, (module_info **)&gPCIx86) != B_OK) + gPCIx86 = NULL; + status = init_hard_clock(); if (status < B_OK) goto err1; @@ -220,6 +225,9 @@ err2: uninit_hard_clock(); err1: put_module(B_PCI_MODULE_NAME); + if (gPCIx86 != NULL) + put_module(B_PCI_X86_MODULE_NAME); + return status; } @@ -243,4 +251,6 @@ _fbsd_uninit_driver(driver_t *driver) uninit_mutexes(); put_module(B_PCI_MODULE_NAME); + if (gPCIx86 != NULL) + put_module(B_PCI_X86_MODULE_NAME); }