ChangeSet 1.1002.3.16, 2003/02/25 11:32:31-08:00, david-b@pacbell.net [PATCH] USB ohci: "registers" sysfs file This is a slightly cleaned up version of Kevin's patch to add a "registers" sysfs debug file. Minor style and whitespace fixups, prints the other register, resolved config/build issues (minor). It also has two minor tweaks: a fix for a potential assertion violation on a "dead-hc" cleanup path (rare), and wasting less time blocking irqs when they're already blocked. drivers/usb/host/ohci-dbg.c | 248 +++++++++++++++++++++++++++++++++----------- drivers/usb/host/ohci-hcd.c | 18 ++- drivers/usb/host/ohci-pci.c | 2 drivers/usb/host/ohci-q.c | 21 +-- 4 files changed, 212 insertions(+), 77 deletions(-) diff -Nru a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c --- a/drivers/usb/host/ohci-dbg.c Fri Feb 28 14:50:17 2003 +++ b/drivers/usb/host/ohci-dbg.c Fri Feb 28 14:50:17 2003 @@ -1,50 +1,50 @@ /* * OHCI HCD (Host Controller Driver) for USB. - * + * * (C) Copyright 1999 Roman Weissgaerber * (C) Copyright 2000-2002 David Brownell - * + * * This file is licenced under the GPL. */ - + /*-------------------------------------------------------------------------*/ #ifdef DEBUG #define edstring(ed_type) ({ char *temp; \ switch (ed_type) { \ - case PIPE_CONTROL: temp = "CTRL"; break; \ - case PIPE_BULK: temp = "BULK"; break; \ - case PIPE_INTERRUPT: temp = "INTR"; break; \ - default: temp = "ISOC"; break; \ + case PIPE_CONTROL: temp = "ctrl"; break; \ + case PIPE_BULK: temp = "bulk"; break; \ + case PIPE_INTERRUPT: temp = "intr"; break; \ + default: temp = "isoc"; break; \ }; temp;}) #define pipestring(pipe) edstring(usb_pipetype(pipe)) -/* debug| print the main components of an URB +/* debug| print the main components of an URB * small: 0) header + data packets 1) just header */ static void __attribute__((unused)) urb_print (struct urb * urb, char * str, int small) { unsigned int pipe= urb->pipe; - + if (!urb->dev || !urb->dev->bus) { dbg("%s URB: no dev", str); return; } - + #ifndef OHCI_VERBOSE_DEBUG if (urb->status != 0) #endif - dbg("%s %p dev:%d,ep=%d-%c,%s,flags:%x,len:%d/%d,stat:%d", + dbg("%s %p dev=%d ep=%d%s-%s flags=%x len=%d/%d stat=%d", str, urb, usb_pipedevice (pipe), - usb_pipeendpoint (pipe), - usb_pipeout (pipe)? 'O': 'I', + usb_pipeendpoint (pipe), + usb_pipeout (pipe)? "out" : "in", pipestring (pipe), - urb->transfer_flags, - urb->actual_length, + urb->transfer_flags, + urb->actual_length, urb->transfer_buffer_length, urb->status); @@ -54,27 +54,43 @@ if (usb_pipecontrol (pipe)) { printk (KERN_DEBUG __FILE__ ": setup(8):"); - for (i = 0; i < 8 ; i++) + for (i = 0; i < 8 ; i++) printk (" %02x", ((__u8 *) urb->setup_packet) [i]); printk ("\n"); } if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { - printk (KERN_DEBUG __FILE__ ": data(%d/%d):", - urb->actual_length, + printk (KERN_DEBUG __FILE__ ": data(%d/%d):", + urb->actual_length, urb->transfer_buffer_length); - len = usb_pipeout (pipe)? + len = usb_pipeout (pipe)? urb->transfer_buffer_length: urb->actual_length; - for (i = 0; i < 16 && i < len; i++) + for (i = 0; i < 16 && i < len; i++) printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]); printk ("%s stat:%d\n", i < len? "...": "", urb->status); } - } + } #endif } -static void ohci_dump_intr_mask (struct ohci_hcd *ohci, char *label, __u32 mask) +#define ohci_dbg_sw(ohci, next, size, format, arg...) \ + do { \ + if (next) { \ + unsigned s_len; \ + s_len = snprintf (*next, *size, format, ## arg ); \ + *size -= s_len; *next += s_len; \ + } else \ + ohci_dbg(ohci,format, ## arg ); \ + } while (0); + + +static void ohci_dump_intr_mask ( + struct ohci_hcd *ohci, + char *label, + u32 mask, + char **next, + unsigned *size) { - ohci_dbg (ohci, "%s: 0x%08x%s%s%s%s%s%s%s%s%s\n", + ohci_dbg_sw (ohci, next, size, "%s 0x%08x%s%s%s%s%s%s%s%s%s\n", label, mask, (mask & OHCI_INTR_MIE) ? " MIE" : "", @@ -89,10 +105,15 @@ ); } -static void maybe_print_eds (struct ohci_hcd *ohci, char *label, __u32 value) +static void maybe_print_eds ( + struct ohci_hcd *ohci, + char *label, + u32 value, + char **next, + unsigned *size) { if (value) - ohci_dbg (ohci, "%s %08x\n", label, value); + ohci_dbg_sw (ohci, next, size, "%s %08x\n", label, value); } static char *hcfs2string (int state) @@ -107,19 +128,22 @@ } // dump control and status registers -static void ohci_dump_status (struct ohci_hcd *controller) +static void +ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size) { struct ohci_regs *regs = controller->regs; - __u32 temp; + u32 temp; temp = readl (®s->revision) & 0xff; - ohci_dbg (controller, "OHCI %d.%d, %s legacy support registers\n", + ohci_dbg_sw (controller, next, size, + "OHCI %d.%d, %s legacy support registers\n", 0x03 & (temp >> 4), (temp & 0x0f), (temp & 0x10) ? "with" : "NO"); temp = readl (®s->control); - ohci_dbg (controller, - "control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d\n", temp, + ohci_dbg_sw (controller, next, size, + "control 0x%03x%s%s%s HCFS=%s%s%s%s%s CBSR=%d\n", + temp, (temp & OHCI_CTRL_RWE) ? " RWE" : "", (temp & OHCI_CTRL_RWC) ? " RWC" : "", (temp & OHCI_CTRL_IR) ? " IR" : "", @@ -132,7 +156,8 @@ ); temp = readl (®s->cmdstatus); - ohci_dbg (controller, "cmdstatus: 0x%08x SOC=%d%s%s%s%s\n", temp, + ohci_dbg_sw (controller, next, size, + "cmdstatus 0x%05x SOC=%d%s%s%s%s\n", temp, (temp & OHCI_SOC) >> 16, (temp & OHCI_OCR) ? " OCR" : "", (temp & OHCI_BLF) ? " BLF" : "", @@ -140,24 +165,59 @@ (temp & OHCI_HCR) ? " HCR" : "" ); - ohci_dump_intr_mask (controller, "intrstatus", readl (®s->intrstatus)); - ohci_dump_intr_mask (controller, "intrenable", readl (®s->intrenable)); + ohci_dump_intr_mask (controller, "intrstatus", + readl (®s->intrstatus), next, size); + ohci_dump_intr_mask (controller, "intrenable", + readl (®s->intrenable), next, size); // intrdisable always same as intrenable - maybe_print_eds (controller, "ed_periodcurrent", readl (®s->ed_periodcurrent)); - - maybe_print_eds (controller, "ed_controlhead", readl (®s->ed_controlhead)); - maybe_print_eds (controller, "ed_controlcurrent", readl (®s->ed_controlcurrent)); + maybe_print_eds (controller, "ed_periodcurrent", + readl (®s->ed_periodcurrent), next, size); - maybe_print_eds (controller, "ed_bulkhead", readl (®s->ed_bulkhead)); - maybe_print_eds (controller, "ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + maybe_print_eds (controller, "ed_controlhead", + readl (®s->ed_controlhead), next, size); + maybe_print_eds (controller, "ed_controlcurrent", + readl (®s->ed_controlcurrent), next, size); + + maybe_print_eds (controller, "ed_bulkhead", + readl (®s->ed_bulkhead), next, size); + maybe_print_eds (controller, "ed_bulkcurrent", + readl (®s->ed_bulkcurrent), next, size); + + maybe_print_eds (controller, "donehead", + readl (®s->donehead), next, size); +} + +#define dbg_port_sw(hc,num,value,next,size) \ + ohci_dbg_sw (hc, next, size, \ + "roothub.portstatus [%d] " \ + "0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \ + num, temp, \ + (temp & RH_PS_PRSC) ? " PRSC" : "", \ + (temp & RH_PS_OCIC) ? " OCIC" : "", \ + (temp & RH_PS_PSSC) ? " PSSC" : "", \ + (temp & RH_PS_PESC) ? " PESC" : "", \ + (temp & RH_PS_CSC) ? " CSC" : "", \ + \ + (temp & RH_PS_LSDA) ? " LSDA" : "", \ + (temp & RH_PS_PPS) ? " PPS" : "", \ + (temp & RH_PS_PRS) ? " PRS" : "", \ + (temp & RH_PS_POCI) ? " POCI" : "", \ + (temp & RH_PS_PSS) ? " PSS" : "", \ + \ + (temp & RH_PS_PES) ? " PES" : "", \ + (temp & RH_PS_CCS) ? " CCS" : "" \ + ); - maybe_print_eds (controller, "donehead", readl (®s->donehead)); -} -static void ohci_dump_roothub (struct ohci_hcd *controller, int verbose) +static void +ohci_dump_roothub ( + struct ohci_hcd *controller, + int verbose, + char **next, + unsigned *size) { - __u32 temp, ndp, i; + u32 temp, ndp, i; temp = roothub_a (controller); if (temp == ~(u32)0) @@ -165,8 +225,8 @@ ndp = (temp & RH_A_NDP); if (verbose) { - ohci_dbg (controller, - "roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d\n", temp, + ohci_dbg_sw (controller, next, size, + "roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d\n", temp, ((temp & RH_A_POTPGT) >> 24) & 0xff, (temp & RH_A_NOCP) ? " NOCP" : "", (temp & RH_A_OCPM) ? " OCPM" : "", @@ -176,15 +236,15 @@ ndp ); temp = roothub_b (controller); - ohci_dbg (controller, - "roothub.b: %08x PPCM=%04x DR=%04x\n", + ohci_dbg_sw (controller, next, size, + "roothub.b %08x PPCM=%04x DR=%04x\n", temp, (temp & RH_B_PPCM) >> 16, (temp & RH_B_DR) ); temp = roothub_status (controller); - ohci_dbg (controller, - "roothub.status: %08x%s%s%s%s%s%s\n", + ohci_dbg_sw (controller, next, size, + "roothub.status %08x%s%s%s%s%s%s\n", temp, (temp & RH_HS_CRWE) ? " CRWE" : "", (temp & RH_HS_OCIC) ? " OCIC" : "", @@ -194,10 +254,10 @@ (temp & RH_HS_LPS) ? " LPS" : "" ); } - + for (i = 0; i < ndp; i++) { temp = roothub_portstatus (controller, i); - dbg_port (controller, "", i, temp); + dbg_port_sw (controller, i, temp, next, size); } } @@ -206,11 +266,11 @@ ohci_dbg (controller, "OHCI controller state\n"); // dumps some of the state we know about - ohci_dump_status (controller); + ohci_dump_status (controller, NULL, 0); if (controller->hcca) ohci_dbg (controller, "hcca frame #%04x\n", controller->hcca->frame_no); - ohci_dump_roothub (controller, 1); + ohci_dump_roothub (controller, 1, NULL, 0); } static const char data0 [] = "DATA0"; @@ -277,8 +337,7 @@ u32 tmp = ed->hwINFO; char *type = ""; - ohci_dbg (ohci, - "%s, ed %p state 0x%x type %s; next ed %08x\n", + ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x", label, ed, ed->state, edstring (ed->type), le32_to_cpup (&ed->hwNextED)); @@ -297,8 +356,7 @@ 0x000f & (le32_to_cpu (tmp) >> 7), type, 0x007f & le32_to_cpu (tmp)); - ohci_dbg (ohci, - " tds: head %08x %s%s tail %08x%s", + ohci_dbg (ohci, " tds: head %08x %s%s tail %08x%s", tmp = le32_to_cpup (&ed->hwHeadP), (ed->hwHeadP & ED_C) ? data1 : data0, (ed->hwHeadP & ED_H) ? " HALT" : "", @@ -321,6 +379,8 @@ #else static inline void ohci_dump (struct ohci_hcd *controller, int verbose) {} +#undef OHCI_VERBOSE_DEBUG + #endif /* DEBUG */ /*-------------------------------------------------------------------------*/ @@ -359,7 +419,7 @@ struct td *td; temp = snprintf (buf, size, - "ed/%p %cs dev%d ep%d-%s max %d %08x%s%s %s", + "ed/%p %cs dev%d ep%d%s max %d %08x%s%s %s", ed, (info & ED_LOWSPEED) ? 'l' : 'f', scratch & 0x7f, @@ -475,7 +535,7 @@ u32 scratch = cpu_to_le32p (&ed->hwINFO); temp = snprintf (next, size, - " (%cs dev%d%s ep%d-%s" + " (%cs dev%d%s ep%d%s" " max %d %08x%s%s)", (info & ED_LOWSPEED) ? 'l' : 'f', scratch & 0x7f, @@ -518,11 +578,78 @@ #undef DBG_SCHED_LIMIT +static ssize_t +show_registers (struct device *dev, char *buf) +{ + struct ohci_hcd *ohci; + struct ohci_regs *regs; + unsigned long flags; + unsigned temp, size; + char *next; + u32 rdata; + + ohci = dev_to_ohci(dev); + regs = ohci->regs; + next = buf; + size = PAGE_SIZE; + + spin_lock_irqsave (&ohci->lock, flags); + + /* dump driver info, then registers in spec order */ + + ohci_dbg_sw (ohci, &next, &size, + "%s version " DRIVER_VERSION "\n", hcd_name); + + ohci_dump_status(ohci, &next, &size); + + /* hcca */ + if (ohci->hcca) + ohci_dbg_sw (ohci, &next, &size, + "hcca frame 0x%04x\n", ohci->hcca->frame_no); + + /* other registers mostly affect frame timings */ + rdata = readl (®s->fminterval); + temp = snprintf (next, size, + "fmintvl 0x%08x %sFSMPS=0x%04x FI=0x%04x\n", + rdata, (rdata >> 31) ? " FIT" : "", + (rdata >> 16) & 0xefff, rdata & 0xffff); + size -= temp; + next += temp; + + rdata = readl (®s->fmremaining); + temp = snprintf (next, size, "fmremaining 0x%08x %sFR=0x%04x\n", + rdata, (rdata >> 31) ? " FRT" : "", + rdata & 0x3fff); + size -= temp; + next += temp; + + rdata = readl (®s->periodicstart); + temp = snprintf (next, size, "periodicstart 0x%04x\n", + rdata & 0x3fff); + size -= temp; + next += temp; + + rdata = readl (®s->lsthresh); + temp = snprintf (next, size, "lsthresh 0x%04x\n", + rdata & 0x3fff); + size -= temp; + next += temp; + + /* roothub */ + ohci_dump_roothub (ohci, 1, &next, &size); + + spin_unlock_irqrestore (&ohci->lock, flags); + + return PAGE_SIZE - size; +} +static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL); + + static inline void create_debug_files (struct ohci_hcd *bus) { device_create_file (bus->hcd.controller, &dev_attr_async); device_create_file (bus->hcd.controller, &dev_attr_periodic); - // registers + device_create_file (bus->hcd.controller, &dev_attr_registers); ohci_dbg (bus, "created debug files\n"); } @@ -530,6 +657,7 @@ { device_remove_file (bus->hcd.controller, &dev_attr_async); device_remove_file (bus->hcd.controller, &dev_attr_periodic); + device_remove_file (bus->hcd.controller, &dev_attr_registers); } #endif diff -Nru a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c --- a/drivers/usb/host/ohci-hcd.c Fri Feb 28 14:50:17 2003 +++ b/drivers/usb/host/ohci-hcd.c Fri Feb 28 14:50:17 2003 @@ -17,6 +17,8 @@ * * History: * + * 2003/02/24 show registers in sysfs (Kevin Brosius) + * * 2002/09/03 get rid of ed hashtables, rework periodic scheduling and * bandwidth accounting; if debugging, show schedules in driverfs * 2002/07/19 fixes to management of ED and schedule state. @@ -105,11 +107,10 @@ * TO DO: * * - "disabled" and "sleeping" should be in hcd->state - * - bandwidth alloc to generic code * - lots more testing!! */ -#define DRIVER_VERSION "2002-Sep-17" +#define DRIVER_VERSION "2003 Feb 24" #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell" #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver" @@ -277,6 +278,7 @@ urb_print (urb, "UNLINK", 1); #endif + spin_lock_irqsave (&ohci->lock, flags); if (!ohci->disabled) { urb_priv_t *urb_priv; @@ -284,21 +286,24 @@ * handed to us, flag it for unlink and giveback, and force * some upcoming INTR_SF to call finish_unlinks() */ - spin_lock_irqsave (&ohci->lock, flags); urb_priv = urb->hcpriv; if (urb_priv) { urb_priv->state = URB_DEL; if (urb_priv->ed->state == ED_OPER) start_urb_unlink (ohci, urb_priv->ed); } - spin_unlock_irqrestore (&ohci->lock, flags); } else { /* * with HC dead, we won't respect hc queue pointers * any more ... just clean up every urb's memory. */ - finish_urb (ohci, urb, NULL); + if (urb->hcpriv) { + spin_unlock (&ohci->lock); + finish_urb (ohci, urb, NULL); + spin_lock (&ohci->lock); + } } + spin_unlock_irqrestore (&ohci->lock, flags); return 0; } @@ -598,7 +603,8 @@ */ spin_lock (&ohci->lock); if (ohci->ed_rm_list) - finish_unlinks (ohci, le16_to_cpu (ohci->hcca->frame_no), ptregs); + finish_unlinks (ohci, le16_to_cpu (ohci->hcca->frame_no), + ptregs); if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list) writel (OHCI_INTR_SF, ®s->intrdisable); spin_unlock (&ohci->lock); diff -Nru a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c --- a/drivers/usb/host/ohci-pci.c Fri Feb 28 14:50:17 2003 +++ b/drivers/usb/host/ohci-pci.c Fri Feb 28 14:50:17 2003 @@ -214,7 +214,7 @@ #ifdef DEBUG /* the registers may look crazy here */ - ohci_dump_status (ohci); + ohci_dump_status (ohci, 0, 0); #endif /* Re-enable bus mastering */ diff -Nru a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c --- a/drivers/usb/host/ohci-q.c Fri Feb 28 14:50:17 2003 +++ b/drivers/usb/host/ohci-q.c Fri Feb 28 14:50:17 2003 @@ -30,21 +30,20 @@ /* * URB goes back to driver, and isn't reissued. * It's completely gone from HC data structures. - * PRECONDITION: no locks held (Giveback can call into HCD.) + * PRECONDITION: no locks held, irqs blocked (Giveback can call into HCD.) */ -static void finish_urb (struct ohci_hcd *ohci, struct urb *urb, struct pt_regs *regs) +static void +finish_urb (struct ohci_hcd *ohci, struct urb *urb, struct pt_regs *regs) { - unsigned long flags; - // ASSERT (urb->hcpriv != 0); urb_free_priv (ohci, urb->hcpriv); urb->hcpriv = NULL; - spin_lock_irqsave (&urb->lock, flags); + spin_lock (&urb->lock); if (likely (urb->status == -EINPROGRESS)) urb->status = 0; - spin_unlock_irqrestore (&urb->lock, flags); + spin_unlock (&urb->lock); // what lock protects these? switch (usb_pipetype (urb->pipe)) { @@ -850,7 +849,8 @@ #define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0) /* there are some urbs/eds to unlink; called in_irq(), with HCD locked */ -static void finish_unlinks (struct ohci_hcd *ohci, u16 tick, struct pt_regs *regs) +static void +finish_unlinks (struct ohci_hcd *ohci, u16 tick, struct pt_regs *regs) { struct ed *ed, **last; @@ -978,7 +978,8 @@ * path is finish_unlinks(), which unlinks URBs using ed_rm_list, instead of * scanning the (re-reversed) donelist as this does. */ -static void dl_done_list (struct ohci_hcd *ohci, struct td *td, struct pt_regs *regs) +static void +dl_done_list (struct ohci_hcd *ohci, struct td *td, struct pt_regs *regs) { unsigned long flags; @@ -995,9 +996,9 @@ /* If all this urb's TDs are done, call complete() */ if (urb_priv->td_cnt == urb_priv->length) { - spin_unlock_irqrestore (&ohci->lock, flags); + spin_unlock (&ohci->lock); finish_urb (ohci, urb, regs); - spin_lock_irqsave (&ohci->lock, flags); + spin_lock (&ohci->lock); } /* clean schedule: unlink EDs that are no longer busy */