Hi Martin,

Le Mon, 1 Jun 2009 12:53:43 -0500,
Martin Hicks <[email protected]> a écrit :

> 
> On Tue, May 26, 2009 at 10:32:38AM +0200, Matthieu Fertré wrote:
> > Hi,
> > 
> > here is an update of patch from Konstantin Baydarov
> > <[email protected]>.
> > 
> > The patch was originally posted here:
> > http://oss.sgi.com/archives/kdb/2008-02/msg00006.html
> > 
> > This version has been tested over kdb-v4.4-2.6.30-rc2-*-2.patch and
> > works for me :-)
> > I have not testing building it as a module nor with keyboard
> > attached with EHCI or OHCI, thus it may be broken by the patch.
> 
> Doesn't compile for me against the latest git tree (2.6.30-rc7):

I am very sorry to have let you loose your time. My first patch was
incomplete! My source tree was crappy. A complete patch is
attached...


> 
> Is there any way you can test as a module and make sure that EHCI
> still works?  

In fact, there is no way to build as a module. I was mistaken by the
added EXPORT_SYMBOL but I guess there were in the original patch so
that KDB modules can use the exported functions if they need.

> There were many machines that had both EHCI and UHCI
> controllers.

The problem is that as soon as you plug directly the keyboard to the
USB port, it is recognized at USB 1.0 device and uses UHCI "driver".

I have tested to disable UHCI in the kernel to have only EHCI, the
keyboard is not recognized at all on the console. 

It seems that the only solution to test EHCI is to plug the keyboard on
a USB 2.0 hub and to plug the hub to to the USB port of the
motherboard. Unfortunately, I do not have any such USB 2.0 hub.

> 
> thanks
> mh
> 

Thanks, 

Matthieu Fertré

---
Add support for USB Keyboard attached to UHCI in KDB

Update of patch from Konstantin Baydarov <[email protected]>.

The patch was originally posted here:
http://oss.sgi.com/archives/kdb/2008-02/msg00006.html

diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index c9216a7..51ed359 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -342,8 +342,8 @@ config KDB_CONTINUE_CATASTROPHIC
          setting to 2.
 
 config KDB_USB
-       bool "Support for USB Keyboard in KDB (OHCI and/or EHCI only)"
-       depends on KDB && (USB_OHCI_HCD || USB_EHCI_HCD)
+       bool "Support for USB Keyboard in KDB"
+       depends on KDB && (USB_OHCI_HCD || USB_EHCI_HCD || USB_UHCI_HCD)
        help
          If you want to use kdb from USB keyboards then say Y here.  If you
          say N then kdb can only be used from a PC (AT) keyboard or a serial
diff --git a/arch/x86/kdb/kdba_io.c b/arch/x86/kdb/kdba_io.c
index 087d468..e754dd4 100644
--- a/arch/x86/kdb/kdba_io.c
+++ b/arch/x86/kdb/kdba_io.c
@@ -30,9 +30,8 @@
 
 #ifdef CONFIG_KDB_USB
 
-/* support up to 8 USB keyboards (probably excessive, but...) */
-#define KDB_USB_NUM_KEYBOARDS   8
 struct kdb_usb_kbd_info kdb_usb_kbds[KDB_USB_NUM_KEYBOARDS];
+EXPORT_SYMBOL(kdb_usb_kbds);
 
 extern int kdb_no_usb;
 
@@ -60,7 +59,12 @@ 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;
@@ -83,6 +87,16 @@ kdb_usb_keyboard_attach(struct urb *urb, unsigned char 
*buffer, void *poll_func)
                 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;
