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
- Lines: 1248
- Date:
Sun May 16 22:23:31 1999
- Orig file:
v2.3.2/linux/drivers/usb/ohci.c
- Orig date:
Sat May 15 23:46:03 1999
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, ®s->intrstatus);
+ writel(OHCI_INTR_SF, ®s->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 = ®s->ed_controlhead;
+ break;
+ case HCD_ED_BULK:
+ hw_listhead_p = ®s->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(®s->roothub.portstatus[num]) & PORT_CSC) {
- /* reset the connect status change bit */
- writel(PORT_CSC, ®s->roothub.portstatus[num]);
+ __u32 *portstatus_p = ®s->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(®s->intrstatus);
status &= readl(®s->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, ®s->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, ®s->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(¤t->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)