This patch adds support to KDB for USB keyboards attached
 via EHCI. This happens when USB keyboards are connected via
 a USB2.0 hub forcing the Linux kernel EHCI driver to take control instead
 of the "companion" OHCI or UHCI drivers.

 It also adds a new boot parameter, "kdbnousb", which when set
 will disable KDB from configuring and using USB keyboards. This
 may prove useful if someone experiences a problem with KDB and 
 USB keyboards by allowing KDB to still be used via a serial attached
 keyboard.

Patch is against 2.6.23 (and should be applied after kdb-v4.4-2.6.23-*-2 patch 
set)

Signed-off-by:  Aaron Young ([EMAIL PROTECTED])


Index: linux/arch/i386/kdb/kdba_io.c
===================================================================
--- linux.orig/arch/i386/kdb/kdba_io.c  2007-11-13 13:52:15.000000000 -0600
+++ linux/arch/i386/kdb/kdba_io.c       2007-11-13 14:08:36.856610202 -0600
@@ -34,6 +34,8 @@
 #define KDB_USB_NUM_KEYBOARDS   8
 struct kdb_usb_kbd_info kdb_usb_kbds[KDB_USB_NUM_KEYBOARDS];
 
+extern int kdb_no_usb;
+
 static unsigned char kdb_usb_keycode[256] = {
          0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
         50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,  2,  3,
@@ -63,6 +65,9 @@
         int     i;
         int     rc = -1;
 
+       if (kdb_no_usb)
+               return 0;
+
         /*
          * Search through the array of KDB USB keyboards (kdb_usb_kbds)
          * looking for a free index. If found, assign the keyboard to
@@ -97,6 +102,9 @@
         int     i;
         int     rc = -1;
 
+       if (kdb_no_usb)
+               return 0;
+
         /*
          * Search through the array of KDB USB keyboards (kdb_usb_kbds)
          * looking for the index with the matching URB. If found,
@@ -134,6 +142,9 @@
         int     ret;
         unsigned char keycode, spec;
         extern u_short plain_map[], shift_map[], ctrl_map[];
+
+       if (kdb_no_usb)
+               return -1;
 
         /*
          * Loop through all the USB keyboard(s) and return
Index: linux/arch/ia64/kdb/kdba_io.c
===================================================================
--- linux.orig/arch/ia64/kdb/kdba_io.c  2007-11-13 13:52:16.000000000 -0600
+++ linux/arch/ia64/kdb/kdba_io.c       2007-11-13 14:09:07.484469037 -0600
@@ -44,6 +44,8 @@
 #define KDB_USB_NUM_KEYBOARDS  8
 struct kdb_usb_kbd_info kdb_usb_kbds[KDB_USB_NUM_KEYBOARDS];
 
+extern int kdb_no_usb;
+
 static unsigned char kdb_usb_keycode[256] = {
          0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
         50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,  2,  3,
@@ -73,6 +75,9 @@
        int     i;
        int     rc = -1;
 
+       if (kdb_no_usb)
+               return 0;
+
        /*
         * Search through the array of KDB USB keyboards (kdb_usb_kbds)
         * looking for a free index. If found, assign the keyboard to
@@ -107,6 +112,9 @@
        int     i;
        int     rc = -1;
 
+       if (kdb_no_usb)
+               return 0;
+
        /*
         * Search through the array of KDB USB keyboards (kdb_usb_kbds)
         * looking for the index with the matching URB. If found,
@@ -144,6 +152,9 @@
        int     ret;
        unsigned char keycode, spec;
        extern u_short plain_map[], shift_map[], ctrl_map[];
+
+       if (kdb_no_usb)
+               return -1;
 
        /*
         * Loop through all the USB keyboard(s) and return
Index: linux/arch/x86_64/kdb/kdba_io.c
===================================================================
--- linux.orig/arch/x86_64/kdb/kdba_io.c        2007-11-13 13:52:15.000000000 
-0600
+++ linux/arch/x86_64/kdb/kdba_io.c     2007-11-13 14:09:43.601018582 -0600
@@ -34,6 +34,8 @@
 #define KDB_USB_NUM_KEYBOARDS   8
 struct kdb_usb_kbd_info kdb_usb_kbds[KDB_USB_NUM_KEYBOARDS];
 
+extern int kdb_no_usb;
+
 static unsigned char kdb_usb_keycode[256] = {
          0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
         50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,  2,  3,
@@ -63,6 +65,9 @@
         int     i;
         int     rc = -1;
 
+       if (kdb_no_usb)
+               return 0;
+
         /*
          * Search through the array of KDB USB keyboards (kdb_usb_kbds)
          * looking for a free index. If found, assign the keyboard to
@@ -97,6 +102,9 @@
         int     i;
         int     rc = -1;
 
+       if (kdb_no_usb)
+               return 0;
+
         /*
          * Search through the array of KDB USB keyboards (kdb_usb_kbds)
          * looking for the index with the matching URB. If found,
@@ -134,6 +142,9 @@
         int     ret;
         unsigned char keycode, spec;
         extern u_short plain_map[], shift_map[], ctrl_map[];
+
+       if (kdb_no_usb)
+               return -1;
 
         /*
          * Loop through all the USB keyboard(s) and return
Index: linux/drivers/usb/host/ehci-hcd.c
===================================================================
--- linux.orig/drivers/usb/host/ehci-hcd.c      2007-11-13 13:52:15.000000000 
-0600
+++ linux/drivers/usb/host/ehci-hcd.c   2007-11-13 14:10:30.446918384 -0600
@@ -921,7 +921,24 @@
 int
 ehci_kdb_poll_char(struct urb *urb)
 {
-       /* routine not yet implemented */
+       struct ehci_hcd *ehci;
+
+       /* just to make sure */
+       if (!urb || !urb->dev || !urb->dev->bus)
+               return -1;
+
+       ehci = (struct ehci_hcd *) hcd_to_ehci(bus_to_hcd(urb->dev->bus));
+
+       /* make sure */
+       if (!ehci)
+               return -1;
+
+       if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
+               return -1;
+
+       if (qh_completions_kdb(ehci, urb->hcpriv, urb))
+               return 0;
+
        return -1;
 }
 
