Re: [PATCH] Add support for USB Keyboard attached to UHCI

Konstantin Baydarov <[email protected]>
Newsgroups gmane.linux.kernel.debugging
Message-ID <[email protected]>
On Wed, 7 Nov 2007 17:11:37 +0300
Konstantin Baydarov <[email protected]> wrote:

> On Mon, 5 Nov 2007 10:54:42 -0800 (PST)
> Aaron Young <[email protected]> wrote:
> 
> >   I've only dealt with OHCI driver code before, so I'm not qualified
> > to review the UHCI specific code for you. As you say, it looks like
> > a lot of your infastructure code introduces new globals
> > (kdb_hcd_type) and uses kdb_usb_infos a lot - which is going away
> > with my new implementation.
> > 
> >   Would it be possible for you to wait for my new kdb/usb
> > infastructure code and re-implement your UHCI code against it at
> > that time? I could attempt to take an in depth look at it at that
> > time perhaps...
> > 
> >   Thanks,
> > 
> >   -Aaron
> 
>   Yes I'll intergrate UCHI code to the new KDB.
> ---------------------------
> Use http://oss.sgi.com/ecartis to modify your settings or to
> unsubscribe.

  UCHI support integrated to kdb-v4.4-2.6.24-rc2. Tested on i386 UP PC.

Changes in USB interface:
  - kdb_usb_uhci_keyboard_attach() to manage UHCI Keyboards
  - kdb_uhci_submit_urb() is called by kdb_usb_uhci_keyboard_attach(). UHCI KDB doesn't use HID urb, special urb with same parameters is used.
  - void (*kdb_completion)(struct urb *urb); was added to hc_driver structure to allow KDB UHCI driver reactivate KDB queue head.

Known Issues of KDB driver:
  - Autoreply of pressed key doesn't work
  - When kernel enters KDB first pressed key doesn't appear on KDB
console, because it's caught by UHCI kernel driver.
  - Keyboard hotplug wasn't tested.
  - Only 1 UHCI keyboard supported.

  Jay, it looks you are using spaces instead of tabs or your mail client replaces tabs with spaces, now(after USB patches) we have mixed code style. I think cleanup should be done.

Signed-off-by: Konstantin Baydarov <[email protected]>

Index: linux-2.6.24-rc2/drivers/usb/host/uhci-q.c
===================================================================
--- linux-2.6.24-rc2.orig/drivers/usb/host/uhci-q.c
+++ linux-2.6.24-rc2/drivers/usb/host/uhci-q.c
@@ -25,6 +25,11 @@
  * 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 struct uhci_qh *kdb_uhci_hid_qh;
+#endif
+
 static void uhci_set_next_interrupt(struct uhci_hcd *uhci)
 {
 	if (uhci->is_stopped)
@@ -292,6 +297,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 +1456,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)){
+		/* KDB urb will be enqued only once */
+		kdb_uhci_hid_qh = NULL;
+		qh = kdb_uhci_alloc_qh(uhci, urb->dev, urb->ep);
+		if (!qh)
+			goto err_no_qh;
+		kdb_uhci_hid_qh = qh;
+	} else
+#endif
 	if (urb->ep->hcpriv)
 		qh = urb->ep->hcpriv;
 	else {
@@ -1648,6 +1719,13 @@ static int uhci_advance_check(struct uhc
 	int ret = 1;
 	unsigned status;
 
+#ifdef	CONFIG_KDB_USB
+	/* Don't manage KDB QH */
+	if (qh == kdb_uhci_hid_qh){
+		ret = 0;
+		goto done;
+	}
+#endif
 	if (qh->type == USB_ENDPOINT_XFER_ISOC)
 		goto done;
 
@@ -1740,6 +1818,11 @@ rescan:
 			uhci->next_qh = list_entry(qh->node.next,
 					struct uhci_qh, node);
 
+#ifdef	CONFIG_KDB_USB
+			/* Don't manage KDB QH */
+			if (qh == kdb_uhci_hid_qh)
+				continue;
+#endif
 			if (uhci_advance_check(uhci, qh)) {
 				uhci_scan_qh(uhci, qh);
 				if (qh->state == QH_STATE_ACTIVE) {
@@ -1766,3 +1849,80 @@ rescan:
 	else
 		uhci_set_next_interrupt(uhci);
 }
+
+#ifdef	CONFIG_KDB_USB
+static int kdb_hid_event = 0;	/* eq 1 when key was pressed/released */
+
+/*
+ * Activate KDB UHCI QH, called by KDB poll code.
+ */
+static void kdb_activate_uhci_qh(void)
+{
+	struct urb_priv *urbp;
+	struct uhci_td *td;
+	struct uhci_qh *qh = kdb_uhci_hid_qh;
+	__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_hid_event)
+		return;
+
+	/* Activate KDB TD */
+	kdb_activate_uhci_qh();
+	kdb_hid_event = 0;
+}
+
+/*
+ * Check if state of KDB URB changed (key was pressed/released).
+ */
+static int uhci_check_kdb_uhci_qh(void)
+{
+	struct uhci_qh *qh = kdb_uhci_hid_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_hid_event = 1;
+		return 0;
+	}
+	return -1;
+}
+#endif
Index: linux-2.6.24-rc2/drivers/hid/usbhid/hid-core.c
===================================================================
--- linux-2.6.24-rc2.orig/drivers/hid/usbhid/hid-core.c
+++ linux-2.6.24-rc2/drivers/hid/usbhid/hid-core.c
@@ -1045,8 +1045,17 @@ 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);
-		ret = kdb_usb_keyboard_attach(usbhid->urbin, usbhid->inbuf,
-		    usb_hcd_get_kdb_poll_func(interface_to_usbdev(intf)));
+		extern void * usb_hcd_get_kdb_completion_func(struct usb_device *udev);
+		extern int usb_hcd_check_uhci(struct usb_device *udev);
+
+		if (usb_hcd_check_uhci(interface_to_usbdev(intf)))
+			ret = kdb_usb_uhci_keyboard_attach(usbhid->urbin, usbhid->bufsize,
+				usb_hcd_get_kdb_poll_func(interface_to_usbdev(intf)),
+				usb_hcd_get_kdb_completion_func(interface_to_usbdev(intf)));
+		else
+			ret = kdb_usb_keyboard_attach(usbhid->urbin, usbhid->inbuf,
+			    usb_hcd_get_kdb_poll_func(interface_to_usbdev(intf)));
+
 
 		if (ret == -1)
 			printk(": FAILED to register keyboard (%s) "
Index: linux-2.6.24-rc2/include/linux/kdb.h
===================================================================
--- linux-2.6.24-rc2.orig/include/linux/kdb.h
+++ linux-2.6.24-rc2/include/linux/kdb.h
@@ -143,9 +143,15 @@ extern void smp_kdb_stop(void);
 
 #include <linux/usb.h>
 
+extern int kdb_uhci_keyboard_urb(struct urb *urb);
+
 extern int kdb_usb_keyboard_attach(struct urb *urb, unsigned char *buffer, void *poll_func);
 extern int kdb_usb_keyboard_detach(struct urb *urb);
 
+extern int
+kdb_usb_uhci_keyboard_attach(struct urb *urb, unsigned int usbhid_bufsize,
+				void *poll_func, void *compl_func);
+
 #endif /* CONFIG_KDB_USB */
 
 static inline
Index: linux-2.6.24-rc2/drivers/usb/host/uhci-hcd.c
===================================================================
--- linux-2.6.24-rc2.orig/drivers/usb/host/uhci-hcd.c
+++ linux-2.6.24-rc2/drivers/usb/host/uhci-hcd.c
@@ -50,6 +50,10 @@
 #include "uhci-hcd.h"
 #include "pci-quirks.h"
 
+#ifdef	CONFIG_KDB_USB
+#include <linux/kdb.h>
+#endif
+
 /*
  * Version Information
  */
@@ -430,6 +434,17 @@ static irqreturn_t uhci_irq(struct usb_h
 	return IRQ_HANDLED;
 }
 
+#ifdef	CONFIG_KDB_USB
+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();
+}
+#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 +903,10 @@ 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,
+#endif
 };
 
 static const struct pci_device_id uhci_pci_ids[] = { {
Index: linux-2.6.24-rc2/arch/x86/kdb/kdba_io_32.c
===================================================================
--- linux-2.6.24-rc2.orig/arch/x86/kdb/kdba_io_32.c
+++ linux-2.6.24-rc2/arch/x86/kdb/kdba_io_32.c
@@ -54,6 +54,93 @@ static unsigned char kdb_usb_keycode[256
 	150,158,159,128,136,177,178,176,142,152,173,140
 };
 
+/* Check if URB is managed by KDB code */
+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 1;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL (kdb_uhci_keyboard_urb);
+
+/* Only 1 UHCI Keyboard supported */
+static int kdb_uhci_hid_initialized = 0;
+
+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_submit_urb(int kbd_num, unsigned int usbhid_bufsize, struct urb *hid_inurb)
+{
+	dma_addr_t uhci_inbuf_dma;
+
+	/* Only 1 UHCI Keyboard supported */
+	if (kdb_uhci_hid_initialized)
+		return 1;
+
+	if (!(kdb_usb_kbds[kbd_num].buffer =
+			usb_buffer_alloc(hid_inurb->dev,
+			usbhid_bufsize, GFP_ATOMIC,
+			&uhci_inbuf_dma)))
+		goto out;
+
+	if (!(kdb_usb_kbds[kbd_num].urb =
+			usb_alloc_urb(0, GFP_KERNEL)))
+		goto out;
+
+	kdb_usb_fill_int_urb(kdb_usb_kbds[kbd_num].urb,
+		hid_inurb->dev,
+		hid_inurb->pipe,
+		kdb_usb_kbds[kbd_num].buffer,
+		hid_inurb->transfer_buffer_length,
+		hid_inurb->complete,
+		hid_inurb->context,
+		hid_inurb->interval
+		);
+
+	(kdb_usb_kbds[kbd_num].urb)->transfer_dma = uhci_inbuf_dma;
+	(kdb_usb_kbds[kbd_num].urb)->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	if (usb_submit_urb(kdb_usb_kbds[kbd_num].urb, GFP_ATOMIC))
+		goto out;
+
+	kdb_uhci_hid_initialized = 1;
+out:
+	if (kdb_uhci_hid_initialized)
+		return 0;
+
+	/* Some Error Cleanup */
+	printk("KDB: Error, UHCI Keyboard HID won't work!\n");
+	if (kdb_usb_kbds[kbd_num].buffer)
+		usb_buffer_free(hid_inurb->dev,
+		usbhid_bufsize, kdb_usb_kbds[kbd_num].buffer,
+		uhci_inbuf_dma);
+
+	if (kdb_usb_kbds[kbd_num].urb)
+		usb_free_urb(kdb_usb_kbds[kbd_num].urb);
+
+	return -1;
+}
+
 /*
  * kdb_usb_keyboard_attach()
  * Attach a USB keyboard to kdb.
@@ -78,6 +165,7 @@ 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].urb_complete = NULL;
 
                 rc = 0; /* success */
 
@@ -88,6 +176,44 @@ kdb_usb_keyboard_attach(struct urb *urb,
 }
 EXPORT_SYMBOL_GPL (kdb_usb_keyboard_attach);
 
+int
+kdb_usb_uhci_keyboard_attach(struct urb *urb, unsigned int usbhid_bufsize,
+				void *poll_func, void *compl_func)
+{
+        int     i;
+        int     rc = -1;
+
+        /*
+         * Search through the array of KDB USB keyboards (kdb_usb_kbds)
+         * looking for a free index. If found, assign the keyboard to
+         * the array index.
+         */
+
+        for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) {
+                if (kdb_usb_kbds[i].urb) /* index is already assigned */
+                        continue;
+
+                /* found a free array index */
+		if (!(kdb_uhci_submit_urb(i, usbhid_bufsize, urb))){
+			kdb_usb_kbds[i].poll_func = poll_func;
+			kdb_usb_kbds[i].urb_complete = compl_func;
+		} else {
+			/* Cleanup */
+			kdb_usb_kbds[i].urb = NULL;
+			kdb_usb_kbds[i].buffer = NULL;
+			kdb_usb_kbds[i].poll_func = NULL;
+			kdb_usb_kbds[i].urb_complete = NULL;
+		}
+
+                rc = 0; /* success */
+
+                break;
+        }
+
+        return rc;
+}
+EXPORT_SYMBOL_GPL (kdb_usb_uhci_keyboard_attach);
+
 /*
  * kdb_usb_keyboard_detach()
  * Detach a USB keyboard from kdb.
@@ -135,6 +261,9 @@ get_usb_char(void)
         int     ret;
         unsigned char keycode, spec;
         extern u_short plain_map[], shift_map[], ctrl_map[];
+	int     poll_ret, i2, j, max;
+
+	ret = -1;
 
         /*
          * Loop through all the USB keyboard(s) and return
@@ -148,20 +277,27 @@ get_usb_char(void)
                         continue;
 
                 /* Transfer char */
-                ret = (*kdb_usb_kbds[i].poll_func)(kdb_usb_kbds[i].urb);
-                if (ret == -1) /* error or no characters, try the next kbd */
+                poll_ret = (*kdb_usb_kbds[i].poll_func)(kdb_usb_kbds[i].urb);
+                if (poll_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;
-                }
-
                 /* A normal key is pressed, decode it */
                 if(keycode)
                         keycode = kdb_usb_keycode[keycode];
@@ -172,10 +308,12 @@ get_usb_char(void)
                         {
                         case 0x2:
                         case 0x20: /* Shift */
-                                return shift_map[keycode];
+				ret = shift_map[keycode];
+				break;
                         case 0x1:
                         case 0x10: /* Ctrl */
-                                return ctrl_map[keycode];
+                        	ret = ctrl_map[keycode];
+                        	break;
                         case 0x4:
                         case 0x40: /* Alt */
                                 break;
@@ -184,31 +322,45 @@ get_usb_char(void)
                         switch(keycode)
                         {
                         case 0x1C: /* Enter */
-                                return 13;
-
+                        	ret = 13;
+                        	break;
                         case 0x3A: /* Capslock */
                                 kdb_usb_kbds[i].caps_lock = !(kdb_usb_kbds[i].caps_lock);
                                 break;
                         case 0x0E: /* Backspace */
-                                return 8;
+                        	ret = 8;
+                        	break;
                         case 0x0F: /* TAB */
-                                return 9;
+                        	ret = 9;
+                        	break;
                         case 0x77: /* Pause */
                                 break ;
                         default:
                                 if(!kdb_usb_kbds[i].caps_lock) {
-                                        return plain_map[keycode];
+                        		ret = plain_map[keycode];
+                        		break;
                                 }
                                 else {
-                                        return shift_map[keycode];
+                        		ret = shift_map[keycode];
+                        		break;
                                 }
                         }
                 }
+		/* Key was pressed, return keycode */
+		break;
         }
 
-        /* no chars were returned from any of the USB keyboards */
+	for(i2=0; i2<8; i2++){
+		if (kdb_usb_kbds[i2].buffer)
+			for(j=0; j<8; j++){
+				kdb_usb_kbds[i2].buffer[j] = (char)0;
+			}
+	}
+
+	if (kdb_usb_kbds[i].urb_complete)
+		(*kdb_usb_kbds[i].urb_complete)((struct urb *)kdb_usb_kbds[i].urb);
 
-        return -1;
+        return ret;
 }
 #endif	/* CONFIG_KDB_USB */
 
Index: linux-2.6.24-rc2/include/linux/kdbprivate.h
===================================================================
--- linux-2.6.24-rc2.orig/include/linux/kdbprivate.h
+++ linux-2.6.24-rc2/include/linux/kdbprivate.h
@@ -489,6 +489,7 @@ 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 (*urb_complete)(struct urb *urb); /* called when URB int is processed */
 	int	caps_lock;	/* state of the caps lock for this keyboard */
 };
 #endif /* CONFIG_KDB_USB */
Index: linux-2.6.24-rc2/drivers/usb/core/hcd.c
===================================================================
--- linux-2.6.24-rc2.orig/drivers/usb/core/hcd.c
+++ linux-2.6.24-rc2/drivers/usb/core/hcd.c
@@ -1851,6 +1851,32 @@ 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);
 #endif /* CONFIG_KDB_USB */
 
 /*-------------------------------------------------------------------------*/
Index: linux-2.6.24-rc2/drivers/usb/core/hcd.h
===================================================================
--- linux-2.6.24-rc2.orig/drivers/usb/core/hcd.h
+++ linux-2.6.24-rc2/drivers/usb/core/hcd.h
@@ -213,6 +213,7 @@ 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);
 #endif /* CONFIG_KDB_USB */
 };
 
---------------------------
Use http://oss.sgi.com/ecartis to modify your settings or to unsubscribe.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.