[PATCH] Add support for USB Keyboard attached to UHCI

Konstantin Baydarov kbaidarov at ru.mvista.com
Tue Feb 5 09:34:21 PST 2008


  Hello, Aarong, Jay. Sorry that it takes to much time to review/reply messages related to UHCI.
  ftp://oss.sgi.com/projects/kdb/download/v4.4/USB_UHCI_support-v2.bz2 - isn't complete. Kernel 2.6.24-rc4 doesn't even compile.
  I've redesign my patch to make only minimal changes to common usb layer.
  - I've added two USB Host Controller (HC) specific callbacks kdb_hc_keyboard_attach, kdb_hc_keyboard_detach to the kdb_usb_kbd_info. Callbacks kdb_hc_keyboard_attach, kdb_hc_keyboard_detach are called from kdb_usb_keyboard_attach, kdb_usb_keyboard_detach correspondingly to make USB HC specific KDB work when KBD is attached/detached. So no new global functions to USB layer are required. Currently only UHCI has this callbacks.
  - I've moved all KDB UHCI code to UHCI HC files. Now we have only one kdb_uhci_keyboard_urb() instead of 3 copies (Jay noticed it earlier).
  - As I've added USB HC specific callbacks and moved all UHCI code to UHCI driver, there shouldn't any problems now when USB HD drivers is compiled as kernel modules.
  - Allow CONFIG_KDB_USB option(In arch/x86/Kconfig.debug) if any of USB HC is selected, not only OHCI.
  - One common change was done in get_usb_char(). It was in earlier version of patch but we didn't discuss it. In case of UHCI Keyboard, when 2 keys is pressed simultaneously we get 2 urbs. First urb stores only 1 keycode of the first pressed key in kdb_usb_kbds[i].buffer[2]. Second urb stores 2 keycodes - first keycode in kdb_usb_kbds[i].buffer[2], second keycode in kdb_usb_kbds[i].buffer[3]. As previous code of get_usb_char() just ignore kdb_usb_kbds[i].buffer[3], I've made updates of get_usb_char(). Also I've added USB HC specific URB complete callback to get_usb_char(). I don't know how keycodes are stored in case of OHCI and EHCI, so probably USB HC checks should also be added to get_usb_char() or new USB HC callback should be added to get_usb_char(), that extracts keycodes from URB buffer.

Patch was done for 2.6.24-rc4 KDB kernel with ftp://oss.sgi.com/projects/kdb/download/v4.4/USB_EHCI_support-v2.bz2 applied.

I hope you'll have some time to review changes.

Also I've sent another patch http://oss.sgi.com/archives/kdb/2007-11/msg00037.html , Aarong, I hope you'll have some time to review it too.

With Best Regards,
 Konstantin Baydarov, Software Engineer
 Montavista Russia
 mail-to: kbaidarov at ru.mvista.com
  
Signed-off-by: Konstantin Baydarov <kbaidarov at ru.mvista.com>

Index: linux-2.6.24-rc4_kdb_usb/arch/x86/kdb/kdba_io_32.c
===================================================================
--- linux-2.6.24-rc4_kdb_usb.orig/arch/x86/kdb/kdba_io_32.c
+++ linux-2.6.24-rc4_kdb_usb/arch/x86/kdb/kdba_io_32.c
@@ -20,7 +20,7 @@
 
 #include <linux/kdb.h>
 #include <linux/kdbprivate.h>
-#include <pc_keyb_32.h>
+#include "pc_keyb_32.h"
 
 #ifdef	CONFIG_VT_CONSOLE
 #define KDB_BLINK_LED 1
@@ -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;
@@ -82,6 +86,15 @@ kdb_usb_keyboard_attach(struct urb *urb,
                 kdb_usb_kbds[i].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 */
 
@@ -112,14 +125,21 @@ 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 +162,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;
@@ -162,15 +185,24 @@ get_usb_char(void)
                 if (ret == -1) /* 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];
+                keycode = kdb_usb_kbds[i].buffer[max];
                 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)
@@ -182,10 +214,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;
@@ -194,31 +228,47 @@ 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];
+                                        break;
                                 }
                                 else {
-                                        return shift_map[keycode];
+                                        ret_key = shift_map[keycode];
+                                        break;
                                 }
                         }
                 }
-        }
+                /* 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);
 
-        /* no chars were returned from any of the USB keyboards */
+                break;
+        }
 
-        return -1;
+        return ret_key;
 }
 #endif	/* CONFIG_KDB_USB */
 
Index: linux-2.6.24-rc4_kdb_usb/drivers/hid/usbhid/hid-core.c
===================================================================
--- linux-2.6.24-rc4_kdb_usb.orig/drivers/hid/usbhid/hid-core.c
+++ linux-2.6.24-rc4_kdb_usb/drivers/hid/usbhid/hid-core.c
@@ -1045,8 +1045,20 @@ static int hid_probe(struct usb_interfac
 		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) "
