qubes-linux-kernel/patches.suse/kdb-usb-rework
2010-07-07 13:12:45 +02:00

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);
+}
/*-------------------------------------------------------------------------*/