patch-2.3.3 linux/drivers/usb/ohci.c

Next file: linux/drivers/usb/ohci.h
Previous file: linux/drivers/usb/ohci-debug.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.2/linux/drivers/usb/ohci.c linux/drivers/usb/ohci.c
@@ -29,7 +29,7 @@
  *
  * No filesystems were harmed in the development of this code.
  *
- * $Id: ohci.c,v 1.26 1999/05/11 07:34:47 greg Exp $
+ * $Id: ohci.c,v 1.43 1999/05/16 22:35:24 greg Exp $
  */
 
 #include <linux/config.h>
@@ -59,31 +59,30 @@
 
 static DECLARE_WAIT_QUEUE_HEAD(ohci_configure);
 
-#ifdef OHCI_TIMER
-static struct timer_list ohci_timer;	/* timer for root hub polling */
+#ifdef CONFIG_USB_OHCI_DEBUG
+#define OHCI_DEBUG    /* to make typing it easier.. */
 #endif
 
+#ifdef OHCI_DEBUG
+int MegaDebug = 0;	/* SIGUSR2 to the control thread toggles this */
+#endif
 
-static int ohci_td_result(struct ohci_device *dev, struct ohci_td *td)
-{
-	unsigned int status;
-
-	status = td->info & OHCI_TD_CC;
-
-	/* TODO Debugging code for TD failures goes here */
-
-	return status;
-} /* ohci_td_result() */
 
+#ifdef OHCI_TIMER
+static struct timer_list ohci_timer;	/* timer for root hub polling */
+#endif
 
 static spinlock_t ohci_edtd_lock = SPIN_LOCK_UNLOCKED;
 
 /*
- * Add a TD to the end of the TD list on a given ED.  If td->next_td
- * points to any more TDs, they will be added as well (naturally).
- * Otherwise td->next_td must be 0.
+ * Add a TD to the end of the TD list on a given ED.  This function
+ * does NOT advance the ED's tail_td pointer beyond the given TD.  To
+ * add multiple TDs, call this function once for each TD.  Do not
+ * "simply" update tail_td yourself... This function does more than
+ * that.
  * 
- * The SKIP flag will be cleared after this function.
+ * If this ED is on the controller, you MUST set its SKIP flag before
+ * calling this function.
  *
  * Important!  This function needs locking and atomicity as it works
  * in parallel with the HC's DMA.  Locking ohci_edtd_lock while using
@@ -93,54 +92,62 @@
  */
 static void ohci_add_td_to_ed(struct ohci_td *td, struct ohci_ed *ed)
 {
-	/* don't let the HC pull anything from underneath us */
-	ed->status |= OHCI_ED_SKIP;
+	struct ohci_td *dummy_td, *prev_td;
+
+	if (ed->tail_td == 0) {
+		printk("eek! an ED without a dummy_td\n");
+	}
+
+	/* The ED's tail_td is constant, always pointing to the
+	 * dummy_td.  The reason the ED never processes the dummy is
+	 * that it stops processing TDs as soon as head_td == tail_td.
+	 * When it advances to this last dummy TD it conveniently stops. */
+	dummy_td = bus_to_virt(ed->tail_td);
 
-	if (ed_head_td(ed) == 0) {	/* empty list, put it on the head */
-		set_ed_head_td(ed, virt_to_bus(td));
-		ed->tail_td = 0;
+	/* Dummy's data pointer is used to point to the previous TD */
+	if (ed_head_td(ed) != ed->tail_td) {
+		prev_td = (struct ohci_td *) dummy_td->data;
 	} else {
-		struct ohci_td *tail, *head;
-		head = (ed_head_td(ed) == 0) ? NULL : bus_to_virt(ed_head_td(ed));
-		tail = (ed->tail_td == 0) ? NULL : bus_to_virt(ed->tail_td);
-		if (!tail) {	/* no tail, single element list */
-			td->next_td = head->next_td;
-			head->next_td = virt_to_bus(td);
-			ed->tail_td = virt_to_bus(td);
-		} else {	/* append to the list */
-			td->next_td = tail->next_td;
-			tail->next_td = virt_to_bus(td);
-			ed->tail_td = virt_to_bus(td);
-		}
+		/* if the ED is empty, previous is meaningless */
+		/* We'll be inserting into the head of the list */
+		prev_td = NULL;
 	}
 
-	/* save the ED link in each of the TDs added */
+	/* Store the new back pointer and set up this TD's next */
+	dummy_td->data = td;
+	td->next_td = ed->tail_td;
+
+	/* Store the TD pointer back to the ED */
 	td->ed = ed;
-	while (td->next_td != 0) {
-		td = bus_to_virt(td->next_td);
-		td->ed = ed;
-	}
 
-	/* turn off the SKIP flag */
-	ed->status &= ~OHCI_ED_SKIP;
+	if (!prev_td) { /* No previous TD? then insert us at the head */
+		if (ed_head_td(ed) != ed->tail_td)
+			printk(KERN_DEBUG "Suspicious ED...\n");
+		set_ed_head_td(ed, virt_to_bus(td));	/* put it on the ED */
+	} else {
+		/* add the TD to the end */
+		prev_td->next_td = virt_to_bus(td);
+	}
 } /* ohci_add_td_to_ed() */
 
 
 inline void ohci_start_control(struct ohci *ohci)
 {
 	/* tell the HC to start processing the control list */
-	writel(OHCI_CMDSTAT_CLF, &ohci->regs->cmdstatus);
+	writel_set(OHCI_USB_CLE, &ohci->regs->control);
+	writel_set(OHCI_CMDSTAT_CLF, &ohci->regs->cmdstatus);
 }
 
 inline void ohci_start_bulk(struct ohci *ohci)
 {
 	/* tell the HC to start processing the bulk list */
-	writel(OHCI_CMDSTAT_BLF, &ohci->regs->cmdstatus);
+	writel_set(OHCI_USB_BLE, &ohci->regs->control);
+	writel_set(OHCI_CMDSTAT_BLF, &ohci->regs->cmdstatus);
 }
 
 inline void ohci_start_periodic(struct ohci *ohci)
 {
-	/* enable processing periodc transfers starting next frame */
+	/* enable processing periodic (intr) transfers starting next frame */
 	writel_set(OHCI_USB_PLE, &ohci->regs->control);
 }
 
@@ -152,6 +159,7 @@
 
 /*
  * Add an ED to the hardware register ED list pointed to by hw_listhead_p
+ * This function only makes sense for Control and Bulk EDs.
  */
 static void ohci_add_ed_to_hw(struct ohci_ed *ed, void* hw_listhead_p)
 {
@@ -172,11 +180,11 @@
 	writel(virt_to_bus(ed), hw_listhead_p);
 
 	spin_unlock_irqrestore(&ohci_edtd_lock, flags);
-} /* ohci_add_ed() */
+} /* ohci_add_ed_to_hw() */
 
 
 /*
- *  Put another control ED on the controller's list
+ *  Put a control ED on the controller's list
  */
 void ohci_add_control_ed(struct ohci *ohci, struct ohci_ed *ed)
 {
@@ -184,37 +192,146 @@
 	ohci_start_control(ohci);
 } /* ohci_add_control_ed() */
 