@@ -112,14 +126,23 @@ kdb_usb_keyboard_detach(struct urb *urb)
          */
 
         for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) {
-                if (kdb_usb_kbds[i].urb != urb)
+               if ((kdb_usb_kbds[i].urb != urb) &&
+                   (kdb_usb_kbds[i].hid_urb != urb))
                         continue;
 
                 /* found it, clear the index */
+
+               /* 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);
+
                 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 */
 
@@ -142,6 +165,9 @@ get_usb_char(void)
         int     ret;
         unsigned char keycode, spec;
         extern u_short plain_map[], shift_map[], ctrl_map[];
+       int     ret_key = -1, j, max;
+
+       ret = 1;
 
         if (kdb_no_usb)
                 return -1;
@@ -168,15 +194,24 @@ get_usb_char(void)
                 if (ret < 0) /* error or no characters, try the next kbd */
                         continue;
 
+               /* If 2 keys was pressed simultaneously,
+                * both keycodes will be in buffer.
+                * Last pressed key will be last non
+                * zero byte.
+                */
+               for (j=0; j<4; j++){
+                       if (!kdb_usb_kbds[i].buffer[2+j])
+                               break;
+               }
+               /* Last pressed key */
+               max = j + 1;
+
                 spec = kdb_usb_kbds[i].buffer[0];
                 keycode = kdb_usb_kbds[i].buffer[2];
                 kdb_usb_kbds[i].buffer[0] = (char)0;
                 kdb_usb_kbds[i].buffer[2] = (char)0;
 
-                if(kdb_usb_kbds[i].buffer[3]) {
-                        kdb_usb_kbds[i].buffer[3] = (char)0;
-                        continue;
-                }
+               ret_key = -1;
 
                 /* A normal key is pressed, decode it */
                 if(keycode)
@@ -188,10 +223,12 @@ get_usb_char(void)
                         {
                         case 0x2:
                         case 0x20: /* Shift */
-                                return shift_map[keycode];
+                                ret_key = shift_map[keycode];
+                               break;
                         case 0x1:
                         case 0x10: /* Ctrl */
-                                return ctrl_map[keycode];
+                                ret_key = ctrl_map[keycode];
+                               break;
                         case 0x4:
                         case 0x40: /* Alt */
                                 break;
@@ -200,28 +237,50 @@ get_usb_char(void)
                         switch(keycode)
                         {
                         case 0x1C: /* Enter */
-                                return 13;
+                                ret_key = 13;
+                               break;
 
                         case 0x3A: /* Capslock */
                                 kdb_usb_kbds[i].caps_lock = 
!(kdb_usb_kbds[i].caps_lock);
                                 break;
                         case 0x0E: /* Backspace */
-                                return 8;
+                                ret_key = 8;
+                               break;
                         case 0x0F: /* TAB */
-                                return 9;
+                                ret_key = 9;
+                               break;
                         case 0x77: /* Pause */
                                 break ;
                         default:
                                 if(!kdb_usb_kbds[i].caps_lock) {
-                                        return plain_map[keycode];
+                                        ret_key = plain_map[keycode];
                                 }
                                 else {
-                                        return shift_map[keycode];
+                                        ret_key = shift_map[keycode];
                                 }
                         }
                 }
+
+               if (ret_key != 1) {
+                       /* Key was pressed, return keycode */
+
+                       /* Clear buffer before urb resending */
+                       if (kdb_usb_kbds[i].buffer)
+                               for(j=0; j<8; j++)
+                                       kdb_usb_kbds[i].buffer[j] = (char)0;
+
+                       /* USB Host Controller specific Urb complete callback.
+                        * Currently only UHCI has this callback.
+                        */
+                       if (kdb_usb_kbds[i].kdb_hc_urb_complete)
+                               (*kdb_usb_kbds[i].kdb_hc_urb_complete)((struct 
urb *)kdb_usb_kbds[i].urb);
+
+                       return ret_key;
+               }
         }
 
+
+
         /* no chars were returned from any of the USB keyboards */
 
         return -1;
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 8c55733..8c342d1 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -1195,9 +1195,20 @@ static int hid_probe(struct usb_interface *intf, const 
struct usb_device_id *id)
                int ret;
                struct usbhid_device *usbhid = hid->driver_data;
                extern void *usb_hcd_get_kdb_poll_func(struct usb_device *udev);