Index: linux/drivers/usb/host/ehci-q.c
===================================================================
--- linux.orig/drivers/usb/host/ehci-q.c        2007-10-09 15:31:38.000000000 
-0500
+++ linux/drivers/usb/host/ehci-q.c     2007-11-13 14:11:02.490953190 -0600
@@ -444,6 +444,212 @@
        return count;
 }
 
+#ifdef CONFIG_KDB_USB
+/*
+ * This routine is basically a copy of qh_completions() for use by KDB.
+ * It is modified to only work on qtds which are associated
+ * with 'kdburb'. Also, there are some fixups related to locking.
+ */
+unsigned
+qh_completions_kdb(struct ehci_hcd *ehci, struct ehci_qh *qh, struct urb 
*kdburb)
+{
+       struct ehci_qtd         *last = NULL, *end = qh->dummy;
+       struct list_head        *entry, *tmp;
+       int                     stopped;
+       unsigned                count = 0;
+       int                     do_status = 0;
+       u8                      state;
+       u32                     halt = HALT_BIT(ehci);
+
+       /* verify params are valid */
+       if (!qh || !kdburb)
+               return 0;
+
+       if (unlikely (list_empty (&qh->qtd_list)))
+               return count;
+
+       /* completions (or tasks on other cpus) must never clobber HALT
+        * till we've gone through and cleaned everything up, even when
+        * they add urbs to this qh's queue or mark them for unlinking.
+        *
+        * NOTE:  unlinking expects to be done in queue order.
+        */
+       state = qh->qh_state;
+       qh->qh_state = QH_STATE_COMPLETING;
+       stopped = (state == QH_STATE_IDLE);
+
+       /* remove de-activated QTDs from front of queue.
+        * after faults (including short reads), cleanup this urb
+        * then let the queue advance.
+        * if queue is stopped, handles unlinks.
+        */
+       list_for_each_safe (entry, tmp, &qh->qtd_list) {
+               struct ehci_qtd *qtd;
+               struct urb      *urb;
+               u32             token = 0;
+
+               qtd = list_entry (entry, struct ehci_qtd, qtd_list);
+               urb = qtd->urb;
+
+               if (urb != kdburb)
+                       continue;
+
+               /* clean up any state from previous QTD ...*/
+               if (last) {
+                       if (likely (last->urb != urb)) {
+                               ehci_urb_done (ehci, last->urb);
+                               /*
+                                * ehci_urb_done() makes the assumption
+                                * that it's called with ehci->lock held and
+                                * releases and then reacquires the lock.
+                                * Thus, ehci_urb_done() returns with the lock
+                                * held. We don't want to keep the lock
+                                * held here, so release it. Check it first,
+                                * just in case ehci_urb_done() is ever
+                                * changed to not grab the lock.
+                                */
+                               if (spin_is_locked(&ehci->lock))
+                                       spin_unlock (&ehci->lock);
+
+                               count++;
+                       }
+                       ehci_qtd_free (ehci, last);
+                       last = NULL;
+               }
+
+               /* ignore urbs submitted during completions we reported */
+               if (qtd == end)
+                       break;
+
+               /* hardware copies qtd out of qh overlay */
+               rmb ();
+               token = hc32_to_cpu(ehci, qtd->hw_token);
+
+               /* always clean up qtds the hc de-activated */
+               if ((token & QTD_STS_ACTIVE) == 0) {
+
+                       if ((token & QTD_STS_HALT) != 0) {
+                               stopped = 1;
+
+                       /* magic dummy for some short reads; qh won't advance.
+                        * that silicon quirk can kick in with this dummy too.
+                        */
+                       } else if (IS_SHORT_READ (token)
+                                       && !(qtd->hw_alt_next
+                                               & EHCI_LIST_END(ehci))) {
+                               stopped = 1;
+                               goto halt;
+                       }
+
+               /* stop scanning when we reach qtds the hc is using */
+               } else if (likely (!stopped
+                               && HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) {
+                       break;
+
+               } else {
+                       stopped = 1;
+
+                       if (unlikely (!HC_IS_RUNNING 
(ehci_to_hcd(ehci)->state)))
+                               urb->status = -ESHUTDOWN;
+
+                       /* ignore active urbs unless some previous qtd
+                        * for the urb faulted (including short read) or
+                        * its urb was canceled.  we may patch qh or qtds.
+                        */
+                       if (likely (urb->status == -EINPROGRESS))
+                               continue;
+
+                       /* issue status after short control reads */
+                       if (unlikely (do_status != 0)
+                                       && QTD_PID (token) == 0 /* OUT */) {
+                               do_status = 0;
+                               continue;
+                       }
+
+                       /* token in overlay may be most current */
+                       if (state == QH_STATE_IDLE
+                                       && cpu_to_hc32(ehci, qtd->qtd_dma)
+                                               == qh->hw_current)
+                               token = hc32_to_cpu(ehci, qh->hw_token);
+
+                       /* force halt for unlinked or blocked qh, so we'll
+                        * patch the qh later and so that completions can't
+                        * activate it while we "know" it's stopped.
+                        */
+                       if ((halt & qh->hw_token) == 0) {
+halt:
+                               qh->hw_token |= halt;
+                               wmb ();
+                       }
+               }
+
+               /* remove it from the queue */
+               spin_lock (&urb->lock);
+               qtd_copy_status (ehci, urb, qtd->length, token);
+               do_status = (urb->status == -EREMOTEIO)
+                               && usb_pipecontrol (urb->pipe);
+               spin_unlock (&urb->lock);
+
+               if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
+                       last = list_entry (qtd->qtd_list.prev,
+                                       struct ehci_qtd, qtd_list);
+                       last->hw_next = qtd->hw_next;
+               }
+               list_del (&qtd->qtd_list);
+               last = qtd;
+       }
+
+       /* last urb's completion might still need calling */
+       if (likely (last != NULL)) {
+               ehci_urb_done (ehci, last->urb);
+               /*
+                * ehci_urb_done() makes the assumption
+                * that it's called with ehci->lock held and
+                * releases and then reacquires the lock.
+                * Thus, ehci_urb_done() returns with the lock
+                * held. We don't want to keep the lock
+                * held here, so release it. Check it first, just in
+                * case ehci_urb_done() is ever changed to not grab the lock.
+                */
+               if (spin_is_locked(&ehci->lock))
+                       spin_unlock (&ehci->lock);
+
+               count++;
+               ehci_qtd_free (ehci, last);
+       }
+
+       /* restore original state; caller must unlink or relink */
+       qh->qh_state = state;
+
+       /* be sure the hardware's done with the qh before refreshing
+        * it after fault cleanup, or recovering from silicon wrongly
+        * overlaying the dummy qtd (which reduces DMA chatter).
+        */
+       if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END(ehci)) {
+               switch (state) {
+               case QH_STATE_IDLE:
+                       qh_refresh(ehci, qh);
+                       break;
+               case QH_STATE_LINKED:
+                       /* should be rare for periodic transfers,
+                        * except maybe high bandwidth ...
+                        */
+                       if ((cpu_to_hc32(ehci, QH_SMASK)
+                                       & qh->hw_info2) != 0) {
+                               intr_deschedule (ehci, qh);
+                               (void) qh_schedule (ehci, qh);
+                       } else
+                               unlink_async (ehci, qh);
+                       break;
+               /* otherwise, unlink already started */
+               }
+       }
+
+       return count;
+}
+
+#endif /* CONFIG_KDB_USB */
+
 /*-------------------------------------------------------------------------*/
 
 // high bandwidth multiplier, as encoded in highspeed endpoint descriptors
