[PATCH] KDB USB EHCI Support

Aaron Young <[email protected]>
Newsgroups gmane.linux.kernel.debugging
Message-ID <[email protected]>
 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.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.