+               extern void * usb_hcd_get_kdb_completion_func(struct usb_device 
*udev);
+               extern int usb_hcd_check_uhci(struct usb_device *udev);
+               extern kdb_hc_keyboard_attach_t
+                       usb_hcd_get_hc_keyboard_attach(struct usb_device *udev);
+               extern kdb_hc_keyboard_detach_t
+                       usb_hcd_get_hc_keyboard_detach(struct usb_device *udev);
 
                ret = kdb_usb_keyboard_attach(usbhid->urbin, usbhid->inbuf,
-                   usb_hcd_get_kdb_poll_func(interface_to_usbdev(intf)));
+                                             
usb_hcd_get_kdb_poll_func(interface_to_usbdev(intf)),
+                                             
usb_hcd_get_kdb_completion_func(interface_to_usbdev(intf)),
+                                             
usb_hcd_get_hc_keyboard_attach(interface_to_usbdev(intf)),
+                                             
usb_hcd_get_hc_keyboard_detach(interface_to_usbdev(intf)),
+                                             usbhid->bufsize,
+                                             NULL);
 
                if (ret == -1)
                        printk(": FAILED to register keyboard (%s) "
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 5af2a35..f9d7c97 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -38,6 +38,9 @@
 #include <asm/unaligned.h>
 #include <linux/platform_device.h>
 #include <linux/workqueue.h>
+#ifdef CONFIG_KDB_USB
+#include <linux/kdb.h>
+#endif
 
 #include <linux/usb.h>
 
@@ -2023,7 +2026,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown);
 void *
 usb_hcd_get_kdb_poll_func(struct usb_device *udev)
 {
-       struct usb_hcd  *hcd = bus_to_hcd(udev->bus);
+       struct usb_hcd  *hcd = bus_to_hcd(udev->bus);
 
        if (hcd && hcd->driver)
                return (void *)(hcd->driver->kdb_poll_char);
@@ -2031,8 +2034,63 @@ usb_hcd_get_kdb_poll_func(struct usb_device *udev)
        return NULL;
 }
 EXPORT_SYMBOL_GPL (usb_hcd_get_kdb_poll_func);