+/*
+ *  Put a bulk ED on the controller's list
+ */
+void ohci_add_bulk_ed(struct ohci *ohci, struct ohci_ed *ed)
+{
+	ohci_add_ed_to_hw(ed, &ohci->regs->ed_bulkhead);
+	ohci_start_bulk(ohci);
+} /* ohci_add_bulk_ed() */
 
-#if 0
 /*
- *  Put another control ED on the controller's list
+ *  Put a periodic ED on the appropriate list given the period.
  */
 void ohci_add_periodic_ed(struct ohci *ohci, struct ohci_ed *ed, int period)
 {
-	ohci_add_ed_to_hw(ed, /* XXX */);
+	struct ohci_ed *int_ed;
+	unsigned long flags;
+
+	/*
+	 * Pick a good frequency endpoint based on the requested period
+	 */
+	int_ed = &ohci->root_hub->ed[ms_to_ed_int(period)];
+#ifdef OHCI_DEBUG
+	printk("usb-ohci: Using INT ED queue %d for %dms period\n",
+			ms_to_ed_int(period), period);
+#endif
+
+	spin_lock_irqsave(&ohci_edtd_lock, flags);
+	/*
+	 * Insert this ED at the front of the list.
+	 */
+	ed->next_ed = int_ed->next_ed;
+	int_ed->next_ed = virt_to_bus(ed);
+
+	spin_unlock_irqrestore(&ohci_edtd_lock, flags);
+
 	ohci_start_periodic(ohci);
-} /* ohci_add_control_ed() */
+} /* ohci_add_periodic_ed() */
+
+/*
+ *  Put an isochronous ED on the controller's list
+ */
+inline void ohci_add_isoc_ed(struct ohci *ohci, struct ohci_ed *ed)
+{
+	ohci_add_periodic_ed(ohci, ed, 1);
+}
+
+
+/*
+ * This will be used for the interrupt to wake us up on the next SOF
+ */
+DECLARE_WAIT_QUEUE_HEAD(start_of_frame_wakeup);
+
+/*
+ * Guarantee that an ED is safe to be modified by the HCD (us).
+ *
+ * This function can NOT be called from an interrupt.
+ */
+void ohci_wait_for_ed_safe(struct ohci_regs *regs, struct ohci_ed *ed, int ed_type)
+{
+	__u32 hw_listcurrent;
+
+	/* tell the controller to skip this ED */
+	ed->status |= OHCI_ED_SKIP;
+
+	switch (ed_type) {
+	case HCD_ED_CONTROL:
+		hw_listcurrent = readl(regs->ed_controlcurrent);
+		break;
+	case HCD_ED_BULK:
+		hw_listcurrent = readl(regs->ed_bulkcurrent);
+		break;
+	case HCD_ED_ISOC:
+	case HCD_ED_INT:
+		hw_listcurrent = readl(regs->ed_periodcurrent);
+		break;
+	default:
+		return;
+	}
+
+	/* 
+	 * If the HC is processing this ED we need to wait until the
+	 * at least the next frame.
+	 */
+	if (virt_to_bus(ed) == hw_listcurrent) {
+		DECLARE_WAITQUEUE(wait, current);
+
+#ifdef OHCI_DEBUG
+		printk("Waiting a frame for OHC to finish with ED %p\n", ed);
 #endif
 
+		add_wait_queue(&start_of_frame_wakeup, &wait);
+
+		/* clear the SOF interrupt status and enable it */
+		writel(OHCI_INTR_SF, &regs->intrstatus);
+		writel(OHCI_INTR_SF, &regs->intrenable);
+
+		schedule_timeout(HZ/10);
+
+		remove_wait_queue(&start_of_frame_wakeup, &wait);
+	}
+
+	return; /* The ED is now safe */
+} /* ohci_wait_for_ed_safe() */
+
 
 /*
- *  Remove an ED from the HC list whos bus headpointer is pointed to
- *  by hw_listhead_p
+ *  Remove an ED from the HC's list.
+ *  This function can ONLY be used for Control or Bulk EDs.
  *  
  *  Note that the SKIP bit is left on in the removed ED.
  */