Index: linux-2.6.24-rc4_kdb_usb/drivers/usb/core/hcd.c
===================================================================
--- linux-2.6.24-rc4_kdb_usb.orig/drivers/usb/core/hcd.c
+++ linux-2.6.24-rc4_kdb_usb/drivers/usb/core/hcd.c
@@ -37,6 +37,9 @@
 #include <asm/byteorder.h>
 #include <linux/platform_device.h>
 #include <linux/workqueue.h>
+#ifdef	CONFIG_KDB_USB
+#include <linux/kdb.h>
+#endif
 
 #include <linux/usb.h>
 
@@ -1852,6 +1855,58 @@ usb_hcd_get_kdb_poll_func(struct usb_dev
 	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 */
 
 /*-------------------------------------------------------------------------*/
Index: linux-2.6.24-rc4_kdb_usb/drivers/usb/core/hcd.h
===================================================================
--- linux-2.6.24-rc4_kdb_usb.orig/drivers/usb/core/hcd.h
+++ linux-2.6.24-rc4_kdb_usb/drivers/usb/core/hcd.h
@@ -20,6 +20,9 @@
 #ifdef __KERNEL__
 
 #include <linux/rwsem.h>
+#ifdef	CONFIG_KDB_USB
+#include <linux/kdb.h>
+#endif
 
 /* This file contains declarations of usbcore internals that are mostly
  * used or exposed by Host Controller Drivers.
@@ -213,6 +216,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 */
 };
 
Index: linux-2.6.24-rc4_kdb_usb/drivers/usb/host/uhci-hcd.c
===================================================================
--- linux-2.6.24-rc4_kdb_usb.orig/drivers/usb/host/uhci-hcd.c
+++ linux-2.6.24-rc4_kdb_usb/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
  */
@@ -430,6 +435,214 @@ static irqreturn_t uhci_irq(struct usb_h
 	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
@@ -888,6 +1101,12 @@ static const struct hc_driver uhci_drive
 
 	.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[] = { {
Index: linux-2.6.24-rc4_kdb_usb/drivers/usb/host/uhci-q.c
===================================================================
--- linux-2.6.24-rc4_kdb_usb.orig/drivers/usb/host/uhci-q.c
+++ linux-2.6.24-rc4_kdb_usb/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)
@@ -292,6 +303,58 @@ static struct uhci_qh *uhci_alloc_qh(str
 	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);
@@ -1399,6 +1462,20 @@ static int uhci_urb_enqueue(struct usb_h
 	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 {
@@ -1648,6 +1725,13 @@ static int uhci_advance_check(struct uhc
 	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;
 
@@ -1740,6 +1824,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) {
@@ -1766,3 +1855,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
Index: linux-2.6.24-rc4_kdb_usb/include/linux/kdb.h
===================================================================
--- linux-2.6.24-rc4_kdb_usb.orig/include/linux/kdb.h
+++ linux-2.6.24-rc4_kdb_usb/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 */
Index: linux-2.6.24-rc4_kdb_usb/include/linux/kdbprivate.h
===================================================================
--- linux-2.6.24-rc4_kdb_usb.orig/include/linux/kdbprivate.h
+++ linux-2.6.24-rc4_kdb_usb/include/linux/kdbprivate.h
@@ -485,12 +485,25 @@ 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	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 */
 
 #endif	/* !_KDBPRIVATE_H */
Index: linux-2.6.24-rc4_kdb_usb/arch/x86/Kconfig.debug
===================================================================
--- linux-2.6.24-rc4_kdb_usb.orig/arch/x86/Kconfig.debug
+++ linux-2.6.24-rc4_kdb_usb/arch/x86/Kconfig.debug
@@ -183,10 +183,10 @@ config KDB_CONTINUE_CATASTROPHIC
 	  setting to 2.
 
 config KDB_USB
-	bool "Support for USB Keyboard in KDB (OHCI only)"
-	depends on KDB && USB_OHCI_HCD
+	bool "Support for USB Keyboard in KDB"
+	depends on KDB && (USB_OHCI_HCD || USB_UHCI_HCD || USB_EHCI_HCD)
 	help
-	  If you want to use kdb from a OHCI USB keyboard then say Y here.  If you
+	  If you want to use kdb from a USB keyboard then say Y here.  If you
 	  say N then kdb can only be used from a PC (AT) keyboard or a serial
 	  console.
 
---------------------------
Use http://oss.sgi.com/ecartis to modify your settings or to unsubscribe.


More information about the kdb mailing list