2005-04-17 02:20:36 +04:00
/*
* OHCI HCD ( Host Controller Driver ) for USB .
*
* ( C ) Copyright 1999 Roman Weissgaerber < weissg @ vienna . at >
* ( C ) Copyright 2000 - 2002 David Brownell < dbrownell @ users . sourceforge . net >
*
* 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 ; \
2006-12-05 14:18:31 +03:00
default : temp = " isoc " ; break ; \
2005-04-17 02:20:36 +04:00
} ; temp ; } )
# define pipestring(pipe) edstring(usb_pipetype(pipe))
/* debug| print the main components of an URB
* small : 0 ) header + data packets 1 ) just header
*/
2007-05-12 01:39:44 +04:00
static void __maybe_unused
2007-08-24 23:40:34 +04:00
urb_print ( struct urb * urb , char * str , int small , int status )
2005-04-17 02:20:36 +04:00
{
unsigned int pipe = urb - > pipe ;
if ( ! urb - > dev | | ! urb - > dev - > bus ) {
dbg ( " %s URB: no dev " , str ) ;
return ;
}
# ifndef OHCI_VERBOSE_DEBUG
2007-08-24 23:40:34 +04:00
if ( status ! = 0 )
2005-04-17 02:20:36 +04:00
# endif
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 ) ? " out " : " in " ,
pipestring ( pipe ) ,
urb - > transfer_flags ,
urb - > actual_length ,
urb - > transfer_buffer_length ,
2007-08-24 23:40:34 +04:00
status ) ;
2005-04-17 02:20:36 +04:00
# ifdef OHCI_VERBOSE_DEBUG
if ( ! small ) {
int i , len ;
if ( usb_pipecontrol ( pipe ) ) {
printk ( KERN_DEBUG __FILE__ " : setup(8): " ) ;
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 ,
urb - > transfer_buffer_length ) ;
len = usb_pipeout ( pipe ) ?
urb - > transfer_buffer_length : urb - > actual_length ;
for ( i = 0 ; i < 16 & & i < len ; i + + )
printk ( " %02x " , ( ( __u8 * ) urb - > transfer_buffer ) [ i ] ) ;
2007-08-24 23:40:34 +04:00
printk ( " %s stat:%d \n " , i < len ? " ... " : " " , status ) ;
2005-04-17 02:20:36 +04:00
}
}
# endif
}
# define ohci_dbg_sw(ohci, next, size, format, arg...) \
do { \
2007-08-18 09:19:59 +04:00
if ( next ! = NULL ) { \
2005-04-17 02:20:36 +04:00
unsigned s_len ; \
s_len = scnprintf ( * 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_sw ( ohci , next , size , " %s 0x%08x%s%s%s%s%s%s%s%s%s \n " ,
label ,
mask ,
( mask & OHCI_INTR_MIE ) ? " MIE " : " " ,
( mask & OHCI_INTR_OC ) ? " OC " : " " ,
( mask & OHCI_INTR_RHSC ) ? " RHSC " : " " ,
( mask & OHCI_INTR_FNO ) ? " FNO " : " " ,
( mask & OHCI_INTR_UE ) ? " UE " : " " ,
( mask & OHCI_INTR_RD ) ? " RD " : " " ,
( mask & OHCI_INTR_SF ) ? " SF " : " " ,
( mask & OHCI_INTR_WDH ) ? " WDH " : " " ,
( mask & OHCI_INTR_SO ) ? " SO " : " "
) ;
}
static void maybe_print_eds (
struct ohci_hcd * ohci ,
char * label ,
u32 value ,
char * * next ,
unsigned * size )
{
if ( value )
ohci_dbg_sw ( ohci , next , size , " %s %08x \n " , label , value ) ;
}
static char * hcfs2string ( int state )
{
switch ( state ) {
case OHCI_USB_RESET : return " reset " ;
case OHCI_USB_RESUME : return " resume " ;
case OHCI_USB_OPER : return " operational " ;
case OHCI_USB_SUSPEND : return " suspend " ;
}
return " ? " ;
}
// dump control and status registers
static void
ohci_dump_status ( struct ohci_hcd * controller , char * * next , unsigned * size )
{
struct ohci_regs __iomem * regs = controller - > regs ;
u32 temp ;
temp = ohci_readl ( controller , & regs - > revision ) & 0xff ;
ohci_dbg_sw ( controller , next , size ,
" OHCI %d.%d, %s legacy support registers \n " ,
0x03 & ( temp > > 4 ) , ( temp & 0x0f ) ,
( temp & 0x0100 ) ? " with " : " NO " ) ;
temp = ohci_readl ( controller , & regs - > control ) ;
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 " : " " ,
hcfs2string ( temp & OHCI_CTRL_HCFS ) ,
( temp & OHCI_CTRL_BLE ) ? " BLE " : " " ,
( temp & OHCI_CTRL_CLE ) ? " CLE " : " " ,
( temp & OHCI_CTRL_IE ) ? " IE " : " " ,
( temp & OHCI_CTRL_PLE ) ? " PLE " : " " ,
temp & OHCI_CTRL_CBSR
) ;
temp = ohci_readl ( controller , & regs - > cmdstatus ) ;
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 " : " " ,
( temp & OHCI_CLF ) ? " CLF " : " " ,
( temp & OHCI_HCR ) ? " HCR " : " "
) ;
ohci_dump_intr_mask ( controller , " intrstatus " ,
ohci_readl ( controller , & regs - > intrstatus ) ,
next , size ) ;
ohci_dump_intr_mask ( controller , " intrenable " ,
ohci_readl ( controller , & regs - > intrenable ) ,
next , size ) ;
// intrdisable always same as intrenable
maybe_print_eds ( controller , " ed_periodcurrent " ,
ohci_readl ( controller , & regs - > ed_periodcurrent ) ,
next , size ) ;
maybe_print_eds ( controller , " ed_controlhead " ,
ohci_readl ( controller , & regs - > ed_controlhead ) ,
next , size ) ;
maybe_print_eds ( controller , " ed_controlcurrent " ,
ohci_readl ( controller , & regs - > ed_controlcurrent ) ,
next , size ) ;
maybe_print_eds ( controller , " ed_bulkhead " ,
ohci_readl ( controller , & regs - > ed_bulkhead ) ,
next , size ) ;
maybe_print_eds ( controller , " ed_bulkcurrent " ,
ohci_readl ( controller , & regs - > ed_bulkcurrent ) ,
next , size ) ;
maybe_print_eds ( controller , " donehead " ,
ohci_readl ( controller , & regs - > 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 " : " " , \
2006-12-05 14:18:31 +03:00
\
2005-04-17 02:20:36 +04:00
( 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 " : " " , \
2006-12-05 14:18:31 +03:00
\
2005-04-17 02:20:36 +04:00
( temp & RH_PS_PES ) ? " PES " : " " , \
( temp & RH_PS_CCS ) ? " CCS " : " " \
) ;
static void
ohci_dump_roothub (
struct ohci_hcd * controller ,
int verbose ,
char * * next ,
unsigned * size )
{
2005-08-31 22:52:57 +04:00
u32 temp , i ;
2005-04-17 02:20:36 +04:00
temp = roothub_a ( controller ) ;
if ( temp = = ~ ( u32 ) 0 )
return ;
if ( verbose ) {
ohci_dbg_sw ( controller , next , size ,
2005-08-31 22:52:57 +04:00
" roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d(%d) \n " , temp ,
2005-04-17 02:20:36 +04:00
( ( temp & RH_A_POTPGT ) > > 24 ) & 0xff ,
( temp & RH_A_NOCP ) ? " NOCP " : " " ,
( temp & RH_A_OCPM ) ? " OCPM " : " " ,
( temp & RH_A_DT ) ? " DT " : " " ,
( temp & RH_A_NPS ) ? " NPS " : " " ,
( temp & RH_A_PSM ) ? " PSM " : " " ,
2005-08-31 22:52:57 +04:00
( temp & RH_A_NDP ) , controller - > num_ports
2005-04-17 02:20:36 +04:00
) ;
temp = roothub_b ( controller ) ;
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_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 " : " " ,
( temp & RH_HS_LPSC ) ? " LPSC " : " " ,
( temp & RH_HS_DRWE ) ? " DRWE " : " " ,
( temp & RH_HS_OCI ) ? " OCI " : " " ,
( temp & RH_HS_LPS ) ? " LPS " : " "
) ;
}
2005-08-31 22:52:57 +04:00
for ( i = 0 ; i < controller - > num_ports ; i + + ) {
2005-04-17 02:20:36 +04:00
temp = roothub_portstatus ( controller , i ) ;
dbg_port_sw ( controller , i , temp , next , size ) ;
}
}
static void ohci_dump ( struct ohci_hcd * controller , int verbose )
{
ohci_dbg ( controller , " OHCI controller state \n " ) ;
// dumps some of the state we know about
ohci_dump_status ( controller , NULL , NULL ) ;
if ( controller - > hcca )
ohci_dbg ( controller ,
" hcca frame #%04x \n " , ohci_frame_no ( controller ) ) ;
ohci_dump_roothub ( controller , 1 , NULL , NULL ) ;
}
static const char data0 [ ] = " DATA0 " ;
static const char data1 [ ] = " DATA1 " ;
static void ohci_dump_td ( const struct ohci_hcd * ohci , const char * label ,
const struct td * td )
{
u32 tmp = hc32_to_cpup ( ohci , & td - > hwINFO ) ;
ohci_dbg ( ohci , " %s td %p%s; urb %p index %d; hw next td %08x \n " ,
label , td ,
( tmp & TD_DONE ) ? " (DONE) " : " " ,
td - > urb , td - > index ,
hc32_to_cpup ( ohci , & td - > hwNextTD ) ) ;
if ( ( tmp & TD_ISO ) = = 0 ) {
const char * toggle , * pid ;
u32 cbp , be ;
switch ( tmp & TD_T ) {
case TD_T_DATA0 : toggle = data0 ; break ;
case TD_T_DATA1 : toggle = data1 ; break ;
case TD_T_TOGGLE : toggle = " (CARRY) " ; break ;
default : toggle = " (?) " ; break ;
}
switch ( tmp & TD_DP ) {
case TD_DP_SETUP : pid = " SETUP " ; break ;
case TD_DP_IN : pid = " IN " ; break ;
case TD_DP_OUT : pid = " OUT " ; break ;
default : pid = " (bad pid) " ; break ;
}
ohci_dbg ( ohci , " info %08x CC=%x %s DI=%d %s %s \n " , tmp ,
TD_CC_GET ( tmp ) , /* EC, */ toggle ,
( tmp & TD_DI ) > > 21 , pid ,
( tmp & TD_R ) ? " R " : " " ) ;
cbp = hc32_to_cpup ( ohci , & td - > hwCBP ) ;
be = hc32_to_cpup ( ohci , & td - > hwBE ) ;
ohci_dbg ( ohci , " cbp %08x be %08x (len %d) \n " , cbp , be ,
cbp ? ( be + 1 - cbp ) : 0 ) ;
} else {
unsigned i ;
ohci_dbg ( ohci , " info %08x CC=%x FC=%d DI=%d SF=%04x \n " , tmp ,
TD_CC_GET ( tmp ) ,
( tmp > > 24 ) & 0x07 ,
( tmp & TD_DI ) > > 21 ,
tmp & 0x0000ffff ) ;
ohci_dbg ( ohci , " bp0 %08x be %08x \n " ,
hc32_to_cpup ( ohci , & td - > hwCBP ) & ~ 0x0fff ,
hc32_to_cpup ( ohci , & td - > hwBE ) ) ;
for ( i = 0 ; i < MAXPSW ; i + + ) {
u16 psw = ohci_hwPSW ( ohci , td , i ) ;
int cc = ( psw > > 12 ) & 0x0f ;
ohci_dbg ( ohci , " psw [%d] = %2x, CC=%x %s=%d \n " , i ,
psw , cc ,
( cc > = 0x0e ) ? " OFFSET " : " SIZE " ,
psw & 0x0fff ) ;
}
}
}
/* caller MUST own hcd spinlock if verbose is set! */
2007-05-12 01:39:44 +04:00
static void __maybe_unused
2005-04-17 02:20:36 +04:00
ohci_dump_ed ( const struct ohci_hcd * ohci , const char * label ,
const struct ed * ed , int verbose )
{
u32 tmp = hc32_to_cpu ( ohci , ed - > hwINFO ) ;
char * type = " " ;
ohci_dbg ( ohci , " %s, ed %p state 0x%x type %s; next ed %08x \n " ,
label ,
ed , ed - > state , edstring ( ed - > type ) ,
hc32_to_cpup ( ohci , & ed - > hwNextED ) ) ;
switch ( tmp & ( ED_IN | ED_OUT ) ) {
case ED_OUT : type = " -OUT " ; break ;
case ED_IN : type = " -IN " ; break ;
/* else from TDs ... control */
}
ohci_dbg ( ohci ,
" info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d \n " , tmp ,
0x03ff & ( tmp > > 16 ) ,
( tmp & ED_DEQUEUE ) ? " DQ " : " " ,
( tmp & ED_ISO ) ? " ISO " : " " ,
( tmp & ED_SKIP ) ? " SKIP " : " " ,
( tmp & ED_LOWSPEED ) ? " LOW " : " " ,
0x000f & ( tmp > > 7 ) ,
type ,
0x007f & tmp ) ;
tmp = hc32_to_cpup ( ohci , & ed - > hwHeadP ) ;
ohci_dbg ( ohci , " tds: head %08x %s%s tail %08x%s \n " ,
tmp ,
( tmp & ED_C ) ? data1 : data0 ,
( tmp & ED_H ) ? " HALT " : " " ,
hc32_to_cpup ( ohci , & ed - > hwTailP ) ,
verbose ? " " : " (not listing) " ) ;
if ( verbose ) {
struct list_head * tmp ;
/* use ed->td_list because HC concurrently modifies
* hwNextTD as it accumulates ed_donelist .
*/
list_for_each ( tmp , & ed - > td_list ) {
struct td * td ;
td = list_entry ( tmp , struct td , td_list ) ;
ohci_dump_td ( ohci , " -> " , td ) ;
}
}
}
# else
static inline void ohci_dump ( struct ohci_hcd * controller , int verbose ) { }
# undef OHCI_VERBOSE_DEBUG
# endif /* DEBUG */
/*-------------------------------------------------------------------------*/
# ifdef STUB_DEBUG_FILES
static inline void create_debug_files ( struct ohci_hcd * bus ) { }
static inline void remove_debug_files ( struct ohci_hcd * bus ) { }
# else
2007-09-12 01:07:31 +04:00
static int debug_async_open ( struct inode * , struct file * ) ;
static int debug_periodic_open ( struct inode * , struct file * ) ;
static int debug_registers_open ( struct inode * , struct file * ) ;
static int debug_async_open ( struct inode * , struct file * ) ;
static ssize_t debug_output ( struct file * , char __user * , size_t , loff_t * ) ;
static int debug_close ( struct inode * , struct file * ) ;
static const struct file_operations debug_async_fops = {
. owner = THIS_MODULE ,
. open = debug_async_open ,
. read = debug_output ,
. release = debug_close ,
} ;
static const struct file_operations debug_periodic_fops = {
. owner = THIS_MODULE ,
. open = debug_periodic_open ,
. read = debug_output ,
. release = debug_close ,
} ;
static const struct file_operations debug_registers_fops = {
. owner = THIS_MODULE ,
. open = debug_registers_open ,
. read = debug_output ,
. release = debug_close ,
} ;
static struct dentry * ohci_debug_root ;
struct debug_buffer {
ssize_t ( * fill_func ) ( struct debug_buffer * ) ; /* fill method */
struct device * dev ;
struct mutex mutex ; /* protect filling of buffer */
size_t count ; /* number of characters filled into buffer */
char * page ;
} ;
2005-04-17 02:20:36 +04:00
static ssize_t
show_list ( struct ohci_hcd * ohci , char * buf , size_t count , struct ed * ed )
{
unsigned temp , size = count ;
if ( ! ed )
return 0 ;
/* print first --> last */
while ( ed - > ed_prev )
ed = ed - > ed_prev ;
/* dump a snapshot of the bulk or control schedule */
while ( ed ) {
u32 info = hc32_to_cpu ( ohci , ed - > hwINFO ) ;
u32 headp = hc32_to_cpu ( ohci , ed - > hwHeadP ) ;
struct list_head * entry ;
struct td * td ;
temp = scnprintf ( buf , size ,
" ed/%p %cs dev%d ep%d%s max %d %08x%s%s %s " ,
ed ,
( info & ED_LOWSPEED ) ? ' l ' : ' f ' ,
info & 0x7f ,
( info > > 7 ) & 0xf ,
( info & ED_IN ) ? " in " : " out " ,
0x03ff & ( info > > 16 ) ,
info ,
( info & ED_SKIP ) ? " s " : " " ,
( headp & ED_H ) ? " H " : " " ,
( headp & ED_C ) ? data1 : data0 ) ;
size - = temp ;
buf + = temp ;
list_for_each ( entry , & ed - > td_list ) {
u32 cbp , be ;
td = list_entry ( entry , struct td , td_list ) ;
info = hc32_to_cpup ( ohci , & td - > hwINFO ) ;
cbp = hc32_to_cpup ( ohci , & td - > hwCBP ) ;
be = hc32_to_cpup ( ohci , & td - > hwBE ) ;
temp = scnprintf ( buf , size ,
" \n \t td %p %s %d cc=%x urb %p (%08x) " ,
td ,
( { char * pid ;
switch ( info & TD_DP ) {
case TD_DP_SETUP : pid = " setup " ; break ;
case TD_DP_IN : pid = " in " ; break ;
case TD_DP_OUT : pid = " out " ; break ;
default : pid = " (?) " ; break ;
} pid ; } ) ,
cbp ? ( be + 1 - cbp ) : 0 ,
TD_CC_GET ( info ) , td - > urb , info ) ;
size - = temp ;
buf + = temp ;
}
temp = scnprintf ( buf , size , " \n " ) ;
size - = temp ;
buf + = temp ;
ed = ed - > ed_next ;
}
return count - size ;
}
2007-09-12 01:07:31 +04:00
static ssize_t fill_async_buffer ( struct debug_buffer * buf )
2005-04-17 02:20:36 +04:00
{
struct usb_bus * bus ;
struct usb_hcd * hcd ;
struct ohci_hcd * ohci ;
size_t temp ;
unsigned long flags ;
2007-09-12 01:07:31 +04:00
bus = dev_get_drvdata ( buf - > dev ) ;
2006-08-30 19:32:52 +04:00
hcd = bus_to_hcd ( bus ) ;
2005-04-17 02:20:36 +04:00
ohci = hcd_to_ohci ( hcd ) ;
/* display control and bulk lists together, for simplicity */
spin_lock_irqsave ( & ohci - > lock , flags ) ;
2007-09-12 01:07:31 +04:00
temp = show_list ( ohci , buf - > page , buf - > count , ohci - > ed_controltail ) ;
temp + = show_list ( ohci , buf - > page + temp , buf - > count - temp ,
ohci - > ed_bulktail ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & ohci - > lock , flags ) ;
return temp ;
}
# define DBG_SCHED_LIMIT 64
2007-09-12 01:07:31 +04:00
static ssize_t fill_periodic_buffer ( struct debug_buffer * buf )
2005-04-17 02:20:36 +04:00
{
struct usb_bus * bus ;
struct usb_hcd * hcd ;
struct ohci_hcd * ohci ;
struct ed * * seen , * ed ;
unsigned long flags ;
unsigned temp , size , seen_count ;
char * next ;
unsigned i ;
2006-12-07 07:33:16 +03:00
if ( ! ( seen = kmalloc ( DBG_SCHED_LIMIT * sizeof * seen , GFP_ATOMIC ) ) )
2005-04-17 02:20:36 +04:00
return 0 ;
seen_count = 0 ;
2007-09-12 01:07:31 +04:00
bus = ( struct usb_bus * ) dev_get_drvdata ( buf - > dev ) ;
2006-08-30 19:32:52 +04:00
hcd = bus_to_hcd ( bus ) ;
2005-04-17 02:20:36 +04:00
ohci = hcd_to_ohci ( hcd ) ;
2007-09-12 01:07:31 +04:00
next = buf - > page ;
2005-04-17 02:20:36 +04:00
size = PAGE_SIZE ;
temp = scnprintf ( next , size , " size = %d \n " , NUM_INTS ) ;
size - = temp ;
next + = temp ;
/* dump a snapshot of the periodic schedule (and load) */
spin_lock_irqsave ( & ohci - > lock , flags ) ;
for ( i = 0 ; i < NUM_INTS ; i + + ) {
if ( ! ( ed = ohci - > periodic [ i ] ) )
continue ;
temp = scnprintf ( next , size , " %2d [%3d]: " , i , ohci - > load [ i ] ) ;
size - = temp ;
next + = temp ;
do {
temp = scnprintf ( next , size , " ed%d/%p " ,
ed - > interval , ed ) ;
size - = temp ;
next + = temp ;
for ( temp = 0 ; temp < seen_count ; temp + + ) {
if ( seen [ temp ] = = ed )
break ;
}
/* show more info the first time around */
if ( temp = = seen_count ) {
u32 info = hc32_to_cpu ( ohci , ed - > hwINFO ) ;
struct list_head * entry ;
unsigned qlen = 0 ;
/* qlen measured here in TDs, not urbs */
list_for_each ( entry , & ed - > td_list )
qlen + + ;
temp = scnprintf ( next , size ,
" (%cs dev%d ep%d%s-%s qlen %u "
" max %d %08x%s%s) " ,
( info & ED_LOWSPEED ) ? ' l ' : ' f ' ,
info & 0x7f ,
( info > > 7 ) & 0xf ,
( info & ED_IN ) ? " in " : " out " ,
( info & ED_ISO ) ? " iso " : " int " ,
qlen ,
0x03ff & ( info > > 16 ) ,
info ,
( info & ED_SKIP ) ? " K " : " " ,
( ed - > hwHeadP &
cpu_to_hc32 ( ohci , ED_H ) ) ?
2006-12-05 14:18:31 +03:00
" H " : " " ) ;
2005-04-17 02:20:36 +04:00
size - = temp ;
next + = temp ;
if ( seen_count < DBG_SCHED_LIMIT )
seen [ seen_count + + ] = ed ;
ed = ed - > ed_next ;
} else {
/* we've seen it and what's after */
temp = 0 ;
ed = NULL ;
}
} while ( ed ) ;
temp = scnprintf ( next , size , " \n " ) ;
size - = temp ;
next + = temp ;
}
spin_unlock_irqrestore ( & ohci - > lock , flags ) ;
kfree ( seen ) ;
return PAGE_SIZE - size ;
}
# undef DBG_SCHED_LIMIT
2007-09-12 01:07:31 +04:00
static ssize_t fill_registers_buffer ( struct debug_buffer * buf )
2005-04-17 02:20:36 +04:00
{
struct usb_bus * bus ;
struct usb_hcd * hcd ;
struct ohci_hcd * ohci ;
struct ohci_regs __iomem * regs ;
unsigned long flags ;
unsigned temp , size ;
char * next ;
u32 rdata ;
2007-09-12 01:07:31 +04:00
bus = ( struct usb_bus * ) dev_get_drvdata ( buf - > dev ) ;
2006-08-30 19:32:52 +04:00
hcd = bus_to_hcd ( bus ) ;
2005-04-17 02:20:36 +04:00
ohci = hcd_to_ohci ( hcd ) ;
regs = ohci - > regs ;
2007-09-12 01:07:31 +04:00
next = buf - > page ;
2005-04-17 02:20:36 +04:00
size = PAGE_SIZE ;
spin_lock_irqsave ( & ohci - > lock , flags ) ;
/* dump driver info, then registers in spec order */
ohci_dbg_sw ( ohci , & next , & size ,
" bus %s, device %s \n "
" %s \n "
2008-10-02 19:47:15 +04:00
" %s \n " ,
2005-04-17 02:20:36 +04:00
hcd - > self . controller - > bus - > name ,
2008-05-02 08:02:41 +04:00
dev_name ( hcd - > self . controller ) ,
2005-04-17 02:20:36 +04:00
hcd - > product_desc ,
hcd_name ) ;
2008-03-07 01:00:58 +03:00
if ( ! test_bit ( HCD_FLAG_HW_ACCESSIBLE , & hcd - > flags ) ) {
2005-04-17 02:20:36 +04:00
size - = scnprintf ( next , size ,
" SUSPENDED (no register access) \n " ) ;
goto done ;
}
ohci_dump_status ( ohci , & next , & size ) ;
/* hcca */
if ( ohci - > hcca )
ohci_dbg_sw ( ohci , & next , & size ,
" hcca frame 0x%04x \n " , ohci_frame_no ( ohci ) ) ;
/* other registers mostly affect frame timings */
rdata = ohci_readl ( ohci , & regs - > fminterval ) ;
temp = scnprintf ( 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 = ohci_readl ( ohci , & regs - > fmremaining ) ;
temp = scnprintf ( next , size , " fmremaining 0x%08x %sFR=0x%04x \n " ,
rdata , ( rdata > > 31 ) ? " FRT " : " " ,
rdata & 0x3fff ) ;
size - = temp ;
next + = temp ;
rdata = ohci_readl ( ohci , & regs - > periodicstart ) ;
temp = scnprintf ( next , size , " periodicstart 0x%04x \n " ,
rdata & 0x3fff ) ;
size - = temp ;
next + = temp ;
rdata = ohci_readl ( ohci , & regs - > lsthresh ) ;
temp = scnprintf ( next , size , " lsthresh 0x%04x \n " ,
rdata & 0x3fff ) ;
size - = temp ;
next + = temp ;
2006-08-04 22:31:55 +04:00
temp = scnprintf ( next , size , " hub poll timer %s \n " ,
ohci_to_hcd ( ohci ) - > poll_rh ? " ON " : " off " ) ;
size - = temp ;
next + = temp ;
2005-04-17 02:20:36 +04:00
/* roothub */
ohci_dump_roothub ( ohci , 1 , & next , & size ) ;
done :
spin_unlock_irqrestore ( & ohci - > lock , flags ) ;
2007-09-12 01:07:31 +04:00
2005-04-17 02:20:36 +04:00
return PAGE_SIZE - size ;
}
2007-09-12 01:07:31 +04:00
static struct debug_buffer * alloc_buffer ( struct device * dev ,
ssize_t ( * fill_func ) ( struct debug_buffer * ) )
{
struct debug_buffer * buf ;
buf = kzalloc ( sizeof ( struct debug_buffer ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
2007-09-12 01:07:31 +04:00
if ( buf ) {
buf - > dev = dev ;
buf - > fill_func = fill_func ;
mutex_init ( & buf - > mutex ) ;
}
return buf ;
}
static int fill_buffer ( struct debug_buffer * buf )
{
int ret = 0 ;
if ( ! buf - > page )
buf - > page = ( char * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! buf - > page ) {
ret = - ENOMEM ;
goto out ;
}
ret = buf - > fill_func ( buf ) ;
if ( ret > = 0 ) {
buf - > count = ret ;
ret = 0 ;
}
out :
return ret ;
}
static ssize_t debug_output ( struct file * file , char __user * user_buf ,
size_t len , loff_t * offset )
{
struct debug_buffer * buf = file - > private_data ;
int ret = 0 ;
mutex_lock ( & buf - > mutex ) ;
if ( buf - > count = = 0 ) {
ret = fill_buffer ( buf ) ;
if ( ret ! = 0 ) {
mutex_unlock ( & buf - > mutex ) ;
goto out ;
}
}
mutex_unlock ( & buf - > mutex ) ;
ret = simple_read_from_buffer ( user_buf , len , offset ,
buf - > page , buf - > count ) ;
out :
return ret ;
}
static int debug_close ( struct inode * inode , struct file * file )
{
struct debug_buffer * buf = file - > private_data ;
if ( buf ) {
if ( buf - > page )
free_page ( ( unsigned long ) buf - > page ) ;
kfree ( buf ) ;
}
return 0 ;
}
static int debug_async_open ( struct inode * inode , struct file * file )
{
file - > private_data = alloc_buffer ( inode - > i_private , fill_async_buffer ) ;
return file - > private_data ? 0 : - ENOMEM ;
}
static int debug_periodic_open ( struct inode * inode , struct file * file )
{
file - > private_data = alloc_buffer ( inode - > i_private ,
fill_periodic_buffer ) ;
return file - > private_data ? 0 : - ENOMEM ;
}
static int debug_registers_open ( struct inode * inode , struct file * file )
{
file - > private_data = alloc_buffer ( inode - > i_private ,
fill_registers_buffer ) ;
return file - > private_data ? 0 : - ENOMEM ;
}
2005-04-17 02:20:36 +04:00
static inline void create_debug_files ( struct ohci_hcd * ohci )
{
2007-09-12 01:07:31 +04:00
struct usb_bus * bus = & ohci_to_hcd ( ohci ) - > self ;
struct device * dev = bus - > dev ;
ohci - > debug_dir = debugfs_create_dir ( bus - > bus_name , ohci_debug_root ) ;
if ( ! ohci - > debug_dir )
goto dir_error ;
ohci - > debug_async = debugfs_create_file ( " async " , S_IRUGO ,
ohci - > debug_dir , dev ,
& debug_async_fops ) ;
if ( ! ohci - > debug_async )
goto async_error ;
ohci - > debug_periodic = debugfs_create_file ( " periodic " , S_IRUGO ,
ohci - > debug_dir , dev ,
& debug_periodic_fops ) ;
if ( ! ohci - > debug_periodic )
goto periodic_error ;
ohci - > debug_registers = debugfs_create_file ( " registers " , S_IRUGO ,
ohci - > debug_dir , dev ,
& debug_registers_fops ) ;
if ( ! ohci - > debug_registers )
goto registers_error ;
2005-04-17 02:20:36 +04:00
ohci_dbg ( ohci , " created debug files \n " ) ;
2007-09-12 01:07:31 +04:00
return ;
registers_error :
debugfs_remove ( ohci - > debug_periodic ) ;
periodic_error :
debugfs_remove ( ohci - > debug_async ) ;
async_error :
debugfs_remove ( ohci - > debug_dir ) ;
dir_error :
ohci - > debug_periodic = NULL ;
ohci - > debug_async = NULL ;
ohci - > debug_dir = NULL ;
2005-04-17 02:20:36 +04:00
}
static inline void remove_debug_files ( struct ohci_hcd * ohci )
{
2007-09-12 01:07:31 +04:00
debugfs_remove ( ohci - > debug_registers ) ;
debugfs_remove ( ohci - > debug_periodic ) ;
debugfs_remove ( ohci - > debug_async ) ;
debugfs_remove ( ohci - > debug_dir ) ;
2005-04-17 02:20:36 +04:00
}
# endif
/*-------------------------------------------------------------------------*/