-void ohci_remove_ed_from_hw(struct ohci_ed *ed, __u32* hw_listhead_p)
+void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed, int ed_type)
 {
 	unsigned long flags;
+	struct ohci_regs *regs = ohci->regs;
 	struct ohci_ed *cur;
 	__u32 bus_ed = virt_to_bus(ed);
 	__u32 bus_cur;
+	__u32 *hw_listhead_p;
 
 	if (ed == NULL || !bus_ed)
 		return;
 
-	/* tell the controller this skip ED */
-	ed->status |= OHCI_ED_SKIP;
+	switch (ed_type) {
+	case HCD_ED_CONTROL:
+		hw_listhead_p = &regs->ed_controlhead;
+		break;
+	case HCD_ED_BULK:
+		hw_listhead_p = &regs->ed_bulkhead;
+		break;
+	default:
+		printk("Unknown HCD ED type %d.\n", ed_type);
+		return;
+	}
+
+	/*
+	 * Tell the controller to this skip ED and make sure it is not the
+	 * being accessed by the HC as we speak.
+	 */
+	ohci_wait_for_ed_safe(regs, ed, ed_type);
 
 	bus_cur = readl(hw_listhead_p);
 
@@ -232,7 +349,7 @@
 		struct ohci_ed *prev;
 
 		/* walk the list and unlink the ED if found */
-		for (;;) {
+		do {
 			prev = cur;
 			cur = bus_to_virt(cur->next_ed);
 
@@ -241,17 +358,14 @@
 				prev->next_ed = cur->next_ed;
 				break;
 			}
-
-			if (cur->next_ed == 0)
-				break;
-		}
+		} while (cur->next_ed != 0);
 	}
 
 	/* clear any links from the ED for safety */
 	ed->next_ed = 0;
 
 	spin_unlock_irqrestore(&ohci_edtd_lock, flags);
-} /* ohci_remove_ed_from_hw() */
+} /* ohci_remove_norm_ed_from_hw() */
 
 /*
  *  Remove an ED from the controller's control list.  Note that the SKIP bit
@@ -259,7 +373,7 @@
  */
 inline void ohci_remove_control_ed(struct ohci *ohci, struct ohci_ed *ed)
 {
-	ohci_remove_ed_from_hw(ed, &ohci->regs->ed_controlhead);
+	ohci_remove_norm_ed_from_hw(ohci, ed, HCD_ED_CONTROL);
 }
 
 /*
@@ -268,7 +382,7 @@
  */
 inline void ohci_remove_bulk_ed(struct ohci *ohci, struct ohci_ed *ed)
 {
-	ohci_remove_ed_from_hw(ed, &ohci->regs->ed_bulkhead);
+	ohci_remove_norm_ed_from_hw(ohci, ed, HCD_ED_BULK);
 }
 
 
@@ -321,7 +435,8 @@
 	td->next_td = 0;  /* remove the TDs links */
 	td->ed = NULL;
 
-	/* TODO return this TD to the pool of free TDs */
+	/* return this TD to the pool of free TDs */
+	ohci_free_td(td);
 
 	/* unset the "skip me bit" in this ED */
 	ed->status &= ~OHCI_ED_SKIP;
@@ -332,15 +447,21 @@
 
 /*
  * Get a pointer (virtual) to an available TD from the given device's
- * pool.
- *
- * Return NULL if none are left.
+ * pool.  Return NULL if none are left.
  */
 static struct ohci_td *ohci_get_free_td(struct ohci_device *dev)
 {
 	int idx;
 
+#if 0
+	printk(KERN_DEBUG "in ohci_get_free_td()\n");
+#endif
+
+	/* FIXME: this is horribly inefficient */
 	for (idx=0; idx < NUM_TDS; idx++) {
+#if 0
+		show_ohci_td(&dev->td[idx]);
+#endif
 		if (!td_allocated(dev->td[idx])) {
 			struct ohci_td *new_td = &dev->td[idx];
 			/* zero out the TD */
@@ -353,12 +474,53 @@
 		}
 	}
 
-	printk("usb-ohci error: unable to allocate a TD\n");
+	printk("usb-ohci: unable to allocate a TD\n");
 	return NULL;
 } /* ohci_get_free_td() */
 
 
 /*
+ * Get a pointer (virtual) to an available TD from the given device's
+ * pool.  Return NULL if none are left.
+ */
+static struct ohci_ed *ohci_get_free_ed(struct ohci_device *dev)
+{
+	int idx;
+
+	/* FIXME: this is horribly inefficient */
+	for (idx=0; idx < NUM_EDS; idx++) {
+		if (!ed_allocated(dev->ed[idx])) {
+			struct ohci_ed *new_ed = &dev->ed[idx];
+			/* zero out the ED */
+			memset(new_ed, 0, sizeof(*new_ed));
+			/* all new EDs start with the SKIP bit set */
+			new_ed->status |= OHCI_ED_SKIP;
+			/* mark it as allocated */
+			allocate_ed(new_ed);
+			return new_ed;
+		}
+	}
+
+	printk("usb-ohci: unable to allocate an ED\n");
+	return NULL;
+} /* ohci_get_free_ed() */
+
+
+void ohci_free_ed(struct ohci_ed *ed)
+{
+	if (!ed)
+		return;
+
+	if (ed->tail_td == 0) {
+		printk("yikes! an ED without a dummy_td\n");
+	} else
+		ohci_free_td((struct ohci_td *)bus_to_virt(ed->tail_td));
+
+	ed->status &= ~(__u32)ED_ALLOCATED;
+} /* ohci_free_ed() */
+
+
+/*
  *  Initialize a TD
  *
  * 	dir = OHCI_TD_D_IN, OHCI_TD_D_OUT, or OHCI_TD_D_SETUP
@@ -379,10 +541,58 @@
 	td->dev_id = dev_id;
 	td->completed = completed;
 
+#if 0
+	printk(KERN_DEBUG "ohci_fill_new_td created:\n");
+	show_ohci_td(td);
+#endif
+
 	return td;
 } /* ohci_fill_new_td() */
 
 