+
+void *
+usb_hcd_get_kdb_completion_func(struct usb_device *udev)
+{
+       struct usb_hcd  *hcd = bus_to_hcd(udev->bus);
+
+       if (hcd && hcd->driver)
+               return (void *)(hcd->driver->kdb_completion);
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL (usb_hcd_get_kdb_completion_func);
+
+int
+usb_hcd_check_uhci(struct usb_device *udev)
+{
+       struct usb_hcd  *hcd = bus_to_hcd(udev->bus);
+
+       if (hcd && hcd->driver){
+               if (!(strcmp(hcd->driver->description, "uhci_hcd")))
+                       return 1;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL (usb_hcd_check_uhci);
+
+kdb_hc_keyboard_attach_t
+usb_hcd_get_hc_keyboard_attach(struct usb_device *udev)
+{
+       struct usb_hcd  *hcd = bus_to_hcd(udev->bus);
+
+       if (hcd && hcd->driver){
+               return hcd->driver->kdb_hc_keyboard_attach;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL (usb_hcd_get_hc_keyboard_attach);
+
+kdb_hc_keyboard_detach_t
+usb_hcd_get_hc_keyboard_detach(struct usb_device *udev)
+{
+       struct usb_hcd  *hcd = bus_to_hcd(udev->bus);
+
+       if (hcd && hcd->driver){
+               return hcd->driver->kdb_hc_keyboard_detach;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL (usb_hcd_get_hc_keyboard_detach);
+
+
 #endif /* CONFIG_KDB_USB */
 
+
 /*-------------------------------------------------------------------------*/
 
 #if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index d2e9e35..65b4e96 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -22,6 +22,9 @@
 #ifdef __KERNEL__
 
 #include <linux/rwsem.h>
+#ifdef CONFIG_KDB_USB
+#include <linux/kdb.h>
+#endif
 
 #define MAX_TOPO_LEVEL         6
 
@@ -222,6 +225,9 @@ struct hc_driver {
 #ifdef CONFIG_KDB_USB
        /* KDB poll function for this HC */
        int     (*kdb_poll_char)(struct urb *urb);
+       void    (*kdb_completion)(struct urb *urb);
+       kdb_hc_keyboard_attach_t        kdb_hc_keyboard_attach;
+       kdb_hc_keyboard_detach_t        kdb_hc_keyboard_detach;
 #endif /* CONFIG_KDB_USB */
 };
 
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index cf5e4cf..3e52775 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -50,6 +50,11 @@
 #include "uhci-hcd.h"
 #include "pci-quirks.h"
 
+#ifdef CONFIG_KDB_USB
+#include <linux/kdb.h>
+#include <linux/kdbprivate.h>
+#endif
+
 /*
  * Version Information
  */
@@ -461,6 +466,213 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd)
        return IRQ_HANDLED;
 }
 
+#ifdef CONFIG_KDB_USB
+/* Unlink KDB QH from hardware and software scheduler */
+static void kdb_unlink_uhci_qh(struct urb *urb, struct uhci_qh *qh)
+{
+       unsigned long flags;
+       struct uhci_hcd *uhci;
+
+       uhci = (struct uhci_hcd *) hcd_to_uhci(bus_to_hcd(urb->dev->bus));
+
+       spin_lock_irqsave(&uhci->lock, flags);
+       unlink_interrupt(NULL, qh);
+       list_del(&(qh->node));
+       spin_unlock_irqrestore(&uhci->lock, flags);
+
+}
+
+static int uhci_kdb_poll_char(struct urb *urb)
+{
+       if (!urb) /* can happen if no keyboard attached */
+               return -1;
+
+       return uhci_check_kdb_uhci_qh(kdb_uhci_keyboard_get_qh(urb));
+}
+
+/* Only 1 UHCI Keyboard supported */
+static inline void kdb_usb_fill_int_urb (struct urb *urb,
+                                        struct usb_device *dev,
+                                        unsigned int pipe,
+                                        void *transfer_buffer,
+                                        int buffer_length,
+                                        usb_complete_t complete_fn,
+                                        void *context,
+                                        int interval)
+{
+       urb->dev = dev;
+       urb->pipe = pipe;
+       urb->transfer_buffer = transfer_buffer;
+       urb->transfer_buffer_length = buffer_length;
+       urb->complete = complete_fn;
+       urb->context = context;
+       urb->interval = interval;
+       urb->start_frame = -1;
+}
+
+static int kdb_uhci_keyboard_attach(int i, unsigned int usbhid_bufsize)
+{
+       struct urb *kdb_urb;
+       unsigned char *kdb_buffer;
+       dma_addr_t uhci_inbuf_dma;
+       struct urb *hid_inurb = kdb_usb_kbds[i].urb;
+       int ret = -1;
+
+       kdb_usb_kbds[i].hid_urb = hid_inurb;
+
+       kdb_urb = NULL;
+       kdb_buffer = NULL;
+       if (!(kdb_buffer = usb_buffer_alloc(hid_inurb->dev,
+                                           usbhid_bufsize, GFP_ATOMIC,
+                                           &uhci_inbuf_dma)))
+               goto out;
+
+       if (!(kdb_urb = usb_alloc_urb(0, GFP_KERNEL)))
+               goto out;
+
+       kdb_usb_fill_int_urb(kdb_urb,
+                            hid_inurb->dev,
+                            hid_inurb->pipe,
+                            kdb_buffer,
+                            hid_inurb->transfer_buffer_length,
+                            hid_inurb->complete,
+                            hid_inurb->context,
+                            hid_inurb->interval
+               );
+
+       (kdb_urb)->transfer_dma = uhci_inbuf_dma;
+       (kdb_urb)->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       kdb_usb_kbds[i].urb = kdb_urb;
+       kdb_usb_kbds[i].buffer = kdb_buffer;
+
+       if (usb_submit_urb(kdb_urb, GFP_ATOMIC)){
+               kdb_usb_keyboard_detach(hid_inurb);
+               goto out;
+       }
+       /* Remove KDB special URB from endpoin queue to
+        * prevent hang during hid_disconnect().
+        */
+       list_del(&(kdb_urb->urb_list));
+
+       ret = 0;
+       return ret;
+out:
+       /* Some Error Cleanup */
+       ret = -1;
+       printk("KDB: Error, UHCI Keyboard HID won't work!\n");
+
+       if (kdb_buffer)
+               usb_buffer_free(hid_inurb->dev,
+                               usbhid_bufsize, kdb_buffer,
+                               uhci_inbuf_dma);
+
+       if (kdb_urb)
+               usb_free_urb(kdb_urb);
+
+       return ret;
+}
+
+static int kdb_uhci_keyboard_detach(struct urb *urb, int i)
+{
+       int ret;
+
+       if (kdb_usb_kbds[i].qh && (kdb_usb_kbds[i].hid_urb == urb)) {
+               /* UHCI keyboard */
+               kdb_unlink_uhci_qh(kdb_usb_kbds[i].urb, kdb_usb_kbds[i].qh);
+               ret = 0;
+       }
+       ret = -1;
+
+       return ret;
+}
+
+/* Check if URB is managed by KDB code */
+static int kdb_uhci_keyboard_urb(struct urb *urb)
+{
+       int i;
+
+       for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) {
+               if (kdb_usb_kbds[i].urb && kdb_usb_kbds[i].urb == urb)
+                       return i;
+       }
+       return -1;
+}
+
+/* Check if UHCI QH is managed by KDB code */
+static int kdb_uhci_keyboard_check_uhci_qh(struct uhci_qh *qh)
+{
+       int i;
+
+       for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) {
+               if (kdb_usb_kbds[i].urb && kdb_usb_kbds[i].qh == qh)
+                       return i;
+       }
+       return -1;
+}
+
+/* Set UHCI QH using URB pointer */
+static int kdb_uhci_keyboard_set_qh(struct urb *urb, struct uhci_qh *qh)
+{
+       int i;
+
+       i = kdb_uhci_keyboard_urb(urb);
+       if (i != -1)
+               kdb_usb_kbds[i].qh = qh;
+
+       return 0;
+}
+
+/* Get UHCI QH using URB pointer */
+static struct uhci_qh *kdb_uhci_keyboard_get_qh(struct urb *urb)
+{
+       int i;
+
+       i = kdb_uhci_keyboard_urb(urb);
+       if (i != -1)
+               return kdb_usb_kbds[i].qh;
+
+       return NULL;
+}
+
+/* Set UHCI hid_event using URB pointer */
+static int kdb_uhci_keyboard_set_hid_event(struct urb *urb, int hid_event)
+{
+       int i;
+
+       i = kdb_uhci_keyboard_urb(urb);
+       if (i != -1)
+               kdb_usb_kbds[i].kdb_hid_event = hid_event;
+
+       return 0;
+}
+/* Get UHCI hid_event using URB pointer */
+static int kdb_uhci_keyboard_get_hid_event(struct urb *urb)
+{
+       int i;
+
+       i = kdb_uhci_keyboard_urb(urb);
+       if (i != -1)
+               return kdb_usb_kbds[i].kdb_hid_event;
+
+       return 0;
+}
+
+/* Set UHCI hid_event using UHCI QH pointer */
+static int kdb_uhci_keyboard_set_hid_event_qh(struct uhci_qh *qh, int 
hid_event)
+{
+       int i;
+
+       for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) {
+               if (kdb_usb_kbds[i].urb && kdb_usb_kbds[i].qh == qh){
+                       kdb_usb_kbds[i].kdb_hid_event = hid_event;
+                       return i;
+               }
+       }
+       return -1;
+}
+#endif
+
 /*
  * Store the current frame number in uhci->frame_number if the controller
  * is runnning.  Expand from 11 bits (of which we use only 10) to a
@@ -921,6 +1133,12 @@ static const struct hc_driver uhci_driver = {
 
        .hub_status_data =      uhci_hub_status_data,
        .hub_control =          uhci_hub_control,
+#ifdef CONFIG_KDB_USB
+       .kdb_poll_char =        uhci_kdb_poll_char,
+       .kdb_completion =       kdb_uhci_urb_complete,
+       .kdb_hc_keyboard_attach =       kdb_uhci_keyboard_attach,
+       .kdb_hc_keyboard_detach =       kdb_uhci_keyboard_detach,
+#endif
 };
 
 static const struct pci_device_id uhci_pci_ids[] = { {
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 3e5807d..13def8f 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -25,6 +25,17 @@
  * games with the FSBR code to make sure we get the correct order in all
  * the cases. I don't think it's worth the effort
  */
+#ifdef CONFIG_KDB_USB
+/* KDB HID QH, managed by KDB code */
+static int kdb_uhci_keyboard_check_uhci_qh(struct uhci_qh *qh);
+static int kdb_uhci_keyboard_set_qh(struct urb *urb, struct uhci_qh *qh);
+static struct uhci_qh *kdb_uhci_keyboard_get_qh(struct urb *urb);
+static int kdb_uhci_keyboard_set_hid_event(struct urb *urb, int hid_event);
+static int kdb_uhci_keyboard_get_hid_event(struct urb *urb);
+static int kdb_uhci_keyboard_set_hid_event_qh(struct uhci_qh *qh, int 
hid_event);
+static int kdb_uhci_keyboard_urb(struct urb *urb);
+#endif
+
 static void uhci_set_next_interrupt(struct uhci_hcd *uhci)
 {
        if (uhci->is_stopped)
@@ -288,6 +299,58 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
        return qh;
 }
 
+#ifdef CONFIG_KDB_USB
+/*
+ * Same as uhci_alloc_qh execpt it doesn't change to hep->hcpriv
+ */
+static struct uhci_qh *kdb_uhci_alloc_qh(struct uhci_hcd *uhci,
+                                        struct usb_device *udev, struct 
usb_host_endpoint *hep)
+{
+       dma_addr_t dma_handle;
+       struct uhci_qh *qh;
+
+       qh = dma_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle);
+       if (!qh)
+               return NULL;
+
+       memset(qh, 0, sizeof(*qh));
+       qh->dma_handle = dma_handle;
+
+       qh->element = UHCI_PTR_TERM;
+       qh->link = UHCI_PTR_TERM;
+
+       INIT_LIST_HEAD(&qh->queue);
+       INIT_LIST_HEAD(&qh->node);
+
+       if (udev) {             /* Normal QH */
+               qh->type = hep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+               if (qh->type != USB_ENDPOINT_XFER_ISOC) {
+                       qh->dummy_td = uhci_alloc_td(uhci);
+                       if (!qh->dummy_td) {
+                               dma_pool_free(uhci->qh_pool, qh, dma_handle);
+                               return NULL;
+                       }
+               }
+               qh->state = QH_STATE_IDLE;
+               qh->hep = hep;
+               qh->udev = udev;
+
+               if (qh->type == USB_ENDPOINT_XFER_INT ||
+                   qh->type == USB_ENDPOINT_XFER_ISOC)
+                       qh->load = usb_calc_bus_time(udev->speed,
+                                                    
usb_endpoint_dir_in(&hep->desc),
+                                                    qh->type == 
USB_ENDPOINT_XFER_ISOC,
+                                                    
le16_to_cpu(hep->desc.wMaxPacketSize))
+                               / 1000 + 1;
+
+       } else {                /* Skeleton QH */
+               qh->state = QH_STATE_ACTIVE;
+               qh->type = -1;
+       }
+       return qh;
+}
+#endif
+
 static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
 {
        WARN_ON(qh->state != QH_STATE_IDLE && qh->udev);
@@ -1394,6 +1457,21 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd,
        if (!urbp)
                goto done;
 
+#ifdef CONFIG_KDB_USB
+       /* Always allocate new QH for KDB URB.
+        * KDB HQ will be managed by KDB poll code not by
+        * UHCI HCD Driver.
+        */
+       if (kdb_uhci_keyboard_urb(urb) != -1){
+               /* KDB urb will be enqued only once */
+               kdb_uhci_keyboard_set_qh(urb, NULL);
+               qh = kdb_uhci_alloc_qh(uhci, urb->dev, urb->ep);
+               if (!qh)
+                       goto err_no_qh;
+               kdb_uhci_keyboard_set_qh(urb, qh);
+       } else
+#endif
+
        if (urb->ep->hcpriv)
                qh = urb->ep->hcpriv;
        else {
@@ -1642,6 +1720,14 @@ static int uhci_advance_check(struct uhci_hcd *uhci, 
struct uhci_qh *qh)
        int ret = 1;
        unsigned status;
 
+#ifdef CONFIG_KDB_USB
+       /* Don't manage KDB QH */
+       if(kdb_uhci_keyboard_check_uhci_qh(qh) != -1){
+               ret = 0;
+               goto done;
+       }
+#endif
+
        if (qh->type == USB_ENDPOINT_XFER_ISOC)
                goto done;
 
@@ -1734,6 +1820,11 @@ rescan:
                        uhci->next_qh = list_entry(qh->node.next,
                                        struct uhci_qh, node);
 
+#ifdef CONFIG_KDB_USB
+                       /* Don't manage KDB QH */
+                       if(kdb_uhci_keyboard_check_uhci_qh(qh) != -1)
+                               continue;
+#endif
                        if (uhci_advance_check(uhci, qh)) {
                                uhci_scan_qh(uhci, qh);
                                if (qh->state == QH_STATE_ACTIVE) {
@@ -1760,3 +1851,76 @@ rescan:
        else
                uhci_set_next_interrupt(uhci);
 }
+
+#ifdef CONFIG_KDB_USB
+/*
+ * Activate KDB UHCI QH, called by KDB poll code.
+ */
+static void kdb_activate_uhci_qh(struct uhci_qh *qh)
+{
+       struct urb_priv *urbp;
+       struct uhci_td *td;
+       __le32 status, token;
+
+       urbp = list_entry(qh->queue.next, struct urb_priv, node);
+
+       list_for_each_entry(td, &urbp->td_list, list){
+               status = td->status;
+               token = td->token;
+               barrier();
+               /* Clear Status and ActLen */
+               status &= cpu_to_le32(0xff000000);
+               /* Make TD Active */
+               status |= cpu_to_le32(TD_CTRL_ACTIVE);
+               /* Clear TD Interrupt */
+               status &= cpu_to_le32(~TD_CTRL_IOC);
+               /* Toggle Data Sycronization Bit */
+               if (token & cpu_to_le32(TD_TOKEN_TOGGLE))
+                       token &= cpu_to_le32(~TD_TOKEN_TOGGLE);
+               else
+                       token |= cpu_to_le32(TD_TOKEN_TOGGLE);
+
+               td->token = token;
+               td->status = status;
+               barrier();
+       }
+       /* Activate KDB UHCI Keyboard HID QH */
+       td = list_entry(urbp->td_list.next, struct uhci_td, list);
+       qh->element = LINK_TO_TD(td);
+       barrier();
+}
+
+/*
+ * Called when KDB finishes process key press/release event.
+ */
+static void
+kdb_uhci_urb_complete (struct urb *urb)
+{
+       if (!kdb_uhci_keyboard_get_hid_event(urb))
+               return;
+
+       /* Activate KDB TD */
+       kdb_activate_uhci_qh(kdb_uhci_keyboard_get_qh(urb));
+       kdb_uhci_keyboard_set_hid_event(urb, 0);
+}
+
+/*
+ * Check if state of KDB URB changed (key was pressed/released).
+ */
+static int uhci_check_kdb_uhci_qh(struct uhci_qh *qh)
+{
+       struct urb_priv *urbp = NULL;
+       struct uhci_td *td;
+       unsigned status;
+
+       urbp = list_entry(qh->queue.next, struct urb_priv, node);
+       td = list_entry(urbp->td_list.next, struct uhci_td, list);
+       status = td_status(td);
+       if (!(status & TD_CTRL_ACTIVE)){
+               /* We're okay, the queue has advanced */
+               kdb_uhci_keyboard_set_hid_event_qh(qh, 1);
+               return 0;
+       }
+       return -1;
+}
+#endif
diff --git a/include/linux/kdb.h b/include/linux/kdb.h
index 442409b..f1d8b36 100644
--- a/include/linux/kdb.h
+++ b/include/linux/kdb.h
@@ -143,7 +143,16 @@ extern void smp_kdb_stop(void);
 
 #include <linux/usb.h>
 
-extern int kdb_usb_keyboard_attach(struct urb *urb, unsigned char *buffer, 
void *poll_func);
+typedef int (*kdb_hc_keyboard_attach_t)(int i, unsigned int bufsize);
+typedef int (*kdb_hc_keyboard_detach_t)(struct urb *urb, int i);
+
+extern int 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);
+
 extern int kdb_usb_keyboard_detach(struct urb *urb);
 
 #endif /* CONFIG_KDB_USB */
diff --git a/include/linux/kdbprivate.h b/include/linux/kdbprivate.h
index 746c8c4..e348a31 100644
--- a/include/linux/kdbprivate.h
+++ b/include/linux/kdbprivate.h
@@ -486,13 +486,28 @@ extern char kdb_prompt_str[];
 #ifdef CONFIG_KDB_USB
 #include <linux/usb.h>
 
+/* support up to 8 USB keyboards (probably excessive, but...) */
+#define KDB_USB_NUM_KEYBOARDS   8
+
 struct kdb_usb_kbd_info {
        struct urb *urb;                /* pointer to the URB */
        unsigned char *buffer;          /* pointer to the kbd char buffer */
        int (*poll_func)(struct urb *urb); /* poll function to retrieve chars */
        int     poll_ret;       /* return val from poll_func */
        int     caps_lock;      /* state of the caps lock for this keyboard */
+       struct uhci_qh *qh;
+       int kdb_hid_event;
+       struct urb *hid_urb;    /* pointer to the HID URB */
+       /* USB Host Controller specific callbacks */
+       kdb_hc_keyboard_attach_t kdb_hc_keyboard_attach;
+       kdb_hc_keyboard_detach_t kdb_hc_keyboard_detach;
+       int (*kdb_hc_urb_complete)(struct urb *urb); /* called when URB int is
+                                                       processed */
+
 };
+
+extern struct kdb_usb_kbd_info kdb_usb_kbds[KDB_USB_NUM_KEYBOARDS];
+
 #endif /* CONFIG_KDB_USB */
 
 #ifdef CONFIG_KDB_KDUMP

_______________________________________________
kdb mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/kdb

Reply via email to