2005-04-16 15:20:36 -07:00
/*
* dummy_hcd . c - - Dummy / Loopback USB host and device emulator driver .
*
* Maintainer : Alan Stern < stern @ rowland . harvard . edu >
*
* Copyright ( C ) 2003 David Brownell
* Copyright ( C ) 2003 - 2005 Alan Stern
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
/*
* This exposes a device side " USB gadget " API , driven by requests to a
* Linux - USB host controller driver . USB traffic is simulated ; there ' s
* no need for USB hardware . Use this with two other drivers :
*
* - Gadget driver , responding to requests ( slave ) ;
* - Host - side device driver , as already familiar in Linux .
*
* Having this all in one kernel can help some stages of development ,
* bypassing some hardware ( and driver ) issues . UML could help too .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/ioport.h>
# include <linux/slab.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/timer.h>
# include <linux/list.h>
# include <linux/interrupt.h>
2005-10-29 19:07:23 +01:00
# include <linux/platform_device.h>
2005-04-16 15:20:36 -07:00
# include <linux/usb.h>
2007-10-04 18:05:17 -07:00
# include <linux/usb/gadget.h>
2010-04-24 23:21:52 +02:00
# include <linux/usb/hcd.h>
2005-04-16 15:20:36 -07:00
# include <asm/byteorder.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/system.h>
# include <asm/unaligned.h>
# define DRIVER_DESC "USB Host+Gadget Emulator"
2005-05-10 15:34:16 -04:00
# define DRIVER_VERSION "02 May 2005"
2005-04-16 15:20:36 -07:00
2007-12-06 11:10:39 -05:00
# define POWER_BUDGET 500 /* in mA; use 8 for low-power port testing */
2005-04-16 15:20:36 -07:00
static const char driver_name [ ] = " dummy_hcd " ;
static const char driver_desc [ ] = " USB Host+Gadget Emulator " ;
static const char gadget_name [ ] = " dummy_udc " ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_AUTHOR ( " David Brownell " ) ;
MODULE_LICENSE ( " GPL " ) ;
2011-06-29 16:41:52 +03:00
struct dummy_hcd_module_parameters {
bool is_super_speed ;
2011-06-29 16:41:53 +03:00
bool is_high_speed ;
2011-06-29 16:41:52 +03:00
} ;
static struct dummy_hcd_module_parameters mod_data = {
2011-06-29 16:41:53 +03:00
. is_super_speed = false ,
. is_high_speed = true ,
2011-06-29 16:41:52 +03:00
} ;
module_param_named ( is_super_speed , mod_data . is_super_speed , bool , S_IRUGO ) ;
MODULE_PARM_DESC ( is_super_speed , " true to simulate SuperSpeed connection " ) ;
2011-06-29 16:41:53 +03:00
module_param_named ( is_high_speed , mod_data . is_high_speed , bool , S_IRUGO ) ;
MODULE_PARM_DESC ( is_high_speed , " true to simulate HighSpeed connection " ) ;
2005-04-16 15:20:36 -07:00
/*-------------------------------------------------------------------------*/
/* gadget side driver data structres */
struct dummy_ep {
struct list_head queue ;
unsigned long last_io ; /* jiffies timestamp */
struct usb_gadget * gadget ;
const struct usb_endpoint_descriptor * desc ;
struct usb_ep ep ;
unsigned halted : 1 ;
2008-08-14 15:48:30 -04:00
unsigned wedged : 1 ;
2005-04-16 15:20:36 -07:00
unsigned already_seen : 1 ;
unsigned setup_stage : 1 ;
} ;
struct dummy_request {
struct list_head queue ; /* ep's requests */
struct usb_request req ;
} ;
static inline struct dummy_ep * usb_ep_to_dummy_ep ( struct usb_ep * _ep )
{
return container_of ( _ep , struct dummy_ep , ep ) ;
}
static inline struct dummy_request * usb_request_to_dummy_request
( struct usb_request * _req )
{
return container_of ( _req , struct dummy_request , req ) ;
}
/*-------------------------------------------------------------------------*/
/*
* Every device has ep0 for control requests , plus up to 30 more endpoints ,
* in one of two types :
*
* - Configurable : direction ( in / out ) , type ( bulk , iso , etc ) , and endpoint
* number can be changed . Names like " ep-a " are used for this type .
*
* - Fixed Function : in other cases . some characteristics may be mutable ;
* that ' d be hardware - specific . Names like " ep12out-bulk " are used .
*
* Gadget drivers are responsible for not setting up conflicting endpoint
* configurations , illegal or unsupported packet lengths , and so on .
*/
static const char ep0name [ ] = " ep0 " ;
static const char * const ep_name [ ] = {
ep0name , /* everyone has ep0 */
/* act like a net2280: high speed, six configurable endpoints */
" ep-a " , " ep-b " , " ep-c " , " ep-d " , " ep-e " , " ep-f " ,
/* or like pxa250: fifteen fixed function endpoints */
" ep1in-bulk " , " ep2out-bulk " , " ep3in-iso " , " ep4out-iso " , " ep5in-int " ,
" ep6in-bulk " , " ep7out-bulk " , " ep8in-iso " , " ep9out-iso " , " ep10in-int " ,
" ep11in-bulk " , " ep12out-bulk " , " ep13in-iso " , " ep14out-iso " ,
" ep15in-int " ,
/* or like sa1100: two fixed function endpoints */
" ep1out-bulk " , " ep2in-bulk " ,
} ;
2005-12-11 16:20:08 +01:00
# define DUMMY_ENDPOINTS ARRAY_SIZE(ep_name)
2005-04-16 15:20:36 -07:00
2005-05-03 16:15:43 -04:00
/*-------------------------------------------------------------------------*/
2005-04-16 15:20:36 -07:00
# define FIFO_SIZE 64
struct urbp {
struct urb * urb ;
struct list_head urbp_list ;
} ;
2005-05-10 15:34:16 -04:00
enum dummy_rh_state {
DUMMY_RH_RESET ,
DUMMY_RH_SUSPENDED ,
DUMMY_RH_RUNNING
} ;
2011-06-29 16:41:51 +03:00
struct dummy_hcd {
struct dummy * dum ;
enum dummy_rh_state rh_state ;
struct timer_list timer ;
u32 port_status ;
u32 old_status ;
unsigned long re_timeout ;
struct usb_device * udev ;
struct list_head urbp_list ;
unsigned active : 1 ;
unsigned old_active : 1 ;
unsigned resuming : 1 ;
} ;
2005-04-16 15:20:36 -07:00
struct dummy {
spinlock_t lock ;
/*
* SLAVE / GADGET side support
*/
struct dummy_ep ep [ DUMMY_ENDPOINTS ] ;
int address ;
struct usb_gadget gadget ;
struct usb_gadget_driver * driver ;
struct dummy_request fifo_req ;
u8 fifo_buf [ FIFO_SIZE ] ;
u16 devstatus ;
2005-05-10 15:34:16 -04:00
unsigned udc_suspended : 1 ;
2005-05-03 16:24:04 -04:00
unsigned pullup : 1 ;
2005-04-16 15:20:36 -07:00
/*
* MASTER / HOST side support
*/
2011-06-29 16:41:51 +03:00
struct dummy_hcd * hs_hcd ;
2011-06-29 16:41:52 +03:00
struct dummy_hcd * ss_hcd ;
2005-04-16 15:20:36 -07:00
} ;
2011-06-29 16:41:51 +03:00
static inline struct dummy_hcd * hcd_to_dummy_hcd ( struct usb_hcd * hcd )
2005-04-16 15:20:36 -07:00
{
2011-06-29 16:41:51 +03:00
return ( struct dummy_hcd * ) ( hcd - > hcd_priv ) ;
2005-04-16 15:20:36 -07:00
}
2011-06-29 16:41:51 +03:00
static inline struct usb_hcd * dummy_hcd_to_hcd ( struct dummy_hcd * dum )
2005-04-16 15:20:36 -07:00
{
return container_of ( ( void * ) dum , struct usb_hcd , hcd_priv ) ;
}
2011-06-29 16:41:51 +03:00
static inline struct device * dummy_dev ( struct dummy_hcd * dum )
2005-04-16 15:20:36 -07:00
{
2011-06-29 16:41:51 +03:00
return dummy_hcd_to_hcd ( dum ) - > self . controller ;
2005-04-16 15:20:36 -07:00
}
2005-05-03 16:15:43 -04:00
static inline struct device * udc_dev ( struct dummy * dum )
{
return dum - > gadget . dev . parent ;
}
2005-04-16 15:20:36 -07:00
static inline struct dummy * ep_to_dummy ( struct dummy_ep * ep )
{
return container_of ( ep - > gadget , struct dummy , gadget ) ;
}
2011-06-29 16:41:51 +03:00
static inline struct dummy_hcd * gadget_to_dummy_hcd ( struct usb_gadget * gadget )
2005-04-16 15:20:36 -07:00
{
2011-06-29 16:41:51 +03:00
struct dummy * dum = container_of ( gadget , struct dummy , gadget ) ;
2011-06-29 16:41:52 +03:00
if ( dum - > gadget . speed = = USB_SPEED_SUPER )
return dum - > ss_hcd ;
else
return dum - > hs_hcd ;
2005-04-16 15:20:36 -07:00
}
static inline struct dummy * gadget_dev_to_dummy ( struct device * dev )
{
return container_of ( dev , struct dummy , gadget . dev ) ;
}
2011-06-29 16:41:51 +03:00
static struct dummy the_controller ;
2005-04-16 15:20:36 -07:00
/*-------------------------------------------------------------------------*/
2005-05-03 16:24:04 -04:00
/* SLAVE/GADGET SIDE UTILITY ROUTINES */
/* called with spinlock held */
static void nuke ( struct dummy * dum , struct dummy_ep * ep )
{
while ( ! list_empty ( & ep - > queue ) ) {
struct dummy_request * req ;
req = list_entry ( ep - > queue . next , struct dummy_request , queue ) ;
list_del_init ( & req - > queue ) ;
req - > req . status = - ESHUTDOWN ;
spin_unlock ( & dum - > lock ) ;
req - > req . complete ( & ep - > ep , & req - > req ) ;
spin_lock ( & dum - > lock ) ;
}
}
/* caller must hold lock */
static void
stop_activity ( struct dummy * dum )
{
struct dummy_ep * ep ;
/* prevent any more requests */
dum - > address = 0 ;
/* The timer is left running so that outstanding URBs can fail */
/* nuke any pending requests first, so driver i/o is quiesced */
list_for_each_entry ( ep , & dum - > gadget . ep_list , ep . ep_list )
nuke ( dum , ep ) ;
/* driver now does any non-usb quiescing necessary */
}
2011-06-29 16:41:52 +03:00
/**
* set_link_state_by_speed ( ) - Sets the current state of the link according to
* the hcd speed
* @ dum_hcd : pointer to the dummy_hcd structure to update the link state for
*
* This function updates the port_status according to the link state and the
* speed of the hcd .
*/
static void set_link_state_by_speed ( struct dummy_hcd * dum_hcd )
{
struct dummy * dum = dum_hcd - > dum ;
if ( dummy_hcd_to_hcd ( dum_hcd ) - > speed = = HCD_USB3 ) {
if ( ( dum_hcd - > port_status & USB_SS_PORT_STAT_POWER ) = = 0 ) {
dum_hcd - > port_status = 0 ;
} else if ( ! dum - > pullup | | dum - > udc_suspended ) {
/* UDC suspend must cause a disconnect */
dum_hcd - > port_status & = ~ ( USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_ENABLE ) ;
if ( ( dum_hcd - > old_status &
USB_PORT_STAT_CONNECTION ) ! = 0 )
dum_hcd - > port_status | =
( USB_PORT_STAT_C_CONNECTION < < 16 ) ;
} else {
/* device is connected and not suspended */
dum_hcd - > port_status | = ( USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_SPEED_5GBPS ) ;
if ( ( dum_hcd - > old_status &
USB_PORT_STAT_CONNECTION ) = = 0 )
dum_hcd - > port_status | =
( USB_PORT_STAT_C_CONNECTION < < 16 ) ;
if ( ( dum_hcd - > port_status &
USB_PORT_STAT_ENABLE ) = = 1 & &
( dum_hcd - > port_status &
USB_SS_PORT_LS_U0 ) = = 1 & &
dum_hcd - > rh_state ! = DUMMY_RH_SUSPENDED )
dum_hcd - > active = 1 ;
}
} else {
if ( ( dum_hcd - > port_status & USB_PORT_STAT_POWER ) = = 0 ) {
dum_hcd - > port_status = 0 ;
} else if ( ! dum - > pullup | | dum - > udc_suspended ) {
/* UDC suspend must cause a disconnect */
dum_hcd - > port_status & = ~ ( USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_ENABLE |
USB_PORT_STAT_LOW_SPEED |
USB_PORT_STAT_HIGH_SPEED |
USB_PORT_STAT_SUSPEND ) ;
if ( ( dum_hcd - > old_status &
USB_PORT_STAT_CONNECTION ) ! = 0 )
dum_hcd - > port_status | =
( USB_PORT_STAT_C_CONNECTION < < 16 ) ;
} else {
dum_hcd - > port_status | = USB_PORT_STAT_CONNECTION ;
if ( ( dum_hcd - > old_status &
USB_PORT_STAT_CONNECTION ) = = 0 )
dum_hcd - > port_status | =
( USB_PORT_STAT_C_CONNECTION < < 16 ) ;
if ( ( dum_hcd - > port_status & USB_PORT_STAT_ENABLE ) = = 0 )
dum_hcd - > port_status & = ~ USB_PORT_STAT_SUSPEND ;
else if ( ( dum_hcd - > port_status &
USB_PORT_STAT_SUSPEND ) = = 0 & &
dum_hcd - > rh_state ! = DUMMY_RH_SUSPENDED )
dum_hcd - > active = 1 ;
}
}
}
2005-05-03 16:24:04 -04:00
/* caller must hold lock */
2011-06-29 16:41:51 +03:00
static void set_link_state ( struct dummy_hcd * dum_hcd )
2005-05-03 16:24:04 -04:00
{
2011-06-29 16:41:51 +03:00
struct dummy * dum = dum_hcd - > dum ;
dum_hcd - > active = 0 ;
2011-06-29 16:41:52 +03:00
if ( dum - > pullup )
if ( ( dummy_hcd_to_hcd ( dum_hcd ) - > speed = = HCD_USB3 & &
dum - > gadget . speed ! = USB_SPEED_SUPER ) | |
( dummy_hcd_to_hcd ( dum_hcd ) - > speed ! = HCD_USB3 & &
dum - > gadget . speed = = USB_SPEED_SUPER ) )
return ;
set_link_state_by_speed ( dum_hcd ) ;
2005-05-03 16:24:04 -04:00
2011-06-29 16:41:51 +03:00
if ( ( dum_hcd - > port_status & USB_PORT_STAT_ENABLE ) = = 0 | |
dum_hcd - > active )
dum_hcd - > resuming = 0 ;
2005-05-03 16:24:04 -04:00
2011-06-29 16:41:52 +03:00
/* if !connected or reset */
2011-06-29 16:41:51 +03:00
if ( ( dum_hcd - > port_status & USB_PORT_STAT_CONNECTION ) = = 0 | |
( dum_hcd - > port_status & USB_PORT_STAT_RESET ) ! = 0 ) {
2011-06-29 16:41:52 +03:00
/*
* We ' re connected and not reset ( reset occurred now ) ,
* and driver attached - disconnect !
*/
2011-06-29 16:41:51 +03:00
if ( ( dum_hcd - > old_status & USB_PORT_STAT_CONNECTION ) ! = 0 & &
2011-06-29 16:41:52 +03:00
( dum_hcd - > old_status & USB_PORT_STAT_RESET ) = = 0 & &
dum - > driver ) {
stop_activity ( dum ) ;
spin_unlock ( & dum - > lock ) ;
dum - > driver - > disconnect ( & dum - > gadget ) ;
spin_lock ( & dum - > lock ) ;
2005-05-03 16:24:04 -04:00
}
2011-06-29 16:41:51 +03:00
} else if ( dum_hcd - > active ! = dum_hcd - > old_active ) {
if ( dum_hcd - > old_active & & dum - > driver - > suspend ) {
2011-06-29 16:41:52 +03:00
spin_unlock ( & dum - > lock ) ;
dum - > driver - > suspend ( & dum - > gadget ) ;
spin_lock ( & dum - > lock ) ;
} else if ( ! dum_hcd - > old_active & & dum - > driver - > resume ) {
spin_unlock ( & dum - > lock ) ;
dum - > driver - > resume ( & dum - > gadget ) ;
spin_lock ( & dum - > lock ) ;
2005-05-03 16:24:04 -04:00
}
}
2011-06-29 16:41:51 +03:00
dum_hcd - > old_status = dum_hcd - > port_status ;
dum_hcd - > old_active = dum_hcd - > active ;
2005-05-03 16:24:04 -04:00
}
/*-------------------------------------------------------------------------*/
2005-04-16 15:20:36 -07:00
/* SLAVE/GADGET SIDE DRIVER
*
* This only tracks gadget state . All the work is done when the host
* side tries some ( emulated ) i / o operation . Real device controller
* drivers would do real i / o using dma , fifos , irqs , timers , etc .
*/
# define is_enabled(dum) \
( dum - > port_status & USB_PORT_STAT_ENABLE )
static int
dummy_enable ( struct usb_ep * _ep , const struct usb_endpoint_descriptor * desc )
{
struct dummy * dum ;
2011-06-29 16:41:51 +03:00
struct dummy_hcd * dum_hcd ;
2005-04-16 15:20:36 -07:00
struct dummy_ep * ep ;
unsigned max ;
int retval ;
ep = usb_ep_to_dummy_ep ( _ep ) ;
if ( ! _ep | | ! desc | | ep - > desc | | _ep - > name = = ep0name
| | desc - > bDescriptorType ! = USB_DT_ENDPOINT )
return - EINVAL ;
dum = ep_to_dummy ( ep ) ;
2011-06-29 16:41:51 +03:00
if ( ! dum - > driver )
return - ESHUTDOWN ;
2011-06-16 20:36:57 +02:00
dum_hcd = gadget_to_dummy_hcd ( & dum - > gadget ) ;
2011-06-29 16:41:51 +03:00
if ( ! is_enabled ( dum_hcd ) )
2005-04-16 15:20:36 -07:00
return - ESHUTDOWN ;
2011-06-29 16:41:51 +03:00
/*
* For HS / FS devices only bits 0. .10 of the wMaxPacketSize represent the
* maximum packet size .
2011-06-29 16:41:52 +03:00
* For SS devices the wMaxPacketSize is limited by 1024.
2011-06-29 16:41:51 +03:00
*/
2011-08-23 03:12:03 -07:00
max = usb_endpoint_maxp ( desc ) & 0x7ff ;
2005-04-16 15:20:36 -07:00
/* drivers must not request bad settings, since lower levels
* ( hardware or its drivers ) may not check . some endpoints
* can ' t do iso , many have maxpacket limitations , etc .
*
* since this " hardware " driver is here to help debugging , we
* have some extra sanity checks . ( there could be more though ,
* especially for " ep9out " style fixed function ones . )
*/
retval = - EINVAL ;
switch ( desc - > bmAttributes & 0x03 ) {
case USB_ENDPOINT_XFER_BULK :
if ( strstr ( ep - > ep . name , " -iso " )
| | strstr ( ep - > ep . name , " -int " ) ) {
goto done ;
}
switch ( dum - > gadget . speed ) {
2011-06-29 16:41:52 +03:00
case USB_SPEED_SUPER :
if ( max = = 1024 )
break ;
goto done ;
2005-04-16 15:20:36 -07:00
case USB_SPEED_HIGH :
if ( max = = 512 )
break ;
2008-03-28 14:50:26 -07:00
goto done ;
case USB_SPEED_FULL :
if ( max = = 8 | | max = = 16 | | max = = 32 | | max = = 64 )
2005-04-16 15:20:36 -07:00
/* we'll fake any legal size */
break ;
2008-03-28 14:50:26 -07:00
/* save a return statement */
default :
goto done ;
2005-04-16 15:20:36 -07:00
}
break ;
case USB_ENDPOINT_XFER_INT :
if ( strstr ( ep - > ep . name , " -iso " ) ) /* bulk is ok */
goto done ;
/* real hardware might not handle all packet sizes */
switch ( dum - > gadget . speed ) {
2011-06-29 16:41:52 +03:00
case USB_SPEED_SUPER :
2005-04-16 15:20:36 -07:00
case USB_SPEED_HIGH :
if ( max < = 1024 )
break ;
/* save a return statement */
case USB_SPEED_FULL :
if ( max < = 64 )
break ;
/* save a return statement */
default :
if ( max < = 8 )
break ;
goto done ;
}
break ;
case USB_ENDPOINT_XFER_ISOC :
if ( strstr ( ep - > ep . name , " -bulk " )
| | strstr ( ep - > ep . name , " -int " ) )
goto done ;
/* real hardware might not handle all packet sizes */
switch ( dum - > gadget . speed ) {
2011-06-29 16:41:52 +03:00
case USB_SPEED_SUPER :
2005-04-16 15:20:36 -07:00
case USB_SPEED_HIGH :
if ( max < = 1024 )
break ;
/* save a return statement */
case USB_SPEED_FULL :
if ( max < = 1023 )
break ;
/* save a return statement */
default :
goto done ;
}
break ;
default :
/* few chips support control except on ep0 */
goto done ;
}
_ep - > maxpacket = max ;
ep - > desc = desc ;
2005-05-03 16:15:43 -04:00
dev_dbg ( udc_dev ( dum ) , " enabled %s (ep%d%s-%s) maxpacket %d \n " ,
2005-04-16 15:20:36 -07:00
_ep - > name ,
desc - > bEndpointAddress & 0x0f ,
( desc - > bEndpointAddress & USB_DIR_IN ) ? " in " : " out " ,
( { char * val ;
switch ( desc - > bmAttributes & 0x03 ) {
2011-06-28 16:33:52 +03:00
case USB_ENDPOINT_XFER_BULK :
val = " bulk " ;
break ;
case USB_ENDPOINT_XFER_ISOC :
val = " iso " ;
break ;
case USB_ENDPOINT_XFER_INT :
val = " intr " ;
break ;
default :
val = " ctrl " ;
break ;
2005-04-16 15:20:36 -07:00
} ; val ; } ) ,
max ) ;
/* at this point real hardware should be NAKing transfers
* to that endpoint , until a buffer is queued to it .
*/
2008-08-14 15:48:30 -04:00
ep - > halted = ep - > wedged = 0 ;
2005-04-16 15:20:36 -07:00
retval = 0 ;
done :
return retval ;
}
static int dummy_disable ( struct usb_ep * _ep )
{
struct dummy_ep * ep ;
struct dummy * dum ;
unsigned long flags ;
int retval ;
ep = usb_ep_to_dummy_ep ( _ep ) ;
if ( ! _ep | | ! ep - > desc | | _ep - > name = = ep0name )
return - EINVAL ;
dum = ep_to_dummy ( ep ) ;
spin_lock_irqsave ( & dum - > lock , flags ) ;
ep - > desc = NULL ;
retval = 0 ;
nuke ( dum , ep ) ;
spin_unlock_irqrestore ( & dum - > lock , flags ) ;
2005-05-03 16:15:43 -04:00
dev_dbg ( udc_dev ( dum ) , " disabled %s \n " , _ep - > name ) ;
2005-04-16 15:20:36 -07:00
return retval ;
}
static struct usb_request *
2005-10-21 03:21:58 -04:00
dummy_alloc_request ( struct usb_ep * _ep , gfp_t mem_flags )
2005-04-16 15:20:36 -07:00
{
struct dummy_ep * ep ;
struct dummy_request * req ;
if ( ! _ep )
return NULL ;
ep = usb_ep_to_dummy_ep ( _ep ) ;
2006-02-27 13:34:10 -08:00
req = kzalloc ( sizeof ( * req ) , mem_flags ) ;
2005-04-16 15:20:36 -07:00
if ( ! req )
return NULL ;
INIT_LIST_HEAD ( & req - > queue ) ;
return & req - > req ;
}
static void
dummy_free_request ( struct usb_ep * _ep , struct usb_request * _req )
{
struct dummy_ep * ep ;
struct dummy_request * req ;
ep = usb_ep_to_dummy_ep ( _ep ) ;
if ( ! ep | | ! _req | | ( ! ep - > desc & & _ep - > name ! = ep0name ) )
return ;
req = usb_request_to_dummy_request ( _req ) ;
WARN_ON ( ! list_empty ( & req - > queue ) ) ;
kfree ( req ) ;
}
static void
fifo_complete ( struct usb_ep * ep , struct usb_request * req )
{
}
static int
2005-06-23 20:25:36 +03:00
dummy_queue ( struct usb_ep * _ep , struct usb_request * _req ,
2005-10-21 03:21:58 -04:00
gfp_t mem_flags )
2005-04-16 15:20:36 -07:00
{
struct dummy_ep * ep ;
struct dummy_request * req ;
struct dummy * dum ;
2011-06-29 16:41:51 +03:00
struct dummy_hcd * dum_hcd ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
req = usb_request_to_dummy_request ( _req ) ;
if ( ! _req | | ! list_empty ( & req - > queue ) | | ! _req - > complete )
return - EINVAL ;
ep = usb_ep_to_dummy_ep ( _ep ) ;
if ( ! _ep | | ( ! ep - > desc & & _ep - > name ! = ep0name ) )
return - EINVAL ;
dum = ep_to_dummy ( ep ) ;
2011-06-16 20:36:57 +02:00
dum_hcd = gadget_to_dummy_hcd ( & dum - > gadget ) ;
2011-06-29 16:41:51 +03:00
if ( ! dum - > driver | | ! is_enabled ( dum_hcd ) )
2005-04-16 15:20:36 -07:00
return - ESHUTDOWN ;
#if 0
2005-05-03 16:15:43 -04:00
dev_dbg ( udc_dev ( dum ) , " ep %p queue req %p to %s, len %d buf %p \n " ,
2005-04-16 15:20:36 -07:00
ep , _req , _ep - > name , _req - > length , _req - > buf ) ;
# endif
_req - > status = - EINPROGRESS ;
_req - > actual = 0 ;
spin_lock_irqsave ( & dum - > lock , flags ) ;
/* implement an emulated single-request FIFO */
if ( ep - > desc & & ( ep - > desc - > bEndpointAddress & USB_DIR_IN ) & &
list_empty ( & dum - > fifo_req . queue ) & &
list_empty ( & ep - > queue ) & &
_req - > length < = FIFO_SIZE ) {
req = & dum - > fifo_req ;
req - > req = * _req ;
req - > req . buf = dum - > fifo_buf ;
memcpy ( dum - > fifo_buf , _req - > buf , _req - > length ) ;
req - > req . context = dum ;
req - > req . complete = fifo_complete ;
2008-07-26 08:06:24 -07:00
list_add_tail ( & req - > queue , & ep - > queue ) ;
2005-04-16 15:20:36 -07:00
spin_unlock ( & dum - > lock ) ;
_req - > actual = _req - > length ;
_req - > status = 0 ;
_req - > complete ( _ep , _req ) ;
spin_lock ( & dum - > lock ) ;
2008-07-26 08:06:24 -07:00
} else
list_add_tail ( & req - > queue , & ep - > queue ) ;
2005-04-16 15:20:36 -07:00
spin_unlock_irqrestore ( & dum - > lock , flags ) ;
/* real hardware would likely enable transfers here, in case
* it ' d been left NAKing .
*/
return 0 ;
}
static int dummy_dequeue ( struct usb_ep * _ep , struct usb_request * _req )
{
struct dummy_ep * ep ;
struct dummy * dum ;
int retval = - EINVAL ;
unsigned long flags ;
struct dummy_request * req = NULL ;
if ( ! _ep | | ! _req )
return retval ;
ep = usb_ep_to_dummy_ep ( _ep ) ;
dum = ep_to_dummy ( ep ) ;
if ( ! dum - > driver )
return - ESHUTDOWN ;
2006-07-28 17:07:34 -04:00
local_irq_save ( flags ) ;
spin_lock ( & dum - > lock ) ;
2005-04-16 15:20:36 -07:00
list_for_each_entry ( req , & ep - > queue , queue ) {
if ( & req - > req = = _req ) {
list_del_init ( & req - > queue ) ;
_req - > status = - ECONNRESET ;
retval = 0 ;
break ;
}
}
2006-07-28 17:07:34 -04:00
spin_unlock ( & dum - > lock ) ;
2005-04-16 15:20:36 -07:00
if ( retval = = 0 ) {
2005-05-03 16:15:43 -04:00
dev_dbg ( udc_dev ( dum ) ,
2005-04-16 15:20:36 -07:00
" dequeued req %p from %s, len %d buf %p \n " ,
req , _ep - > name , _req - > length , _req - > buf ) ;
_req - > complete ( _ep , _req ) ;
}
2006-07-28 17:07:34 -04:00
local_irq_restore ( flags ) ;
2005-04-16 15:20:36 -07:00
return retval ;
}
static int
2008-08-14 15:48:30 -04:00
dummy_set_halt_and_wedge ( struct usb_ep * _ep , int value , int wedged )
2005-04-16 15:20:36 -07:00
{
struct dummy_ep * ep ;
struct dummy * dum ;
if ( ! _ep )
return - EINVAL ;
ep = usb_ep_to_dummy_ep ( _ep ) ;
dum = ep_to_dummy ( ep ) ;
if ( ! dum - > driver )
return - ESHUTDOWN ;
if ( ! value )
2008-08-14 15:48:30 -04:00
ep - > halted = ep - > wedged = 0 ;
2005-04-16 15:20:36 -07:00
else if ( ep - > desc & & ( ep - > desc - > bEndpointAddress & USB_DIR_IN ) & &
! list_empty ( & ep - > queue ) )
return - EAGAIN ;
2008-08-14 15:48:30 -04:00
else {
2005-04-16 15:20:36 -07:00
ep - > halted = 1 ;
2008-08-14 15:48:30 -04:00
if ( wedged )
ep - > wedged = 1 ;
}
2005-04-16 15:20:36 -07:00
/* FIXME clear emulated data toggle too */
return 0 ;
}
2008-08-14 15:48:30 -04:00
static int
dummy_set_halt ( struct usb_ep * _ep , int value )
{
return dummy_set_halt_and_wedge ( _ep , value , 0 ) ;
}
static int dummy_set_wedge ( struct usb_ep * _ep )
{
if ( ! _ep | | _ep - > name = = ep0name )
return - EINVAL ;
return dummy_set_halt_and_wedge ( _ep , 1 , 1 ) ;
}
2005-04-16 15:20:36 -07:00
static const struct usb_ep_ops dummy_ep_ops = {
. enable = dummy_enable ,
. disable = dummy_disable ,
. alloc_request = dummy_alloc_request ,
. free_request = dummy_free_request ,
. queue = dummy_queue ,
. dequeue = dummy_dequeue ,
. set_halt = dummy_set_halt ,
2008-08-14 15:48:30 -04:00
. set_wedge = dummy_set_wedge ,
2005-04-16 15:20:36 -07:00
} ;
/*-------------------------------------------------------------------------*/
/* there are both host and device side versions of this call ... */
static int dummy_g_get_frame ( struct usb_gadget * _gadget )
{
struct timeval tv ;
do_gettimeofday ( & tv ) ;
return tv . tv_usec / 1000 ;
}
static int dummy_wakeup ( struct usb_gadget * _gadget )
{
2011-06-29 16:41:51 +03:00
struct dummy_hcd * dum_hcd ;
2005-04-16 15:20:36 -07:00
2011-06-29 16:41:51 +03:00
dum_hcd = gadget_to_dummy_hcd ( _gadget ) ;
if ( ! ( dum_hcd - > dum - > devstatus & ( ( 1 < < USB_DEVICE_B_HNP_ENABLE )
2005-05-02 11:25:17 -04:00
| ( 1 < < USB_DEVICE_REMOTE_WAKEUP ) ) ) )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2011-06-29 16:41:51 +03:00
if ( ( dum_hcd - > port_status & USB_PORT_STAT_CONNECTION ) = = 0 )
2005-05-10 15:34:16 -04:00
return - ENOLINK ;
2011-06-29 16:41:51 +03:00
if ( ( dum_hcd - > port_status & USB_PORT_STAT_SUSPEND ) = = 0 & &
dum_hcd - > rh_state ! = DUMMY_RH_SUSPENDED )
2005-05-10 15:34:16 -04:00
return - EIO ;
/* FIXME: What if the root hub is suspended but the port isn't? */
2005-04-16 15:20:36 -07:00
/* hub notices our request, issues downstream resume, etc */
2011-06-29 16:41:51 +03:00
dum_hcd - > resuming = 1 ;
dum_hcd - > re_timeout = jiffies + msecs_to_jiffies ( 20 ) ;
mod_timer ( & dummy_hcd_to_hcd ( dum_hcd ) - > rh_timer , dum_hcd - > re_timeout ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static int dummy_set_selfpowered ( struct usb_gadget * _gadget , int value )
{
struct dummy * dum ;
2011-06-29 16:41:51 +03:00
dum = ( gadget_to_dummy_hcd ( _gadget ) ) - > dum ;
2005-04-16 15:20:36 -07:00
if ( value )
dum - > devstatus | = ( 1 < < USB_DEVICE_SELF_POWERED ) ;
else
dum - > devstatus & = ~ ( 1 < < USB_DEVICE_SELF_POWERED ) ;
return 0 ;
}
2011-06-23 14:26:14 +02:00
static void dummy_udc_udpate_ep0 ( struct dummy * dum )
{
u32 i ;
if ( dum - > gadget . speed = = USB_SPEED_SUPER ) {
for ( i = 0 ; i < DUMMY_ENDPOINTS ; i + + )
dum - > ep [ i ] . ep . max_streams = 0x10 ;
dum - > ep [ 0 ] . ep . maxpacket = 9 ;
} else {
for ( i = 0 ; i < DUMMY_ENDPOINTS ; i + + )
dum - > ep [ i ] . ep . max_streams = 0 ;
dum - > ep [ 0 ] . ep . maxpacket = 64 ;
}
}
2005-05-03 16:24:04 -04:00
static int dummy_pullup ( struct usb_gadget * _gadget , int value )
{
2011-06-17 10:11:41 +02:00
struct dummy_hcd * dum_hcd ;
2005-05-03 16:24:04 -04:00
struct dummy * dum ;
unsigned long flags ;
2011-06-23 14:26:14 +02:00
dum = gadget_dev_to_dummy ( & _gadget - > dev ) ;
if ( value & & dum - > driver ) {
if ( mod_data . is_super_speed )
dum - > gadget . speed = dum - > driver - > speed ;
else if ( mod_data . is_high_speed )
dum - > gadget . speed = min_t ( u8 , USB_SPEED_HIGH ,
dum - > driver - > speed ) ;
else
dum - > gadget . speed = USB_SPEED_FULL ;
dummy_udc_udpate_ep0 ( dum ) ;
if ( dum - > gadget . speed < dum - > driver - > speed )
dev_dbg ( udc_dev ( dum ) , " This device can perform faster "
" if you connect it to a %s port... \n " ,
( dum - > driver - > speed = = USB_SPEED_SUPER ?
" SuperSpeed " : " HighSpeed " ) ) ;
}
2011-06-17 10:11:41 +02:00
dum_hcd = gadget_to_dummy_hcd ( _gadget ) ;
2005-05-03 16:24:04 -04:00
spin_lock_irqsave ( & dum - > lock , flags ) ;
dum - > pullup = ( value ! = 0 ) ;
2011-06-17 10:11:41 +02:00
set_link_state ( dum_hcd ) ;
2005-05-03 16:24:04 -04:00
spin_unlock_irqrestore ( & dum - > lock , flags ) ;
2011-06-23 14:26:14 +02:00
2011-06-17 10:11:41 +02:00
usb_hcd_poll_rh_status ( dummy_hcd_to_hcd ( dum_hcd ) ) ;
2005-05-03 16:24:04 -04:00
return 0 ;
}
2011-06-23 14:26:17 +02:00
static int dummy_udc_start ( struct usb_gadget * g ,
struct usb_gadget_driver * driver ) ;
static int dummy_udc_stop ( struct usb_gadget * g ,
struct usb_gadget_driver * driver ) ;
2011-06-28 16:33:47 +03:00
2005-04-16 15:20:36 -07:00
static const struct usb_gadget_ops dummy_ops = {
. get_frame = dummy_g_get_frame ,
. wakeup = dummy_wakeup ,
. set_selfpowered = dummy_set_selfpowered ,
2005-05-03 16:24:04 -04:00
. pullup = dummy_pullup ,
2011-06-23 14:26:17 +02:00
. udc_start = dummy_udc_start ,
. udc_stop = dummy_udc_stop ,
2005-04-16 15:20:36 -07:00
} ;
/*-------------------------------------------------------------------------*/
/* "function" sysfs attribute */
static ssize_t
2005-05-17 06:43:37 -04:00
show_function ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct dummy * dum = gadget_dev_to_dummy ( dev ) ;
if ( ! dum - > driver | | ! dum - > driver - > function )
return 0 ;
return scnprintf ( buf , PAGE_SIZE , " %s \n " , dum - > driver - > function ) ;
}
2005-05-10 15:28:38 -04:00
static DEVICE_ATTR ( function , S_IRUGO , show_function , NULL ) ;
2005-04-16 15:20:36 -07:00
/*-------------------------------------------------------------------------*/
/*
* Driver registration / unregistration .
*
* This is basically hardware - specific ; there ' s usually only one real USB
* device ( not host ) controller since that ' s how USB devices are intended
* to work . So most implementations of these api calls will rely on the
* fact that only one driver will ever bind to the hardware . But curious
* hardware can be built with discrete components , so the gadget API doesn ' t
* require that assumption .
*
* For this emulator , it might be convenient to create a usb slave device
* for each driver that registers : just add to a big root hub .
*/
2011-06-23 14:26:17 +02:00
static int dummy_udc_start ( struct usb_gadget * g ,
struct usb_gadget_driver * driver )
2005-04-16 15:20:36 -07:00
{
2011-06-23 14:26:17 +02:00
struct dummy_hcd * dum_hcd = gadget_to_dummy_hcd ( g ) ;
struct dummy * dum = dum_hcd - > dum ;
2005-04-16 15:20:36 -07:00
2011-06-23 14:26:17 +02:00
if ( driver - > speed = = USB_SPEED_UNKNOWN )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
/*
* SLAVE side init . . . the layer above hardware , which
* can ' t enumerate without help from the driver we ' re binding .
*/
2005-05-02 11:25:17 -04:00
2005-04-16 15:20:36 -07:00
dum - > devstatus = 0 ;
dum - > driver = driver ;
2005-05-03 16:15:43 -04:00
dev_dbg ( udc_dev ( dum ) , " binding gadget driver '%s' \n " ,
2005-04-16 15:20:36 -07:00
driver - > driver . name ) ;
return 0 ;
}
2011-06-23 14:26:17 +02:00
static int dummy_udc_stop ( struct usb_gadget * g ,
struct usb_gadget_driver * driver )
2005-04-16 15:20:36 -07:00
{
2011-06-23 14:26:17 +02:00
struct dummy_hcd * dum_hcd = gadget_to_dummy_hcd ( g ) ;
struct dummy * dum = dum_hcd - > dum ;
2005-04-16 15:20:36 -07:00
2005-05-03 16:15:43 -04:00
dev_dbg ( udc_dev ( dum ) , " unregister gadget driver '%s' \n " ,
2005-04-16 15:20:36 -07:00
driver - > driver . name ) ;
dum - > driver = NULL ;
2011-06-16 20:36:55 +02:00
dummy_pullup ( & dum - > gadget , 0 ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
# undef is_enabled
2005-05-03 16:15:43 -04:00
/* The gadget structure is stored inside the hcd structure and will be
* released along with it . */
static void
dummy_gadget_release ( struct device * dev )
{
2011-06-29 16:41:51 +03:00
return ;
2005-05-03 16:15:43 -04:00
}
2011-06-23 14:26:12 +02:00
static void init_dummy_udc_hw ( struct dummy * dum )
{
int i ;
INIT_LIST_HEAD ( & dum - > gadget . ep_list ) ;
for ( i = 0 ; i < DUMMY_ENDPOINTS ; i + + ) {
struct dummy_ep * ep = & dum - > ep [ i ] ;
if ( ! ep_name [ i ] )
break ;
ep - > ep . name = ep_name [ i ] ;
ep - > ep . ops = & dummy_ep_ops ;
list_add_tail ( & ep - > ep . ep_list , & dum - > gadget . ep_list ) ;
ep - > halted = ep - > wedged = ep - > already_seen =
ep - > setup_stage = 0 ;
ep - > ep . maxpacket = ~ 0 ;
ep - > last_io = jiffies ;
ep - > gadget = & dum - > gadget ;
ep - > desc = NULL ;
INIT_LIST_HEAD ( & ep - > queue ) ;
}
dum - > gadget . ep0 = & dum - > ep [ 0 ] . ep ;
list_del_init ( & dum - > ep [ 0 ] . ep . ep_list ) ;
INIT_LIST_HEAD ( & dum - > fifo_req . queue ) ;
2011-06-23 14:26:13 +02:00
# ifdef CONFIG_USB_OTG
dum - > gadget . is_otg = 1 ;
# endif
2011-06-23 14:26:12 +02:00
}
2005-11-14 12:16:30 -05:00
static int dummy_udc_probe ( struct platform_device * pdev )
2005-05-03 16:15:43 -04:00
{
2011-06-29 16:41:51 +03:00
struct dummy * dum = & the_controller ;
2005-05-03 16:15:43 -04:00
int rc ;
dum - > gadget . name = gadget_name ;
dum - > gadget . ops = & dummy_ops ;
2011-11-19 18:27:37 +01:00
dum - > gadget . max_speed = USB_SPEED_SUPER ;
2005-05-03 16:15:43 -04:00
2008-05-02 06:02:41 +02:00
dev_set_name ( & dum - > gadget . dev , " gadget " ) ;
2005-11-14 12:16:30 -05:00
dum - > gadget . dev . parent = & pdev - > dev ;
2005-05-03 16:15:43 -04:00
dum - > gadget . dev . release = dummy_gadget_release ;
rc = device_register ( & dum - > gadget . dev ) ;
2010-10-07 09:40:45 +05:30
if ( rc < 0 ) {
put_device ( & dum - > gadget . dev ) ;
2005-05-03 16:15:43 -04:00
return rc ;
2010-10-07 09:40:45 +05:30
}
2005-05-03 16:15:43 -04:00
2011-06-23 14:26:12 +02:00
init_dummy_udc_hw ( dum ) ;
2011-06-28 16:33:47 +03:00
rc = usb_add_gadget_udc ( & pdev - > dev , & dum - > gadget ) ;
if ( rc < 0 )
goto err_udc ;
2006-09-25 11:55:56 -04:00
rc = device_create_file ( & dum - > gadget . dev , & dev_attr_function ) ;
if ( rc < 0 )
2011-06-28 16:33:47 +03:00
goto err_dev ;
platform_set_drvdata ( pdev , dum ) ;
return rc ;
err_dev :
usb_del_gadget_udc ( & dum - > gadget ) ;
err_udc :
device_unregister ( & dum - > gadget . dev ) ;
2005-05-03 16:15:43 -04:00
return rc ;
}
2005-11-14 12:16:30 -05:00
static int dummy_udc_remove ( struct platform_device * pdev )
2005-05-03 16:15:43 -04:00
{
2005-11-14 12:16:30 -05:00
struct dummy * dum = platform_get_drvdata ( pdev ) ;
2005-05-03 16:15:43 -04:00
2011-06-28 16:33:47 +03:00
usb_del_gadget_udc ( & dum - > gadget ) ;
2005-11-14 12:16:30 -05:00
platform_set_drvdata ( pdev , NULL ) ;
2005-05-03 16:15:43 -04:00
device_remove_file ( & dum - > gadget . dev , & dev_attr_function ) ;
device_unregister ( & dum - > gadget . dev ) ;
return 0 ;
}
2011-06-17 19:43:13 +02:00
static void dummy_udc_pm ( struct dummy * dum , struct dummy_hcd * dum_hcd ,
int suspend )
2005-05-10 15:34:16 -04:00
{
2011-06-17 19:43:13 +02:00
spin_lock_irq ( & dum - > lock ) ;
dum - > udc_suspended = suspend ;
2011-06-17 10:11:41 +02:00
set_link_state ( dum_hcd ) ;
2011-06-17 19:43:13 +02:00
spin_unlock_irq ( & dum - > lock ) ;
}
static int dummy_udc_suspend ( struct platform_device * pdev , pm_message_t state )
{
struct dummy * dum = platform_get_drvdata ( pdev ) ;
struct dummy_hcd * dum_hcd = gadget_to_dummy_hcd ( & dum - > gadget ) ;
2005-05-10 15:34:16 -04:00
2011-06-17 19:43:13 +02:00
dev_dbg ( & pdev - > dev , " %s \n " , __func__ ) ;
dummy_udc_pm ( dum , dum_hcd , 1 ) ;
2011-06-17 10:11:41 +02:00
usb_hcd_poll_rh_status ( dummy_hcd_to_hcd ( dum_hcd ) ) ;
2005-05-10 15:34:16 -04:00
return 0 ;
}
2011-06-17 19:43:13 +02:00
static int dummy_udc_resume ( struct platform_device * pdev )
2005-05-10 15:34:16 -04:00
{
2011-06-17 19:43:13 +02:00
struct dummy * dum = platform_get_drvdata ( pdev ) ;
struct dummy_hcd * dum_hcd = gadget_to_dummy_hcd ( & dum - > gadget ) ;
2005-05-10 15:34:16 -04:00
2011-06-17 19:43:13 +02:00
dev_dbg ( & pdev - > dev , " %s \n " , __func__ ) ;
dummy_udc_pm ( dum , dum_hcd , 0 ) ;
2011-06-17 10:11:41 +02:00
usb_hcd_poll_rh_status ( dummy_hcd_to_hcd ( dum_hcd ) ) ;
2005-05-10 15:34:16 -04:00
return 0 ;
}
2005-11-09 22:32:44 +00:00
static struct platform_driver dummy_udc_driver = {
2005-05-03 16:15:43 -04:00
. probe = dummy_udc_probe ,
. remove = dummy_udc_remove ,
2005-05-10 15:34:16 -04:00
. suspend = dummy_udc_suspend ,
. resume = dummy_udc_resume ,
2005-11-09 22:32:44 +00:00
. driver = {
. name = ( char * ) gadget_name ,
. owner = THIS_MODULE ,
} ,
2005-05-03 16:15:43 -04:00
} ;
2005-04-16 15:20:36 -07:00
/*-------------------------------------------------------------------------*/
/* MASTER/HOST SIDE DRIVER
*
* this uses the hcd framework to hook up to host side drivers .
* its root hub will only have one device , otherwise it acts like
* a normal host controller .
*
* when urbs are queued , they ' re just stuck on a list that we
* scan in a timer callback . that callback connects writes from
* the host with reads from the device , and so on , based on the
* usb 2.0 rules .
*/
static int dummy_urb_enqueue (
struct usb_hcd * hcd ,
struct urb * urb ,
2005-10-21 03:21:58 -04:00
gfp_t mem_flags
2005-04-16 15:20:36 -07:00
) {
2011-06-29 16:41:51 +03:00
struct dummy_hcd * dum_hcd ;
2005-04-16 15:20:36 -07:00
struct urbp * urbp ;
unsigned long flags ;
2007-08-08 11:48:02 -04:00
int rc ;
2005-04-16 15:20:36 -07:00
if ( ! urb - > transfer_buffer & & urb - > transfer_buffer_length )
return - EINVAL ;
urbp = kmalloc ( sizeof * urbp , mem_flags ) ;
if ( ! urbp )
return - ENOMEM ;
urbp - > urb = urb ;
2011-06-29 16:41:51 +03:00
dum_hcd = hcd_to_dummy_hcd ( hcd ) ;
spin_lock_irqsave ( & dum_hcd - > dum - > lock , flags ) ;
2007-08-08 11:48:02 -04:00
rc = usb_hcd_link_urb_to_ep ( hcd , urb ) ;
if ( rc ) {
kfree ( urbp ) ;
goto done ;
}
2005-04-16 15:20:36 -07:00
2011-06-29 16:41:51 +03:00
if ( ! dum_hcd - > udev ) {
dum_hcd - > udev = urb - > dev ;
usb_get_dev ( dum_hcd - > udev ) ;
} else if ( unlikely ( dum_hcd - > udev ! = urb - > dev ) )
dev_err ( dummy_dev ( dum_hcd ) , " usb_device address has changed! \n " ) ;
2005-04-16 15:20:36 -07:00
2011-06-29 16:41:51 +03:00
list_add_tail ( & urbp - > urbp_list , & dum_hcd - > urbp_list ) ;
2005-04-16 15:20:36 -07:00
urb - > hcpriv = urbp ;
if ( usb_pipetype ( urb - > pipe ) = = PIPE_CONTROL )
urb - > error_count = 1 ; /* mark as a new urb */
/* kick the scheduler, it'll do the rest */
2011-06-29 16:41:51 +03:00
if ( ! timer_pending ( & dum_hcd - > timer ) )
mod_timer ( & dum_hcd - > timer , jiffies + 1 ) ;
2005-04-16 15:20:36 -07:00
2007-08-08 11:48:02 -04:00
done :
2011-06-29 16:41:51 +03:00
spin_unlock_irqrestore ( & dum_hcd - > dum - > lock , flags ) ;
2007-08-08 11:48:02 -04:00
return rc ;
2005-04-16 15:20:36 -07:00
}
2007-08-08 11:48:02 -04:00
static int dummy_urb_dequeue ( struct usb_hcd * hcd , struct urb * urb , int status )
2005-04-16 15:20:36 -07:00
{
2011-06-29 16:41:51 +03:00
struct dummy_hcd * dum_hcd ;
2005-05-10 15:34:16 -04:00
unsigned long flags ;
2007-08-08 11:48:02 -04:00
int rc ;
2005-05-10 15:34:16 -04:00
/* giveback happens automatically in timer callback,
* so make sure the callback happens */
2011-06-29 16:41:51 +03:00
dum_hcd = hcd_to_dummy_hcd ( hcd ) ;
spin_lock_irqsave ( & dum_hcd - > dum - > lock , flags ) ;
2007-08-08 11:48:02 -04:00
rc = usb_hcd_check_unlink_urb ( hcd , urb , status ) ;
2011-06-29 16:41:51 +03:00
if ( ! rc & & dum_hcd - > rh_state ! = DUMMY_RH_RUNNING & &
! list_empty ( & dum_hcd - > urbp_list ) )
mod_timer ( & dum_hcd - > timer , jiffies ) ;
2007-08-08 11:48:02 -04:00
2011-06-29 16:41:51 +03:00
spin_unlock_irqrestore ( & dum_hcd - > dum - > lock , flags ) ;
2007-08-08 11:48:02 -04:00
return rc ;
2005-04-16 15:20:36 -07:00
}
/* transfer up to a frame's worth; caller must own lock */
static int
2007-08-24 15:40:10 -04:00
transfer ( struct dummy * dum , struct urb * urb , struct dummy_ep * ep , int limit ,
int * status )
2005-04-16 15:20:36 -07:00
{
struct dummy_request * req ;
top :
/* if there's no request queued, the device is NAKing; return */
list_for_each_entry ( req , & ep - > queue , queue ) {
unsigned host_len , dev_len , len ;
int is_short , to_host ;
int rescan = 0 ;
/* 1..N packets of ep->ep.maxpacket each ... the last one
* may be short ( including zero length ) .
*
* writer can send a zlp explicitly ( length 0 ) or implicitly
* ( length mod maxpacket zero , and ' zero ' flag ) ; they always
* terminate reads .
*/
host_len = urb - > transfer_buffer_length - urb - > actual_length ;
dev_len = req - > req . length - req - > req . actual ;
len = min ( host_len , dev_len ) ;
/* FIXME update emulated data toggle too */
to_host = usb_pipein ( urb - > pipe ) ;
if ( unlikely ( len = = 0 ) )
is_short = 1 ;
else {
char * ubuf , * rbuf ;
/* not enough bandwidth left? */
if ( limit < ep - > ep . maxpacket & & limit < len )
break ;
len = min ( len , ( unsigned ) limit ) ;
if ( len = = 0 )
break ;
/* use an extra pass for the final short packet */
if ( len > ep - > ep . maxpacket ) {
rescan = 1 ;
len - = ( len % ep - > ep . maxpacket ) ;
}
is_short = ( len % ep - > ep . maxpacket ) ! = 0 ;
/* else transfer packet(s) */
ubuf = urb - > transfer_buffer + urb - > actual_length ;
rbuf = req - > req . buf + req - > req . actual ;
if ( to_host )
memcpy ( ubuf , rbuf , len ) ;
else
memcpy ( rbuf , ubuf , len ) ;
ep - > last_io = jiffies ;
limit - = len ;
urb - > actual_length + = len ;
req - > req . actual + = len ;
}
/* short packets terminate, maybe with overflow/underflow.
* it ' s only really an error to write too much .
*
* partially filling a buffer optionally blocks queue advances
* ( so completion handlers can clean up the queue ) but we don ' t
2007-08-21 15:39:21 -04:00
* need to emulate such data - in - flight .
2005-04-16 15:20:36 -07:00
*/
if ( is_short ) {
if ( host_len = = dev_len ) {
req - > req . status = 0 ;
2007-08-24 15:40:10 -04:00
* status = 0 ;
2005-04-16 15:20:36 -07:00
} else if ( to_host ) {
req - > req . status = 0 ;
if ( dev_len > host_len )
2007-08-24 15:40:10 -04:00
* status = - EOVERFLOW ;
2005-04-16 15:20:36 -07:00
else
2007-08-24 15:40:10 -04:00
* status = 0 ;
2005-04-16 15:20:36 -07:00
} else if ( ! to_host ) {
2007-08-24 15:40:10 -04:00
* status = 0 ;
2005-04-16 15:20:36 -07:00
if ( host_len > dev_len )
req - > req . status = - EOVERFLOW ;
else
req - > req . status = 0 ;
}
/* many requests terminate without a short packet */
} else {
if ( req - > req . length = = req - > req . actual
& & ! req - > req . zero )
req - > req . status = 0 ;
if ( urb - > transfer_buffer_length = = urb - > actual_length
& & ! ( urb - > transfer_flags
2007-08-24 15:40:10 -04:00
& URB_ZERO_PACKET ) )
* status = 0 ;
2005-04-16 15:20:36 -07:00
}
/* device side completion --> continuable */
if ( req - > req . status ! = - EINPROGRESS ) {
list_del_init ( & req - > queue ) ;
spin_unlock ( & dum - > lock ) ;
req - > req . complete ( & ep - > ep , & req - > req ) ;
spin_lock ( & dum - > lock ) ;
/* requests might have been unlinked... */
rescan = 1 ;
}
/* host side completion --> terminate */
2007-08-24 15:40:10 -04:00
if ( * status ! = - EINPROGRESS )
2005-04-16 15:20:36 -07:00
break ;
/* rescan to continue with any other queued i/o */
if ( rescan )
goto top ;
}
return limit ;
}
static int periodic_bytes ( struct dummy * dum , struct dummy_ep * ep )
{
int limit = ep - > ep . maxpacket ;
if ( dum - > gadget . speed = = USB_SPEED_HIGH ) {
int tmp ;
/* high bandwidth mode */
2011-08-23 03:12:03 -07:00
tmp = usb_endpoint_maxp ( ep - > desc ) ;
2005-04-16 15:20:36 -07:00
tmp = ( tmp > > 11 ) & 0x03 ;
tmp * = 8 /* applies to entire frame */ ;
limit + = limit * tmp ;
}
2011-06-29 16:41:52 +03:00
if ( dum - > gadget . speed = = USB_SPEED_SUPER ) {
switch ( ep - > desc - > bmAttributes & 0x03 ) {
case USB_ENDPOINT_XFER_ISOC :
/* Sec. 4.4.8.2 USB3.0 Spec */
limit = 3 * 16 * 1024 * 8 ;
break ;
case USB_ENDPOINT_XFER_INT :
/* Sec. 4.4.7.2 USB3.0 Spec */
limit = 3 * 1024 * 8 ;
break ;
case USB_ENDPOINT_XFER_BULK :
default :
break ;
}
}
2005-04-16 15:20:36 -07:00
return limit ;
}
2011-06-29 16:41:51 +03:00
# define is_active(dum_hcd) ((dum_hcd->port_status & \
2005-04-16 15:20:36 -07:00
( USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | \
USB_PORT_STAT_SUSPEND ) ) \
= = ( USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE ) )
static struct dummy_ep * find_endpoint ( struct dummy * dum , u8 address )
{
int i ;
2011-06-29 16:41:52 +03:00
if ( ! is_active ( ( dum - > gadget . speed = = USB_SPEED_SUPER ?
dum - > ss_hcd : dum - > hs_hcd ) ) )
2005-04-16 15:20:36 -07:00
return NULL ;
if ( ( address & ~ USB_DIR_IN ) = = 0 )
return & dum - > ep [ 0 ] ;
for ( i = 1 ; i < DUMMY_ENDPOINTS ; i + + ) {
struct dummy_ep * ep = & dum - > ep [ i ] ;
if ( ! ep - > desc )
continue ;
if ( ep - > desc - > bEndpointAddress = = address )
return ep ;
}
return NULL ;
}
# undef is_active
# define Dev_Request (USB_TYPE_STANDARD | USB_RECIP_DEVICE)
# define Dev_InRequest (Dev_Request | USB_DIR_IN)
# define Intf_Request (USB_TYPE_STANDARD | USB_RECIP_INTERFACE)
# define Intf_InRequest (Intf_Request | USB_DIR_IN)
# define Ep_Request (USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)
# define Ep_InRequest (Ep_Request | USB_DIR_IN)
2010-11-01 17:38:05 +02:00
/**
* handle_control_request ( ) - handles all control transfers
* @ dum : pointer to dummy ( the_controller )
* @ urb : the urb request to handle
* @ setup : pointer to the setup data for a USB device control
* request
* @ status : pointer to request handling status
*
* Return 0 - if the request was handled
* 1 - if the request wasn ' t handles
* error code on error
*/
2011-06-29 16:41:51 +03:00
static int handle_control_request ( struct dummy_hcd * dum_hcd , struct urb * urb ,
2010-11-01 17:38:05 +02:00
struct usb_ctrlrequest * setup ,
int * status )
{
struct dummy_ep * ep2 ;
2011-06-29 16:41:51 +03:00
struct dummy * dum = dum_hcd - > dum ;
2010-11-01 17:38:05 +02:00
int ret_val = 1 ;
unsigned w_index ;
unsigned w_value ;
w_index = le16_to_cpu ( setup - > wIndex ) ;
w_value = le16_to_cpu ( setup - > wValue ) ;
switch ( setup - > bRequest ) {
case USB_REQ_SET_ADDRESS :
if ( setup - > bRequestType ! = Dev_Request )
break ;
dum - > address = w_value ;
* status = 0 ;
dev_dbg ( udc_dev ( dum ) , " set_address = %d \n " ,
w_value ) ;
ret_val = 0 ;
break ;
case USB_REQ_SET_FEATURE :
if ( setup - > bRequestType = = Dev_Request ) {
ret_val = 0 ;
switch ( w_value ) {
case USB_DEVICE_REMOTE_WAKEUP :
break ;
case USB_DEVICE_B_HNP_ENABLE :
dum - > gadget . b_hnp_enable = 1 ;
break ;
case USB_DEVICE_A_HNP_SUPPORT :
dum - > gadget . a_hnp_support = 1 ;
break ;
case USB_DEVICE_A_ALT_HNP_SUPPORT :
dum - > gadget . a_alt_hnp_support = 1 ;
break ;
2011-06-29 16:41:52 +03:00
case USB_DEVICE_U1_ENABLE :
if ( dummy_hcd_to_hcd ( dum_hcd ) - > speed = =
HCD_USB3 )
w_value = USB_DEV_STAT_U1_ENABLED ;
else
ret_val = - EOPNOTSUPP ;
break ;
case USB_DEVICE_U2_ENABLE :
if ( dummy_hcd_to_hcd ( dum_hcd ) - > speed = =
HCD_USB3 )
w_value = USB_DEV_STAT_U2_ENABLED ;
else
ret_val = - EOPNOTSUPP ;
break ;
case USB_DEVICE_LTM_ENABLE :
if ( dummy_hcd_to_hcd ( dum_hcd ) - > speed = =
HCD_USB3 )
w_value = USB_DEV_STAT_LTM_ENABLED ;
else
ret_val = - EOPNOTSUPP ;
break ;
2010-11-01 17:38:05 +02:00
default :
ret_val = - EOPNOTSUPP ;
}
if ( ret_val = = 0 ) {
dum - > devstatus | = ( 1 < < w_value ) ;
* status = 0 ;
}
} else if ( setup - > bRequestType = = Ep_Request ) {
/* endpoint halt */
ep2 = find_endpoint ( dum , w_index ) ;
if ( ! ep2 | | ep2 - > ep . name = = ep0name ) {
ret_val = - EOPNOTSUPP ;
break ;
}
ep2 - > halted = 1 ;
ret_val = 0 ;
* status = 0 ;
}
break ;
case USB_REQ_CLEAR_FEATURE :
if ( setup - > bRequestType = = Dev_Request ) {
ret_val = 0 ;
switch ( w_value ) {
case USB_DEVICE_REMOTE_WAKEUP :
w_value = USB_DEVICE_REMOTE_WAKEUP ;
break ;
2011-06-29 16:41:52 +03:00
case USB_DEVICE_U1_ENABLE :
if ( dummy_hcd_to_hcd ( dum_hcd ) - > speed = =
HCD_USB3 )
w_value = USB_DEV_STAT_U1_ENABLED ;
else
ret_val = - EOPNOTSUPP ;
break ;
case USB_DEVICE_U2_ENABLE :
if ( dummy_hcd_to_hcd ( dum_hcd ) - > speed = =
HCD_USB3 )
w_value = USB_DEV_STAT_U2_ENABLED ;
else
ret_val = - EOPNOTSUPP ;
break ;
case USB_DEVICE_LTM_ENABLE :
if ( dummy_hcd_to_hcd ( dum_hcd ) - > speed = =
HCD_USB3 )
w_value = USB_DEV_STAT_LTM_ENABLED ;
else
ret_val = - EOPNOTSUPP ;
break ;
2010-11-01 17:38:05 +02:00
default :
ret_val = - EOPNOTSUPP ;
break ;
}
if ( ret_val = = 0 ) {
dum - > devstatus & = ~ ( 1 < < w_value ) ;
* status = 0 ;
}
} else if ( setup - > bRequestType = = Ep_Request ) {
/* endpoint halt */
ep2 = find_endpoint ( dum , w_index ) ;
if ( ! ep2 ) {
ret_val = - EOPNOTSUPP ;
break ;
}
if ( ! ep2 - > wedged )
ep2 - > halted = 0 ;
ret_val = 0 ;
* status = 0 ;
}
break ;
case USB_REQ_GET_STATUS :
if ( setup - > bRequestType = = Dev_InRequest
| | setup - > bRequestType = = Intf_InRequest
| | setup - > bRequestType = = Ep_InRequest ) {
char * buf ;
/*
* device : remote wakeup , selfpowered
* interface : nothing
* endpoint : halt
*/
buf = ( char * ) urb - > transfer_buffer ;
if ( urb - > transfer_buffer_length > 0 ) {
if ( setup - > bRequestType = = Ep_InRequest ) {
ep2 = find_endpoint ( dum , w_index ) ;
if ( ! ep2 ) {
ret_val = - EOPNOTSUPP ;
break ;
}
buf [ 0 ] = ep2 - > halted ;
} else if ( setup - > bRequestType = =
Dev_InRequest ) {
buf [ 0 ] = ( u8 ) dum - > devstatus ;
} else
buf [ 0 ] = 0 ;
}
if ( urb - > transfer_buffer_length > 1 )
buf [ 1 ] = 0 ;
urb - > actual_length = min_t ( u32 , 2 ,
urb - > transfer_buffer_length ) ;
ret_val = 0 ;
* status = 0 ;
}
break ;
}
return ret_val ;
}
2005-04-16 15:20:36 -07:00
/* drive both sides of the transfers; looks like irq handlers to
* both drivers except the callbacks aren ' t in_irq ( ) .
*/
2011-06-29 16:41:51 +03:00
static void dummy_timer ( unsigned long _dum_hcd )
2005-04-16 15:20:36 -07:00
{
2011-06-29 16:41:51 +03:00
struct dummy_hcd * dum_hcd = ( struct dummy_hcd * ) _dum_hcd ;
struct dummy * dum = dum_hcd - > dum ;
2005-04-16 15:20:36 -07:00
struct urbp * urbp , * tmp ;
unsigned long flags ;
int limit , total ;
int i ;
/* simplistic model for one frame's bandwidth */
switch ( dum - > gadget . speed ) {
case USB_SPEED_LOW :
total = 8 /*bytes*/ * 12 /*packets*/ ;
break ;
case USB_SPEED_FULL :
total = 64 /*bytes*/ * 19 /*packets*/ ;
break ;
case USB_SPEED_HIGH :
total = 512 /*bytes*/ * 13 /*packets*/ * 8 /*uframes*/ ;
break ;
2011-06-29 16:41:52 +03:00
case USB_SPEED_SUPER :
/* Bus speed is 500000 bytes/ms, so use a little less */
total = 490000 ;
break ;
2005-04-16 15:20:36 -07:00
default :
2011-06-29 16:41:51 +03:00
dev_err ( dummy_dev ( dum_hcd ) , " bogus device speed \n " ) ;
2005-04-16 15:20:36 -07:00
return ;
}
/* FIXME if HZ != 1000 this will probably misbehave ... */
/* look at each urb queued by the host side driver */
spin_lock_irqsave ( & dum - > lock , flags ) ;
2011-06-29 16:41:51 +03:00
if ( ! dum_hcd - > udev ) {
dev_err ( dummy_dev ( dum_hcd ) ,
2005-04-16 15:20:36 -07:00
" timer fired with no URBs pending? \n " ) ;
spin_unlock_irqrestore ( & dum - > lock , flags ) ;
return ;
}
for ( i = 0 ; i < DUMMY_ENDPOINTS ; i + + ) {
if ( ! ep_name [ i ] )
break ;
dum - > ep [ i ] . already_seen = 0 ;
}
restart :
2011-06-29 16:41:51 +03:00
list_for_each_entry_safe ( urbp , tmp , & dum_hcd - > urbp_list , urbp_list ) {
2005-04-16 15:20:36 -07:00
struct urb * urb ;
struct dummy_request * req ;
u8 address ;
struct dummy_ep * ep = NULL ;
int type ;
2007-08-24 15:40:10 -04:00
int status = - EINPROGRESS ;
2005-04-16 15:20:36 -07:00
urb = urbp - > urb ;
2007-08-21 15:40:36 -04:00
if ( urb - > unlinked )
2005-04-16 15:20:36 -07:00
goto return_urb ;
2011-06-29 16:41:51 +03:00
else if ( dum_hcd - > rh_state ! = DUMMY_RH_RUNNING )
2005-05-10 15:34:16 -04:00
continue ;
2005-04-16 15:20:36 -07:00
type = usb_pipetype ( urb - > pipe ) ;
/* used up this frame's non-periodic bandwidth?
* FIXME there ' s infinite bandwidth for control and
* periodic transfers . . . unrealistic .
*/
if ( total < = 0 & & type = = PIPE_BULK )
continue ;
/* find the gadget's ep for this request (if configured) */
address = usb_pipeendpoint ( urb - > pipe ) ;
if ( usb_pipein ( urb - > pipe ) )
address | = USB_DIR_IN ;
ep = find_endpoint ( dum , address ) ;
if ( ! ep ) {
/* set_configuration() disagreement */
2011-06-29 16:41:51 +03:00
dev_dbg ( dummy_dev ( dum_hcd ) ,
2005-04-16 15:20:36 -07:00
" no ep configured for urb %p \n " ,
urb ) ;
2007-08-24 15:40:10 -04:00
status = - EPROTO ;
2005-04-16 15:20:36 -07:00
goto return_urb ;
}
if ( ep - > already_seen )
continue ;
ep - > already_seen = 1 ;
if ( ep = = & dum - > ep [ 0 ] & & urb - > error_count ) {
ep - > setup_stage = 1 ; /* a new urb */
urb - > error_count = 0 ;
}
if ( ep - > halted & & ! ep - > setup_stage ) {
/* NOTE: must not be iso! */
2011-06-29 16:41:51 +03:00
dev_dbg ( dummy_dev ( dum_hcd ) , " ep %s halted, urb %p \n " ,
2005-04-16 15:20:36 -07:00
ep - > ep . name , urb ) ;
2007-08-24 15:40:10 -04:00
status = - EPIPE ;
2005-04-16 15:20:36 -07:00
goto return_urb ;
}
/* FIXME make sure both ends agree on maxpacket */
/* handle control requests */
if ( ep = = & dum - > ep [ 0 ] & & ep - > setup_stage ) {
struct usb_ctrlrequest setup ;
int value = 1 ;
setup = * ( struct usb_ctrlrequest * ) urb - > setup_packet ;
/* paranoia, in case of stale queued data */
list_for_each_entry ( req , & ep - > queue , queue ) {
list_del_init ( & req - > queue ) ;
req - > req . status = - EOVERFLOW ;
2005-05-03 16:15:43 -04:00
dev_dbg ( udc_dev ( dum ) , " stale req = %p \n " ,
2005-04-16 15:20:36 -07:00
req ) ;
spin_unlock ( & dum - > lock ) ;
req - > req . complete ( & ep - > ep , & req - > req ) ;
spin_lock ( & dum - > lock ) ;
ep - > already_seen = 0 ;
goto restart ;
}
/* gadget driver never sees set_address or operations
* on standard feature flags . some hardware doesn ' t
* even expose them .
*/
ep - > last_io = jiffies ;
ep - > setup_stage = 0 ;
ep - > halted = 0 ;
2011-06-29 16:41:51 +03:00
value = handle_control_request ( dum_hcd , urb , & setup ,
2010-11-01 17:38:05 +02:00
& status ) ;
2005-04-16 15:20:36 -07:00
/* gadget driver handles all other requests. block
* until setup ( ) returns ; no reentrancy issues etc .
*/
if ( value > 0 ) {
spin_unlock ( & dum - > lock ) ;
value = dum - > driver - > setup ( & dum - > gadget ,
& setup ) ;
spin_lock ( & dum - > lock ) ;
if ( value > = 0 ) {
/* no delays (max 64KB data stage) */
limit = 64 * 1024 ;
goto treat_control_like_bulk ;
}
/* error, see below */
}
if ( value < 0 ) {
if ( value ! = - EOPNOTSUPP )
2005-05-03 16:15:43 -04:00
dev_dbg ( udc_dev ( dum ) ,
2005-04-16 15:20:36 -07:00
" setup --> %d \n " ,
value ) ;
2007-08-24 15:40:10 -04:00
status = - EPIPE ;
2005-04-16 15:20:36 -07:00
urb - > actual_length = 0 ;
}
goto return_urb ;
}
/* non-control requests */
limit = total ;
switch ( usb_pipetype ( urb - > pipe ) ) {
case PIPE_ISOCHRONOUS :
/* FIXME is it urb->interval since the last xfer?
* use urb - > iso_frame_desc [ i ] .
* complete whether or not ep has requests queued .
* report random errors , to debug drivers .
*/
limit = max ( limit , periodic_bytes ( dum , ep ) ) ;
2007-08-24 15:40:10 -04:00
status = - ENOSYS ;
2005-04-16 15:20:36 -07:00
break ;
case PIPE_INTERRUPT :
/* FIXME is it urb->interval since the last xfer?
* this almost certainly polls too fast .
*/
limit = max ( limit , periodic_bytes ( dum , ep ) ) ;
/* FALLTHROUGH */
// case PIPE_BULK: case PIPE_CONTROL:
default :
treat_control_like_bulk :
ep - > last_io = jiffies ;
2007-08-24 15:40:10 -04:00
total = transfer ( dum , urb , ep , limit , & status ) ;
2005-04-16 15:20:36 -07:00
break ;
}
/* incomplete transfer? */
2007-08-24 15:40:10 -04:00
if ( status = = - EINPROGRESS )
2005-04-16 15:20:36 -07:00
continue ;
return_urb :
list_del ( & urbp - > urbp_list ) ;
kfree ( urbp ) ;
if ( ep )
ep - > already_seen = ep - > setup_stage = 0 ;
2011-06-29 16:41:51 +03:00
usb_hcd_unlink_urb_from_ep ( dummy_hcd_to_hcd ( dum_hcd ) , urb ) ;
2005-04-16 15:20:36 -07:00
spin_unlock ( & dum - > lock ) ;
2011-06-29 16:41:51 +03:00
usb_hcd_giveback_urb ( dummy_hcd_to_hcd ( dum_hcd ) , urb , status ) ;
2005-04-16 15:20:36 -07:00
spin_lock ( & dum - > lock ) ;
goto restart ;
}
2011-06-29 16:41:51 +03:00
if ( list_empty ( & dum_hcd - > urbp_list ) ) {
usb_put_dev ( dum_hcd - > udev ) ;
dum_hcd - > udev = NULL ;
} else if ( dum_hcd - > rh_state = = DUMMY_RH_RUNNING ) {
2005-05-10 15:34:16 -04:00
/* want a 1 msec delay here */
2011-06-29 16:41:51 +03:00
mod_timer ( & dum_hcd - > timer , jiffies + msecs_to_jiffies ( 1 ) ) ;
2005-04-16 15:20:36 -07:00
}
spin_unlock_irqrestore ( & dum - > lock , flags ) ;
}
/*-------------------------------------------------------------------------*/
# define PORT_C_MASK \
2005-04-29 16:30:48 -04:00
( ( USB_PORT_STAT_C_CONNECTION \
| USB_PORT_STAT_C_ENABLE \
| USB_PORT_STAT_C_SUSPEND \
| USB_PORT_STAT_C_OVERCURRENT \
| USB_PORT_STAT_C_RESET ) < < 16 )
2005-04-16 15:20:36 -07:00
static int dummy_hub_status ( struct usb_hcd * hcd , char * buf )
{
2011-06-29 16:41:51 +03:00
struct dummy_hcd * dum_hcd ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
2005-05-10 15:34:16 -04:00
int retval = 0 ;
2005-04-16 15:20:36 -07:00
2011-06-29 16:41:51 +03:00
dum_hcd = hcd_to_dummy_hcd ( hcd ) ;
2005-04-16 15:20:36 -07:00
2011-06-29 16:41:51 +03:00
spin_lock_irqsave ( & dum_hcd - > dum - > lock , flags ) ;
2010-06-22 16:39:10 -04:00
if ( ! HCD_HW_ACCESSIBLE ( hcd ) )
2005-05-10 15:34:16 -04:00
goto done ;
2005-05-03 16:24:04 -04:00
2011-06-29 16:41:51 +03:00
if ( dum_hcd - > resuming & & time_after_eq ( jiffies , dum_hcd - > re_timeout ) ) {
dum_hcd - > port_status | = ( USB_PORT_STAT_C_SUSPEND < < 16 ) ;
dum_hcd - > port_status & = ~ USB_PORT_STAT_SUSPEND ;
set_link_state ( dum_hcd ) ;
2005-05-03 16:24:04 -04:00
}
2011-06-29 16:41:51 +03:00
if ( ( dum_hcd - > port_status & PORT_C_MASK ) ! = 0 ) {
2005-04-16 15:20:36 -07:00
* buf = ( 1 < < 1 ) ;
2011-06-29 16:41:51 +03:00
dev_dbg ( dummy_dev ( dum_hcd ) , " port status 0x%08x has changes \n " ,
dum_hcd - > port_status ) ;
2005-04-16 15:20:36 -07:00
retval = 1 ;
2011-06-29 16:41:51 +03:00
if ( dum_hcd - > rh_state = = DUMMY_RH_SUSPENDED )
2005-05-10 15:34:16 -04:00
usb_hcd_resume_root_hub ( hcd ) ;
2005-04-16 15:20:36 -07:00
}
2005-05-10 15:34:16 -04:00
done :
2011-06-29 16:41:51 +03:00
spin_unlock_irqrestore ( & dum_hcd - > dum - > lock , flags ) ;
2005-04-16 15:20:36 -07:00
return retval ;
}
2011-06-29 16:41:52 +03:00
static inline void
ss_hub_descriptor ( struct usb_hub_descriptor * desc )
{
memset ( desc , 0 , sizeof * desc ) ;
desc - > bDescriptorType = 0x2a ;
desc - > bDescLength = 12 ;
desc - > wHubCharacteristics = cpu_to_le16 ( 0x0001 ) ;
desc - > bNbrPorts = 1 ;
desc - > u . ss . bHubHdrDecLat = 0x04 ; /* Worst case: 0.4 micro sec*/
desc - > u . ss . DeviceRemovable = 0xffff ;
}
2005-04-16 15:20:36 -07:00
static inline void
hub_descriptor ( struct usb_hub_descriptor * desc )
{
memset ( desc , 0 , sizeof * desc ) ;
desc - > bDescriptorType = 0x29 ;
desc - > bDescLength = 9 ;
2008-04-28 07:00:16 +01:00
desc - > wHubCharacteristics = cpu_to_le16 ( 0x0001 ) ;
2005-04-16 15:20:36 -07:00
desc - > bNbrPorts = 1 ;
2001-09-17 00:00:00 -07:00
desc - > u . hs . DeviceRemovable [ 0 ] = 0xff ;
desc - > u . hs . DeviceRemovable [ 1 ] = 0xff ;
2005-04-16 15:20:36 -07:00
}
static int dummy_hub_control (
struct usb_hcd * hcd ,
u16 typeReq ,
u16 wValue ,
u16 wIndex ,
char * buf ,
u16 wLength
) {
2011-06-29 16:41:51 +03:00
struct dummy_hcd * dum_hcd ;
2005-04-16 15:20:36 -07:00
int retval = 0 ;
unsigned long flags ;
2010-06-22 16:39:10 -04:00
if ( ! HCD_HW_ACCESSIBLE ( hcd ) )
2005-05-10 15:34:16 -04:00
return - ETIMEDOUT ;
2011-06-29 16:41:51 +03:00
dum_hcd = hcd_to_dummy_hcd ( hcd ) ;
spin_lock_irqsave ( & dum_hcd - > dum - > lock , flags ) ;
2005-04-16 15:20:36 -07:00
switch ( typeReq ) {
case ClearHubFeature :
break ;
case ClearPortFeature :
switch ( wValue ) {
case USB_PORT_FEAT_SUSPEND :
2011-06-29 16:41:52 +03:00
if ( hcd - > speed = = HCD_USB3 ) {
dev_dbg ( dummy_dev ( dum_hcd ) ,
" USB_PORT_FEAT_SUSPEND req not "
" supported for USB 3.0 roothub \n " ) ;
goto error ;
}
2011-06-29 16:41:51 +03:00
if ( dum_hcd - > port_status & USB_PORT_STAT_SUSPEND ) {
2005-04-16 15:20:36 -07:00
/* 20msec resume signaling */
2011-06-29 16:41:51 +03:00
dum_hcd - > resuming = 1 ;
dum_hcd - > re_timeout = jiffies +
2005-05-03 16:24:04 -04:00
msecs_to_jiffies ( 20 ) ;
2005-04-16 15:20:36 -07:00
}
break ;
case USB_PORT_FEAT_POWER :
2011-06-29 16:41:52 +03:00
if ( hcd - > speed = = HCD_USB3 ) {
if ( dum_hcd - > port_status & USB_PORT_STAT_POWER )
dev_dbg ( dummy_dev ( dum_hcd ) ,
" power-off \n " ) ;
} else
if ( dum_hcd - > port_status &
USB_SS_PORT_STAT_POWER )
dev_dbg ( dummy_dev ( dum_hcd ) ,
" power-off \n " ) ;
2005-05-03 16:24:04 -04:00
/* FALLS THROUGH */
2005-04-16 15:20:36 -07:00
default :
2011-06-29 16:41:51 +03:00
dum_hcd - > port_status & = ~ ( 1 < < wValue ) ;
set_link_state ( dum_hcd ) ;
2005-04-16 15:20:36 -07:00
}
break ;
case GetHubDescriptor :
2011-06-29 16:41:52 +03:00
if ( hcd - > speed = = HCD_USB3 & &
( wLength < USB_DT_SS_HUB_SIZE | |
wValue ! = ( USB_DT_SS_HUB < < 8 ) ) ) {
dev_dbg ( dummy_dev ( dum_hcd ) ,
" Wrong hub descriptor type for "
" USB 3.0 roothub. \n " ) ;
goto error ;
}
if ( hcd - > speed = = HCD_USB3 )
ss_hub_descriptor ( ( struct usb_hub_descriptor * ) buf ) ;
else
hub_descriptor ( ( struct usb_hub_descriptor * ) buf ) ;
2005-04-16 15:20:36 -07:00
break ;
case GetHubStatus :
2009-02-11 14:11:36 -08:00
* ( __le32 * ) buf = cpu_to_le32 ( 0 ) ;
2005-04-16 15:20:36 -07:00
break ;
case GetPortStatus :
if ( wIndex ! = 1 )
retval = - EPIPE ;
/* whoever resets or resumes must GetPortStatus to
* complete it ! !
*/
2011-06-29 16:41:51 +03:00
if ( dum_hcd - > resuming & &
time_after_eq ( jiffies , dum_hcd - > re_timeout ) ) {
dum_hcd - > port_status | = ( USB_PORT_STAT_C_SUSPEND < < 16 ) ;
dum_hcd - > port_status & = ~ USB_PORT_STAT_SUSPEND ;
2005-04-16 15:20:36 -07:00
}
2011-06-29 16:41:51 +03:00
if ( ( dum_hcd - > port_status & USB_PORT_STAT_RESET ) ! = 0 & &
time_after_eq ( jiffies , dum_hcd - > re_timeout ) ) {
dum_hcd - > port_status | = ( USB_PORT_STAT_C_RESET < < 16 ) ;
dum_hcd - > port_status & = ~ USB_PORT_STAT_RESET ;
if ( dum_hcd - > dum - > pullup ) {
dum_hcd - > port_status | = USB_PORT_STAT_ENABLE ;
2011-06-29 16:41:52 +03:00
if ( hcd - > speed < HCD_USB3 ) {
switch ( dum_hcd - > dum - > gadget . speed ) {
case USB_SPEED_HIGH :
dum_hcd - > port_status | =
USB_PORT_STAT_HIGH_SPEED ;
break ;
case USB_SPEED_LOW :
dum_hcd - > dum - > gadget . ep0 - >
maxpacket = 8 ;
dum_hcd - > port_status | =
USB_PORT_STAT_LOW_SPEED ;
break ;
default :
dum_hcd - > dum - > gadget . speed =
USB_SPEED_FULL ;
break ;
}
2005-04-16 15:20:36 -07:00
}
}
}
2011-06-29 16:41:51 +03:00
set_link_state ( dum_hcd ) ;
( ( __le16 * ) buf ) [ 0 ] = cpu_to_le16 ( dum_hcd - > port_status ) ;
( ( __le16 * ) buf ) [ 1 ] = cpu_to_le16 ( dum_hcd - > port_status > > 16 ) ;
2005-04-16 15:20:36 -07:00
break ;
case SetHubFeature :
retval = - EPIPE ;
break ;
case SetPortFeature :
switch ( wValue ) {
2011-06-29 16:41:52 +03:00
case USB_PORT_FEAT_LINK_STATE :
if ( hcd - > speed ! = HCD_USB3 ) {
dev_dbg ( dummy_dev ( dum_hcd ) ,
" USB_PORT_FEAT_LINK_STATE req not "
" supported for USB 2.0 roothub \n " ) ;
goto error ;
}
/*
* Since this is dummy we don ' t have an actual link so
* there is nothing to do for the SET_LINK_STATE cmd
*/
break ;
case USB_PORT_FEAT_U1_TIMEOUT :
case USB_PORT_FEAT_U2_TIMEOUT :
/* TODO: add suspend/resume support! */
if ( hcd - > speed ! = HCD_USB3 ) {
dev_dbg ( dummy_dev ( dum_hcd ) ,
" USB_PORT_FEAT_U1/2_TIMEOUT req not "
" supported for USB 2.0 roothub \n " ) ;
goto error ;
}
break ;
2005-04-16 15:20:36 -07:00
case USB_PORT_FEAT_SUSPEND :
2011-06-29 16:41:52 +03:00
/* Applicable only for USB2.0 hub */
if ( hcd - > speed = = HCD_USB3 ) {
dev_dbg ( dummy_dev ( dum_hcd ) ,
" USB_PORT_FEAT_SUSPEND req not "
" supported for USB 3.0 roothub \n " ) ;
goto error ;
}
2011-06-29 16:41:51 +03:00
if ( dum_hcd - > active ) {
dum_hcd - > port_status | = USB_PORT_STAT_SUSPEND ;
2005-05-03 16:24:04 -04:00
/* HNP would happen here; for now we
* assume b_bus_req is always true .
*/
2011-06-29 16:41:51 +03:00
set_link_state ( dum_hcd ) ;
2005-05-03 16:24:04 -04:00
if ( ( ( 1 < < USB_DEVICE_B_HNP_ENABLE )
2011-06-29 16:41:51 +03:00
& dum_hcd - > dum - > devstatus ) ! = 0 )
dev_dbg ( dummy_dev ( dum_hcd ) ,
2005-05-02 11:25:17 -04:00
" no HNP yet! \n " ) ;
2005-04-16 15:20:36 -07:00
}
break ;
2005-05-03 16:24:04 -04:00
case USB_PORT_FEAT_POWER :
2011-06-29 16:41:52 +03:00
if ( hcd - > speed = = HCD_USB3 )
dum_hcd - > port_status | = USB_SS_PORT_STAT_POWER ;
else
dum_hcd - > port_status | = USB_PORT_STAT_POWER ;
2011-06-29 16:41:51 +03:00
set_link_state ( dum_hcd ) ;
2005-05-03 16:24:04 -04:00
break ;
2011-06-29 16:41:52 +03:00
case USB_PORT_FEAT_BH_PORT_RESET :
/* Applicable only for USB3.0 hub */
if ( hcd - > speed ! = HCD_USB3 ) {
dev_dbg ( dummy_dev ( dum_hcd ) ,
" USB_PORT_FEAT_BH_PORT_RESET req not "
" supported for USB 2.0 roothub \n " ) ;
goto error ;
}
/* FALLS THROUGH */
2005-04-16 15:20:36 -07:00
case USB_PORT_FEAT_RESET :
2005-05-03 16:24:04 -04:00
/* if it's already enabled, disable */
2011-06-29 16:41:52 +03:00
if ( hcd - > speed = = HCD_USB3 ) {
dum_hcd - > port_status = 0 ;
dum_hcd - > port_status =
( USB_SS_PORT_STAT_POWER |
USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_RESET ) ;
} else
dum_hcd - > port_status & = ~ ( USB_PORT_STAT_ENABLE
2005-05-03 16:24:04 -04:00
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED ) ;
2011-06-29 16:41:51 +03:00
/*
* We want to reset device status . All but the
* Self powered feature
*/
dum_hcd - > dum - > devstatus & =
( 1 < < USB_DEVICE_SELF_POWERED ) ;
2011-06-29 16:41:52 +03:00
/*
* FIXME USB3 .0 : what is the correct reset signaling
* interval ? Is it still 50 msec as for HS ?
*/
2011-06-29 16:41:51 +03:00
dum_hcd - > re_timeout = jiffies + msecs_to_jiffies ( 50 ) ;
2005-05-03 16:24:04 -04:00
/* FALLS THROUGH */
2005-04-16 15:20:36 -07:00
default :
2011-06-29 16:41:52 +03:00
if ( hcd - > speed = = HCD_USB3 ) {
if ( ( dum_hcd - > port_status &
USB_SS_PORT_STAT_POWER ) ! = 0 ) {
dum_hcd - > port_status | = ( 1 < < wValue ) ;
set_link_state ( dum_hcd ) ;
}
} else
if ( ( dum_hcd - > port_status &
USB_PORT_STAT_POWER ) ! = 0 ) {
dum_hcd - > port_status | = ( 1 < < wValue ) ;
set_link_state ( dum_hcd ) ;
}
}
break ;
case GetPortErrorCount :
if ( hcd - > speed ! = HCD_USB3 ) {
dev_dbg ( dummy_dev ( dum_hcd ) ,
" GetPortErrorCount req not "
" supported for USB 2.0 roothub \n " ) ;
goto error ;
}
/* We'll always return 0 since this is a dummy hub */
* ( __le32 * ) buf = cpu_to_le32 ( 0 ) ;
break ;
case SetHubDepth :
if ( hcd - > speed ! = HCD_USB3 ) {
dev_dbg ( dummy_dev ( dum_hcd ) ,
" SetHubDepth req not supported for "
" USB 2.0 roothub \n " ) ;
goto error ;
2005-04-16 15:20:36 -07:00
}
break ;
default :
2011-06-29 16:41:51 +03:00
dev_dbg ( dummy_dev ( dum_hcd ) ,
2005-04-16 15:20:36 -07:00
" hub control req%04x v%04x i%04x l%d \n " ,
typeReq , wValue , wIndex , wLength ) ;
2011-06-29 16:41:52 +03:00
error :
2005-04-16 15:20:36 -07:00
/* "protocol stall" on error */
retval = - EPIPE ;
}
2011-06-29 16:41:51 +03:00
spin_unlock_irqrestore ( & dum_hcd - > dum - > lock , flags ) ;
2005-05-03 16:27:26 -04:00
2011-06-29 16:41:51 +03:00
if ( ( dum_hcd - > port_status & PORT_C_MASK ) ! = 0 )
2005-05-03 16:27:26 -04:00
usb_hcd_poll_rh_status ( hcd ) ;
2005-04-16 15:20:36 -07:00
return retval ;
}
2005-10-13 17:08:02 -04:00
static int dummy_bus_suspend ( struct usb_hcd * hcd )
2005-05-10 15:34:16 -04:00
{
2011-06-29 16:41:51 +03:00
struct dummy_hcd * dum_hcd = hcd_to_dummy_hcd ( hcd ) ;
2005-05-10 15:34:16 -04:00
2008-03-03 16:08:34 -08:00
dev_dbg ( & hcd - > self . root_hub - > dev , " %s \n " , __func__ ) ;
2005-11-29 12:08:15 -05:00
2011-06-29 16:41:51 +03:00
spin_lock_irq ( & dum_hcd - > dum - > lock ) ;
dum_hcd - > rh_state = DUMMY_RH_SUSPENDED ;
set_link_state ( dum_hcd ) ;
2005-11-29 12:08:15 -05:00
hcd - > state = HC_STATE_SUSPENDED ;
2011-06-29 16:41:51 +03:00
spin_unlock_irq ( & dum_hcd - > dum - > lock ) ;
2005-05-10 15:34:16 -04:00
return 0 ;
}
2005-10-13 17:08:02 -04:00
static int dummy_bus_resume ( struct usb_hcd * hcd )
2005-05-10 15:34:16 -04:00
{
2011-06-29 16:41:51 +03:00
struct dummy_hcd * dum_hcd = hcd_to_dummy_hcd ( hcd ) ;
2005-11-29 12:08:15 -05:00
int rc = 0 ;
2008-03-03 16:08:34 -08:00
dev_dbg ( & hcd - > self . root_hub - > dev , " %s \n " , __func__ ) ;
2005-05-10 15:34:16 -04:00
2011-06-29 16:41:51 +03:00
spin_lock_irq ( & dum_hcd - > dum - > lock ) ;
2010-06-22 16:39:10 -04:00
if ( ! HCD_HW_ACCESSIBLE ( hcd ) ) {
2007-06-21 16:25:35 -04:00
rc = - ESHUTDOWN ;
2005-11-29 12:08:15 -05:00
} else {
2011-06-29 16:41:51 +03:00
dum_hcd - > rh_state = DUMMY_RH_RUNNING ;
set_link_state ( dum_hcd ) ;
if ( ! list_empty ( & dum_hcd - > urbp_list ) )
mod_timer ( & dum_hcd - > timer , jiffies ) ;
2005-11-29 12:08:15 -05:00
hcd - > state = HC_STATE_RUNNING ;
}
2011-06-29 16:41:51 +03:00
spin_unlock_irq ( & dum_hcd - > dum - > lock ) ;
2005-11-29 12:08:15 -05:00
return rc ;
2005-05-10 15:34:16 -04:00
}
2005-04-16 15:20:36 -07:00
/*-------------------------------------------------------------------------*/
static inline ssize_t
show_urb ( char * buf , size_t size , struct urb * urb )
{
int ep = usb_pipeendpoint ( urb - > pipe ) ;
return snprintf ( buf , size ,
" urb/%p %s ep%d%s%s len %d/%d \n " ,
urb ,
( { char * s ;
switch ( urb - > dev - > speed ) {
2011-06-28 16:33:52 +03:00
case USB_SPEED_LOW :
s = " ls " ;
break ;
case USB_SPEED_FULL :
s = " fs " ;
break ;
case USB_SPEED_HIGH :
s = " hs " ;
break ;
2011-06-29 16:41:52 +03:00
case USB_SPEED_SUPER :
s = " ss " ;
break ;
2011-06-28 16:33:52 +03:00
default :
s = " ? " ;
break ;
2005-04-16 15:20:36 -07:00
} ; s ; } ) ,
ep , ep ? ( usb_pipein ( urb - > pipe ) ? " in " : " out " ) : " " ,
( { char * s ; \
switch ( usb_pipetype ( urb - > pipe ) ) { \
2011-06-28 16:33:52 +03:00
case PIPE_CONTROL : \
s = " " ; \
break ; \
case PIPE_BULK : \
s = " -bulk " ; \
break ; \
case PIPE_INTERRUPT : \
s = " -int " ; \
break ; \
default : \
s = " -iso " ; \
break ; \
2005-04-16 15:20:36 -07:00
} ; s ; } ) ,
urb - > actual_length , urb - > transfer_buffer_length ) ;
}
static ssize_t
2005-05-17 06:43:37 -04:00
show_urbs ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
2011-06-29 16:41:51 +03:00
struct dummy_hcd * dum_hcd = hcd_to_dummy_hcd ( hcd ) ;
2005-04-16 15:20:36 -07:00
struct urbp * urbp ;
size_t size = 0 ;
unsigned long flags ;
2011-06-29 16:41:51 +03:00
spin_lock_irqsave ( & dum_hcd - > dum - > lock , flags ) ;
list_for_each_entry ( urbp , & dum_hcd - > urbp_list , urbp_list ) {
2005-04-16 15:20:36 -07:00
size_t temp ;
temp = show_urb ( buf , PAGE_SIZE - size , urbp - > urb ) ;
buf + = temp ;
size + = temp ;
}
2011-06-29 16:41:51 +03:00
spin_unlock_irqrestore ( & dum_hcd - > dum - > lock , flags ) ;
2005-04-16 15:20:36 -07:00
return size ;
}
static DEVICE_ATTR ( urbs , S_IRUGO , show_urbs , NULL ) ;
2011-06-29 16:41:52 +03:00
static int dummy_start_ss ( struct dummy_hcd * dum_hcd )
{
init_timer ( & dum_hcd - > timer ) ;
dum_hcd - > timer . function = dummy_timer ;
dum_hcd - > timer . data = ( unsigned long ) dum_hcd ;
dum_hcd - > rh_state = DUMMY_RH_RUNNING ;
INIT_LIST_HEAD ( & dum_hcd - > urbp_list ) ;
dummy_hcd_to_hcd ( dum_hcd ) - > power_budget = POWER_BUDGET ;
dummy_hcd_to_hcd ( dum_hcd ) - > state = HC_STATE_RUNNING ;
dummy_hcd_to_hcd ( dum_hcd ) - > uses_new_polling = 1 ;
# ifdef CONFIG_USB_OTG
dummy_hcd_to_hcd ( dum_hcd ) - > self . otg_port = 1 ;
# endif
return 0 ;
/* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
return device_create_file ( dummy_dev ( dum_hcd ) , & dev_attr_urbs ) ;
}
2011-06-29 16:41:51 +03:00
static int dummy_start ( struct usb_hcd * hcd )
2005-04-16 15:20:36 -07:00
{
2011-06-29 16:41:51 +03:00
struct dummy_hcd * dum_hcd = hcd_to_dummy_hcd ( hcd ) ;
2005-04-16 15:20:36 -07:00
/*
* MASTER side init . . . we emulate a root hub that ' ll only ever
* talk to one device ( the slave side ) . Also appears in sysfs ,
* just like more familiar pci - based HCDs .
*/
2011-06-29 16:41:52 +03:00
if ( ! usb_hcd_is_primary_hcd ( hcd ) )
return dummy_start_ss ( dum_hcd ) ;
2011-06-29 16:41:51 +03:00
spin_lock_init ( & dum_hcd - > dum - > lock ) ;
init_timer ( & dum_hcd - > timer ) ;
dum_hcd - > timer . function = dummy_timer ;
dum_hcd - > timer . data = ( unsigned long ) dum_hcd ;
dum_hcd - > rh_state = DUMMY_RH_RUNNING ;
2005-04-16 15:20:36 -07:00
2011-06-29 16:41:51 +03:00
INIT_LIST_HEAD ( & dum_hcd - > urbp_list ) ;
2005-04-16 15:20:36 -07:00
2007-12-06 11:10:39 -05:00
hcd - > power_budget = POWER_BUDGET ;
2005-04-16 15:20:36 -07:00
hcd - > state = HC_STATE_RUNNING ;
2005-05-03 16:27:26 -04:00
hcd - > uses_new_polling = 1 ;
2005-04-16 15:20:36 -07:00
2005-05-02 11:25:17 -04:00
# ifdef CONFIG_USB_OTG
hcd - > self . otg_port = 1 ;
# endif
2005-04-16 15:20:36 -07:00
/* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
2011-06-29 16:41:51 +03:00
return device_create_file ( dummy_dev ( dum_hcd ) , & dev_attr_urbs ) ;
2005-04-16 15:20:36 -07:00
}
static void dummy_stop ( struct usb_hcd * hcd )
{
struct dummy * dum ;
2011-06-29 16:41:51 +03:00
dum = ( hcd_to_dummy_hcd ( hcd ) ) - > dum ;
device_remove_file ( dummy_dev ( hcd_to_dummy_hcd ( hcd ) ) , & dev_attr_urbs ) ;
usb_gadget_unregister_driver ( dum - > driver ) ;
dev_info ( dummy_dev ( hcd_to_dummy_hcd ( hcd ) ) , " stopped \n " ) ;
2005-04-16 15:20:36 -07:00
}
/*-------------------------------------------------------------------------*/
static int dummy_h_get_frame ( struct usb_hcd * hcd )
{
return dummy_g_get_frame ( NULL ) ;
}
2011-06-29 16:41:51 +03:00
static int dummy_setup ( struct usb_hcd * hcd )
{
if ( usb_hcd_is_primary_hcd ( hcd ) ) {
the_controller . hs_hcd = hcd_to_dummy_hcd ( hcd ) ;
the_controller . hs_hcd - > dum = & the_controller ;
2011-06-29 16:41:52 +03:00
/*
* Mark the first roothub as being USB 2.0 .
* The USB 3.0 roothub will be registered later by
* dummy_hcd_probe ( )
*/
2011-06-29 16:41:51 +03:00
hcd - > speed = HCD_USB2 ;
hcd - > self . root_hub - > speed = USB_SPEED_HIGH ;
2011-06-29 16:41:52 +03:00
} else {
the_controller . ss_hcd = hcd_to_dummy_hcd ( hcd ) ;
the_controller . ss_hcd - > dum = & the_controller ;
hcd - > speed = HCD_USB3 ;
hcd - > self . root_hub - > speed = USB_SPEED_SUPER ;
2011-06-29 16:41:51 +03:00
}
return 0 ;
}
2011-06-29 16:41:52 +03:00
/* Change a group of bulk endpoints to support multiple stream IDs */
int dummy_alloc_streams ( struct usb_hcd * hcd , struct usb_device * udev ,
struct usb_host_endpoint * * eps , unsigned int num_eps ,
unsigned int num_streams , gfp_t mem_flags )
{
if ( hcd - > speed ! = HCD_USB3 )
dev_dbg ( dummy_dev ( hcd_to_dummy_hcd ( hcd ) ) ,
" %s() - ERROR! Not supported for USB2.0 roothub \n " ,
__func__ ) ;
return 0 ;
}
/* Reverts a group of bulk endpoints back to not using stream IDs. */
int dummy_free_streams ( struct usb_hcd * hcd , struct usb_device * udev ,
struct usb_host_endpoint * * eps , unsigned int num_eps ,
gfp_t mem_flags )
{
if ( hcd - > speed ! = HCD_USB3 )
dev_dbg ( dummy_dev ( hcd_to_dummy_hcd ( hcd ) ) ,
" %s() - ERROR! Not supported for USB2.0 roothub \n " ,
__func__ ) ;
return 0 ;
}
static struct hc_driver dummy_hcd = {
2005-04-16 15:20:36 -07:00
. description = ( char * ) driver_name ,
. product_desc = " Dummy host controller " ,
2011-06-29 16:41:51 +03:00
. hcd_priv_size = sizeof ( struct dummy_hcd ) ,
2005-04-16 15:20:36 -07:00
2011-06-29 16:41:52 +03:00
. flags = HCD_USB3 | HCD_SHARED ,
2005-04-16 15:20:36 -07:00
2011-06-29 16:41:51 +03:00
. reset = dummy_setup ,
2005-04-16 15:20:36 -07:00
. start = dummy_start ,
. stop = dummy_stop ,
. urb_enqueue = dummy_urb_enqueue ,
. urb_dequeue = dummy_urb_dequeue ,
. get_frame_number = dummy_h_get_frame ,
. hub_status_data = dummy_hub_status ,
. hub_control = dummy_hub_control ,
2005-10-13 17:08:02 -04:00
. bus_suspend = dummy_bus_suspend ,
. bus_resume = dummy_bus_resume ,
2011-06-29 16:41:52 +03:00
. alloc_streams = dummy_alloc_streams ,
. free_streams = dummy_free_streams ,
2005-04-16 15:20:36 -07:00
} ;
2005-11-14 12:16:30 -05:00
static int dummy_hcd_probe ( struct platform_device * pdev )
2005-04-16 15:20:36 -07:00
{
2011-06-29 16:41:51 +03:00
struct usb_hcd * hs_hcd ;
2011-06-29 16:41:52 +03:00
struct usb_hcd * ss_hcd ;
2005-04-16 15:20:36 -07:00
int retval ;
2005-11-14 12:16:30 -05:00
dev_info ( & pdev - > dev , " %s, driver " DRIVER_VERSION " \n " , driver_desc ) ;
2005-04-16 15:20:36 -07:00
2011-06-29 16:41:52 +03:00
if ( ! mod_data . is_super_speed )
dummy_hcd . flags = HCD_USB2 ;
2011-06-29 16:41:51 +03:00
hs_hcd = usb_create_hcd ( & dummy_hcd , & pdev - > dev , dev_name ( & pdev - > dev ) ) ;
if ( ! hs_hcd )
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
2011-06-29 16:41:51 +03:00
hs_hcd - > has_tt = 1 ;
2005-04-16 15:20:36 -07:00
2011-06-29 16:41:51 +03:00
retval = usb_add_hcd ( hs_hcd , 0 , 0 ) ;
2005-04-16 15:20:36 -07:00
if ( retval ! = 0 ) {
2011-06-29 16:41:51 +03:00
usb_put_hcd ( hs_hcd ) ;
2011-06-29 16:41:52 +03:00
return retval ;
2005-04-16 15:20:36 -07:00
}
2011-06-29 16:41:52 +03:00
if ( mod_data . is_super_speed ) {
ss_hcd = usb_create_shared_hcd ( & dummy_hcd , & pdev - > dev ,
dev_name ( & pdev - > dev ) , hs_hcd ) ;
if ( ! ss_hcd ) {
retval = - ENOMEM ;
goto dealloc_usb2_hcd ;
}
retval = usb_add_hcd ( ss_hcd , 0 , 0 ) ;
if ( retval )
goto put_usb3_hcd ;
}
return 0 ;
put_usb3_hcd :
usb_put_hcd ( ss_hcd ) ;
dealloc_usb2_hcd :
usb_put_hcd ( hs_hcd ) ;
the_controller . hs_hcd = the_controller . ss_hcd = NULL ;
2005-04-16 15:20:36 -07:00
return retval ;
}
2011-06-29 16:41:51 +03:00
static int dummy_hcd_remove ( struct platform_device * pdev )
2005-04-16 15:20:36 -07:00
{
2011-06-29 16:41:51 +03:00
struct dummy * dum ;
dum = ( hcd_to_dummy_hcd ( platform_get_drvdata ( pdev ) ) ) - > dum ;
2011-06-29 16:41:52 +03:00
if ( dum - > ss_hcd ) {
usb_remove_hcd ( dummy_hcd_to_hcd ( dum - > ss_hcd ) ) ;
usb_put_hcd ( dummy_hcd_to_hcd ( dum - > ss_hcd ) ) ;
}
2011-06-29 16:41:51 +03:00
usb_remove_hcd ( dummy_hcd_to_hcd ( dum - > hs_hcd ) ) ;
usb_put_hcd ( dummy_hcd_to_hcd ( dum - > hs_hcd ) ) ;
2011-06-29 16:41:52 +03:00
2011-06-29 16:41:51 +03:00
the_controller . hs_hcd = NULL ;
2011-06-29 16:41:52 +03:00
the_controller . ss_hcd = NULL ;
2005-04-16 15:20:36 -07:00
2005-05-03 16:15:43 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2005-11-14 12:16:30 -05:00
static int dummy_hcd_suspend ( struct platform_device * pdev , pm_message_t state )
2005-05-10 15:34:16 -04:00
{
struct usb_hcd * hcd ;
2011-06-29 16:41:51 +03:00
struct dummy_hcd * dum_hcd ;
2005-11-29 12:08:15 -05:00
int rc = 0 ;
2005-05-10 15:34:16 -04:00
2008-03-03 16:08:34 -08:00
dev_dbg ( & pdev - > dev , " %s \n " , __func__ ) ;
2005-05-10 15:34:16 -04:00
2005-11-29 12:08:15 -05:00
hcd = platform_get_drvdata ( pdev ) ;
2011-06-29 16:41:51 +03:00
dum_hcd = hcd_to_dummy_hcd ( hcd ) ;
if ( dum_hcd - > rh_state = = DUMMY_RH_RUNNING ) {
2005-11-29 12:08:15 -05:00
dev_warn ( & pdev - > dev , " Root hub isn't suspended! \n " ) ;
rc = - EBUSY ;
} else
clear_bit ( HCD_FLAG_HW_ACCESSIBLE , & hcd - > flags ) ;
return rc ;
2005-05-10 15:34:16 -04:00
}
2005-11-14 12:16:30 -05:00
static int dummy_hcd_resume ( struct platform_device * pdev )
2005-05-10 15:34:16 -04:00
{
struct usb_hcd * hcd ;
2008-03-03 16:08:34 -08:00
dev_dbg ( & pdev - > dev , " %s \n " , __func__ ) ;
2005-05-10 15:34:16 -04:00
2005-11-29 12:08:15 -05:00
hcd = platform_get_drvdata ( pdev ) ;
set_bit ( HCD_FLAG_HW_ACCESSIBLE , & hcd - > flags ) ;
2005-05-10 15:34:16 -04:00
usb_hcd_poll_rh_status ( hcd ) ;
return 0 ;
}
2005-11-09 22:32:44 +00:00
static struct platform_driver dummy_hcd_driver = {
2005-05-03 16:15:43 -04:00
. probe = dummy_hcd_probe ,
. remove = dummy_hcd_remove ,
2005-05-10 15:34:16 -04:00
. suspend = dummy_hcd_suspend ,
. resume = dummy_hcd_resume ,
2005-11-09 22:32:44 +00:00
. driver = {
. name = ( char * ) driver_name ,
. owner = THIS_MODULE ,
} ,
2005-05-03 16:15:43 -04:00
} ;
2005-04-16 15:20:36 -07:00
2005-05-03 16:15:43 -04:00
/*-------------------------------------------------------------------------*/
2005-04-16 15:20:36 -07:00
2008-04-07 15:03:25 -04:00
static struct platform_device * the_udc_pdev ;
static struct platform_device * the_hcd_pdev ;
2005-04-16 15:20:36 -07:00
static int __init init ( void )
{
2008-04-07 15:03:25 -04:00
int retval = - ENOMEM ;
2005-04-16 15:20:36 -07:00
if ( usb_disabled ( ) )
return - ENODEV ;
2005-05-03 16:15:43 -04:00
2011-06-29 16:41:53 +03:00
if ( ! mod_data . is_high_speed & & mod_data . is_super_speed )
return - EINVAL ;
2008-04-07 15:03:25 -04:00
the_hcd_pdev = platform_device_alloc ( driver_name , - 1 ) ;
if ( ! the_hcd_pdev )
2005-04-16 15:20:36 -07:00
return retval ;
2008-04-07 15:03:25 -04:00
the_udc_pdev = platform_device_alloc ( gadget_name , - 1 ) ;
if ( ! the_udc_pdev )
goto err_alloc_udc ;
2005-05-03 16:15:43 -04:00
2008-04-07 15:03:25 -04:00
retval = platform_driver_register ( & dummy_hcd_driver ) ;
if ( retval < 0 )
goto err_register_hcd_driver ;
retval = platform_driver_register ( & dummy_udc_driver ) ;
2005-05-03 16:15:43 -04:00
if ( retval < 0 )
goto err_register_udc_driver ;
2008-04-07 15:03:25 -04:00
retval = platform_device_add ( the_hcd_pdev ) ;
2005-05-03 16:15:43 -04:00
if ( retval < 0 )
2008-04-07 15:03:25 -04:00
goto err_add_hcd ;
2011-06-29 16:41:52 +03:00
if ( ! the_controller . hs_hcd | |
( ! the_controller . ss_hcd & & mod_data . is_super_speed ) ) {
2011-04-15 20:37:06 +02:00
/*
* The hcd was added successfully but its probe function failed
* for some reason .
*/
retval = - EINVAL ;
goto err_add_udc ;
}
2008-04-07 15:03:25 -04:00
retval = platform_device_add ( the_udc_pdev ) ;
2005-05-03 16:15:43 -04:00
if ( retval < 0 )
2008-04-07 15:03:25 -04:00
goto err_add_udc ;
2011-04-15 20:37:06 +02:00
if ( ! platform_get_drvdata ( the_udc_pdev ) ) {
/*
* The udc was added successfully but its probe function failed
* for some reason .
*/
retval = - EINVAL ;
goto err_probe_udc ;
}
2005-05-03 16:15:43 -04:00
return retval ;
2011-04-15 20:37:06 +02:00
err_probe_udc :
platform_device_del ( the_udc_pdev ) ;
2008-04-07 15:03:25 -04:00
err_add_udc :
platform_device_del ( the_hcd_pdev ) ;
err_add_hcd :
platform_driver_unregister ( & dummy_udc_driver ) ;
2005-05-03 16:15:43 -04:00
err_register_udc_driver :
2008-04-07 15:03:25 -04:00
platform_driver_unregister ( & dummy_hcd_driver ) ;
err_register_hcd_driver :
platform_device_put ( the_udc_pdev ) ;
err_alloc_udc :
platform_device_put ( the_hcd_pdev ) ;
2005-04-16 15:20:36 -07:00
return retval ;
}
module_init ( init ) ;
static void __exit cleanup ( void )
{
2008-04-07 15:03:25 -04:00
platform_device_unregister ( the_udc_pdev ) ;
platform_device_unregister ( the_hcd_pdev ) ;
platform_driver_unregister ( & dummy_udc_driver ) ;
platform_driver_unregister ( & dummy_hcd_driver ) ;
2005-04-16 15:20:36 -07:00
}
module_exit ( cleanup ) ;