+/*
+ *  Initialize a new ED on device dev, including allocating and putting the
+ *  dummy tail_td on its queue if it doesn't already have one.  Any
+ *  TDs on this ED other than the dummy will be lost (so there better
+ *  not be any!).  This assumes that the ED is Allocated and will
+ *  force the Allocated bit on.
+ */
+struct ohci_ed *ohci_fill_ed(struct ohci_device *dev, struct ohci_ed *ed, int maxpacketsize, int lowspeed, int endp_id, int isoc_tds)
+{
+	struct ohci_td *dummy_td;
+
+	if (ed_head_td(ed) != ed->tail_td)
+		printk("Reusing a non-empty ED %p!\n", ed);
+
+	if (!ed->tail_td) {
+		dummy_td = ohci_get_free_td(dev);
+		if (dummy_td == NULL) {
+			printk("Error allocating dummy TD for ED %p\n", ed);
+			return NULL;	/* no dummy available! */
+		}
+		make_dumb_td(dummy_td);	/* flag it as a dummy */
+		ed->tail_td = virt_to_bus(dummy_td);
+	} else {
+		dummy_td = bus_to_virt(ed->tail_td);
+		if (!td_dummy(*dummy_td))
+			printk("ED %p's dummy %p is screwy\n", ed, dummy_td);
+	}
+
+	/* set the head TD to the dummy and clear the Carry & Halted bits */
+	ed->_head_td = ed->tail_td;
+
+	ed->status = \
+		ed_set_maxpacket(maxpacketsize) |
+		ed_set_speed(lowspeed) |
+		(endp_id & 0x7ff) |
+		((isoc_tds == 0) ? OHCI_ED_F_NORM : OHCI_ED_F_ISOC);
+	allocate_ed(ed);
+	ed->next_ed = 0;
+
+	return ed;
+} /* ohci_fill_ed() */
+
+
 /**********************************
  * OHCI interrupt list operations *
  **********************************/
@@ -404,26 +614,26 @@
 	struct ohci_td *td;
 	struct ohci_ed *interrupt_ed;	/* endpoint descriptor for this irq */
 
-	/*
-	 * Pick a good frequency endpoint based on the requested period
-	 */
-	interrupt_ed = &dev->ohci->root_hub->ed[ms_to_ed_int(period)];
+	/* Get an ED and TD */
+	interrupt_ed = ohci_get_free_ed(dev);
+	if (!interrupt_ed) {
+		printk("Out of EDs on device %p in ohci_request_irq\n", dev);
+		return -1;
+	}
+
+	td = ohci_get_free_td(dev);
+	if (!td) {
+		printk("Out of TDs in ohci_request_irq\n");
+		ohci_free_ed(interrupt_ed);
+		return -1;
+	}
 
 	/*
 	 * Set the max packet size, device speed, endpoint number, usb
 	 * device number (function address), and type of TD.
-	 *
-	 * FIXME: Isochronous transfers need a pool of special 32 byte
-	 * TDs (32 byte aligned) in order to be supported.
 	 */
-	interrupt_ed->status = \
-		ed_set_maxpacket(usb_maxpacket(pipe)) |
-		ed_set_speed(usb_pipeslow(pipe)) |
-		usb_pipe_endpdev(pipe) |
-		OHCI_ED_F_NORM;
-
-	td = ohci_get_free_td(dev);
-	/* FIXME: check for NULL */
+	ohci_fill_ed(dev, interrupt_ed, usb_maxpacket(pipe), usb_pipeslow(pipe),
+		usb_pipe_endpdev(pipe), 0 /* normal TDs */);
 
 	/* Fill in the TD */
 	ohci_fill_new_td(td, td_set_dir_out(usb_pipeout(pipe)),
@@ -432,42 +642,23 @@
 			&dev->data, DATA_BUF_LEN,
 			dev_id, handler);
 	/*
-	 * TODO: be aware that OHCI won't advance out of the 4kb
-	 * page cur_buf started in.  It'll wrap around to the start
-	 * of the page...  annoying or useful? you decide.
-	 *
-	 * We should make sure dev->data doesn't cross a page...
+	 * TODO: be aware of how the OHCI controller deals with DMA
+	 * spanning more than one page.
 	 */
 
-	/* FIXME: this just guarantees that its the end of the list */
-	td->next_td = 0;
+	/*
+	 *  Put the TD onto our ED and make sure its ready to run
+	 */
+	ohci_add_td_to_ed(td, interrupt_ed);
+	interrupt_ed->status &= ~OHCI_ED_SKIP;
+	ohci_unhalt_ed(interrupt_ed);
 
 	/* Linus did this. see asm/system.h; scary concept... I don't
 	 * know if its needed here or not but it won't hurt. */
 	wmb();
 
-	/*
-	 *  Put the TD onto our ED
-	 */
-	{
-		unsigned long flags;
-		spin_lock_irqsave(&ohci_edtd_lock, flags);
-		ohci_add_td_to_ed(td, interrupt_ed);
-		spin_unlock_irqrestore(&ohci_edtd_lock, flags);
-	}
-
-#if 0
 	/* Assimilate the new ED into the collective */
-	/*
-	 *  When dynamic ED allocation is done, this call will be
-	 *  useful.  For now, the correct ED already on the
-	 *  controller's proper periodic ED lists was chosen above.
-	 */
 	ohci_add_periodic_ed(dev->ohci, interrupt_ed, period);
-#else
-	/* enable periodic (interrupt) transfers on the HC */
-	ohci_start_periodic(dev->ohci);
-#endif
 
 	return 0;
 } /* ohci_request_irq() */
@@ -486,6 +677,12 @@
  */
 static int ohci_control_completed(int stats, void *buffer, void *dev_id)
 {
+	/* pass the TDs completion status back to control_msg */
+	if (dev_id) {
+		int *completion_status = (int *)dev_id;
+		*completion_status = stats;
+	}
+
 	wake_up(&control_wakeup);
 	return 0;
 } /* ohci_control_completed() */
@@ -502,44 +699,46 @@
  *   - The command itself
  *   - An optional data phase (if len > 0)
  *   - Status complete phase
