diff --git a/sys/compat/linuxkpi/common/include/linux/interrupt.h b/sys/compat/linuxkpi/common/include/linux/interrupt.h --- a/sys/compat/linuxkpi/common/include/linux/interrupt.h +++ b/sys/compat/linuxkpi/common/include/linux/interrupt.h @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -51,10 +52,15 @@ struct resource *res; void *arg; irqreturn_t (*handler)(int, void *); + irqreturn_t (*thread_handler)(int, void *); void *tag; unsigned int irq; }; +void linux_irq_handler(void *); +void lkpi_devm_irq_release(struct device *, void *); +void lkpi_irq_release(struct device *, struct irq_ent *); + static inline int linux_irq_rid(struct device *dev, unsigned int irq) { @@ -65,8 +71,6 @@ return (0); } -extern void linux_irq_handler(void *); - static inline struct irq_ent * linux_irq_ent(struct device *dev, unsigned int irq) { @@ -80,8 +84,9 @@ } static inline int -request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, - const char *name, void *arg) +_request_irq(struct device *xdev, unsigned int irq, + irq_handler_t handler, irq_handler_t thread_handler, + unsigned long flags, const char *name, void *arg) { struct resource *res; struct irq_ent *irqe; @@ -92,27 +97,70 @@ dev = linux_pci_find_irq_dev(irq); if (dev == NULL) return -ENXIO; + if (xdev != NULL && xdev != dev) + return -ENXIO; rid = linux_irq_rid(dev, irq); res = bus_alloc_resource_any(dev->bsddev, SYS_RES_IRQ, &rid, flags | RF_ACTIVE); if (res == NULL) return (-ENXIO); - irqe = kmalloc(sizeof(*irqe), GFP_KERNEL); + if (xdev != NULL) + irqe = lkpi_devres_alloc(lkpi_devm_irq_release, sizeof(*irqe), + GFP_KERNEL | __GFP_ZERO); + else + irqe = kzalloc(sizeof(*irqe), GFP_KERNEL); irqe->dev = dev; irqe->res = res; irqe->arg = arg; irqe->handler = handler; + irqe->thread_handler = thread_handler; irqe->irq = irq; + error = bus_setup_intr(dev->bsddev, res, INTR_TYPE_NET | INTR_MPSAFE, NULL, linux_irq_handler, irqe, &irqe->tag); - if (error) { - bus_release_resource(dev->bsddev, SYS_RES_IRQ, rid, irqe->res); - kfree(irqe); - return (-error); - } + if (error) + goto errout; list_add(&irqe->links, &dev->irqents); + if (xdev != NULL) + devres_add(xdev, irqe); return 0; + +errout: + bus_release_resource(dev->bsddev, SYS_RES_IRQ, rid, irqe->res); + if (xdev != NULL) + devres_free(irqe); + else + kfree(irqe); + return (-error); +} + +static inline int +request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, + const char *name, void *arg) +{ + + return (_request_irq(NULL, irq, handler, NULL, flags, name, arg)); +} + +static inline int +request_threaded_irq(int irq, irq_handler_t handler, + irq_handler_t thread_handler, unsigned long flags, + const char *name, void *arg) +{ + + return (_request_irq(NULL, irq, handler, thread_handler, + flags, name, arg)); +} + +static inline int +devm_request_threaded_irq(struct device *dev, int irq, + irq_handler_t handler, irq_handler_t thread_handler, + unsigned long flags, const char *name, void *arg) +{ + + return (_request_irq(dev, irq, handler, thread_handler, + flags, name, arg)); } static inline int @@ -166,26 +214,41 @@ } static inline void -free_irq(unsigned int irq, void *device) +free_irq(unsigned int irq, void *device __unused) { struct irq_ent *irqe; struct device *dev; - int rid; dev = linux_pci_find_irq_dev(irq); if (dev == NULL) return; - rid = linux_irq_rid(dev, irq); irqe = linux_irq_ent(dev, irq); if (irqe == NULL) return; - if (irqe->tag != NULL) - bus_teardown_intr(dev->bsddev, irqe->res, irqe->tag); - bus_release_resource(dev->bsddev, SYS_RES_IRQ, rid, irqe->res); - list_del(&irqe->links); + lkpi_irq_release(dev, irqe); kfree(irqe); } +static inline void +devm_free_irq(struct device *xdev, unsigned int irq, void *p) +{ + struct device *dev; + struct irq_ent *irqe; + + dev = linux_pci_find_irq_dev(irq); + if (dev == NULL) + return; + if (xdev != dev) + return; + irqe = linux_irq_ent(dev, irq); + if (irqe == NULL) + return; + lkpi_irq_release(dev, irqe); + lkpi_devres_unlink(dev, irqe); + lkpi_devres_free(irqe); + return; +} + static inline int irq_set_affinity_hint(int vector, cpumask_t *mask) { diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c --- a/sys/compat/linuxkpi/common/src/linux_compat.c +++ b/sys/compat/linuxkpi/common/src/linux_compat.c @@ -2463,6 +2463,30 @@ free(ar, M_KMALLOC); } +void +lkpi_irq_release(struct device *dev, struct irq_ent *irqe) +{ + + if (irqe->tag != NULL) + bus_teardown_intr(dev->bsddev, irqe->res, irqe->tag); + if (irqe->res != NULL) + bus_release_resource(dev->bsddev, SYS_RES_IRQ, + rman_get_rid(irqe->res), irqe->res); + list_del(&irqe->links); +} + +void +lkpi_devm_irq_release(struct device *dev, void *p) +{ + struct irq_ent *irqe; + + if (dev == NULL || p == NULL) + return; + + irqe = p; + lkpi_irq_release(dev, irqe); +} + void linux_irq_handler(void *ent) { @@ -2472,7 +2496,12 @@ return; irqe = ent; - irqe->handler(irqe->irq, irqe->arg); + if (irqe->handler(irqe->irq, irqe->arg) == IRQ_WAKE_THREAD && + irqe->thread_handler != NULL) { + THREAD_SLEEPING_OK(); + irqe->thread_handler(irqe->irq, irqe->arg); + THREAD_NO_SLEEPING(); + } } #if defined(__i386__) || defined(__amd64__)