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.