399 lines
11 KiB
Plaintext
399 lines
11 KiB
Plaintext
From: Jeff Mahoney <jeffm@suse.com>
|
|
Subject: kdb: Cleanup KDB_USB
|
|
Patch-mainline: Who knows?
|
|
|
|
kdb-common adds a KDB interface for USB keyboards. Unfortunately it
|
|
ends up duplicating some core USB functions wholesale. As these functions
|
|
change, the KDB implementation lags behind.
|
|
|
|
This patch avoids the duplication of functionality and hooks into the
|
|
existing functions.
|
|
|
|
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
|
|
---
|
|
arch/ia64/kdb/kdba_io.c | 25 ++++
|
|
drivers/usb/host/ehci-q.c | 276 +++++++++-------------------------------------
|
|
2 files changed, 79 insertions(+), 222 deletions(-)
|
|
|
|
--- a/arch/ia64/kdb/kdba_io.c
|
|
+++ b/arch/ia64/kdb/kdba_io.c
|
|
@@ -70,7 +70,11 @@ static unsigned char kdb_usb_keycode[256
|
|
* Attach a USB keyboard to kdb.
|
|
*/
|
|
int
|
|
-kdb_usb_keyboard_attach(struct urb *urb, unsigned char *buffer, void *poll_func)
|
|
+kdb_usb_keyboard_attach(struct urb *urb, unsigned char *buffer,
|
|
+ void *poll_func, void *compl_func,
|
|
+ kdb_hc_keyboard_attach_t kdb_hc_keyboard_attach,
|
|
+ kdb_hc_keyboard_detach_t kdb_hc_keyboard_detach,
|
|
+ unsigned int bufsize, struct urb *hid_urb)
|
|
{
|
|
int i;
|
|
int rc = -1;
|
|
@@ -93,6 +97,17 @@ kdb_usb_keyboard_attach(struct urb *urb,
|
|
kdb_usb_kbds[i].buffer = buffer;
|
|
kdb_usb_kbds[i].poll_func = poll_func;
|
|
|
|
+ kdb_usb_kbds[i].kdb_hc_urb_complete = compl_func;
|
|
+ kdb_usb_kbds[i].kdb_hc_keyboard_attach = kdb_hc_keyboard_attach;
|
|
+ kdb_usb_kbds[i].kdb_hc_keyboard_detach = kdb_hc_keyboard_detach;
|
|
+
|
|
+ /* USB Host Controller specific Keyboadr attach callback.
|
|
+ * Currently only UHCI has this callback.
|
|
+ */
|
|
+ if (kdb_usb_kbds[i].kdb_hc_keyboard_attach)
|
|
+ kdb_usb_kbds[i].kdb_hc_keyboard_attach(i, bufsize);
|
|
+
|
|
+
|
|
rc = 0; /* success */
|
|
|
|
break;
|
|
@@ -125,11 +140,19 @@ kdb_usb_keyboard_detach(struct urb *urb)
|
|
if (kdb_usb_kbds[i].urb != urb)
|
|
continue;
|
|
|
|
+ /* USB Host Controller specific Keyboard detach callback.
|
|
+ * Currently only UHCI has this callback.
|
|
+ */
|
|
+ if (kdb_usb_kbds[i].kdb_hc_keyboard_detach)
|
|
+ kdb_usb_kbds[i].kdb_hc_keyboard_detach(urb, i);
|
|
+
|
|
+
|
|
/* found it, clear the index */
|
|
kdb_usb_kbds[i].urb = NULL;
|
|
kdb_usb_kbds[i].buffer = NULL;
|
|
kdb_usb_kbds[i].poll_func = NULL;
|
|
kdb_usb_kbds[i].caps_lock = 0;
|
|
+ kdb_usb_kbds[i].hid_urb = NULL;
|
|
|
|
rc = 0; /* success */
|
|
|
|
--- a/drivers/usb/host/ehci-q.c
|
|
+++ b/drivers/usb/host/ehci-q.c
|
|
@@ -296,6 +296,28 @@ __acquires(ehci->lock)
|
|
spin_lock (&ehci->lock);
|
|
}
|
|
|
|
+/*
|
|
+ * Lock hackery here...
|
|
+ * ehci_urb_done() makes the assumption that it's called with ehci->lock held.
|
|
+ * So, lock it if it isn't already.
|
|
+ */
|
|
+static void
|
|
+kdb_ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status)
|
|
+__acquires(ehci->lock)
|
|
+__releases(ehci->lock)
|
|
+{
|
|
+#ifdef CONFIG_KDB_USB
|
|
+ int locked;
|
|
+ if (!spin_is_locked(&ehci->lock)) {
|
|
+ spin_lock(&ehci->lock);
|
|
+ locked = 1;
|
|
+ }
|
|
+ ehci_urb_done(ehci, urb, status);
|
|
+ if (locked)
|
|
+ spin_unlock(&ehci->lock);
|
|
+#endif
|
|
+}
|
|
+
|
|
static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
|
|
static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
|
|
|
|
@@ -305,9 +327,16 @@ static int qh_schedule (struct ehci_hcd
|
|
* Process and free completed qtds for a qh, returning URBs to drivers.
|
|
* Chases up to qh->hw_current. Returns number of completions called,
|
|
* indicating how much "real" work we did.
|
|
+ *
|
|
+ * The KDB part is ugly but KDB wants its own copy and it keeps getting
|
|
+ * out of sync. The difference with kdb=1 is that we will only process
|
|
+ * qtds that are associated with kdburb. ehci_urb_done also releases
|
|
+ * and retakes ehci->lock. We may not have that lock while KDB is
|
|
+ * running.
|
|
*/
|
|
static unsigned
|
|
-qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|
+__qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, int kdb,
|
|
+ struct urb *kdburb)
|
|
{
|
|
struct ehci_qtd *last, *end = qh->dummy;
|
|
struct list_head *entry, *tmp;
|
|
@@ -318,6 +347,9 @@ qh_completions (struct ehci_hcd *ehci, s
|
|
const __le32 halt = HALT_BIT(ehci);
|
|
struct ehci_qh_hw *hw = qh->hw;
|
|
|
|
+ if (kdb && !kdburb)
|
|
+ return 0;
|
|
+
|
|
if (unlikely (list_empty (&qh->qtd_list)))
|
|
return count;
|
|
|
|
@@ -353,10 +385,18 @@ qh_completions (struct ehci_hcd *ehci, s
|
|
qtd = list_entry (entry, struct ehci_qtd, qtd_list);
|
|
urb = qtd->urb;
|
|
|
|
+ if (kdburb && urb != kdburb)
|
|
+ continue;
|
|
+
|
|
/* clean up any state from previous QTD ...*/
|
|
if (last) {
|
|
if (likely (last->urb != urb)) {
|
|
- ehci_urb_done(ehci, last->urb, last_status);
|
|
+ if (kdb)
|
|
+ kdb_ehci_urb_done(ehci, last->urb,
|
|
+ last_status);
|
|
+ else
|
|
+ ehci_urb_done(ehci, last->urb,
|
|
+ last_status);
|
|
count++;
|
|
last_status = -EINPROGRESS;
|
|
}
|
|
@@ -522,7 +562,10 @@ halt:
|
|
|
|
/* last urb's completion might still need calling */
|
|
if (likely (last != NULL)) {
|
|
- ehci_urb_done(ehci, last->urb, last_status);
|
|
+ if (kdb)
|
|
+ kdb_ehci_urb_done(ehci, last->urb, last_status);
|
|
+ else
|
|
+ ehci_urb_done(ehci, last->urb, last_status);
|
|
count++;
|
|
ehci_qtd_free (ehci, last);
|
|
}
|
|
@@ -577,227 +620,18 @@ halt:
|
|
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)
|
|
+static unsigned
|
|
+qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|
{
|
|
- struct ehci_qtd *last = NULL, *end = qh->dummy;
|
|
- struct list_head *entry, *tmp;
|
|
- int last_status = -EINPROGRESS;
|
|
- 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;
|
|
- int qtd_status;
|
|
-
|
|
- 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)) {
|
|
- /*
|
|
- * Lock hackery here...
|
|
- * ehci_urb_done() makes the assumption
|
|
- * that it's called with ehci->lock held.
|
|
- * So, lock it if it isn't already.
|
|
- */
|
|
- if (!spin_is_locked(&ehci->lock))
|
|
- spin_lock(&ehci->lock);
|
|
-
|
|
- ehci_urb_done(ehci, last->urb, last_status);
|
|
-
|
|
- /*
|
|
- * ehci_urb_done() releases and reacquires
|
|
- * ehci->lock, so release it here.
|
|
- */
|
|
- if (spin_is_locked(&ehci->lock))
|
|
- spin_unlock (&ehci->lock);
|
|
-
|
|
- count++;
|
|
- }
|
|
- ehci_qtd_free (ehci, last);
|
|
- last = NULL;
|
|
- last_status = -EINPROGRESS;
|
|
- }
|
|
-
|
|
- /* 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)))
|
|
- last_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(last_status == -EINPROGRESS &&
|
|
- !urb->unlinked))
|
|
- 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 */
|
|
- qtd_status = qtd_copy_status(ehci, urb, qtd->length, token);
|
|
- if (unlikely(qtd_status == -EREMOTEIO)) {
|
|
- do_status = (!urb->unlinked &&
|
|
- usb_pipecontrol(urb->pipe));
|
|
- qtd_status = 0;
|
|
- }
|
|
- if (likely(last_status == -EINPROGRESS))
|
|
- last_status = qtd_status;
|
|
-
|
|
- 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)) {
|
|
- /*
|
|
- * Lock hackery here...
|
|
- * ehci_urb_done() makes the assumption
|
|
- * that it's called with ehci->lock held.
|
|
- * So, lock it if it isn't already.
|
|
- */
|
|
- if (!spin_is_locked(&ehci->lock))
|
|
- spin_lock(&ehci->lock);
|
|
-
|
|
- ehci_urb_done(ehci, last->urb, last_status);
|
|
-
|
|
- /*
|
|
- * ehci_urb_done() releases and reacquires
|
|
- * ehci->lock, so release it here.
|
|
- */
|
|
- 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;
|
|
+ return __qh_completions(ehci, qh, 0, NULL);
|
|
}
|
|
|
|
-#endif /* CONFIG_KDB_USB */
|
|
+unsigned
|
|
+qh_completions_kdb(struct ehci_hcd *ehci, struct ehci_qh *qh,
|
|
+ struct urb *kdburb)
|
|
+{
|
|
+ return __qh_completions(ehci, qh, 1, kdburb);
|
|
+}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|