Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
0e65e377
Commit
0e65e377
authored
Oct 02, 2002
by
David Brownell
Committed by
Greg Kroah-Hartman
Oct 02, 2002
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[PATCH] USB: framework for testing usbcore
USB test driver
parent
3737f5e6
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
579 additions
and
0 deletions
+579
-0
drivers/usb/Makefile
drivers/usb/Makefile
+1
-0
drivers/usb/misc/Config.help
drivers/usb/misc/Config.help
+6
-0
drivers/usb/misc/Config.in
drivers/usb/misc/Config.in
+1
-0
drivers/usb/misc/Makefile
drivers/usb/misc/Makefile
+1
-0
drivers/usb/misc/usbtest.c
drivers/usb/misc/usbtest.c
+570
-0
No files found.
drivers/usb/Makefile
View file @
0e65e377
...
...
@@ -60,6 +60,7 @@ obj-$(CONFIG_USB_EMI26) += misc/
obj-$(CONFIG_USB_LCD)
+=
misc/
obj-$(CONFIG_USB_RIO500)
+=
misc/
obj-$(CONFIG_USB_SPEEDTOUCH)
+=
misc/
obj-$(CONFIG_USB_TEST)
+=
misc/
obj-$(CONFIG_USB_TIGL)
+=
misc/
obj-$(CONFIG_USB_USS720)
+=
misc/
...
...
drivers/usb/misc/Config.help
View file @
0e65e377
...
...
@@ -112,6 +112,12 @@ CONFIG_USB_SPEEDTCH
For more information, see Johan Verrept's webpages at
<http://linux-usb.sourceforge.net/SpeedTouch/>.
CONFIG_USB_TEST
This driver is for testing host controller software. It is used
with specialized device firmware for regression and stress testing,
to help prevent problems from cropping up with 'real" drivers.
CONFIG_USB_LCD
Say Y here if you want to connect an USBLCD to your computer's
USB port. The USBLCD is a small USB interface board for
...
...
drivers/usb/misc/Config.in
View file @
0e65e377
...
...
@@ -9,3 +9,4 @@ dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CO
dep_tristate ' Tieman Voyager USB Braille display support (EXPERIMENTAL)' CONFIG_USB_BRLVGER $CONFIG_USB $CONFIG_EXPERIMENTAL
dep_tristate ' USB LCD driver support' CONFIG_USB_LCD $CONFIG_USB
dep_tristate ' Alcatel Speedtouch ADSL USB Modem' CONFIG_USB_SPEEDTOUCH $CONFIG_USB $CONFIG_ATM
dep_tristate ' USB testing driver (DEVELOPMENT)' CONFIG_USB_TEST $CONFIG_USB_DEVICEFS $CONFIG_EXPERIMENTAL
drivers/usb/misc/Makefile
View file @
0e65e377
...
...
@@ -11,6 +11,7 @@ obj-$(CONFIG_USB_EMI26) += emi26.o
obj-$(CONFIG_USB_LCD)
+=
usblcd.o
obj-$(CONFIG_USB_RIO500)
+=
rio500.o
obj-$(CONFIG_USB_SPEEDTOUCH)
+=
speedtouch.o atmsar.o
obj-$(CONFIG_USB_TEST)
+=
usbtest.o
obj-$(CONFIG_USB_TIGL)
+=
tiglusb.o
obj-$(CONFIG_USB_USS720)
+=
uss720.o
...
...
drivers/usb/misc/usbtest.c
0 → 100644
View file @
0e65e377
#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/time.h>
#include <asm/scatterlist.h>
#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
# define DEBUG
#endif
#include <linux/usb.h>
/*-------------------------------------------------------------------------*/
// FIXME make these public somewhere; usbdevfs.h?
//
struct
usbtest_param
{
// inputs
unsigned
test_num
;
/* 0..(TEST_CASES-1) */
int
iterations
;
int
length
;
int
vary
;
int
sglen
;
// outputs
struct
timeval
duration
;
};
#define USBTEST_REQUEST _IOWR('U', 100, struct usbtest_param)
/*-------------------------------------------------------------------------*/
/* this is accessed only through usbfs ioctl calls.
* one ioctl to issue a test ... no locking needed!!!
* tests create other threads if they need them.
* urbs and buffers are allocated dynamically,
* and data generated deterministically.
*
* there's a minor complication on disconnect(), since
* usbfs.disconnect() waits till our ioctl completes.
*/
struct
usbtest_dev
{
struct
usb_interface
*
intf
;
struct
testdev_info
*
info
;
char
id
[
32
];
int
in_pipe
;
int
out_pipe
;
};
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 */
/*-------------------------------------------------------------------------*/
/* 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
)
{
complete
((
struct
completion
*
)
urb
->
context
);
}
static
struct
urb
*
simple_alloc_urb
(
struct
usb_device
*
udev
,
int
pipe
,
long
bytes
)
{
struct
urb
*
urb
;
if
(
bytes
<
0
)
return
0
;
urb
=
usb_alloc_urb
(
0
,
SLAB_KERNEL
);
if
(
!
urb
)
return
urb
;
usb_fill_bulk_urb
(
urb
,
udev
,
pipe
,
0
,
bytes
,
simple_callback
,
0
);
urb
->
interval
=
(
udev
->
speed
==
USB_SPEED_HIGH
)
?
(
INTERRUPT_RATE
<<
3
)
:
INTERRUPT_RATE
,
urb
->
transfer_flags
=
URB_NO_DMA_MAP
;
urb
->
transfer_buffer
=
usb_buffer_alloc
(
udev
,
bytes
,
SLAB_KERNEL
,
&
urb
->
transfer_dma
);
if
(
!
urb
->
transfer_buffer
)
{
usb_free_urb
(
urb
);
urb
=
0
;
}
else
memset
(
urb
->
transfer_buffer
,
0
,
bytes
);
return
urb
;
}
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
)
{
struct
usb_device
*
udev
=
urb
->
dev
;
int
max
=
urb
->
transfer_buffer_length
;
struct
completion
completion
;
int
retval
=
0
;
urb
->
context
=
&
completion
;
while
(
iterations
--
>
0
&&
retval
==
0
)
{
init_completion
(
&
completion
);
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
(
vary
)
{
int
len
=
urb
->
transfer_buffer_length
;
len
+=
max
;
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
;
// FIXME for unlink or fault handling tests, don't report
// failure if retval is as we expected ...
if
(
retval
)
dbg
(
"simple_io failed, iterations left %d, status %d"
,
iterations
,
retval
);
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
0
;
memset
(
sg
,
0
,
nents
*
sizeof
*
sg
);
for
(
i
=
0
;
i
<
nents
;
i
++
)
{
char
*
buf
;
buf
=
kmalloc
(
size
,
SLAB_KERNEL
);
if
(
!
buf
)
{
free_sglist
(
sg
,
i
);
return
0
;
}
memset
(
buf
,
0
,
size
);
/* kmalloc pages are always physically contiguous! */
sg
[
i
].
page
=
virt_to_page
(
buf
);
sg
[
i
].
offset
=
((
unsigned
)
buf
)
&
~
PAGE_MASK
;
sg
[
i
].
length
=
size
;
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
;
/* 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
;
}
/*-------------------------------------------------------------------------*/
/* 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.
*/
static
int
usbtest_ioctl
(
struct
usb_interface
*
intf
,
unsigned
int
code
,
void
*
buf
)
{
struct
usbtest_dev
*
dev
=
dev_get_drvdata
(
&
intf
->
dev
);
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
;
// 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
;
/*
* 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
* requests, and so on.
*/
do_gettimeofday
(
&
start
);
switch
(
param
->
test_num
)
{
case
0
:
dbg
(
"%s TEST 0: NOP"
,
dev
->
id
);
retval
=
0
;
break
;
/* Simple non-queued bulk I/O tests */
case
1
:
if
(
dev
->
out_pipe
==
0
)
break
;
dbg
(
"%s TEST 1: write %d bytes %u times"
,
dev
->
id
,
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
);
simple_free_urb
(
urb
);
break
;
case
2
:
if
(
dev
->
in_pipe
==
0
)
break
;
dbg
(
"%s TEST 2: read %d bytes %u times"
,
dev
->
id
,
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
);
simple_free_urb
(
urb
);
break
;
case
3
:
if
(
dev
->
out_pipe
==
0
||
param
->
vary
==
0
)
break
;
dbg
(
"%s TEST 3: write/%d 0..%d bytes %u times"
,
dev
->
id
,
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
);
simple_free_urb
(
urb
);
break
;
case
4
:
if
(
dev
->
in_pipe
==
0
||
param
->
vary
==
0
)
break
;
dbg
(
"%s TEST 3: read/%d 0..%d bytes %u times"
,
dev
->
id
,
param
->
vary
,
param
->
length
,
param
->
iterations
);
urb
=
simple_alloc_urb
(
udev
,
dev
->
out_pipe
,
param
->
length
);
if
(
!
urb
)
{
retval
=
-
ENOMEM
;
break
;
}
// FIRMWARE: bulk source (maybe generates short writes)
retval
=
simple_io
(
urb
,
param
->
iterations
,
param
->
vary
);
simple_free_urb
(
urb
);
break
;
/* Queued bulk I/O tests */
case
5
:
if
(
dev
->
out_pipe
==
0
||
param
->
sglen
==
0
)
break
;
dbg
(
"%s TEST 5: write %d sglists, %d entries of %d bytes"
,
dev
->
id
,
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
;
dbg
(
"%s TEST 6: read %d sglists, %d entries of %d bytes"
,
dev
->
id
,
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
;
dbg
(
"%s TEST 7: write/%d %d sglists, %d entries 0..%d bytes"
,
dev
->
id
,
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
;
dbg
(
"%s TEST 8: read/%d %d sglists, %d entries 0..%d bytes"
,
dev
->
id
,
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
;
/* test cases for the unlink/cancel codepaths need a thread to
* usb_unlink_urb() or usg_sg_cancel(), and a way to check if
* the urb/sg_request was properly canceled.
*
* for the unlink-queued cases, the usb_sg_*() code uses/tests
* the "streamed" cleanup mode, not the "packet" one
*/
}
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
;
}
return
retval
;
}
/*-------------------------------------------------------------------------*/
/* most programmable USB devices can be given firmware that will support the
* test cases above. one basic question is which endpoints to use for
* testing; endpoint numbers are not always firmware-selectable.
*
* for now, the driver_info in the device_id table entry just encodes the
* endpoint info for a pair of bulk-capable endpoints, which we can use
* for some interrupt transfer tests too. later this could get fancier.
*/
#define EP_PAIR(in,out) (((in)<<4)|(out))
static
int
force_interrupt
=
0
;
MODULE_PARM
(
force_interrupt
,
"i"
);
MODULE_PARM_DESC
(
force_interrupt
,
"0 = test bulk (default), else interrupt"
);
static
int
usbtest_probe
(
struct
usb_interface
*
intf
,
const
struct
usb_device_id
*
id
)
{
struct
usb_device
*
udev
;
struct
usbtest_dev
*
dev
;
unsigned
long
driver_info
=
id
->
driver_info
;
udev
=
interface_to_usbdev
(
intf
);
dev
=
kmalloc
(
sizeof
*
dev
,
SLAB_KERNEL
);
if
(
!
dev
)
return
-
ENOMEM
;
memset
(
dev
,
0
,
sizeof
*
dev
);
snprintf
(
dev
->
id
,
sizeof
dev
->
id
,
"%s-%s"
,
udev
->
bus
->
bus_name
,
udev
->
devpath
);
dev
->
intf
=
intf
;
/* NOTE this doesn't yet test the handful of difference that are
* visible with high speed devices: bigger maxpacket (1K) and
* "high bandwidth" modes (up to 3 packets/uframe).
*/
if
(
force_interrupt
||
udev
->
speed
==
USB_SPEED_LOW
)
{
if
(
driver_info
&
0xf0
)
dev
->
in_pipe
=
usb_rcvintpipe
(
udev
,
(
driver_info
>>
4
)
&
0x0f
);
if
(
driver_info
&
0x0f
)
dev
->
out_pipe
=
usb_sndintpipe
(
udev
,
driver_info
&
0x0f
);
#if 1
// FIXME disabling this until we finally get rid of
// interrupt "automagic" resubmission
dbg
(
"%s: no interrupt transfers for now"
,
dev
->
id
);
kfree
(
dev
);
return
-
ENODEV
;
#endif
}
else
{
if
(
driver_info
&
0xf0
)
dev
->
in_pipe
=
usb_rcvbulkpipe
(
udev
,
(
driver_info
>>
4
)
&
0x0f
);
if
(
driver_info
&
0x0f
)
dev
->
out_pipe
=
usb_sndbulkpipe
(
udev
,
driver_info
&
0x0f
);
}
dev_set_drvdata
(
&
intf
->
dev
,
dev
);
info
(
"bound to %s ...%s%s"
,
dev
->
id
,
dev
->
out_pipe
?
" writes"
:
""
,
dev
->
in_pipe
?
" reads"
:
""
);
return
0
;
}
static
void
usbtest_disconnect
(
struct
usb_interface
*
intf
)
{
struct
usbtest_dev
*
dev
=
dev_get_drvdata
(
&
intf
->
dev
);
dev_set_drvdata
(
&
intf
->
dev
,
0
);
info
(
"unbound %s"
,
dev
->
id
);
kfree
(
intf
->
private_data
);
}
/* Basic testing only needs a device that can source or sink bulk traffic.
*/
static
struct
usb_device_id
id_table
[]
=
{
/* EZ-USB FX2 "bulksrc" or "bulkloop" firmware from Cypress
* reads disabled on this one, my version has some problem there
*/
{
USB_DEVICE
(
0x0547
,
0x1002
),
.
driver_info
=
EP_PAIR
(
0
,
2
),
},
#if 1
// this does not coexist with a real iBOT2 driver!
// it makes a nice source of high speed bulk-in data
{
USB_DEVICE
(
0x0b62
,
0x0059
),
.
driver_info
=
EP_PAIR
(
2
,
0
),
},
#endif
/* can that old "usbstress-0.3" firmware be used with this? */
{
}
};
MODULE_DEVICE_TABLE
(
usb
,
id_table
);
static
struct
usb_driver
usbtest_driver
=
{
.
owner
=
THIS_MODULE
,
.
name
=
"usbtest"
,
.
id_table
=
id_table
,
.
probe
=
usbtest_probe
,
.
ioctl
=
usbtest_ioctl
,
.
disconnect
=
usbtest_disconnect
,
};
/*-------------------------------------------------------------------------*/
static
int
__init
usbtest_init
(
void
)
{
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 HCD Testing Driver"
);
MODULE_LICENSE
(
"GPL"
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment