Backport threaded IRQ support using workqueues
authorLuis R. Rodriguez <lrodriguez@atheros.com>
Wed, 23 Sep 2009 17:26:47 +0000 (10:26 -0700)
committerLuis R. Rodriguez <lrodriguez@atheros.com>
Wed, 23 Sep 2009 17:26:47 +0000 (10:26 -0700)
b43 makes use this.

Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
compat/compat-2.6.31.h
compat/patches/09-threaded-irq.patch [new file with mode: 0644]

index 95a649cda5acc3d20e89c81e58f2bc3939e50ffc..154209c96f77d5aa64d0cb6ca01a173a03433f1d 100644 (file)
@@ -8,6 +8,8 @@
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31))
 
 #include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
 #include <net/dst.h>
 #include <net/genetlink.h>
 
 #define SDIO_DEVICE_ID_MARVELL_8688WLAN                0x9104
 #endif
 
+struct compat_threaded_irq {
+       unsigned int irq;
+       irq_handler_t handler;
+       irq_handler_t thread_fn;
+       void *dev_id;
+       char wq_name[64];
+       struct workqueue_struct *wq;
+       struct work_struct work;
+};
+
 /*
  * kmemleak was introduced on 2.6.31, since older kernels do not have
  * we simply ignore its tuning.
@@ -91,6 +103,81 @@ static inline void skb_dst_drop(struct sk_buff *skb)
 extern int genl_register_family_with_ops(struct genl_family *family,
        struct genl_ops *ops, size_t n_ops);
 
+
+/* Backport threaded IRQ support */
+
+static inline
+void compat_irq_work(struct work_struct *work)
+{
+       struct compat_threaded_irq *comp = container_of(work, struct compat_threaded_irq, work);
+       comp->thread_fn(comp->irq, comp->dev_id);
+}
+
+static inline
+irqreturn_t compat_irq_dispatcher(int irq, void *dev_id)
+{
+       struct compat_threaded_irq *comp = dev_id;
+       irqreturn_t res;
+
+       res = comp->handler(irq, comp->dev_id);
+       if (res == IRQ_WAKE_THREAD) {
+               queue_work(comp->wq, &comp->work);
+               res = IRQ_HANDLED;
+       }
+
+       return res;
+}
+
+static inline
+int compat_request_threaded_irq(struct compat_threaded_irq *comp,
+                               unsigned int irq,
+                               irq_handler_t handler,
+                               irq_handler_t thread_fn,
+                               unsigned long flags,
+                               const char *name,
+                               void *dev_id)
+{
+       comp->irq = irq;
+       comp->handler = handler;
+       comp->thread_fn = thread_fn;
+       comp->dev_id = dev_id;
+       INIT_WORK(&comp->work, compat_irq_work);
+
+       if (!comp->wq) {
+               snprintf(comp->wq_name, sizeof(comp->wq_name),
+                        "compirq/%u-%s", irq, name);
+               comp->wq = create_singlethread_workqueue(comp->wq_name);
+               if (!comp->wq) {
+                       printk(KERN_ERR "Failed to create compat-threaded-IRQ workqueue %s\n",
+                              comp->wq_name);
+                       return -ENOMEM;
+               }
+       }
+       return request_irq(irq, compat_irq_dispatcher, flags, name, comp);
+}
+
+static inline
+void compat_free_threaded_irq(struct compat_threaded_irq *comp)
+{
+       free_irq(comp->irq, comp);
+}
+
+static inline
+void compat_destroy_threaded_irq(struct compat_threaded_irq *comp)
+{
+       if (comp->wq)
+               destroy_workqueue(comp->wq);
+       comp->wq = NULL;
+}
+
+static inline
+void compat_synchronize_threaded_irq(struct compat_threaded_irq *comp)
+{
+       synchronize_irq(comp->irq);
+       cancel_work_sync(&comp->work);
+}
+
+
 #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) */
 
 #endif /* LINUX_26_31_COMPAT_H */
diff --git a/compat/patches/09-threaded-irq.patch b/compat/patches/09-threaded-irq.patch
new file mode 100644 (file)
index 0000000..e6daa15
--- /dev/null
@@ -0,0 +1,63 @@
+The 2.6.31 kernel has threaded IRQ support and b43 is the first
+wireless driver that makes use of it. To support threaded IRSs
+on older kernels we built our own struct compat_threaded_irq
+to queue_work() onto it as the kernel thread be running the
+thread in process context as well.
+
+--- a/drivers/net/wireless/b43/main.c  2009-09-23 10:17:44.852134031 -0700
++++ b/drivers/net/wireless/b43/main.c  2009-09-23 10:21:45.887734082 -0700
+@@ -3908,8 +3908,13 @@ redo:
+       if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) {
+               b43_sdio_free_irq(dev);
+       } else {
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
++              compat_synchronize_threaded_irq(&dev->irq_compat);
++              compat_free_threaded_irq(&dev->irq_compat);
++#else
+               synchronize_irq(dev->dev->irq);
+               free_irq(dev->dev->irq, dev);
++#endif
+       }
+       mutex_lock(&wl->mutex);
+       dev = wl->current_dev;
+@@ -3948,9 +3953,17 @@ static int b43_wireless_core_start(struc
+                       goto out;
+               }
+       } else {
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
+               err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler,
+                                          b43_interrupt_thread_handler,
+                                          IRQF_SHARED, KBUILD_MODNAME, dev);
++#else
++              err = compat_request_threaded_irq(&dev->irq_compat,
++                                                dev->dev->irq,
++                                                b43_interrupt_handler,
++                                                b43_interrupt_thread_handler,
++                                                IRQF_SHARED, KBUILD_MODNAME, dev);
++#endif
+               if (err) {
+                       b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq);
+                       goto out;
+@@ -4656,6 +4669,10 @@ static int b43_setup_bands(struct b43_wl
+ static void b43_wireless_core_detach(struct b43_wldev *dev)
+ {
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
++      if (dev->dev->bus->bustype != SSB_BUSTYPE_SDIO)
++              compat_destroy_threaded_irq(&dev->irq_compat);
++#endif
+       /* We release firmware that late to not be required to re-request
+        * is all the time when we reinit the core. */
+       b43_release_firmware(dev);
+--- a/drivers/net/wireless/b43/b43.h   2009-09-23 10:17:59.383718279 -0700
++++ b/drivers/net/wireless/b43/b43.h   2009-09-23 10:22:17.127771634 -0700
+@@ -822,6 +822,9 @@ struct b43_wldev {
+       unsigned int tx_count;
+       unsigned int rx_count;
+ #endif
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
++      struct compat_threaded_irq irq_compat;
++#endif
+ };
+ static inline struct b43_wl *hw_to_b43_wl(struct ieee80211_hw *hw)