+ *
+ * This function can NOT be called from an interrupt.
  */
 static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, void *cmd, void *data, int len)
 {
 	struct ohci_device *dev = usb_to_ohci(usb);
-	/*
-	 * ideally dev->ed should be linked into the root hub's
-	 * control_ed list and used instead of just using it directly.
-	 * This could present a problem as is with more than one
-	 * device.  (but who wants to use a keyboard AND a mouse
-	 * anyways? ;)
-	 */
-	struct ohci_ed *control_ed = &dev->ohci->root_hub->ed[ED_CONTROL];
+	struct ohci_ed *control_ed = ohci_get_free_ed(dev);
 	struct ohci_td *setup_td, *data_td, *status_td;
 	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int completion_status = -1;
 
-#if 0
-	printk(KERN_DEBUG "entering ohci_control_msg %p (ohci_dev: %p) pipe 0x%x, cmd %p, data %p, len %d\n", usb, dev, pipe, cmd, data, len);
+#ifdef OHCI_DEBUG 
+	printk(KERN_DEBUG "ohci_control_msg %p (ohci_dev: %p) pipe %x, cmd %p, data %p, len %d\n", usb, dev, pipe, cmd, data, len);
 #endif
+	if (!control_ed) {
+		printk("usb-ohci: couldn't get ED for dev %p\n", dev);
+		return -1;
+	}
+
+	/* get a TD to send this control message with */
+	setup_td = ohci_get_free_td(dev);
+	if (!setup_td) {
+		printk("usb-ohci: couldn't get TD for dev %p [cntl setup]\n", dev);
+		ohci_free_ed(control_ed);
+		return -1;
+	}
 
 	/*
 	 * Set the max packet size, device speed, endpoint number, usb
 	 * device number (function address), and type of TD.
 	 *
 	 */
-	control_ed->status = \
-		ed_set_maxpacket(usb_maxpacket(pipe)) |
-		ed_set_speed(usb_pipeslow(pipe)) |
-		usb_pipe_endpdev(pipe) |
-		OHCI_ED_F_NORM;
+	ohci_fill_ed(dev, control_ed, usb_maxpacket(pipe), usb_pipeslow(pipe),
+		usb_pipe_endpdev(pipe), 0 /* normal TDs */);
 
 	/*
 	 * Build the control TD
 	 */
 
-	/* get a TD to send this control message with */
-	setup_td = ohci_get_free_td(dev);
-	/* TODO check for NULL */
-
 	/*
 	 * Set the not accessed condition code, allow odd sized data,
 	 * and set the data transfer type to SETUP.  Setup DATA always
@@ -555,7 +754,13 @@
 			NULL, NULL);
 
 	/* allocate the next TD */
-	data_td = ohci_get_free_td(dev);  /* TODO check for NULL */
+	data_td = ohci_get_free_td(dev);
+	if (!data_td) {
+		printk("usb-ohci: couldn't get TD for dev %p [cntl data]\n", dev);
+		ohci_free_td(setup_td);
+		ohci_free_ed(control_ed);
+		return -1;
+	}
 
 	/* link to the next TD */
 	setup_td->next_td = virt_to_bus(data_td);
@@ -570,48 +775,59 @@
 				NULL, NULL);
 
 		/*
-		 * XXX we should check that the data buffer doesn't
-		 * cross a 4096 byte boundary.  If so, it needs to be
-		 * copied into a single 4096 byte aligned area for the
-		 * OHCI's TD logic to see it all, or multiple TDs need
-		 * to be made for each page.
+		 * TODO: Normal TDs can transfer up to 8192 bytes on OHCI.
+		 * However, for that to happen, the data must -start-
+		 * on a nice 4kb page.  We need to check for data
+		 * sizes > 4096 and, if they cross more than two 4096
+		 * byte pages of memory one or more additional TDs
+		 * will need to be created.  (repeat doing this in a
+		 * loop until all of the DATA is on a TD)
 		 *
-		 * It's not likely a control transfer will run into
-		 * this problem.. (famous last words)
+		 * Control transfers are -highly unlikely- to need to
+		 * transfer this much data.. but who knows.. sadistic
+		 * hardware is sure to exist.
 		 */
 
 		status_td = ohci_get_free_td(dev);  /* TODO check for NULL */
+		if (!status_td) {
+			printk("usb-ohci: couldn't get TD for dev %p [cntl status]\n", dev);
+			ohci_free_td(setup_td);
+			ohci_free_td(data_td);
+			ohci_free_ed(control_ed);
+			return -1;
+		}
+
 		data_td->next_td = virt_to_bus(status_td);
 	} else {
 		status_td = data_td; /* no data_td, use it for status */
 	}
 
-	/* The control status packet always uses a DATA1 */
+	/* The control status packet always uses a DATA1
+	 * Give "dev_id" the address of completion_status so that the
+	 * TDs status can be passed back to us from the IRQ. */
 	ohci_fill_new_td(status_td,
 			td_set_dir_in(usb_pipeout(pipe) | (len == 0)),
 			TOGGLE_DATA1,
-			0,
-			NULL, 0,
-			NULL, ohci_control_completed);
+			0 /* flags */,
+			NULL /* data */, 0 /* data len */,
+			&completion_status, ohci_control_completed);
 	status_td->next_td = 0; /* end of TDs */
 
 	/*
-	 * Start the control transaction..
-	 */
-	current->state = TASK_UNINTERRUPTIBLE;
-	add_wait_queue(&control_wakeup, &wait);
-
-	/*
 	 * Add the chain of 2-3 control TDs to the control ED's TD list
 	 */
-	{
-		unsigned long flags;
-		spin_lock_irqsave(&ohci_edtd_lock, flags);
-		ohci_add_td_to_ed(setup_td, control_ed);
-		spin_unlock_irqrestore(&ohci_edtd_lock, flags);
-	}
+	spin_lock_irqsave(&ohci_edtd_lock, flags);
+	control_ed->status |= OHCI_ED_SKIP;
+	ohci_add_td_to_ed(setup_td, control_ed);
+	if (data_td != status_td)
+		ohci_add_td_to_ed(data_td, control_ed);
+	ohci_add_td_to_ed(status_td, control_ed);
+	control_ed->status &= ~OHCI_ED_SKIP;
+	ohci_unhalt_ed(control_ed);
+	spin_unlock_irqrestore(&ohci_edtd_lock, flags);
 