Index: linux/drivers/usb/host/ohci-hcd.c
===================================================================
--- linux.orig/drivers/usb/host/ohci-hcd.c      2007-11-13 13:52:14.000000000 
-0600
+++ linux/drivers/usb/host/ohci-hcd.c   2007-11-13 14:13:50.996159433 -0600
@@ -857,10 +857,19 @@
        struct ohci_hcd *ohci;
        struct ohci_regs * regs;
 
-       if (!urb)       /* should not happen */
+       /* just to make sure */
+       if (!urb || !urb->dev || !urb->dev->bus)
                return -1;
 
        ohci = (struct ohci_hcd *) hcd_to_ohci(bus_to_hcd(urb->dev->bus));
+
+       /* make sure */
+       if (!ohci)
+               return -1;
+
+       if (!HC_IS_RUNNING (ohci_to_hcd(ohci)->state))
+               return -1;
+
        regs = ohci->regs;
 
        /* if the urb is not currently in progress resubmit it */
Index: linux/kdb/kdb_io.c
===================================================================
--- linux.orig/kdb/kdb_io.c     2007-11-13 13:52:14.000000000 -0600
+++ linux/kdb/kdb_io.c  2007-11-13 14:11:53.265345038 -0600
@@ -659,4 +659,18 @@
        return;
 }
 
+#ifdef CONFIG_KDB_USB
+
+int kdb_no_usb = 0;
+
+static int __init opt_kdbnousb(char *str)
+{
+       kdb_no_usb = 1;
+       return 0;
+}
+
+early_param("kdbnousb", opt_kdbnousb);
+
+#endif
+
 EXPORT_SYMBOL(kdb_read);
---------------------------
Use http://oss.sgi.com/ecartis to modify your settings or to unsubscribe.

Reply via email to