2005-04-17 02:20:36 +04:00
# include <linux/config.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/mm.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
2005-09-17 11:55:31 +04:00
# include <linux/scatterlist.h>
2005-04-17 02:20:36 +04:00
# include <linux/usb.h>
/*-------------------------------------------------------------------------*/
// FIXME make these public somewhere; usbdevfs.h?
//
struct usbtest_param {
// inputs
unsigned test_num ; /* 0..(TEST_CASES-1) */
unsigned iterations ;
unsigned length ;
unsigned vary ;
unsigned sglen ;
// outputs
struct timeval duration ;
} ;
# define USBTEST_REQUEST _IOWR('U', 100, struct usbtest_param)
/*-------------------------------------------------------------------------*/
# define GENERIC /* let probe() bind using module params */
/* Some devices that can be used for testing will have "real" drivers.
* Entries for those need to be enabled here by hand , after disabling
* that " real " driver .
*/
//#define IBOT2 /* grab iBOT2 webcams */
//#define KEYSPAN_19Qi /* grab un-renumerated serial adapter */
/*-------------------------------------------------------------------------*/
struct usbtest_info {
const char * name ;
u8 ep_in ; /* bulk/intr source */
u8 ep_out ; /* bulk/intr sink */
unsigned autoconf : 1 ;
unsigned ctrl_out : 1 ;
unsigned iso : 1 ; /* try iso in/out */
int alt ;
} ;
/* this is accessed only through usbfs ioctl calls.
* one ioctl to issue a test . . . one lock per device .
* tests create other threads if they need them .
* urbs and buffers are allocated dynamically ,
* and data generated deterministically .
*/
struct usbtest_dev {
struct usb_interface * intf ;
struct usbtest_info * info ;
int in_pipe ;
int out_pipe ;
int in_iso_pipe ;
int out_iso_pipe ;
struct usb_endpoint_descriptor * iso_in , * iso_out ;
struct semaphore sem ;
# define TBUF_SIZE 256
u8 * buf ;
} ;
static struct usb_device * testdev_to_usbdev ( struct usbtest_dev * test )
{
return interface_to_usbdev ( test - > intf ) ;
}
/* set up all urbs so they can be used with either bulk or interrupt */
# define INTERRUPT_RATE 1 /* msec/transfer */
# define xprintk(tdev,level,fmt,args...) \
dev_printk ( level , & ( tdev ) - > intf - > dev , fmt , # # args )
# ifdef DEBUG
# define DBG(dev,fmt,args...) \
xprintk ( dev , KERN_DEBUG , fmt , # # args )
# else
# define DBG(dev,fmt,args...) \
do { } while ( 0 )
# endif /* DEBUG */
# ifdef VERBOSE
# define VDBG DBG
# else
# define VDBG(dev,fmt,args...) \
do { } while ( 0 )
# endif /* VERBOSE */
# define ERROR(dev,fmt,args...) \
xprintk ( dev , KERN_ERR , fmt , # # args )
# define WARN(dev,fmt,args...) \
xprintk ( dev , KERN_WARNING , fmt , # # args )
# define INFO(dev,fmt,args...) \
xprintk ( dev , KERN_INFO , fmt , # # args )
/*-------------------------------------------------------------------------*/
static int
get_endpoints ( struct usbtest_dev * dev , struct usb_interface * intf )
{
int tmp ;
struct usb_host_interface * alt ;
struct usb_host_endpoint * in , * out ;
struct usb_host_endpoint * iso_in , * iso_out ;
struct usb_device * udev ;
for ( tmp = 0 ; tmp < intf - > num_altsetting ; tmp + + ) {
unsigned ep ;
in = out = NULL ;
iso_in = iso_out = NULL ;
alt = intf - > altsetting + tmp ;
/* take the first altsetting with in-bulk + out-bulk;
* ignore other endpoints and altsetttings .
*/
for ( ep = 0 ; ep < alt - > desc . bNumEndpoints ; ep + + ) {
struct usb_host_endpoint * e ;
e = alt - > endpoint + ep ;
switch ( e - > desc . bmAttributes ) {
case USB_ENDPOINT_XFER_BULK :
break ;
case USB_ENDPOINT_XFER_ISOC :
if ( dev - > info - > iso )
goto try_iso ;
// FALLTHROUGH
default :
continue ;
}
if ( e - > desc . bEndpointAddress & USB_DIR_IN ) {
if ( ! in )
in = e ;
} else {
if ( ! out )
out = e ;
}
continue ;
try_iso :
if ( e - > desc . bEndpointAddress & USB_DIR_IN ) {
if ( ! iso_in )
iso_in = e ;
} else {
if ( ! iso_out )
iso_out = e ;
}
}
if ( ( in & & out ) | | ( iso_in & & iso_out ) )
goto found ;
}
return - EINVAL ;
found :
udev = testdev_to_usbdev ( dev ) ;
if ( alt - > desc . bAlternateSetting ! = 0 ) {
tmp = usb_set_interface ( udev ,
alt - > desc . bInterfaceNumber ,
alt - > desc . bAlternateSetting ) ;
if ( tmp < 0 )
return tmp ;
}
if ( in ) {
dev - > in_pipe = usb_rcvbulkpipe ( udev ,
in - > desc . bEndpointAddress & USB_ENDPOINT_NUMBER_MASK ) ;
dev - > out_pipe = usb_sndbulkpipe ( udev ,
out - > desc . bEndpointAddress & USB_ENDPOINT_NUMBER_MASK ) ;
}
if ( iso_in ) {
dev - > iso_in = & iso_in - > desc ;
dev - > in_iso_pipe = usb_rcvisocpipe ( udev ,
iso_in - > desc . bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK ) ;
dev - > iso_out = & iso_out - > desc ;
dev - > out_iso_pipe = usb_sndisocpipe ( udev ,
iso_out - > desc . bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK ) ;
}
return 0 ;
}
/*-------------------------------------------------------------------------*/
/* Support for testing basic non-queued I/O streams.
*
* These just package urbs as requests that can be easily canceled .
* Each urb ' s data buffer is dynamically allocated ; callers can fill
* them with non - zero test data ( or test for it ) when appropriate .
*/
static void simple_callback ( struct urb * urb , struct pt_regs * regs )
{
complete ( ( struct completion * ) urb - > context ) ;
}
static struct urb * simple_alloc_urb (
struct usb_device * udev ,
int pipe ,
unsigned long bytes
)
{
struct urb * urb ;
if ( bytes < 0 )
return NULL ;
urb = usb_alloc_urb ( 0 , SLAB_KERNEL ) ;
if ( ! urb )
return urb ;
usb_fill_bulk_urb ( urb , udev , pipe , NULL , bytes , simple_callback , NULL ) ;
urb - > interval = ( udev - > speed = = USB_SPEED_HIGH )
? ( INTERRUPT_RATE < < 3 )
: INTERRUPT_RATE ;
urb - > transfer_flags = URB_NO_TRANSFER_DMA_MAP ;
if ( usb_pipein ( pipe ) )
urb - > transfer_flags | = URB_SHORT_NOT_OK ;
urb - > transfer_buffer = usb_buffer_alloc ( udev , bytes , SLAB_KERNEL ,
& urb - > transfer_dma ) ;
if ( ! urb - > transfer_buffer ) {
usb_free_urb ( urb ) ;
urb = NULL ;
} else
memset ( urb - > transfer_buffer , 0 , bytes ) ;
return urb ;
}
static unsigned pattern = 0 ;
module_param ( pattern , uint , S_IRUGO ) ;
// MODULE_PARM_DESC (pattern, "i/o pattern (0 == zeroes)");
static inline void simple_fill_buf ( struct urb * urb )
{
unsigned i ;
u8 * buf = urb - > transfer_buffer ;
unsigned len = urb - > transfer_buffer_length ;
switch ( pattern ) {
default :
// FALLTHROUGH
case 0 :
memset ( buf , 0 , len ) ;
break ;
case 1 : /* mod63 */
for ( i = 0 ; i < len ; i + + )
* buf + + = ( u8 ) ( i % 63 ) ;
break ;
}
}
static inline int simple_check_buf ( struct urb * urb )
{
unsigned i ;
u8 expected ;
u8 * buf = urb - > transfer_buffer ;
unsigned len = urb - > actual_length ;
for ( i = 0 ; i < len ; i + + , buf + + ) {
switch ( pattern ) {
/* all-zeroes has no synchronization issues */
case 0 :
expected = 0 ;
break ;
/* mod63 stays in sync with short-terminated transfers,
* or otherwise when host and gadget agree on how large
* each usb transfer request should be . resync is done
* with set_interface or set_config .
*/
case 1 : /* mod63 */
expected = i % 63 ;
break ;
/* always fail unsupported patterns */
default :
expected = ! * buf ;
break ;
}
if ( * buf = = expected )
continue ;
dbg ( " buf[%d] = %d (not %d) " , i , * buf , expected ) ;
return - EINVAL ;
}
return 0 ;
}
static void simple_free_urb ( struct urb * urb )
{
usb_buffer_free ( urb - > dev , urb - > transfer_buffer_length ,
urb - > transfer_buffer , urb - > transfer_dma ) ;
usb_free_urb ( urb ) ;
}
static int simple_io (
struct urb * urb ,
int iterations ,
int vary ,
int expected ,
const char * label
)
{
struct usb_device * udev = urb - > dev ;
int max = urb - > transfer_buffer_length ;
struct completion completion ;
int retval = 0 ;
urb - > context = & completion ;
while ( retval = = 0 & & iterations - - > 0 ) {
init_completion ( & completion ) ;
if ( usb_pipeout ( urb - > pipe ) )
simple_fill_buf ( urb ) ;
if ( ( retval = usb_submit_urb ( urb , SLAB_KERNEL ) ) ! = 0 )
break ;
/* NOTE: no timeouts; can't be broken out of by interrupt */
wait_for_completion ( & completion ) ;
retval = urb - > status ;
urb - > dev = udev ;
if ( retval = = 0 & & usb_pipein ( urb - > pipe ) )
retval = simple_check_buf ( urb ) ;
if ( vary ) {
int len = urb - > transfer_buffer_length ;
len + = vary ;
len % = max ;
if ( len = = 0 )
len = ( vary < max ) ? vary : max ;
urb - > transfer_buffer_length = len ;
}
/* FIXME if endpoint halted, clear halt (and log) */
}
urb - > transfer_buffer_length = max ;
if ( expected ! = retval )
dev_dbg ( & udev - > dev ,
" %s failed, iterations left %d, status %d (not %d) \n " ,
label , iterations , retval , expected ) ;
return retval ;
}
/*-------------------------------------------------------------------------*/
/* We use scatterlist primitives to test queued I/O.
* Yes , this also tests the scatterlist primitives .
*/
static void free_sglist ( struct scatterlist * sg , int nents )
{
unsigned i ;
if ( ! sg )
return ;
for ( i = 0 ; i < nents ; i + + ) {
if ( ! sg [ i ] . page )
continue ;
kfree ( page_address ( sg [ i ] . page ) + sg [ i ] . offset ) ;
}
kfree ( sg ) ;
}
static struct scatterlist *
alloc_sglist ( int nents , int max , int vary )
{
struct scatterlist * sg ;
unsigned i ;
unsigned size = max ;
sg = kmalloc ( nents * sizeof * sg , SLAB_KERNEL ) ;
if ( ! sg )
return NULL ;
for ( i = 0 ; i < nents ; i + + ) {
char * buf ;
2006-04-02 22:20:15 +04:00
unsigned j ;
2005-04-17 02:20:36 +04:00
2006-02-27 23:29:43 +03:00
buf = kzalloc ( size , SLAB_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! buf ) {
free_sglist ( sg , i ) ;
return NULL ;
}
/* kmalloc pages are always physically contiguous! */
2005-09-17 11:55:31 +04:00
sg_init_one ( & sg [ i ] , buf , size ) ;
2005-04-17 02:20:36 +04:00
2006-04-02 22:20:15 +04:00
switch ( pattern ) {
case 0 :
/* already zeroed */
break ;
case 1 :
for ( j = 0 ; j < size ; j + + )
* buf + + = ( u8 ) ( j % 63 ) ;
break ;
}
2005-04-17 02:20:36 +04:00
if ( vary ) {
size + = vary ;
size % = max ;
if ( size = = 0 )
size = ( vary < max ) ? vary : max ;
}
}
return sg ;
}
static int perform_sglist (
struct usb_device * udev ,
unsigned iterations ,
int pipe ,
struct usb_sg_request * req ,
struct scatterlist * sg ,
int nents
)
{
int retval = 0 ;
while ( retval = = 0 & & iterations - - > 0 ) {
retval = usb_sg_init ( req , udev , pipe ,
( udev - > speed = = USB_SPEED_HIGH )
? ( INTERRUPT_RATE < < 3 )
: INTERRUPT_RATE ,
sg , nents , 0 , SLAB_KERNEL ) ;
if ( retval )
break ;
usb_sg_wait ( req ) ;
retval = req - > status ;
2006-04-02 22:20:15 +04:00
/* FIXME check resulting data pattern */
2005-04-17 02:20:36 +04:00
/* FIXME if endpoint halted, clear halt (and log) */
}
// FIXME for unlink or fault handling tests, don't report
// failure if retval is as we expected ...
if ( retval )
dbg ( " perform_sglist failed, iterations left %d, status %d " ,
iterations , retval ) ;
return retval ;
}
/*-------------------------------------------------------------------------*/
/* unqueued control message testing
*
* there ' s a nice set of device functional requirements in chapter 9 of the
* usb 2.0 spec , which we can apply to ANY device , even ones that don ' t use
* special test firmware .
*
* we know the device is configured ( or suspended ) by the time it ' s visible
* through usbfs . we can ' t change that , so we won ' t test enumeration ( which
* worked ' well enough ' to get here , this time ) , power management ( ditto ) ,
* or remote wakeup ( which needs human interaction ) .
*/
static unsigned realworld = 1 ;
module_param ( realworld , uint , 0 ) ;
2005-04-23 00:17:00 +04:00
MODULE_PARM_DESC ( realworld , " clear to demand stricter spec compliance " ) ;
2005-04-17 02:20:36 +04:00
static int get_altsetting ( struct usbtest_dev * dev )
{
struct usb_interface * iface = dev - > intf ;
struct usb_device * udev = interface_to_usbdev ( iface ) ;
int retval ;
retval = usb_control_msg ( udev , usb_rcvctrlpipe ( udev , 0 ) ,
USB_REQ_GET_INTERFACE , USB_DIR_IN | USB_RECIP_INTERFACE ,
0 , iface - > altsetting [ 0 ] . desc . bInterfaceNumber ,
dev - > buf , 1 , USB_CTRL_GET_TIMEOUT ) ;
switch ( retval ) {
case 1 :
return dev - > buf [ 0 ] ;
case 0 :
retval = - ERANGE ;
// FALLTHROUGH
default :
return retval ;
}
}
static int set_altsetting ( struct usbtest_dev * dev , int alternate )
{
struct usb_interface * iface = dev - > intf ;
struct usb_device * udev ;
if ( alternate < 0 | | alternate > = 256 )
return - EINVAL ;
udev = interface_to_usbdev ( iface ) ;
return usb_set_interface ( udev ,
iface - > altsetting [ 0 ] . desc . bInterfaceNumber ,
alternate ) ;
}
static int is_good_config ( char * buf , int len )
{
struct usb_config_descriptor * config ;
if ( len < sizeof * config )
return 0 ;
config = ( struct usb_config_descriptor * ) buf ;
switch ( config - > bDescriptorType ) {
case USB_DT_CONFIG :
case USB_DT_OTHER_SPEED_CONFIG :
if ( config - > bLength ! = 9 ) {
dbg ( " bogus config descriptor length " ) ;
return 0 ;
}
/* this bit 'must be 1' but often isn't */
if ( ! realworld & & ! ( config - > bmAttributes & 0x80 ) ) {
dbg ( " high bit of config attributes not set " ) ;
return 0 ;
}
if ( config - > bmAttributes & 0x1f ) { /* reserved == 0 */
dbg ( " reserved config bits set " ) ;
return 0 ;
}
break ;
default :
return 0 ;
}
if ( le16_to_cpu ( config - > wTotalLength ) = = len ) /* read it all */
return 1 ;
if ( le16_to_cpu ( config - > wTotalLength ) > = TBUF_SIZE ) /* max partial read */
return 1 ;
dbg ( " bogus config descriptor read size " ) ;
return 0 ;
}
/* sanity test for standard requests working with usb_control_mesg() and some
* of the utility functions which use it .
*
* this doesn ' t test how endpoint halts behave or data toggles get set , since
* we won ' t do I / O to bulk / interrupt endpoints here ( which is how to change
* halt or toggle ) . toggle testing is impractical without support from hcds .
*
* this avoids failing devices linux would normally work with , by not testing
* config / altsetting operations for devices that only support their defaults .
* such devices rarely support those needless operations .
*
* NOTE that since this is a sanity test , it ' s not examining boundary cases
* to see if usbcore , hcd , and device all behave right . such testing would
* involve varied read sizes and other operation sequences .
*/
static int ch9_postconfig ( struct usbtest_dev * dev )
{
struct usb_interface * iface = dev - > intf ;
struct usb_device * udev = interface_to_usbdev ( iface ) ;
int i , alt , retval ;
/* [9.2.3] if there's more than one altsetting, we need to be able to
* set and get each one . mostly trusts the descriptors from usbcore .
*/
for ( i = 0 ; i < iface - > num_altsetting ; i + + ) {
/* 9.2.3 constrains the range here */
alt = iface - > altsetting [ i ] . desc . bAlternateSetting ;
if ( alt < 0 | | alt > = iface - > num_altsetting ) {
dev_dbg ( & iface - > dev ,
" invalid alt [%d].bAltSetting = %d \n " ,
i , alt ) ;
}
/* [real world] get/set unimplemented if there's only one */
if ( realworld & & iface - > num_altsetting = = 1 )
continue ;
/* [9.4.10] set_interface */
retval = set_altsetting ( dev , alt ) ;
if ( retval ) {
dev_dbg ( & iface - > dev , " can't set_interface = %d, %d \n " ,
alt , retval ) ;
return retval ;
}
/* [9.4.4] get_interface always works */
retval = get_altsetting ( dev ) ;
if ( retval ! = alt ) {
dev_dbg ( & iface - > dev , " get alt should be %d, was %d \n " ,
alt , retval ) ;
return ( retval < 0 ) ? retval : - EDOM ;
}
}
/* [real world] get_config unimplemented if there's only one */
if ( ! realworld | | udev - > descriptor . bNumConfigurations ! = 1 ) {
int expected = udev - > actconfig - > desc . bConfigurationValue ;
/* [9.4.2] get_configuration always works
* . . . although some cheap devices ( like one TI Hub I ' ve got )
* won ' t return config descriptors except before set_config .
*/
retval = usb_control_msg ( udev , usb_rcvctrlpipe ( udev , 0 ) ,
USB_REQ_GET_CONFIGURATION ,
USB_DIR_IN | USB_RECIP_DEVICE ,
0 , 0 , dev - > buf , 1 , USB_CTRL_GET_TIMEOUT ) ;
if ( retval ! = 1 | | dev - > buf [ 0 ] ! = expected ) {
2005-04-23 00:17:00 +04:00
dev_dbg ( & iface - > dev , " get config --> %d %d (1 %d) \n " ,
retval , dev - > buf [ 0 ] , expected ) ;
2005-04-17 02:20:36 +04:00
return ( retval < 0 ) ? retval : - EDOM ;
}
}
/* there's always [9.4.3] a device descriptor [9.6.1] */
retval = usb_get_descriptor ( udev , USB_DT_DEVICE , 0 ,
dev - > buf , sizeof udev - > descriptor ) ;
if ( retval ! = sizeof udev - > descriptor ) {
dev_dbg ( & iface - > dev , " dev descriptor --> %d \n " , retval ) ;
return ( retval < 0 ) ? retval : - EDOM ;
}
/* there's always [9.4.3] at least one config descriptor [9.6.3] */
for ( i = 0 ; i < udev - > descriptor . bNumConfigurations ; i + + ) {
retval = usb_get_descriptor ( udev , USB_DT_CONFIG , i ,
dev - > buf , TBUF_SIZE ) ;
if ( ! is_good_config ( dev - > buf , retval ) ) {
dev_dbg ( & iface - > dev ,
" config [%d] descriptor --> %d \n " ,
i , retval ) ;
return ( retval < 0 ) ? retval : - EDOM ;
}
// FIXME cross-checking udev->config[i] to make sure usbcore
// parsed it right (etc) would be good testing paranoia
}
/* and sometimes [9.2.6.6] speed dependent descriptors */
if ( le16_to_cpu ( udev - > descriptor . bcdUSB ) = = 0x0200 ) {
struct usb_qualifier_descriptor * d = NULL ;
/* device qualifier [9.6.2] */
retval = usb_get_descriptor ( udev ,
USB_DT_DEVICE_QUALIFIER , 0 , dev - > buf ,
sizeof ( struct usb_qualifier_descriptor ) ) ;
if ( retval = = - EPIPE ) {
if ( udev - > speed = = USB_SPEED_HIGH ) {
dev_dbg ( & iface - > dev ,
" hs dev qualifier --> %d \n " ,
retval ) ;
return ( retval < 0 ) ? retval : - EDOM ;
}
/* usb2.0 but not high-speed capable; fine */
} else if ( retval ! = sizeof ( struct usb_qualifier_descriptor ) ) {
dev_dbg ( & iface - > dev , " dev qualifier --> %d \n " , retval ) ;
return ( retval < 0 ) ? retval : - EDOM ;
} else
d = ( struct usb_qualifier_descriptor * ) dev - > buf ;
/* might not have [9.6.2] any other-speed configs [9.6.4] */
if ( d ) {
unsigned max = d - > bNumConfigurations ;
for ( i = 0 ; i < max ; i + + ) {
retval = usb_get_descriptor ( udev ,
USB_DT_OTHER_SPEED_CONFIG , i ,
dev - > buf , TBUF_SIZE ) ;
if ( ! is_good_config ( dev - > buf , retval ) ) {
dev_dbg ( & iface - > dev ,
" other speed config --> %d \n " ,
retval ) ;
return ( retval < 0 ) ? retval : - EDOM ;
}
}
}
}
// FIXME fetch strings from at least the device descriptor
/* [9.4.5] get_status always works */
retval = usb_get_status ( udev , USB_RECIP_DEVICE , 0 , dev - > buf ) ;
if ( retval ! = 2 ) {
dev_dbg ( & iface - > dev , " get dev status --> %d \n " , retval ) ;
return ( retval < 0 ) ? retval : - EDOM ;
}
// FIXME configuration.bmAttributes says if we could try to set/clear
// the device's remote wakeup feature ... if we can, test that here
retval = usb_get_status ( udev , USB_RECIP_INTERFACE ,
iface - > altsetting [ 0 ] . desc . bInterfaceNumber , dev - > buf ) ;
if ( retval ! = 2 ) {
dev_dbg ( & iface - > dev , " get interface status --> %d \n " , retval ) ;
return ( retval < 0 ) ? retval : - EDOM ;
}
// FIXME get status for each endpoint in the interface
return 0 ;
}
/*-------------------------------------------------------------------------*/
/* use ch9 requests to test whether:
* ( a ) queues work for control , keeping N subtests queued and
* active ( auto - resubmit ) for M loops through the queue .
* ( b ) protocol stalls ( control - only ) will autorecover .
* it ' s not like bulk / intr ; no halt clearing .
* ( c ) short control reads are reported and handled .
* ( d ) queues are always processed in - order
*/
struct ctrl_ctx {
spinlock_t lock ;
struct usbtest_dev * dev ;
struct completion complete ;
unsigned count ;
unsigned pending ;
int status ;
struct urb * * urb ;
struct usbtest_param * param ;
int last ;
} ;
# define NUM_SUBCASES 15 /* how many test subcases here? */
struct subcase {
struct usb_ctrlrequest setup ;
int number ;
int expected ;
} ;
static void ctrl_complete ( struct urb * urb , struct pt_regs * regs )
{
struct ctrl_ctx * ctx = urb - > context ;
struct usb_ctrlrequest * reqp ;
struct subcase * subcase ;
int status = urb - > status ;
reqp = ( struct usb_ctrlrequest * ) urb - > setup_packet ;
subcase = container_of ( reqp , struct subcase , setup ) ;
spin_lock ( & ctx - > lock ) ;
ctx - > count - - ;
ctx - > pending - - ;
/* queue must transfer and complete in fifo order, unless
* usb_unlink_urb ( ) is used to unlink something not at the
* physical queue head ( not tested ) .
*/
if ( subcase - > number > 0 ) {
if ( ( subcase - > number - ctx - > last ) ! = 1 ) {
dbg ( " subcase %d completed out of order, last %d " ,
subcase - > number , ctx - > last ) ;
status = - EDOM ;
ctx - > last = subcase - > number ;
goto error ;
}
}
ctx - > last = subcase - > number ;
/* succeed or fault in only one way? */
if ( status = = subcase - > expected )
status = 0 ;
/* async unlink for cleanup? */
else if ( status ! = - ECONNRESET ) {
/* some faults are allowed, not required */
if ( subcase - > expected > 0 & & (
( ( urb - > status = = - subcase - > expected /* happened */
| | urb - > status = = 0 ) ) ) ) /* didn't */
status = 0 ;
/* sometimes more than one fault is allowed */
else if ( subcase - > number = = 12 & & status = = - EPIPE )
status = 0 ;
else
dbg ( " subtest %d error, status %d " ,
subcase - > number , status ) ;
}
/* unexpected status codes mean errors; ideally, in hardware */
if ( status ) {
error :
if ( ctx - > status = = 0 ) {
int i ;
ctx - > status = status ;
info ( " control queue %02x.%02x, err %d, %d left " ,
reqp - > bRequestType , reqp - > bRequest ,
status , ctx - > count ) ;
/* FIXME this "unlink everything" exit route should
* be a separate test case .
*/
/* unlink whatever's still pending */
for ( i = 1 ; i < ctx - > param - > sglen ; i + + ) {
struct urb * u = ctx - > urb [
( i + subcase - > number ) % ctx - > param - > sglen ] ;
if ( u = = urb | | ! u - > dev )
continue ;
2006-05-15 21:23:53 +04:00
spin_unlock ( & ctx - > lock ) ;
2005-04-17 02:20:36 +04:00
status = usb_unlink_urb ( u ) ;
2006-05-15 21:23:53 +04:00
spin_lock ( & ctx - > lock ) ;
2005-04-17 02:20:36 +04:00
switch ( status ) {
case - EINPROGRESS :
case - EBUSY :
case - EIDRM :
continue ;
default :
dbg ( " urb unlink --> %d " , status ) ;
}
}
status = ctx - > status ;
}
}
/* resubmit if we need to, else mark this as done */
if ( ( status = = 0 ) & & ( ctx - > pending < ctx - > count ) ) {
if ( ( status = usb_submit_urb ( urb , SLAB_ATOMIC ) ) ! = 0 ) {
dbg ( " can't resubmit ctrl %02x.%02x, err %d " ,
reqp - > bRequestType , reqp - > bRequest , status ) ;
urb - > dev = NULL ;
} else
ctx - > pending + + ;
} else
urb - > dev = NULL ;
/* signal completion when nothing's queued */
if ( ctx - > pending = = 0 )
complete ( & ctx - > complete ) ;
spin_unlock ( & ctx - > lock ) ;
}
static int
test_ctrl_queue ( struct usbtest_dev * dev , struct usbtest_param * param )
{
struct usb_device * udev = testdev_to_usbdev ( dev ) ;
struct urb * * urb ;
struct ctrl_ctx context ;
int i ;
spin_lock_init ( & context . lock ) ;
context . dev = dev ;
init_completion ( & context . complete ) ;
context . count = param - > sglen * param - > iterations ;
context . pending = 0 ;
context . status = - ENOMEM ;
context . param = param ;
context . last = - 1 ;
/* allocate and init the urbs we'll queue.
* as with bulk / intr sglists , sglen is the queue depth ; it also
* controls which subtests run ( more tests than sglen ) or rerun .
*/
2006-02-27 23:29:43 +03:00
urb = kcalloc ( param - > sglen , sizeof ( struct urb * ) , SLAB_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! urb )
return - ENOMEM ;
for ( i = 0 ; i < param - > sglen ; i + + ) {
int pipe = usb_rcvctrlpipe ( udev , 0 ) ;
unsigned len ;
struct urb * u ;
struct usb_ctrlrequest req ;
struct subcase * reqp ;
int expected = 0 ;
/* requests here are mostly expected to succeed on any
* device , but some are chosen to trigger protocol stalls
* or short reads .
*/
memset ( & req , 0 , sizeof req ) ;
req . bRequest = USB_REQ_GET_DESCRIPTOR ;
req . bRequestType = USB_DIR_IN | USB_RECIP_DEVICE ;
switch ( i % NUM_SUBCASES ) {
case 0 : // get device descriptor
req . wValue = cpu_to_le16 ( USB_DT_DEVICE < < 8 ) ;
len = sizeof ( struct usb_device_descriptor ) ;
break ;
case 1 : // get first config descriptor (only)
req . wValue = cpu_to_le16 ( ( USB_DT_CONFIG < < 8 ) | 0 ) ;
len = sizeof ( struct usb_config_descriptor ) ;
break ;
case 2 : // get altsetting (OFTEN STALLS)
req . bRequest = USB_REQ_GET_INTERFACE ;
req . bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE ;
// index = 0 means first interface
len = 1 ;
expected = EPIPE ;
break ;
case 3 : // get interface status
req . bRequest = USB_REQ_GET_STATUS ;
req . bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE ;
// interface 0
len = 2 ;
break ;
case 4 : // get device status
req . bRequest = USB_REQ_GET_STATUS ;
req . bRequestType = USB_DIR_IN | USB_RECIP_DEVICE ;
len = 2 ;
break ;
case 5 : // get device qualifier (MAY STALL)
req . wValue = cpu_to_le16 ( USB_DT_DEVICE_QUALIFIER < < 8 ) ;
len = sizeof ( struct usb_qualifier_descriptor ) ;
if ( udev - > speed ! = USB_SPEED_HIGH )
expected = EPIPE ;
break ;
case 6 : // get first config descriptor, plus interface
req . wValue = cpu_to_le16 ( ( USB_DT_CONFIG < < 8 ) | 0 ) ;
len = sizeof ( struct usb_config_descriptor ) ;
len + = sizeof ( struct usb_interface_descriptor ) ;
break ;
case 7 : // get interface descriptor (ALWAYS STALLS)
req . wValue = cpu_to_le16 ( USB_DT_INTERFACE < < 8 ) ;
// interface == 0
len = sizeof ( struct usb_interface_descriptor ) ;
expected = EPIPE ;
break ;
// NOTE: two consecutive stalls in the queue here.
// that tests fault recovery a bit more aggressively.
case 8 : // clear endpoint halt (USUALLY STALLS)
req . bRequest = USB_REQ_CLEAR_FEATURE ;
req . bRequestType = USB_RECIP_ENDPOINT ;
// wValue 0 == ep halt
// wIndex 0 == ep0 (shouldn't halt!)
len = 0 ;
pipe = usb_sndctrlpipe ( udev , 0 ) ;
expected = EPIPE ;
break ;
case 9 : // get endpoint status
req . bRequest = USB_REQ_GET_STATUS ;
req . bRequestType = USB_DIR_IN | USB_RECIP_ENDPOINT ;
// endpoint 0
len = 2 ;
break ;
case 10 : // trigger short read (EREMOTEIO)
req . wValue = cpu_to_le16 ( ( USB_DT_CONFIG < < 8 ) | 0 ) ;
len = 1024 ;
expected = - EREMOTEIO ;
break ;
// NOTE: two consecutive _different_ faults in the queue.
case 11 : // get endpoint descriptor (ALWAYS STALLS)
req . wValue = cpu_to_le16 ( USB_DT_ENDPOINT < < 8 ) ;
// endpoint == 0
len = sizeof ( struct usb_interface_descriptor ) ;
expected = EPIPE ;
break ;
// NOTE: sometimes even a third fault in the queue!
case 12 : // get string 0 descriptor (MAY STALL)
req . wValue = cpu_to_le16 ( USB_DT_STRING < < 8 ) ;
// string == 0, for language IDs
len = sizeof ( struct usb_interface_descriptor ) ;
// may succeed when > 4 languages
expected = EREMOTEIO ; // or EPIPE, if no strings
break ;
case 13 : // short read, resembling case 10
req . wValue = cpu_to_le16 ( ( USB_DT_CONFIG < < 8 ) | 0 ) ;
// last data packet "should" be DATA1, not DATA0
len = 1024 - udev - > descriptor . bMaxPacketSize0 ;
expected = - EREMOTEIO ;
break ;
case 14 : // short read; try to fill the last packet
req . wValue = cpu_to_le16 ( ( USB_DT_DEVICE < < 8 ) | 0 ) ;
// device descriptor size == 18 bytes
len = udev - > descriptor . bMaxPacketSize0 ;
switch ( len ) {
case 8 : len = 24 ; break ;
case 16 : len = 32 ; break ;
}
expected = - EREMOTEIO ;
break ;
default :
err ( " bogus number of ctrl queue testcases! " ) ;
context . status = - EINVAL ;
goto cleanup ;
}
req . wLength = cpu_to_le16 ( len ) ;
urb [ i ] = u = simple_alloc_urb ( udev , pipe , len ) ;
if ( ! u )
goto cleanup ;
reqp = usb_buffer_alloc ( udev , sizeof * reqp , SLAB_KERNEL ,
& u - > setup_dma ) ;
if ( ! reqp )
goto cleanup ;
reqp - > setup = req ;
reqp - > number = i % NUM_SUBCASES ;
reqp - > expected = expected ;
u - > setup_packet = ( char * ) & reqp - > setup ;
2005-10-18 18:08:31 +04:00
u - > transfer_flags | = URB_NO_SETUP_DMA_MAP ;
2005-04-17 02:20:36 +04:00
u - > context = & context ;
u - > complete = ctrl_complete ;
}
/* queue the urbs */
context . urb = urb ;
spin_lock_irq ( & context . lock ) ;
for ( i = 0 ; i < param - > sglen ; i + + ) {
context . status = usb_submit_urb ( urb [ i ] , SLAB_ATOMIC ) ;
if ( context . status ! = 0 ) {
dbg ( " can't submit urb[%d], status %d " ,
i , context . status ) ;
context . count = context . pending ;
break ;
}
context . pending + + ;
}
spin_unlock_irq ( & context . lock ) ;
/* FIXME set timer and time out; provide a disconnect hook */
/* wait for the last one to complete */
if ( context . pending > 0 )
wait_for_completion ( & context . complete ) ;
cleanup :
for ( i = 0 ; i < param - > sglen ; i + + ) {
if ( ! urb [ i ] )
continue ;
urb [ i ] - > dev = udev ;
if ( urb [ i ] - > setup_packet )
usb_buffer_free ( udev , sizeof ( struct usb_ctrlrequest ) ,
urb [ i ] - > setup_packet ,
urb [ i ] - > setup_dma ) ;
simple_free_urb ( urb [ i ] ) ;
}
kfree ( urb ) ;
return context . status ;
}
# undef NUM_SUBCASES
/*-------------------------------------------------------------------------*/
static void unlink1_callback ( struct urb * urb , struct pt_regs * regs )
{
int status = urb - > status ;
// we "know" -EPIPE (stall) never happens
if ( ! status )
status = usb_submit_urb ( urb , SLAB_ATOMIC ) ;
if ( status ) {
urb - > status = status ;
complete ( ( struct completion * ) urb - > context ) ;
}
}
static int unlink1 ( struct usbtest_dev * dev , int pipe , int size , int async )
{
struct urb * urb ;
struct completion completion ;
int retval = 0 ;
init_completion ( & completion ) ;
urb = simple_alloc_urb ( testdev_to_usbdev ( dev ) , pipe , size ) ;
if ( ! urb )
return - ENOMEM ;
urb - > context = & completion ;
urb - > complete = unlink1_callback ;
/* keep the endpoint busy. there are lots of hc/hcd-internal
* states , and testing should get to all of them over time .
*
* FIXME want additional tests for when endpoint is STALLing
* due to errors , or is just NAKing requests .
*/
if ( ( retval = usb_submit_urb ( urb , SLAB_KERNEL ) ) ! = 0 ) {
dev_dbg ( & dev - > intf - > dev , " submit fail %d \n " , retval ) ;
return retval ;
}
/* unlinking that should always work. variable delay tests more
* hcd states and code paths , even with little other system load .
*/
msleep ( jiffies % ( 2 * INTERRUPT_RATE ) ) ;
if ( async ) {
retry :
retval = usb_unlink_urb ( urb ) ;
if ( retval = = - EBUSY | | retval = = - EIDRM ) {
/* we can't unlink urbs while they're completing.
* or if they ' ve completed , and we haven ' t resubmitted .
* " normal " drivers would prevent resubmission , but
* since we ' re testing unlink paths , we can ' t .
*/
dev_dbg ( & dev - > intf - > dev , " unlink retry \n " ) ;
goto retry ;
}
} else
usb_kill_urb ( urb ) ;
if ( ! ( retval = = 0 | | retval = = - EINPROGRESS ) ) {
dev_dbg ( & dev - > intf - > dev , " unlink fail %d \n " , retval ) ;
return retval ;
}
wait_for_completion ( & completion ) ;
retval = urb - > status ;
simple_free_urb ( urb ) ;
if ( async )
return ( retval = = - ECONNRESET ) ? 0 : retval - 1000 ;
else
return ( retval = = - ENOENT | | retval = = - EPERM ) ?
0 : retval - 2000 ;
}
static int unlink_simple ( struct usbtest_dev * dev , int pipe , int len )
{
int retval = 0 ;
/* test sync and async paths */
retval = unlink1 ( dev , pipe , len , 1 ) ;
if ( ! retval )
retval = unlink1 ( dev , pipe , len , 0 ) ;
return retval ;
}
/*-------------------------------------------------------------------------*/
static int verify_not_halted ( int ep , struct urb * urb )
{
int retval ;
u16 status ;
/* shouldn't look or act halted */
retval = usb_get_status ( urb - > dev , USB_RECIP_ENDPOINT , ep , & status ) ;
if ( retval < 0 ) {
dbg ( " ep %02x couldn't get no-halt status, %d " , ep , retval ) ;
return retval ;
}
if ( status ! = 0 ) {
dbg ( " ep %02x bogus status: %04x != 0 " , ep , status ) ;
return - EINVAL ;
}
retval = simple_io ( urb , 1 , 0 , 0 , __FUNCTION__ ) ;
if ( retval ! = 0 )
return - EINVAL ;
return 0 ;
}
static int verify_halted ( int ep , struct urb * urb )
{
int retval ;
u16 status ;
/* should look and act halted */
retval = usb_get_status ( urb - > dev , USB_RECIP_ENDPOINT , ep , & status ) ;
if ( retval < 0 ) {
dbg ( " ep %02x couldn't get halt status, %d " , ep , retval ) ;
return retval ;
}
if ( status ! = 1 ) {
dbg ( " ep %02x bogus status: %04x != 1 " , ep , status ) ;
return - EINVAL ;
}
retval = simple_io ( urb , 1 , 0 , - EPIPE , __FUNCTION__ ) ;
if ( retval ! = - EPIPE )
return - EINVAL ;
retval = simple_io ( urb , 1 , 0 , - EPIPE , " verify_still_halted " ) ;
if ( retval ! = - EPIPE )
return - EINVAL ;
return 0 ;
}
static int test_halt ( int ep , struct urb * urb )
{
int retval ;
/* shouldn't look or act halted now */
retval = verify_not_halted ( ep , urb ) ;
if ( retval < 0 )
return retval ;
/* set halt (protocol test only), verify it worked */
retval = usb_control_msg ( urb - > dev , usb_sndctrlpipe ( urb - > dev , 0 ) ,
USB_REQ_SET_FEATURE , USB_RECIP_ENDPOINT ,
USB_ENDPOINT_HALT , ep ,
NULL , 0 , USB_CTRL_SET_TIMEOUT ) ;
if ( retval < 0 ) {
dbg ( " ep %02x couldn't set halt, %d " , ep , retval ) ;
return retval ;
}
retval = verify_halted ( ep , urb ) ;
if ( retval < 0 )
return retval ;
/* clear halt (tests API + protocol), verify it worked */
retval = usb_clear_halt ( urb - > dev , urb - > pipe ) ;
if ( retval < 0 ) {
dbg ( " ep %02x couldn't clear halt, %d " , ep , retval ) ;
return retval ;
}
retval = verify_not_halted ( ep , urb ) ;
if ( retval < 0 )
return retval ;
/* NOTE: could also verify SET_INTERFACE clear halts ... */
return 0 ;
}
static int halt_simple ( struct usbtest_dev * dev )
{
int ep ;
int retval = 0 ;
struct urb * urb ;
urb = simple_alloc_urb ( testdev_to_usbdev ( dev ) , 0 , 512 ) ;
if ( urb = = NULL )
return - ENOMEM ;
if ( dev - > in_pipe ) {
ep = usb_pipeendpoint ( dev - > in_pipe ) | USB_DIR_IN ;
urb - > pipe = dev - > in_pipe ;
retval = test_halt ( ep , urb ) ;
if ( retval < 0 )
goto done ;
}
if ( dev - > out_pipe ) {
ep = usb_pipeendpoint ( dev - > out_pipe ) ;
urb - > pipe = dev - > out_pipe ;
retval = test_halt ( ep , urb ) ;
}
done :
simple_free_urb ( urb ) ;
return retval ;
}
/*-------------------------------------------------------------------------*/
/* Control OUT tests use the vendor control requests from Intel's
* USB 2.0 compliance test device : write a buffer , read it back .
*
* Intel ' s spec only _requires_ that it work for one packet , which
* is pretty weak . Some HCDs place limits here ; most devices will
* need to be able to handle more than one OUT data packet . We ' ll
* try whatever we ' re told to try .
*/
static int ctrl_out ( struct usbtest_dev * dev ,
unsigned count , unsigned length , unsigned vary )
{
unsigned i , j , len , retval ;
u8 * buf ;
char * what = " ? " ;
struct usb_device * udev ;
2005-04-23 00:17:00 +04:00
if ( length < 1 | | length > 0xffff | | vary > = length )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
buf = kmalloc ( length , SLAB_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
udev = testdev_to_usbdev ( dev ) ;
len = length ;
retval = 0 ;
/* NOTE: hardware might well act differently if we pushed it
* with lots back - to - back queued requests .
*/
for ( i = 0 ; i < count ; i + + ) {
/* write patterned data */
for ( j = 0 ; j < len ; j + + )
buf [ j ] = i + j ;
retval = usb_control_msg ( udev , usb_sndctrlpipe ( udev , 0 ) ,
0x5b , USB_DIR_OUT | USB_TYPE_VENDOR ,
0 , 0 , buf , len , USB_CTRL_SET_TIMEOUT ) ;
if ( retval ! = len ) {
what = " write " ;
2005-04-23 00:17:00 +04:00
if ( retval > = 0 ) {
INFO ( dev , " ctrl_out, wlen %d (expected %d) \n " ,
retval , len ) ;
retval = - EBADMSG ;
}
2005-04-17 02:20:36 +04:00
break ;
}
/* read it back -- assuming nothing intervened!! */
retval = usb_control_msg ( udev , usb_rcvctrlpipe ( udev , 0 ) ,
0x5c , USB_DIR_IN | USB_TYPE_VENDOR ,
0 , 0 , buf , len , USB_CTRL_GET_TIMEOUT ) ;
if ( retval ! = len ) {
what = " read " ;
2005-04-23 00:17:00 +04:00
if ( retval > = 0 ) {
INFO ( dev , " ctrl_out, rlen %d (expected %d) \n " ,
retval , len ) ;
retval = - EBADMSG ;
}
2005-04-17 02:20:36 +04:00
break ;
}
/* fail if we can't verify */
for ( j = 0 ; j < len ; j + + ) {
if ( buf [ j ] ! = ( u8 ) ( i + j ) ) {
INFO ( dev , " ctrl_out, byte %d is %d not %d \n " ,
j , buf [ j ] , ( u8 ) i + j ) ;
retval = - EBADMSG ;
break ;
}
}
if ( retval < 0 ) {
what = " verify " ;
break ;
}
len + = vary ;
2005-04-23 00:17:00 +04:00
/* [real world] the "zero bytes IN" case isn't really used.
* hardware can easily trip up in this wierd case , since its
* status stage is IN , not OUT like other ep0in transfers .
*/
2005-04-17 02:20:36 +04:00
if ( len > length )
2005-04-23 00:17:00 +04:00
len = realworld ? 1 : 0 ;
2005-04-17 02:20:36 +04:00
}
if ( retval < 0 )
INFO ( dev , " ctrl_out %s failed, code %d, count %d \n " ,
what , retval , i ) ;
kfree ( buf ) ;
return retval ;
}
/*-------------------------------------------------------------------------*/
/* ISO tests ... mimics common usage
* - buffer length is split into N packets ( mostly maxpacket sized )
* - multi - buffers according to sglen
*/
struct iso_context {
unsigned count ;
unsigned pending ;
spinlock_t lock ;
struct completion done ;
unsigned long errors ;
struct usbtest_dev * dev ;
} ;
static void iso_callback ( struct urb * urb , struct pt_regs * regs )
{
struct iso_context * ctx = urb - > context ;
spin_lock ( & ctx - > lock ) ;
ctx - > count - - ;
if ( urb - > error_count > 0 )
ctx - > errors + = urb - > error_count ;
if ( urb - > status = = 0 & & ctx - > count > ( ctx - > pending - 1 ) ) {
int status = usb_submit_urb ( urb , GFP_ATOMIC ) ;
switch ( status ) {
case 0 :
goto done ;
default :
dev_dbg ( & ctx - > dev - > intf - > dev ,
" iso resubmit err %d \n " ,
status ) ;
/* FALLTHROUGH */
case - ENODEV : /* disconnected */
break ;
}
}
simple_free_urb ( urb ) ;
ctx - > pending - - ;
if ( ctx - > pending = = 0 ) {
if ( ctx - > errors )
dev_dbg ( & ctx - > dev - > intf - > dev ,
" iso test, %lu errors \n " ,
ctx - > errors ) ;
complete ( & ctx - > done ) ;
}
done :
spin_unlock ( & ctx - > lock ) ;
}
static struct urb * iso_alloc_urb (
struct usb_device * udev ,
int pipe ,
struct usb_endpoint_descriptor * desc ,
long bytes
)
{
struct urb * urb ;
unsigned i , maxp , packets ;
if ( bytes < 0 | | ! desc )
return NULL ;
maxp = 0x7ff & le16_to_cpu ( desc - > wMaxPacketSize ) ;
maxp * = 1 + ( 0x3 & ( le16_to_cpu ( desc - > wMaxPacketSize ) > > 11 ) ) ;
packets = ( bytes + maxp - 1 ) / maxp ;
urb = usb_alloc_urb ( packets , SLAB_KERNEL ) ;
if ( ! urb )
return urb ;
urb - > dev = udev ;
urb - > pipe = pipe ;
urb - > number_of_packets = packets ;
urb - > transfer_buffer_length = bytes ;
urb - > transfer_buffer = usb_buffer_alloc ( udev , bytes , SLAB_KERNEL ,
& urb - > transfer_dma ) ;
if ( ! urb - > transfer_buffer ) {
usb_free_urb ( urb ) ;
return NULL ;
}
memset ( urb - > transfer_buffer , 0 , bytes ) ;
for ( i = 0 ; i < packets ; i + + ) {
/* here, only the last packet will be short */
urb - > iso_frame_desc [ i ] . length = min ( ( unsigned ) bytes , maxp ) ;
bytes - = urb - > iso_frame_desc [ i ] . length ;
urb - > iso_frame_desc [ i ] . offset = maxp * i ;
}
urb - > complete = iso_callback ;
// urb->context = SET BY CALLER
urb - > interval = 1 < < ( desc - > bInterval - 1 ) ;
urb - > transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP ;
return urb ;
}
static int
test_iso_queue ( struct usbtest_dev * dev , struct usbtest_param * param ,
int pipe , struct usb_endpoint_descriptor * desc )
{
struct iso_context context ;
struct usb_device * udev ;
unsigned i ;
unsigned long packets = 0 ;
int status ;
struct urb * urbs [ 10 ] ; /* FIXME no limit */
if ( param - > sglen > 10 )
return - EDOM ;
context . count = param - > iterations * param - > sglen ;
context . pending = param - > sglen ;
context . errors = 0 ;
context . dev = dev ;
init_completion ( & context . done ) ;
spin_lock_init ( & context . lock ) ;
memset ( urbs , 0 , sizeof urbs ) ;
udev = testdev_to_usbdev ( dev ) ;
dev_dbg ( & dev - > intf - > dev ,
" ... iso period %d %sframes, wMaxPacket %04x \n " ,
1 < < ( desc - > bInterval - 1 ) ,
( udev - > speed = = USB_SPEED_HIGH ) ? " micro " : " " ,
le16_to_cpu ( desc - > wMaxPacketSize ) ) ;
for ( i = 0 ; i < param - > sglen ; i + + ) {
urbs [ i ] = iso_alloc_urb ( udev , pipe , desc ,
param - > length ) ;
if ( ! urbs [ i ] ) {
status = - ENOMEM ;
goto fail ;
}
packets + = urbs [ i ] - > number_of_packets ;
urbs [ i ] - > context = & context ;
}
packets * = param - > iterations ;
dev_dbg ( & dev - > intf - > dev ,
" ... total %lu msec (%lu packets) \n " ,
( packets * ( 1 < < ( desc - > bInterval - 1 ) ) )
/ ( ( udev - > speed = = USB_SPEED_HIGH ) ? 8 : 1 ) ,
packets ) ;
spin_lock_irq ( & context . lock ) ;
for ( i = 0 ; i < param - > sglen ; i + + ) {
status = usb_submit_urb ( urbs [ i ] , SLAB_ATOMIC ) ;
if ( status < 0 ) {
ERROR ( dev , " submit iso[%d], error %d \n " , i , status ) ;
if ( i = = 0 ) {
spin_unlock_irq ( & context . lock ) ;
goto fail ;
}
simple_free_urb ( urbs [ i ] ) ;
context . pending - - ;
}
}
spin_unlock_irq ( & context . lock ) ;
wait_for_completion ( & context . done ) ;
return 0 ;
fail :
for ( i = 0 ; i < param - > sglen ; i + + ) {
if ( urbs [ i ] )
simple_free_urb ( urbs [ i ] ) ;
}
return status ;
}
/*-------------------------------------------------------------------------*/
/* We only have this one interface to user space, through usbfs.
* User mode code can scan usbfs to find N different devices ( maybe on
* different busses ) to use when testing , and allocate one thread per
* test . So discovery is simplified , and we have no device naming issues .
*
* Don ' t use these only as stress / load tests . Use them along with with
* other USB bus activity : plugging , unplugging , mousing , mp3 playback ,
* video capture , and so on . Run different tests at different times , in
* different sequences . Nothing here should interact with other devices ,
* except indirectly by consuming USB bandwidth and CPU resources for test
* threads and request completion . But the only way to know that for sure
* is to test when HC queues are in use by many devices .
*/
static int
usbtest_ioctl ( struct usb_interface * intf , unsigned int code , void * buf )
{
struct usbtest_dev * dev = usb_get_intfdata ( intf ) ;
struct usb_device * udev = testdev_to_usbdev ( dev ) ;
struct usbtest_param * param = buf ;
int retval = - EOPNOTSUPP ;
struct urb * urb ;
struct scatterlist * sg ;
struct usb_sg_request req ;
struct timeval start ;
unsigned i ;
// FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is.
if ( code ! = USBTEST_REQUEST )
return - EOPNOTSUPP ;
if ( param - > iterations < = 0 | | param - > length < 0
| | param - > sglen < 0 | | param - > vary < 0 )
return - EINVAL ;
if ( down_interruptible ( & dev - > sem ) )
return - ERESTARTSYS ;
2005-09-04 02:56:57 +04:00
if ( intf - > dev . power . power_state . event ! = PM_EVENT_ON ) {
2005-04-23 00:17:00 +04:00
up ( & dev - > sem ) ;
return - EHOSTUNREACH ;
}
2005-04-17 02:20:36 +04:00
/* some devices, like ez-usb default devices, need a non-default
* altsetting to have any active endpoints . some tests change
* altsettings ; force a default so most tests don ' t need to check .
*/
if ( dev - > info - > alt > = 0 ) {
int res ;
if ( intf - > altsetting - > desc . bInterfaceNumber ) {
up ( & dev - > sem ) ;
return - ENODEV ;
}
res = set_altsetting ( dev , dev - > info - > alt ) ;
if ( res ) {
dev_err ( & intf - > dev ,
" set altsetting to %d failed, %d \n " ,
dev - > info - > alt , res ) ;
up ( & dev - > sem ) ;
return res ;
}
}
/*
* Just a bunch of test cases that every HCD is expected to handle .
*
* Some may need specific firmware , though it ' d be good to have
* one firmware image to handle all the test cases .
*
* FIXME add more tests ! cancel requests , verify the data , control
* queueing , concurrent read + write threads , and so on .
*/
do_gettimeofday ( & start ) ;
switch ( param - > test_num ) {
case 0 :
dev_dbg ( & intf - > dev , " TEST 0: NOP \n " ) ;
retval = 0 ;
break ;
/* Simple non-queued bulk I/O tests */
case 1 :
if ( dev - > out_pipe = = 0 )
break ;
dev_dbg ( & intf - > dev ,
" TEST 1: write %d bytes %u times \n " ,
param - > length , param - > iterations ) ;
urb = simple_alloc_urb ( udev , dev - > out_pipe , param - > length ) ;
if ( ! urb ) {
retval = - ENOMEM ;
break ;
}
// FIRMWARE: bulk sink (maybe accepts short writes)
retval = simple_io ( urb , param - > iterations , 0 , 0 , " test1 " ) ;
simple_free_urb ( urb ) ;
break ;
case 2 :
if ( dev - > in_pipe = = 0 )
break ;
dev_dbg ( & intf - > dev ,
" TEST 2: read %d bytes %u times \n " ,
param - > length , param - > iterations ) ;
urb = simple_alloc_urb ( udev , dev - > in_pipe , param - > length ) ;
if ( ! urb ) {
retval = - ENOMEM ;
break ;
}
// FIRMWARE: bulk source (maybe generates short writes)
retval = simple_io ( urb , param - > iterations , 0 , 0 , " test2 " ) ;
simple_free_urb ( urb ) ;
break ;
case 3 :
if ( dev - > out_pipe = = 0 | | param - > vary = = 0 )
break ;
dev_dbg ( & intf - > dev ,
" TEST 3: write/%d 0..%d bytes %u times \n " ,
param - > vary , param - > length , param - > iterations ) ;
urb = simple_alloc_urb ( udev , dev - > out_pipe , param - > length ) ;
if ( ! urb ) {
retval = - ENOMEM ;
break ;
}
// FIRMWARE: bulk sink (maybe accepts short writes)
retval = simple_io ( urb , param - > iterations , param - > vary ,
0 , " test3 " ) ;
simple_free_urb ( urb ) ;
break ;
case 4 :
if ( dev - > in_pipe = = 0 | | param - > vary = = 0 )
break ;
dev_dbg ( & intf - > dev ,
" TEST 4: read/%d 0..%d bytes %u times \n " ,
param - > vary , param - > length , param - > iterations ) ;
urb = simple_alloc_urb ( udev , dev - > in_pipe , param - > length ) ;
if ( ! urb ) {
retval = - ENOMEM ;
break ;
}
// FIRMWARE: bulk source (maybe generates short writes)
retval = simple_io ( urb , param - > iterations , param - > vary ,
0 , " test4 " ) ;
simple_free_urb ( urb ) ;
break ;
/* Queued bulk I/O tests */
case 5 :
if ( dev - > out_pipe = = 0 | | param - > sglen = = 0 )
break ;
dev_dbg ( & intf - > dev ,
" TEST 5: write %d sglists %d entries of %d bytes \n " ,
param - > iterations ,
param - > sglen , param - > length ) ;
sg = alloc_sglist ( param - > sglen , param - > length , 0 ) ;
if ( ! sg ) {
retval = - ENOMEM ;
break ;
}
// FIRMWARE: bulk sink (maybe accepts short writes)
retval = perform_sglist ( udev , param - > iterations , dev - > out_pipe ,
& req , sg , param - > sglen ) ;
free_sglist ( sg , param - > sglen ) ;
break ;
case 6 :
if ( dev - > in_pipe = = 0 | | param - > sglen = = 0 )
break ;
dev_dbg ( & intf - > dev ,
" TEST 6: read %d sglists %d entries of %d bytes \n " ,
param - > iterations ,
param - > sglen , param - > length ) ;
sg = alloc_sglist ( param - > sglen , param - > length , 0 ) ;
if ( ! sg ) {
retval = - ENOMEM ;
break ;
}
// FIRMWARE: bulk source (maybe generates short writes)
retval = perform_sglist ( udev , param - > iterations , dev - > in_pipe ,
& req , sg , param - > sglen ) ;
free_sglist ( sg , param - > sglen ) ;
break ;
case 7 :
if ( dev - > out_pipe = = 0 | | param - > sglen = = 0 | | param - > vary = = 0 )
break ;
dev_dbg ( & intf - > dev ,
" TEST 7: write/%d %d sglists %d entries 0..%d bytes \n " ,
param - > vary , param - > iterations ,
param - > sglen , param - > length ) ;
sg = alloc_sglist ( param - > sglen , param - > length , param - > vary ) ;
if ( ! sg ) {
retval = - ENOMEM ;
break ;
}
// FIRMWARE: bulk sink (maybe accepts short writes)
retval = perform_sglist ( udev , param - > iterations , dev - > out_pipe ,
& req , sg , param - > sglen ) ;
free_sglist ( sg , param - > sglen ) ;
break ;
case 8 :
if ( dev - > in_pipe = = 0 | | param - > sglen = = 0 | | param - > vary = = 0 )
break ;
dev_dbg ( & intf - > dev ,
" TEST 8: read/%d %d sglists %d entries 0..%d bytes \n " ,
param - > vary , param - > iterations ,
param - > sglen , param - > length ) ;
sg = alloc_sglist ( param - > sglen , param - > length , param - > vary ) ;
if ( ! sg ) {
retval = - ENOMEM ;
break ;
}
// FIRMWARE: bulk source (maybe generates short writes)
retval = perform_sglist ( udev , param - > iterations , dev - > in_pipe ,
& req , sg , param - > sglen ) ;
free_sglist ( sg , param - > sglen ) ;
break ;
/* non-queued sanity tests for control (chapter 9 subset) */
case 9 :
retval = 0 ;
dev_dbg ( & intf - > dev ,
" TEST 9: ch9 (subset) control tests, %d times \n " ,
param - > iterations ) ;
for ( i = param - > iterations ; retval = = 0 & & i - - ; /* NOP */ )
retval = ch9_postconfig ( dev ) ;
if ( retval )
dbg ( " ch9 subset failed, iterations left %d " , i ) ;
break ;
/* queued control messaging */
case 10 :
if ( param - > sglen = = 0 )
break ;
retval = 0 ;
dev_dbg ( & intf - > dev ,
" TEST 10: queue %d control calls, %d times \n " ,
param - > sglen ,
param - > iterations ) ;
retval = test_ctrl_queue ( dev , param ) ;
break ;
/* simple non-queued unlinks (ring with one urb) */
case 11 :
if ( dev - > in_pipe = = 0 | | ! param - > length )
break ;
retval = 0 ;
dev_dbg ( & intf - > dev , " TEST 11: unlink %d reads of %d \n " ,
param - > iterations , param - > length ) ;
for ( i = param - > iterations ; retval = = 0 & & i - - ; /* NOP */ )
retval = unlink_simple ( dev , dev - > in_pipe ,
param - > length ) ;
if ( retval )
dev_dbg ( & intf - > dev , " unlink reads failed %d, "
" iterations left %d \n " , retval , i ) ;
break ;
case 12 :
if ( dev - > out_pipe = = 0 | | ! param - > length )
break ;
retval = 0 ;
dev_dbg ( & intf - > dev , " TEST 12: unlink %d writes of %d \n " ,
param - > iterations , param - > length ) ;
for ( i = param - > iterations ; retval = = 0 & & i - - ; /* NOP */ )
retval = unlink_simple ( dev , dev - > out_pipe ,
param - > length ) ;
if ( retval )
dev_dbg ( & intf - > dev , " unlink writes failed %d, "
" iterations left %d \n " , retval , i ) ;
break ;
/* ep halt tests */
case 13 :
if ( dev - > out_pipe = = 0 & & dev - > in_pipe = = 0 )
break ;
retval = 0 ;
dev_dbg ( & intf - > dev , " TEST 13: set/clear %d halts \n " ,
param - > iterations ) ;
for ( i = param - > iterations ; retval = = 0 & & i - - ; /* NOP */ )
retval = halt_simple ( dev ) ;
if ( retval )
DBG ( dev , " halts failed, iterations left %d \n " , i ) ;
break ;
/* control write tests */
case 14 :
if ( ! dev - > info - > ctrl_out )
break ;
2005-04-23 00:17:00 +04:00
dev_dbg ( & intf - > dev , " TEST 14: %d ep0out, %d..%d vary %d \n " ,
param - > iterations ,
realworld ? 1 : 0 , param - > length ,
param - > vary ) ;
2005-04-17 02:20:36 +04:00
retval = ctrl_out ( dev , param - > iterations ,
param - > length , param - > vary ) ;
break ;
/* iso write tests */
case 15 :
if ( dev - > out_iso_pipe = = 0 | | param - > sglen = = 0 )
break ;
dev_dbg ( & intf - > dev ,
" TEST 15: write %d iso, %d entries of %d bytes \n " ,
param - > iterations ,
param - > sglen , param - > length ) ;
// FIRMWARE: iso sink
retval = test_iso_queue ( dev , param ,
dev - > out_iso_pipe , dev - > iso_out ) ;
break ;
/* iso read tests */
case 16 :
if ( dev - > in_iso_pipe = = 0 | | param - > sglen = = 0 )
break ;
dev_dbg ( & intf - > dev ,
" TEST 16: read %d iso, %d entries of %d bytes \n " ,
param - > iterations ,
param - > sglen , param - > length ) ;
// FIRMWARE: iso source
retval = test_iso_queue ( dev , param ,
dev - > in_iso_pipe , dev - > iso_in ) ;
break ;
// FIXME unlink from queue (ring with N urbs)
// FIXME scatterlist cancel (needs helper thread)
}
do_gettimeofday ( & param - > duration ) ;
param - > duration . tv_sec - = start . tv_sec ;
param - > duration . tv_usec - = start . tv_usec ;
if ( param - > duration . tv_usec < 0 ) {
param - > duration . tv_usec + = 1000 * 1000 ;
param - > duration . tv_sec - = 1 ;
}
up ( & dev - > sem ) ;
return retval ;
}
/*-------------------------------------------------------------------------*/
static unsigned force_interrupt = 0 ;
module_param ( force_interrupt , uint , 0 ) ;
MODULE_PARM_DESC ( force_interrupt , " 0 = test default; else interrupt " ) ;
# ifdef GENERIC
static unsigned short vendor ;
module_param ( vendor , ushort , 0 ) ;
MODULE_PARM_DESC ( vendor , " vendor code (from usb-if) " ) ;
static unsigned short product ;
module_param ( product , ushort , 0 ) ;
MODULE_PARM_DESC ( product , " product code (from vendor) " ) ;
# endif
static int
usbtest_probe ( struct usb_interface * intf , const struct usb_device_id * id )
{
struct usb_device * udev ;
struct usbtest_dev * dev ;
struct usbtest_info * info ;
char * rtest , * wtest ;
char * irtest , * iwtest ;
udev = interface_to_usbdev ( intf ) ;
# ifdef GENERIC
/* specify devices by module parameters? */
if ( id - > match_flags = = 0 ) {
/* vendor match required, product match optional */
if ( ! vendor | | le16_to_cpu ( udev - > descriptor . idVendor ) ! = ( u16 ) vendor )
return - ENODEV ;
if ( product & & le16_to_cpu ( udev - > descriptor . idProduct ) ! = ( u16 ) product )
return - ENODEV ;
dbg ( " matched module params, vend=0x%04x prod=0x%04x " ,
le16_to_cpu ( udev - > descriptor . idVendor ) ,
le16_to_cpu ( udev - > descriptor . idProduct ) ) ;
}
# endif
2006-02-27 23:29:43 +03:00
dev = kzalloc ( sizeof ( * dev ) , SLAB_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev )
return - ENOMEM ;
info = ( struct usbtest_info * ) id - > driver_info ;
dev - > info = info ;
init_MUTEX ( & dev - > sem ) ;
dev - > intf = intf ;
/* cacheline-aligned scratch for i/o */
if ( ( dev - > buf = kmalloc ( TBUF_SIZE , SLAB_KERNEL ) ) = = NULL ) {
kfree ( dev ) ;
return - ENOMEM ;
}
/* NOTE this doesn't yet test the handful of difference that are
* visible with high speed interrupts : bigger maxpacket ( 1 K ) and
* " high bandwidth " modes ( up to 3 packets / uframe ) .
*/
rtest = wtest = " " ;
irtest = iwtest = " " ;
if ( force_interrupt | | udev - > speed = = USB_SPEED_LOW ) {
if ( info - > ep_in ) {
dev - > in_pipe = usb_rcvintpipe ( udev , info - > ep_in ) ;
rtest = " intr-in " ;
}
if ( info - > ep_out ) {
dev - > out_pipe = usb_sndintpipe ( udev , info - > ep_out ) ;
wtest = " intr-out " ;
}
} else {
if ( info - > autoconf ) {
int status ;
status = get_endpoints ( dev , intf ) ;
if ( status < 0 ) {
dbg ( " couldn't get endpoints, %d \n " , status ) ;
return status ;
}
/* may find bulk or ISO pipes */
} else {
if ( info - > ep_in )
dev - > in_pipe = usb_rcvbulkpipe ( udev ,
info - > ep_in ) ;
if ( info - > ep_out )
dev - > out_pipe = usb_sndbulkpipe ( udev ,
info - > ep_out ) ;
}
if ( dev - > in_pipe )
rtest = " bulk-in " ;
if ( dev - > out_pipe )
wtest = " bulk-out " ;
if ( dev - > in_iso_pipe )
irtest = " iso-in " ;
if ( dev - > out_iso_pipe )
iwtest = " iso-out " ;
}
usb_set_intfdata ( intf , dev ) ;
dev_info ( & intf - > dev , " %s \n " , info - > name ) ;
dev_info ( & intf - > dev , " %s speed {control%s%s%s%s%s} tests%s \n " ,
( { char * tmp ;
switch ( udev - > speed ) {
case USB_SPEED_LOW : tmp = " low " ; break ;
case USB_SPEED_FULL : tmp = " full " ; break ;
case USB_SPEED_HIGH : tmp = " high " ; break ;
default : tmp = " unknown " ; break ;
} ; tmp ; } ) ,
info - > ctrl_out ? " in/out " : " " ,
rtest , wtest ,
irtest , iwtest ,
info - > alt > = 0 ? " (+alt) " : " " ) ;
return 0 ;
}
2005-04-23 00:17:00 +04:00
static int usbtest_suspend ( struct usb_interface * intf , pm_message_t message )
{
return 0 ;
}
static int usbtest_resume ( struct usb_interface * intf )
{
return 0 ;
}
2005-04-17 02:20:36 +04:00
static void usbtest_disconnect ( struct usb_interface * intf )
{
struct usbtest_dev * dev = usb_get_intfdata ( intf ) ;
down ( & dev - > sem ) ;
usb_set_intfdata ( intf , NULL ) ;
dev_dbg ( & intf - > dev , " disconnect \n " ) ;
kfree ( dev ) ;
}
/* Basic testing only needs a device that can source or sink bulk traffic.
* Any device can test control transfers ( default with GENERIC binding ) .
*
* Several entries work with the default EP0 implementation that ' s built
* into EZ - USB chips . There ' s a default vendor ID which can be overridden
* by ( very ) small config EEPROMS , but otherwise all these devices act
* identically until firmware is loaded : only EP0 works . It turns out
* to be easy to make other endpoints work , without modifying that EP0
* behavior . For now , we expect that kind of firmware .
*/
/* an21xx or fx versions of ez-usb */
static struct usbtest_info ez1_info = {
. name = " EZ-USB device " ,
. ep_in = 2 ,
. ep_out = 2 ,
. alt = 1 ,
} ;
/* fx2 version of ez-usb */
static struct usbtest_info ez2_info = {
. name = " FX2 device " ,
. ep_in = 6 ,
. ep_out = 2 ,
. alt = 1 ,
} ;
/* ezusb family device with dedicated usb test firmware,
*/
static struct usbtest_info fw_info = {
. name = " usb test device " ,
. ep_in = 2 ,
. ep_out = 2 ,
. alt = 1 ,
. autoconf = 1 , // iso and ctrl_out need autoconf
. ctrl_out = 1 ,
. iso = 1 , // iso_ep's are #8 in/out
} ;
/* peripheral running Linux and 'zero.c' test firmware, or
* its user - mode cousin . different versions of this use
* different hardware with the same vendor / product codes .
* host side MUST rely on the endpoint descriptors .
*/
static struct usbtest_info gz_info = {
. name = " Linux gadget zero " ,
. autoconf = 1 ,
. ctrl_out = 1 ,
. alt = 0 ,
} ;
static struct usbtest_info um_info = {
. name = " Linux user mode test driver " ,
. autoconf = 1 ,
. alt = - 1 ,
} ;
static struct usbtest_info um2_info = {
. name = " Linux user mode ISO test driver " ,
. autoconf = 1 ,
. iso = 1 ,
. alt = - 1 ,
} ;
# ifdef IBOT2
/* this is a nice source of high speed bulk data;
* uses an FX2 , with firmware provided in the device
*/
static struct usbtest_info ibot2_info = {
. name = " iBOT2 webcam " ,
. ep_in = 2 ,
. alt = - 1 ,
} ;
# endif
# ifdef GENERIC
/* we can use any device to test control traffic */
static struct usbtest_info generic_info = {
. name = " Generic USB device " ,
. alt = - 1 ,
} ;
# endif
// FIXME remove this
static struct usbtest_info hact_info = {
. name = " FX2/hact " ,
//.ep_in = 6,
. ep_out = 2 ,
. alt = - 1 ,
} ;
static struct usb_device_id id_table [ ] = {
{ USB_DEVICE ( 0x0547 , 0x1002 ) ,
. driver_info = ( unsigned long ) & hact_info ,
} ,
/*-------------------------------------------------------------*/
/* EZ-USB devices which download firmware to replace (or in our
* case augment ) the default device implementation .
*/
/* generic EZ-USB FX controller */
{ USB_DEVICE ( 0x0547 , 0x2235 ) ,
. driver_info = ( unsigned long ) & ez1_info ,
} ,
/* CY3671 development board with EZ-USB FX */
{ USB_DEVICE ( 0x0547 , 0x0080 ) ,
. driver_info = ( unsigned long ) & ez1_info ,
} ,
/* generic EZ-USB FX2 controller (or development board) */
{ USB_DEVICE ( 0x04b4 , 0x8613 ) ,
. driver_info = ( unsigned long ) & ez2_info ,
} ,
/* re-enumerated usb test device firmware */
{ USB_DEVICE ( 0xfff0 , 0xfff0 ) ,
. driver_info = ( unsigned long ) & fw_info ,
} ,
/* "Gadget Zero" firmware runs under Linux */
{ USB_DEVICE ( 0x0525 , 0xa4a0 ) ,
. driver_info = ( unsigned long ) & gz_info ,
} ,
/* so does a user-mode variant */
{ USB_DEVICE ( 0x0525 , 0xa4a4 ) ,
. driver_info = ( unsigned long ) & um_info ,
} ,
/* ... and a user-mode variant that talks iso */
{ USB_DEVICE ( 0x0525 , 0xa4a3 ) ,
. driver_info = ( unsigned long ) & um2_info ,
} ,
# ifdef KEYSPAN_19Qi
/* Keyspan 19qi uses an21xx (original EZ-USB) */
// this does not coexist with the real Keyspan 19qi driver!
{ USB_DEVICE ( 0x06cd , 0x010b ) ,
. driver_info = ( unsigned long ) & ez1_info ,
} ,
# endif
/*-------------------------------------------------------------*/
# ifdef IBOT2
/* iBOT2 makes a nice source of high speed bulk-in data */
// this does not coexist with a real iBOT2 driver!
{ USB_DEVICE ( 0x0b62 , 0x0059 ) ,
. driver_info = ( unsigned long ) & ibot2_info ,
} ,
# endif
/*-------------------------------------------------------------*/
# ifdef GENERIC
/* module params can specify devices to use for control tests */
{ . driver_info = ( unsigned long ) & generic_info , } ,
# endif
/*-------------------------------------------------------------*/
{ }
} ;
MODULE_DEVICE_TABLE ( usb , id_table ) ;
static struct usb_driver usbtest_driver = {
. name = " usbtest " ,
. id_table = id_table ,
. probe = usbtest_probe ,
. ioctl = usbtest_ioctl ,
. disconnect = usbtest_disconnect ,
2005-04-23 00:17:00 +04:00
. suspend = usbtest_suspend ,
. resume = usbtest_resume ,
2005-04-17 02:20:36 +04:00
} ;
/*-------------------------------------------------------------------------*/
static int __init usbtest_init ( void )
{
# ifdef GENERIC
if ( vendor )
dbg ( " params: vend=0x%04x prod=0x%04x " , vendor , product ) ;
# endif
return usb_register ( & usbtest_driver ) ;
}
module_init ( usbtest_init ) ;
static void __exit usbtest_exit ( void )
{
usb_deregister ( & usbtest_driver ) ;
}
module_exit ( usbtest_exit ) ;
MODULE_DESCRIPTION ( " USB Core/HCD Testing Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;