-#if 0
+#ifdef OHCI_DEBUG
+	if (MegaDebug) {
 	/* complete transaction debugging output (before) */
 	printk(KERN_DEBUG " Control ED %lx:\n", virt_to_bus(control_ed));
 	show_ohci_ed(control_ed);
@@ -623,48 +839,55 @@
 	}
 	printk(KERN_DEBUG " Status TD %lx:\n", virt_to_bus(status_td));
 	show_ohci_td(status_td);
+	printk(KERN_DEBUG " Controller Status:\n");
+	show_ohci_status(dev->ohci);
+	}
 #endif
 
+	/*
+	 * Start the control transaction..
+	 */
+	current->state = TASK_UNINTERRUPTIBLE;
+	add_wait_queue(&control_wakeup, &wait);
+
 	/* Give the ED to the HC */
 	ohci_add_control_ed(dev->ohci, control_ed);
 
-	/* FIXME:
-	 * this should really check to see that the transaction completed.
-	 */
 	schedule_timeout(HZ/10);
 
 	remove_wait_queue(&control_wakeup, &wait);
 
-#if 0
+#ifdef OHCI_DEBUG
+	if (MegaDebug) {
 	/* complete transaction debugging output (after) */
-	printk(KERN_DEBUG " (after) Control ED:\n");
+	printk(KERN_DEBUG " *after* Control ED %lx:\n", virt_to_bus(control_ed));
 	show_ohci_ed(control_ed);
-	printk(KERN_DEBUG " (after) Setup TD:\n");
+	printk(KERN_DEBUG " *after* Setup TD %lx:\n", virt_to_bus(setup_td));
 	show_ohci_td(setup_td);
 	if (data_td != status_td) {
-		printk(KERN_DEBUG " (after) Data TD:\n");
+		printk(KERN_DEBUG " *after* Data TD %lx:\n", virt_to_bus(data_td));
 		show_ohci_td(data_td);
 	}
-	printk(KERN_DEBUG " (after) Status TD:\n");
+	printk(KERN_DEBUG " *after* Status TD %lx:\n", virt_to_bus(status_td));
 	show_ohci_td(status_td);
+	printk(KERN_DEBUG " *after* Controller Status:\n");
+	show_ohci_status(dev->ohci);
+	}
 #endif
 
-	/* clean up incase it failed */
-	/* XXX only do this if their ed pointer still points to control_ed
-	 * incase they've been reclaimed and used by something else
-	 * already. -greg */
-	ohci_remove_td_from_ed(setup_td, control_ed);
-	ohci_remove_td_from_ed(data_td, control_ed);
-	ohci_remove_td_from_ed(status_td, control_ed);
-
-	/* remove the control ED */
+	/* clean up */
+	ohci_free_td(setup_td);
+	if (data_td != status_td)
+		ohci_free_td(data_td);
+	ohci_free_td(status_td);
+	/* remove the control ED from the HC */
 	ohci_remove_control_ed(dev->ohci, control_ed);
+	ohci_free_ed(control_ed);	 /* return it to the pool */
 
 #if 0
 	printk(KERN_DEBUG "leaving ohci_control_msg\n");
 #endif
-
-	return ohci_td_result(dev, status_td);
+	return completion_status;
 } /* ohci_control_msg() */
 
 
@@ -675,6 +898,7 @@
 {
 	struct usb_device *usb_dev;
 	struct ohci_device *dev;
+	int idx;
 
 	/*
 	 * Allocate the generic USB device
@@ -696,6 +920,12 @@
 
 	memset(dev, 0, sizeof(*dev));
 
+	/* Initialize all EDs in a new device with the skip flag so that
+	 * they are ignored by the controller until set otherwise. */
+	for (idx = 0; idx < NUM_EDS; ++idx) {
+		dev->ed[idx].status |= OHCI_ED_SKIP;
+	}
+
 	/*
 	 * Link them together
 	 */
@@ -752,10 +982,10 @@
  */
 static int reset_hc(struct ohci *ohci)
 {
-	int timeout = 1000;  /* prevent an infinite loop */
+	int timeout = 10000;  /* prevent an infinite loop */
 
 #if 0
-	printk(KERN_DEBUG "usb-ohci: resetting HC %p\n", ohci);
+	printk(KERN_INFO "usb-ohci: resetting HC %p\n", ohci);
 #endif
 
 	writel(~0x0, &ohci->regs->intrdisable);    /* Disable HC interrupts */
@@ -770,7 +1000,7 @@
 		udelay(1);
 	}
 
-	printk(KERN_DEBUG "usb-ohci: HC %p reset.\n", ohci);
+	printk(KERN_INFO "usb-ohci: HC %p reset.\n", ohci);
 
 	return 0;
 } /* reset_hc() */
@@ -783,6 +1013,7 @@
 {
 	int ret = 0;
 	int fminterval;
+	__u32 what_to_enable;
 
 	fminterval = readl(&ohci->regs->fminterval) & 0x3fff;
 #if 0
@@ -812,9 +1043,13 @@
 	 * useful for debugging and as a bus heartbeat. -greg
 	 */
 	/* Choose the interrupts we care about */
-	writel( OHCI_INTR_MIE | /* OHCI_INTR_RHSC | */
-		OHCI_INTR_WDH | OHCI_INTR_FNO,
-		&ohci->regs->intrenable);
+	what_to_enable = OHCI_INTR_MIE |
+#ifdef OHCI_RHSC_INT
+			OHCI_INTR_RHSC |
+#endif
+			/* | OHCI_INTR_FNO */
+			OHCI_INTR_WDH;
+	writel( what_to_enable, &ohci->regs->intrenable);
 
 	/* Enter the USB Operational state & start the frames a flowing.. */
 	writel_set(OHCI_USB_OPER, &ohci->regs->control);
@@ -861,7 +1096,7 @@
 	/*
 	 * Wait for the reset to complete.
 	 */
-	wait_ms(10);
+	wait_ms(20);
 
 	/* check port status to see that the reset completed */
 	status = readl(&ohci->regs->roothub.portstatus[port]);
@@ -886,10 +1121,12 @@
 	struct usb_device *usb_dev;
 	struct ohci_device *dev;
 	/* memory I/O address of the port status register */
-	void *portaddr = &ohci->regs->roothub.portstatus[port];
+	__u32 *portaddr = &ohci->regs->roothub.portstatus[port];
 	int portstatus;	
 
-	printk(KERN_DEBUG "ohci_connect_change(%p, %d)\n", ohci, port);
+#ifdef OHCI_DEBUG
+	printk(KERN_DEBUG "ohci_connect_change on port %d\n", port);
+#endif
 
 	/*
 	 * Because of the status change we have to forget
@@ -903,6 +1140,14 @@
 	/* disable the port if nothing is connected */
 	if (!(portstatus & PORT_CCS)) {
 		writel(PORT_CCS, portaddr);
+		/* We need to reset the CSC bit -after- disabling the
+		 * port because it causes the CSC bit to come on
+		 * again... */
+		wait_ms(20);
+		writel(PORT_CSC, portaddr);
+#ifdef OHCI_DEBUG
+		printk(KERN_DEBUG "ohci port %d disabled, nothing connected.\n", port);
+#endif
 		return;
 	}
 
@@ -943,15 +1188,18 @@
 	struct ohci_regs *regs = ohci->regs;
 	int num = 0;
 	int maxport = readl(&ohci->regs->roothub) & 0xff;
+	__u32 rh_change_flags = PORT_CSC | PORT_PESC;	/* root hub status changes */
 
-#if 1
+#ifdef OHCI_DEBUG
 	printk(KERN_DEBUG "entering ohci_check_configuration %p\n", ohci);
 #endif
 
 	do {
-		if (readl(&regs->roothub.portstatus[num]) & PORT_CSC) {
-			/* reset the connect status change bit */
-			writel(PORT_CSC, &regs->roothub.portstatus[num]);
+		__u32 *portstatus_p = &regs->roothub.portstatus[num];
+		if (readl(portstatus_p) & rh_change_flags) {
+			/* acknowledge the root hub status changes */
+			writel_set(rh_change_flags, portstatus_p);
+			/* disable the port if nothing is on it */
 			/* check the port for a nifty device */
 			ohci_connect_change(ohci, num);
 		}
@@ -977,8 +1225,8 @@
 		int maxport = ohci->root_hub->usb->maxchild;
 
 		do {
-			if (readl(&ohci->regs->roothub.portstatus[num]) &
-					PORT_CSC) {
+			__u32 *portstatus_p = &ohci->regs->roothub.portstatus[num];
+			if (readl(portstatus_p) & PORT_CSC) {
 				if (waitqueue_active(&ohci_configure))
 					wake_up(&ohci_configure);
 				return;
@@ -1037,6 +1285,9 @@
 	while (td != NULL) {
 		struct ohci_td *next_td = td->next_dl_td;
 
+		if (td_dummy(*td))
+			printk("yikes! reaping a dummy TD\n");
+
 		/* FIXME: munge td->info into a future standard status format */
 		/* Check if TD should be re-queued */
 		if ((td->completed != NULL) &&
@@ -1044,14 +1295,15 @@
 		{
 			/* Mark the TD as active again:
 			 * Set the not accessed condition code
-			 * FIXME: should this reset OHCI_TD_ERRCNT?
+			 * Reset the Error count
+			 * [FIXME: report errors to the device's driver]
 			 */
 			td->info |= OHCI_TD_CC_NEW;
+			clear_td_errorcount(td);
 
 			/* point it back to the start of the data buffer */
 			td->cur_buf = virt_to_bus(td->data);
 
-			/* XXX disabled for debugging reasons right now.. */
 			/* insert it back on its ED */
 			ohci_add_td_to_ed(td, td->ed);
 		} else {
@@ -1066,9 +1318,6 @@
 } /* ohci_reap_donelist() */
 
 
-#if 0
-static int in_int = 0;
-#endif
 /*
  * Get annoyed at the controller for bothering us.
  * This pretty much follows the OHCI v1.0a spec, section 5.3.
@@ -1080,19 +1329,10 @@
 	struct ohci_hcca *hcca = ohci->root_hub->hcca;
 	__u32 status, context;
 
-#if 0
-	/* for debugging to keep IRQs from running away. */
-	if (in_int >= 2)
-		return;
-	++in_int;
-	return;
-#endif
-
 	/* Save the status of the interrupts that are enabled */
 	status = readl(&regs->intrstatus);
 	status &= readl(&regs->intrenable);
 
-
 	/* make context = the interrupt status bits that we care about */
 	if (hcca->donehead != 0) {
 		context = OHCI_INTR_WDH;   /* hcca donehead needs processing */
@@ -1122,6 +1362,10 @@
 		context &= ~OHCI_INTR_WDH;  /* mark this as checked */
 	}
 
+#ifdef OHCI_RHSC_INT
+	/* NOTE: this is very funky on some USB controllers (ie: it
+	 * doesn't work right).  Using the ohci_timer instead to poll
+	 * the root hub is a much better choice. */
 	/* Process any root hub status changes */
 	if (context & OHCI_INTR_RHSC) {
 		/* Wake the thread to process root hub events */
@@ -1134,10 +1378,17 @@
 		 * The control thread will re-enable it after it has
 		 * checked the root hub status.
 		 */
-	} else {
-		/* check the root hub status anyways. Some controllers
-		 * might not generate the interrupt properly. (?) */
-		ohci_root_hub_events(ohci);
+	}
+#endif
+
+	/* Start of Frame interrupts, used during safe ED removal */
+	if (context & (OHCI_INTR_SF)) {
+		writel(OHCI_INTR_SF, &regs->intrstatus);
+		if (waitqueue_active(&start_of_frame_wakeup))
+			wake_up(&start_of_frame_wakeup);
+		/* Do NOT mark the frame start interrupt as checked
+		 * as we don't want to receive any more of them until
+		 * asked. */
 	}
 
 	/* Check those "other" pesky bits */
@@ -1166,8 +1417,8 @@
 		context &= ~OHCI_INTR_OC;  /* mark this as checked */
 	}
 
-	/* Mask out any remaining unprocessed interrupts so we don't
-	 * get any more of them. */
+	/* Mask out any remaining unprocessed or unmasked interrupts
+	 * so that we don't get any more of them. */
 	if (context & ~OHCI_INTR_MIE) {
 		writel(context, &regs->intrdisable);
 	}
@@ -1275,7 +1526,9 @@
 
 	/*
 	 * Initialize the polling table to call interrupts at the
-	 * intended intervals.
+	 * intended intervals.  Note that these EDs are just
+	 * placeholders.  They have their SKIP bit set and are used as
+	 * list heads to insert real EDs onto.
 	 */
 	dev->hcca->int_table[0] = virt_to_bus(&dev->ed[ED_INT_1]);
 	for (i = 1; i < NUM_INTS; i++) {
@@ -1297,18 +1550,14 @@
 	}
 
 	/*
-	 * Tell the controller where the control and bulk lists are
+	 * Tell the controller where the control and bulk lists are.
 	 * The lists start out empty.
 	 */
 	writel(0, &ohci->regs->ed_controlhead);
 	writel(0, &ohci->regs->ed_bulkhead);
-	/*
-	writel(virt_to_bus(&dev->ed[ED_CONTROL]), &ohci->regs->ed_controlhead);
-	writel(virt_to_bus(&dev->ed[ED_BULK]), &ohci->regs->ed_bulkhead);
-	*/
 
-#if 0
-	printk(KERN_DEBUG "alloc_ohci(): controller\n");
+#ifdef OHCI_DEBUG
+	printk(KERN_INFO "alloc_ohci(): controller\n");
 	show_ohci_status(ohci);
 #endif
 
@@ -1325,7 +1574,7 @@
  */
 static void release_ohci(struct ohci *ohci)
 {
-	printk(KERN_DEBUG "entering release_ohci %p\n", ohci);
+	printk(KERN_INFO "Releasing OHCI controller 0x%p\n", ohci);
 
 #ifdef OHCI_TIMER
 	/* stop our timer */
@@ -1378,7 +1627,7 @@
 	 * This thread doesn't need any user-level access,
 	 * so get rid of all of our resources..
 	 */
-	printk("ohci_control_thread code at %p\n", &ohci_control_thread);
+	printk(KERN_INFO "ohci-control thread code for 0x%p code at 0x%p\n", __ohci, &ohci_control_thread);
 	exit_mm(current);
 	exit_files(current);
 	exit_fs(current);
@@ -1391,7 +1640,7 @@
 	if (start_hc(ohci) < 0) {
 		printk("usb-ohci: failed to start the controller\n");
 		release_ohci(ohci);
-		printk(KERN_DEBUG "leaving ohci_control_thread %p\n", __ohci);
+		printk(KERN_INFO "leaving ohci_control_thread %p\n", __ohci);
 		return 0;
 	}
 
@@ -1405,11 +1654,11 @@
 		ohci_check_configuration(ohci);
 
 		/* re-enable root hub status change interrupts. */
-#if 0
+#ifdef OHCI_RHSC_INT
 		writel(OHCI_INTR_RHSC, &ohci->regs->intrenable);
 #endif
 
-		printk(KERN_DEBUG "ohci-control thread sleeping\n");
+		printk(KERN_INFO "ohci-control thread sleeping\n");
 		interruptible_sleep_on(&ohci_configure);
 #ifdef CONFIG_APM
 		if (apm_resume) {
@@ -1431,9 +1680,14 @@
 			spin_unlock_irq(&current->sigmask_lock);
 
 			if(signr == SIGUSR1) {
-				/* FIXME: have it do a full ed/td queue dump */
+				/* TODO: have it do a full ed/td queue dump? */
 				printk(KERN_DEBUG "OHCI status dump:\n");
 				show_ohci_status(ohci);
+			} else if (signr == SIGUSR2) {
+				/* toggle mega TD/ED debugging output */
+				MegaDebug = !MegaDebug;
+				printk(KERN_DEBUG "usb-ohci: Mega debugging %sabled.\n",
+						MegaDebug ? "en" : "dis");
 			} else {
 				/* unknown signal, exit the thread */
 				break;
@@ -1444,7 +1698,7 @@
 	reset_hc(ohci);
 	release_ohci(ohci);
 
-	printk(KERN_DEBUG "leaving ohci_control_thread %p\n", __ohci);
+	printk(KERN_INFO "ohci-control thread for 0x%p exiting\n", __ohci);
 
 	return 0;
 } /* ohci_control_thread() */
@@ -1538,8 +1792,8 @@
 
 		ohci->irq = irq;
 
-#if 0
-		printk(KERN_DEBUG "usb-ohci: starting ohci-control thread\n");
+#ifdef OHCI_DEBUG
+		printk(KERN_INFO "usb-ohci: forking ohci-control thread for 0x%p\n", ohci);
 #endif
 
 		/* fork off the handler */
@@ -1555,7 +1809,7 @@
 	}
 	release_ohci(ohci);
 
-#if 0
+#ifdef OHCI_DEBUG
 	printk(KERN_DEBUG "leaving found_ohci %d %p\n", irq, mem_base);
 #endif
 
@@ -1597,6 +1851,11 @@
 	}
         MOD_INC_USE_COUNT;
 
+#ifdef OHCI_DEBUG
+	printk("usb-ohci: Warning! Gobs of debugging output has been enabled.\n");
+	printk("          Check your kern.debug logs for the bulk of it.\n");
+#endif
+
 	if (found_ohci(dev->irq, (void *) mem_base) < 0) {
 		MOD_DEC_USE_COUNT;
 		return -1;
@@ -1672,6 +1931,7 @@
 	}
 	return retval;
 } /* ohci_init */
+
 
 /* vim:sw=8
  */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)