2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0+
2008-07-09 14:56:51 -06:00
/*
* Copyright ( C ) 2003 - 2008 Takahiro Hirofuchi
2016-06-13 11:33:40 +09:00
* Copyright ( C ) 2015 - 2016 Nobuo Iwata
2008-07-09 14:56:51 -06:00
*/
2011-05-11 22:33:43 -07:00
# include <linux/init.h>
2012-10-22 06:45:00 +11:00
# include <linux/file.h>
2011-05-11 22:33:43 -07:00
# include <linux/kernel.h>
2011-03-02 00:13:05 +01:00
# include <linux/kthread.h>
2011-05-11 22:33:43 -07:00
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
2008-07-09 14:56:51 -06:00
# include "usbip_common.h"
# include "vhci.h"
# define DRIVER_AUTHOR "Takahiro Hirofuchi"
2011-05-11 22:33:44 -07:00
# define DRIVER_DESC "USB / IP 'Virtual' Host Controller (VHCI) Driver"
2008-07-09 14:56:51 -06:00
/*
* TODO
* - update root hub emulation
* - move the emulation code to userland ?
* porting to other operating systems
* minimize kernel code
* - add suspend / resume code
* - clean up everything
*/
/* See usb gadget dummy hcd */
static int vhci_hub_status ( struct usb_hcd * hcd , char * buff ) ;
static int vhci_hub_control ( struct usb_hcd * hcd , u16 typeReq , u16 wValue ,
2011-05-06 03:47:50 -07:00
u16 wIndex , char * buff , u16 wLength ) ;
2008-07-09 14:56:51 -06:00
static int vhci_urb_enqueue ( struct usb_hcd * hcd , struct urb * urb ,
2011-05-06 03:47:50 -07:00
gfp_t mem_flags ) ;
2008-07-09 14:56:51 -06:00
static int vhci_urb_dequeue ( struct usb_hcd * hcd , struct urb * urb , int status ) ;
static int vhci_start ( struct usb_hcd * vhci_hcd ) ;
static void vhci_stop ( struct usb_hcd * hcd ) ;
static int vhci_get_frame_number ( struct usb_hcd * hcd ) ;
static const char driver_name [ ] = " vhci_hcd " ;
2009-11-13 15:13:43 -05:00
static const char driver_desc [ ] = " USB/IP Virtual Host Controller " ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
int vhci_num_controllers = VHCI_NR_HCS ;
2017-06-08 13:04:07 +08:00
struct vhci * vhcis ;
2008-07-09 14:56:51 -06:00
2011-05-06 03:47:50 -07:00
static const char * const bit_desc [ ] = {
2008-07-09 14:56:51 -06:00
" CONNECTION " , /*0*/
" ENABLE " , /*1*/
" SUSPEND " , /*2*/
" OVER_CURRENT " , /*3*/
" RESET " , /*4*/
2017-06-08 13:04:12 +08:00
" L1 " , /*5*/
2011-05-06 03:47:50 -07:00
" R6 " , /*6*/
" R7 " , /*7*/
2008-07-09 14:56:51 -06:00
" POWER " , /*8*/
" LOWSPEED " , /*9*/
" HIGHSPEED " , /*10*/
" PORT_TEST " , /*11*/
" INDICATOR " , /*12*/
2011-05-06 03:47:50 -07:00
" R13 " , /*13*/
" R14 " , /*14*/
" R15 " , /*15*/
2008-07-09 14:56:51 -06:00
" C_CONNECTION " , /*16*/
" C_ENABLE " , /*17*/
" C_SUSPEND " , /*18*/
" C_OVER_CURRENT " , /*19*/
" C_RESET " , /*20*/
2017-06-08 13:04:12 +08:00
" C_L1 " , /*21*/
2011-05-06 03:47:50 -07:00
" R22 " , /*22*/
" R23 " , /*23*/
" R24 " , /*24*/
" R25 " , /*25*/
" R26 " , /*26*/
" R27 " , /*27*/
" R28 " , /*28*/
" R29 " , /*29*/
" R30 " , /*30*/
" R31 " , /*31*/
2008-07-09 14:56:51 -06:00
} ;
2017-06-08 13:04:12 +08:00
static const char * const bit_desc_ss [ ] = {
" CONNECTION " , /*0*/
" ENABLE " , /*1*/
" SUSPEND " , /*2*/
" OVER_CURRENT " , /*3*/
" RESET " , /*4*/
" L1 " , /*5*/
" R6 " , /*6*/
" R7 " , /*7*/
" R8 " , /*8*/
" POWER " , /*9*/
" HIGHSPEED " , /*10*/
" PORT_TEST " , /*11*/
" INDICATOR " , /*12*/
" R13 " , /*13*/
" R14 " , /*14*/
" R15 " , /*15*/
" C_CONNECTION " , /*16*/
" C_ENABLE " , /*17*/
" C_SUSPEND " , /*18*/
" C_OVER_CURRENT " , /*19*/
" C_RESET " , /*20*/
" C_BH_RESET " , /*21*/
" C_LINK_STATE " , /*22*/
" C_CONFIG_ERROR " , /*23*/
" R24 " , /*24*/
" R25 " , /*25*/
" R26 " , /*26*/
" R27 " , /*27*/
" R28 " , /*28*/
" R29 " , /*29*/
" R30 " , /*30*/
" R31 " , /*31*/
} ;
static void dump_port_status_diff ( u32 prev_status , u32 new_status , bool usb3 )
2008-07-09 14:56:51 -06:00
{
int i = 0 ;
2011-06-14 08:28:26 +02:00
u32 bit = 1 ;
2017-06-08 13:04:12 +08:00
const char * const * desc = bit_desc ;
if ( usb3 )
desc = bit_desc_ss ;
2011-06-14 08:28:26 +02:00
pr_debug ( " status prev -> new: %08x -> %08x \n " , prev_status , new_status ) ;
while ( bit ) {
u32 prev = prev_status & bit ;
u32 new = new_status & bit ;
char change ;
if ( ! prev & & new )
change = ' + ' ;
else if ( prev & & ! new )
change = ' - ' ;
else
change = ' ' ;
2017-06-08 13:04:12 +08:00
if ( prev | | new ) {
pr_debug ( " %c%s \n " , change , desc [ i ] ) ;
if ( bit = = 1 ) /* USB_PORT_STAT_CONNECTION */
pr_debug ( " %c%s \n " , change , " USB_PORT_STAT_SPEED_5GBPS " ) ;
}
2011-06-14 08:28:26 +02:00
bit < < = 1 ;
i + + ;
2008-07-09 14:56:51 -06:00
}
2011-05-19 16:47:32 -07:00
pr_debug ( " \n " ) ;
2008-07-09 14:56:51 -06:00
}
2016-06-13 11:33:40 +09:00
void rh_port_connect ( struct vhci_device * vdev , enum usb_device_speed speed )
2008-07-09 14:56:51 -06:00
{
2017-06-08 13:04:09 +08:00
struct vhci_hcd * vhci_hcd = vdev_to_vhci_hcd ( vdev ) ;
struct vhci * vhci = vhci_hcd - > vhci ;
2016-06-13 11:33:40 +09:00
int rhport = vdev - > rhport ;
u32 status ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_rh ( " rh_port_connect %d \n " , rhport ) ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
spin_lock_irqsave ( & vhci - > lock , flags ) ;
2017-06-08 13:04:09 +08:00
status = vhci_hcd - > port_status [ rhport ] ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
status | = USB_PORT_STAT_CONNECTION | ( 1 < < USB_PORT_FEAT_C_CONNECTION ) ;
2008-07-09 14:56:51 -06:00
switch ( speed ) {
case USB_SPEED_HIGH :
2016-06-13 11:33:40 +09:00
status | = USB_PORT_STAT_HIGH_SPEED ;
2008-07-09 14:56:51 -06:00
break ;
case USB_SPEED_LOW :
2016-06-13 11:33:40 +09:00
status | = USB_PORT_STAT_LOW_SPEED ;
2008-07-09 14:56:51 -06:00
break ;
default :
break ;
}
2017-06-08 13:04:09 +08:00
vhci_hcd - > port_status [ rhport ] = status ;
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
2017-06-08 13:04:09 +08:00
usb_hcd_poll_rh_status ( vhci_hcd_to_hcd ( vhci_hcd ) ) ;
2008-07-09 14:56:51 -06:00
}
2016-06-13 11:33:40 +09:00
static void rh_port_disconnect ( struct vhci_device * vdev )
2008-07-09 14:56:51 -06:00
{
2017-06-08 13:04:09 +08:00
struct vhci_hcd * vhci_hcd = vdev_to_vhci_hcd ( vdev ) ;
struct vhci * vhci = vhci_hcd - > vhci ;
2016-06-13 11:33:40 +09:00
int rhport = vdev - > rhport ;
u32 status ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_rh ( " rh_port_disconnect %d \n " , rhport ) ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
spin_lock_irqsave ( & vhci - > lock , flags ) ;
2017-06-08 13:04:09 +08:00
status = vhci_hcd - > port_status [ rhport ] ;
2016-06-13 11:33:40 +09:00
status & = ~ USB_PORT_STAT_CONNECTION ;
status | = ( 1 < < USB_PORT_FEAT_C_CONNECTION ) ;
2012-10-10 13:34:27 -04:00
2017-06-08 13:04:09 +08:00
vhci_hcd - > port_status [ rhport ] = status ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2017-06-08 13:04:09 +08:00
usb_hcd_poll_rh_status ( vhci_hcd_to_hcd ( vhci_hcd ) ) ;
2008-07-09 14:56:51 -06:00
}
2011-05-06 03:47:50 -07:00
# define PORT_C_MASK \
( ( USB_PORT_STAT_C_CONNECTION \
| USB_PORT_STAT_C_ENABLE \
| USB_PORT_STAT_C_SUSPEND \
| USB_PORT_STAT_C_OVERCURRENT \
2008-07-09 14:56:51 -06:00
| USB_PORT_STAT_C_RESET ) < < 16 )
/*
2012-06-13 15:37:18 -04:00
* Returns 0 if the status hasn ' t changed , or the number of bytes in buf .
* Ports are 0 - indexed from the HCD point of view ,
* and 1 - indexed from the USB core pointer of view .
2008-07-09 14:56:51 -06:00
*
* @ buf : a bitmap to show which port status has been changed .
2012-06-13 15:37:18 -04:00
* bit 0 : reserved
2008-07-09 14:56:51 -06:00
* bit 1 : the status of port 0 has been changed .
* bit 2 : the status of port 1 has been changed .
* . . .
*/
static int vhci_hub_status ( struct usb_hcd * hcd , char * buf )
{
2017-06-08 13:04:09 +08:00
struct vhci_hcd * vhci_hcd = hcd_to_vhci_hcd ( hcd ) ;
struct vhci * vhci = vhci_hcd - > vhci ;
int retval = DIV_ROUND_UP ( VHCI_HC_PORTS + 1 , 8 ) ;
2008-07-09 14:56:51 -06:00
int rhport ;
int changed = 0 ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2008-07-09 14:56:51 -06:00
2012-06-13 15:37:18 -04:00
memset ( buf , 0 , retval ) ;
2008-07-09 14:56:51 -06:00
2016-02-02 17:36:39 +00:00
spin_lock_irqsave ( & vhci - > lock , flags ) ;
2010-06-22 16:39:10 -04:00
if ( ! HCD_HW_ACCESSIBLE ( hcd ) ) {
2012-06-13 15:37:18 -04:00
usbip_dbg_vhci_rh ( " hw accessible flag not on? \n " ) ;
2008-07-09 14:56:51 -06:00
goto done ;
}
/* check pseudo status register for each port */
2016-06-13 11:33:40 +09:00
for ( rhport = 0 ; rhport < VHCI_HC_PORTS ; rhport + + ) {
2017-06-08 13:04:09 +08:00
if ( ( vhci_hcd - > port_status [ rhport ] & PORT_C_MASK ) ) {
2008-07-09 14:56:51 -06:00
/* The status of a port has been changed, */
2012-06-13 15:37:18 -04:00
usbip_dbg_vhci_rh ( " port %d status changed \n " , rhport ) ;
2008-07-09 14:56:51 -06:00
2012-06-13 15:37:18 -04:00
buf [ ( rhport + 1 ) / 8 ] | = 1 < < ( rhport + 1 ) % 8 ;
2008-07-09 14:56:51 -06:00
changed = 1 ;
}
}
2012-09-06 16:49:50 +05:30
if ( ( hcd - > state = = HC_STATE_SUSPENDED ) & & ( changed = = 1 ) )
2008-07-09 14:56:51 -06:00
usb_hcd_resume_root_hub ( hcd ) ;
done :
2016-02-02 17:36:39 +00:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2012-06-13 15:37:18 -04:00
return changed ? retval : 0 ;
2008-07-09 14:56:51 -06:00
}
2017-06-08 13:04:10 +08:00
/* usb 3.0 root hub device descriptor */
static struct {
struct usb_bos_descriptor bos ;
struct usb_ss_cap_descriptor ss_cap ;
} __packed usb3_bos_desc = {
. bos = {
. bLength = USB_DT_BOS_SIZE ,
. bDescriptorType = USB_DT_BOS ,
. wTotalLength = cpu_to_le16 ( sizeof ( usb3_bos_desc ) ) ,
. bNumDeviceCaps = 1 ,
} ,
. ss_cap = {
. bLength = USB_DT_USB_SS_CAP_SIZE ,
. bDescriptorType = USB_DT_DEVICE_CAPABILITY ,
. bDevCapabilityType = USB_SS_CAP_TYPE ,
. wSpeedSupported = cpu_to_le16 ( USB_5GBPS_OPERATION ) ,
. bFunctionalitySupport = ilog2 ( USB_5GBPS_OPERATION ) ,
} ,
} ;
static inline void
ss_hub_descriptor ( struct usb_hub_descriptor * desc )
{
memset ( desc , 0 , sizeof * desc ) ;
desc - > bDescriptorType = USB_DT_SS_HUB ;
desc - > bDescLength = 12 ;
desc - > wHubCharacteristics = cpu_to_le16 (
HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM ) ;
desc - > bNbrPorts = VHCI_HC_PORTS ;
desc - > u . ss . bHubHdrDecLat = 0x04 ; /* Worst case: 0.4 micro sec*/
desc - > u . ss . DeviceRemovable = 0xffff ;
}
2008-07-09 14:56:51 -06:00
static inline void hub_descriptor ( struct usb_hub_descriptor * desc )
{
2017-05-10 18:18:26 +02:00
int width ;
2008-07-09 14:56:51 -06:00
memset ( desc , 0 , sizeof ( * desc ) ) ;
2015-03-29 01:45:02 +03:00
desc - > bDescriptorType = USB_DT_HUB ;
2015-08-19 10:47:46 +05:30
desc - > wHubCharacteristics = cpu_to_le16 (
2015-01-19 02:00:56 +03:00
HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM ) ;
2017-05-10 18:18:26 +02:00
2016-06-13 11:33:40 +09:00
desc - > bNbrPorts = VHCI_HC_PORTS ;
2017-05-10 18:18:26 +02:00
BUILD_BUG_ON ( VHCI_HC_PORTS > USB_MAXCHILDREN ) ;
width = desc - > bNbrPorts / 8 + 1 ;
desc - > bDescLength = USB_DT_HUB_NONVAR_SIZE + 2 * width ;
memset ( & desc - > u . hs . DeviceRemovable [ 0 ] , 0 , width ) ;
memset ( & desc - > u . hs . DeviceRemovable [ width ] , 0xff , width ) ;
2008-07-09 14:56:51 -06:00
}
static int vhci_hub_control ( struct usb_hcd * hcd , u16 typeReq , u16 wValue ,
u16 wIndex , char * buf , u16 wLength )
{
2017-06-08 13:04:09 +08:00
struct vhci_hcd * vhci_hcd ;
struct vhci * vhci ;
2008-07-09 14:56:51 -06:00
int retval = 0 ;
2018-10-05 16:17:44 -06:00
int rhport = - 1 ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2018-10-05 16:17:44 -06:00
bool invalid_rhport = false ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
u32 prev_port_status [ VHCI_HC_PORTS ] ;
2008-07-09 14:56:51 -06:00
2010-06-22 16:39:10 -04:00
if ( ! HCD_HW_ACCESSIBLE ( hcd ) )
2008-07-09 14:56:51 -06:00
return - ETIMEDOUT ;
/*
* NOTE :
2017-06-08 13:04:10 +08:00
* wIndex ( bits 0 - 7 ) shows the port number and begins from 1 ?
2008-07-09 14:56:51 -06:00
*/
2017-06-08 13:04:10 +08:00
wIndex = ( ( __u8 ) ( wIndex & 0x00ff ) ) ;
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_rh ( " typeReq %x wValue %x wIndex %x \n " , typeReq , wValue ,
2011-05-06 03:47:50 -07:00
wIndex ) ;
2017-06-08 13:04:10 +08:00
2018-10-05 16:17:44 -06:00
/*
* wIndex can be 0 for some request types ( typeReq ) . rhport is
* in valid range when wIndex > = 1 and < VHCI_HC_PORTS .
*
* Reference port_status [ ] only with valid rhport when
* invalid_rhport is false .
*/
if ( wIndex < 1 | | wIndex > VHCI_HC_PORTS ) {
invalid_rhport = true ;
if ( wIndex > VHCI_HC_PORTS )
pr_err ( " invalid port number %d \n " , wIndex ) ;
} else
rhport = wIndex - 1 ;
2008-07-09 14:56:51 -06:00
2017-06-08 13:04:09 +08:00
vhci_hcd = hcd_to_vhci_hcd ( hcd ) ;
vhci = vhci_hcd - > vhci ;
2008-07-09 14:56:51 -06:00
2017-06-08 13:04:09 +08:00
spin_lock_irqsave ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
/* store old status and compare now and old later */
2009-07-21 00:46:13 -06:00
if ( usbip_dbg_flag_vhci_rh ) {
2018-10-05 16:17:44 -06:00
if ( ! invalid_rhport )
memcpy ( prev_port_status , vhci_hcd - > port_status ,
sizeof ( prev_port_status ) ) ;
2008-07-09 14:56:51 -06:00
}
switch ( typeReq ) {
case ClearHubFeature :
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_rh ( " ClearHubFeature \n " ) ;
2008-07-09 14:56:51 -06:00
break ;
case ClearPortFeature :
2018-10-05 16:17:44 -06:00
if ( invalid_rhport ) {
pr_err ( " invalid port number %d \n " , wIndex ) ;
2018-04-05 16:31:49 -06:00
goto error ;
2018-10-05 16:17:44 -06:00
}
2008-07-09 14:56:51 -06:00
switch ( wValue ) {
case USB_PORT_FEAT_SUSPEND :
2017-06-08 13:04:10 +08:00
if ( hcd - > speed = = HCD_USB3 ) {
pr_err ( " ClearPortFeature: USB_PORT_FEAT_SUSPEND req not "
" supported for USB 3.0 roothub \n " ) ;
goto error ;
}
usbip_dbg_vhci_rh (
" ClearPortFeature: USB_PORT_FEAT_SUSPEND \n " ) ;
2017-06-08 13:04:09 +08:00
if ( vhci_hcd - > port_status [ rhport ] & USB_PORT_STAT_SUSPEND ) {
2008-07-09 14:56:51 -06:00
/* 20msec signaling */
2017-06-08 13:04:09 +08:00
vhci_hcd - > resuming = 1 ;
2017-06-08 13:04:10 +08:00
vhci_hcd - > re_timeout = jiffies + msecs_to_jiffies ( 20 ) ;
2008-07-09 14:56:51 -06:00
}
break ;
case USB_PORT_FEAT_POWER :
2014-03-19 23:04:57 +01:00
usbip_dbg_vhci_rh (
" ClearPortFeature: USB_PORT_FEAT_POWER \n " ) ;
2017-06-08 13:04:10 +08:00
if ( hcd - > speed = = HCD_USB3 )
vhci_hcd - > port_status [ rhport ] & = ~ USB_SS_PORT_STAT_POWER ;
else
vhci_hcd - > port_status [ rhport ] & = ~ USB_PORT_STAT_POWER ;
2017-02-09 01:49:56 -06:00
break ;
2008-07-09 14:56:51 -06:00
default :
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_rh ( " ClearPortFeature: default %x \n " ,
2011-05-06 03:47:50 -07:00
wValue ) ;
2017-06-08 13:04:09 +08:00
vhci_hcd - > port_status [ rhport ] & = ~ ( 1 < < wValue ) ;
2011-05-06 03:47:54 -07:00
break ;
2008-07-09 14:56:51 -06:00
}
break ;
case GetHubDescriptor :
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_rh ( " GetHubDescriptor \n " ) ;
2017-06-08 13:04:10 +08:00
if ( hcd - > speed = = HCD_USB3 & &
( wLength < USB_DT_SS_HUB_SIZE | |
wValue ! = ( USB_DT_SS_HUB < < 8 ) ) ) {
pr_err ( " 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 ) ;
break ;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR :
if ( hcd - > speed ! = HCD_USB3 )
goto error ;
if ( ( wValue > > 8 ) ! = USB_DT_BOS )
goto error ;
memcpy ( buf , & usb3_bos_desc , sizeof ( usb3_bos_desc ) ) ;
retval = sizeof ( usb3_bos_desc ) ;
2008-07-09 14:56:51 -06:00
break ;
case GetHubStatus :
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_rh ( " GetHubStatus \n " ) ;
2014-06-13 11:35:13 +03:00
* ( __le32 * ) buf = cpu_to_le32 ( 0 ) ;
2008-07-09 14:56:51 -06:00
break ;
case GetPortStatus :
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_rh ( " GetPortStatus port %x \n " , wIndex ) ;
2018-10-05 16:17:44 -06:00
if ( invalid_rhport ) {
2011-05-19 16:47:32 -07:00
pr_err ( " invalid port number %d \n " , wIndex ) ;
2008-07-09 14:56:51 -06:00
retval = - EPIPE ;
2018-10-05 16:17:44 -06:00
goto error ;
2008-07-09 14:56:51 -06:00
}
2012-10-10 13:34:27 -04:00
/* we do not care about resume. */
2008-07-09 14:56:51 -06:00
/* whoever resets or resumes must GetPortStatus to
* complete it ! !
2012-10-10 13:34:27 -04:00
*/
2017-06-08 13:04:09 +08:00
if ( vhci_hcd - > resuming & & time_after ( jiffies , vhci_hcd - > re_timeout ) ) {
2017-06-08 13:04:10 +08:00
vhci_hcd - > port_status [ rhport ] | = ( 1 < < USB_PORT_FEAT_C_SUSPEND ) ;
vhci_hcd - > port_status [ rhport ] & = ~ ( 1 < < USB_PORT_FEAT_SUSPEND ) ;
2017-06-08 13:04:09 +08:00
vhci_hcd - > resuming = 0 ;
vhci_hcd - > re_timeout = 0 ;
2008-07-09 14:56:51 -06:00
}
2017-06-08 13:04:09 +08:00
if ( ( vhci_hcd - > port_status [ rhport ] & ( 1 < < USB_PORT_FEAT_RESET ) ) ! =
0 & & time_after ( jiffies , vhci_hcd - > re_timeout ) ) {
2017-06-08 13:04:10 +08:00
vhci_hcd - > port_status [ rhport ] | = ( 1 < < USB_PORT_FEAT_C_RESET ) ;
vhci_hcd - > port_status [ rhport ] & = ~ ( 1 < < USB_PORT_FEAT_RESET ) ;
2017-06-08 13:04:09 +08:00
vhci_hcd - > re_timeout = 0 ;
2008-07-09 14:56:51 -06:00
2017-06-08 13:04:09 +08:00
if ( vhci_hcd - > vdev [ rhport ] . ud . status = =
2011-05-06 03:47:50 -07:00
VDEV_ST_NOTASSIGNED ) {
2014-03-19 23:04:57 +01:00
usbip_dbg_vhci_rh (
" enable rhport %d (status %u) \n " ,
rhport ,
2017-06-08 13:04:09 +08:00
vhci_hcd - > vdev [ rhport ] . ud . status ) ;
vhci_hcd - > port_status [ rhport ] | =
2011-05-06 03:47:50 -07:00
USB_PORT_STAT_ENABLE ;
2008-07-09 14:56:51 -06:00
}
2017-06-08 13:04:10 +08:00
if ( hcd - > speed < HCD_USB3 ) {
switch ( vhci_hcd - > vdev [ rhport ] . speed ) {
case USB_SPEED_HIGH :
vhci_hcd - > port_status [ rhport ] | =
USB_PORT_STAT_HIGH_SPEED ;
break ;
case USB_SPEED_LOW :
vhci_hcd - > port_status [ rhport ] | =
USB_PORT_STAT_LOW_SPEED ;
break ;
default :
pr_err ( " vhci_device speed not set \n " ) ;
break ;
}
}
2008-07-09 14:56:51 -06:00
}
2017-06-08 13:04:09 +08:00
( ( __le16 * ) buf ) [ 0 ] = cpu_to_le16 ( vhci_hcd - > port_status [ rhport ] ) ;
2014-03-19 23:04:57 +01:00
( ( __le16 * ) buf ) [ 1 ] =
2017-06-08 13:04:09 +08:00
cpu_to_le16 ( vhci_hcd - > port_status [ rhport ] > > 16 ) ;
2008-07-09 14:56:51 -06:00
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_rh ( " GetPortStatus bye %x %x \n " , ( ( u16 * ) buf ) [ 0 ] ,
2011-05-06 03:47:50 -07:00
( ( u16 * ) buf ) [ 1 ] ) ;
2008-07-09 14:56:51 -06:00
break ;
case SetHubFeature :
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_rh ( " SetHubFeature \n " ) ;
2008-07-09 14:56:51 -06:00
retval = - EPIPE ;
break ;
case SetPortFeature :
switch ( wValue ) {
2017-06-08 13:04:10 +08:00
case USB_PORT_FEAT_LINK_STATE :
usbip_dbg_vhci_rh (
" SetPortFeature: USB_PORT_FEAT_LINK_STATE \n " ) ;
if ( hcd - > speed ! = HCD_USB3 ) {
pr_err ( " 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 :
usbip_dbg_vhci_rh (
" SetPortFeature: USB_PORT_FEAT_U1_TIMEOUT \n " ) ;
case USB_PORT_FEAT_U2_TIMEOUT :
usbip_dbg_vhci_rh (
" SetPortFeature: USB_PORT_FEAT_U2_TIMEOUT \n " ) ;
/* TODO: add suspend/resume support! */
if ( hcd - > speed ! = HCD_USB3 ) {
pr_err ( " USB_PORT_FEAT_U1/2_TIMEOUT req not "
" supported for USB 2.0 roothub \n " ) ;
goto error ;
}
break ;
2008-07-09 14:56:51 -06:00
case USB_PORT_FEAT_SUSPEND :
2014-03-19 23:04:57 +01:00
usbip_dbg_vhci_rh (
" SetPortFeature: USB_PORT_FEAT_SUSPEND \n " ) ;
2017-06-08 13:04:10 +08:00
/* Applicable only for USB2.0 hub */
if ( hcd - > speed = = HCD_USB3 ) {
pr_err ( " USB_PORT_FEAT_SUSPEND req not "
" supported for USB 3.0 roothub \n " ) ;
goto error ;
}
2018-10-05 16:17:44 -06:00
if ( invalid_rhport ) {
pr_err ( " invalid port number %d \n " , wIndex ) ;
2018-04-05 16:31:49 -06:00
goto error ;
2018-10-05 16:17:44 -06:00
}
2018-04-05 16:31:49 -06:00
2017-06-08 13:04:10 +08:00
vhci_hcd - > port_status [ rhport ] | = USB_PORT_STAT_SUSPEND ;
2008-07-09 14:56:51 -06:00
break ;
2017-06-08 13:04:10 +08:00
case USB_PORT_FEAT_POWER :
usbip_dbg_vhci_rh (
" SetPortFeature: USB_PORT_FEAT_POWER \n " ) ;
2018-10-05 16:17:44 -06:00
if ( invalid_rhport ) {
pr_err ( " invalid port number %d \n " , wIndex ) ;
2018-04-05 16:31:49 -06:00
goto error ;
2018-10-05 16:17:44 -06:00
}
2017-06-08 13:04:10 +08:00
if ( hcd - > speed = = HCD_USB3 )
vhci_hcd - > port_status [ rhport ] | = USB_SS_PORT_STAT_POWER ;
else
vhci_hcd - > port_status [ rhport ] | = USB_PORT_STAT_POWER ;
break ;
case USB_PORT_FEAT_BH_PORT_RESET :
usbip_dbg_vhci_rh (
" SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET \n " ) ;
2018-10-05 16:17:44 -06:00
if ( invalid_rhport ) {
pr_err ( " invalid port number %d \n " , wIndex ) ;
2018-04-05 16:31:49 -06:00
goto error ;
2018-10-05 16:17:44 -06:00
}
2017-06-08 13:04:10 +08:00
/* Applicable only for USB3.0 hub */
if ( hcd - > speed ! = HCD_USB3 ) {
pr_err ( " USB_PORT_FEAT_BH_PORT_RESET req not "
" supported for USB 2.0 roothub \n " ) ;
goto error ;
}
/* FALLS THROUGH */
2008-07-09 14:56:51 -06:00
case USB_PORT_FEAT_RESET :
2014-03-19 23:04:57 +01:00
usbip_dbg_vhci_rh (
" SetPortFeature: USB_PORT_FEAT_RESET \n " ) ;
2018-10-05 16:17:44 -06:00
if ( invalid_rhport ) {
pr_err ( " invalid port number %d \n " , wIndex ) ;
2018-04-05 16:31:49 -06:00
goto error ;
2018-10-05 16:17:44 -06:00
}
2017-06-08 13:04:10 +08:00
/* if it's already enabled, disable */
if ( hcd - > speed = = HCD_USB3 ) {
vhci_hcd - > port_status [ rhport ] = 0 ;
vhci_hcd - > port_status [ rhport ] =
( USB_SS_PORT_STAT_POWER |
USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_RESET ) ;
} else if ( vhci_hcd - > port_status [ rhport ] & USB_PORT_STAT_ENABLE ) {
vhci_hcd - > port_status [ rhport ] & = ~ ( USB_PORT_STAT_ENABLE
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED ) ;
2008-07-09 14:56:51 -06:00
}
2017-06-08 13:04:10 +08:00
2008-07-09 14:56:51 -06:00
/* 50msec reset signaling */
2017-06-08 13:04:09 +08:00
vhci_hcd - > re_timeout = jiffies + msecs_to_jiffies ( 50 ) ;
2008-07-09 14:56:51 -06:00
2017-06-08 13:04:10 +08:00
/* FALLS THROUGH */
2008-07-09 14:56:51 -06:00
default :
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_rh ( " SetPortFeature: default %d \n " ,
2011-05-06 03:47:50 -07:00
wValue ) ;
2018-10-05 16:17:44 -06:00
if ( invalid_rhport ) {
pr_err ( " invalid port number %d \n " , wIndex ) ;
2018-04-05 16:31:49 -06:00
goto error ;
2018-10-05 16:17:44 -06:00
}
2017-06-08 13:04:10 +08:00
if ( hcd - > speed = = HCD_USB3 ) {
if ( ( vhci_hcd - > port_status [ rhport ] &
USB_SS_PORT_STAT_POWER ) ! = 0 ) {
vhci_hcd - > port_status [ rhport ] | = ( 1 < < wValue ) ;
}
} else
if ( ( vhci_hcd - > port_status [ rhport ] &
USB_PORT_STAT_POWER ) ! = 0 ) {
vhci_hcd - > port_status [ rhport ] | = ( 1 < < wValue ) ;
}
}
break ;
case GetPortErrorCount :
usbip_dbg_vhci_rh ( " GetPortErrorCount \n " ) ;
if ( hcd - > speed ! = HCD_USB3 ) {
pr_err ( " 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 :
usbip_dbg_vhci_rh ( " SetHubDepth \n " ) ;
if ( hcd - > speed ! = HCD_USB3 ) {
pr_err ( " SetHubDepth req not supported for "
" USB 2.0 roothub \n " ) ;
goto error ;
2008-07-09 14:56:51 -06:00
}
break ;
default :
2017-06-08 13:04:10 +08:00
pr_err ( " default hub control req: %04x v%04x i%04x l%d \n " ,
typeReq , wValue , wIndex , wLength ) ;
error :
2008-07-09 14:56:51 -06:00
/* "protocol stall" on error */
retval = - EPIPE ;
}
2009-07-21 00:46:13 -06:00
if ( usbip_dbg_flag_vhci_rh ) {
2011-05-19 16:47:32 -07:00
pr_debug ( " port %d \n " , rhport ) ;
2011-06-13 23:47:39 +02:00
/* Only dump valid port status */
2018-10-05 16:17:44 -06:00
if ( ! invalid_rhport ) {
2011-06-14 08:28:26 +02:00
dump_port_status_diff ( prev_port_status [ rhport ] ,
2017-06-08 13:04:12 +08:00
vhci_hcd - > port_status [ rhport ] ,
hcd - > speed = = HCD_USB3 ) ;
2011-06-13 23:47:39 +02:00
}
2008-07-09 14:56:51 -06:00
}
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_rh ( " bye \n " ) ;
2008-07-09 14:56:51 -06:00
2017-06-08 13:04:09 +08:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
2018-10-05 16:17:44 -06:00
if ( ! invalid_rhport & &
( vhci_hcd - > port_status [ rhport ] & PORT_C_MASK ) ! = 0 ) {
2017-06-08 13:04:10 +08:00
usb_hcd_poll_rh_status ( hcd ) ;
2018-10-05 16:17:44 -06:00
}
2017-06-08 13:04:10 +08:00
2008-07-09 14:56:51 -06:00
return retval ;
}
2017-04-06 06:03:22 +08:00
static void vhci_tx_urb ( struct urb * urb , struct vhci_device * vdev )
2008-07-09 14:56:51 -06:00
{
struct vhci_priv * priv ;
2017-06-08 13:04:09 +08:00
struct vhci_hcd * vhci_hcd ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2008-07-09 14:56:51 -06:00
if ( ! vdev ) {
2011-05-19 16:47:32 -07:00
pr_err ( " could not get virtual device " ) ;
2008-07-09 14:56:51 -06:00
return ;
}
2017-06-08 13:04:09 +08:00
vhci_hcd = vdev_to_vhci_hcd ( vdev ) ;
2008-07-09 14:56:51 -06:00
2009-07-21 00:46:13 -06:00
priv = kzalloc ( sizeof ( struct vhci_priv ) , GFP_ATOMIC ) ;
2008-07-09 14:56:51 -06:00
if ( ! priv ) {
usbip_event_add ( & vdev - > ud , VDEV_EVENT_ERROR_MALLOC ) ;
return ;
}
2016-02-02 17:36:39 +00:00
spin_lock_irqsave ( & vdev - > priv_lock , flags ) ;
2013-02-11 09:41:29 -08:00
2017-06-08 13:04:09 +08:00
priv - > seqnum = atomic_inc_return ( & vhci_hcd - > seqnum ) ;
2008-07-09 14:56:51 -06:00
if ( priv - > seqnum = = 0xffff )
2011-05-19 16:47:32 -07:00
dev_info ( & urb - > dev - > dev , " seqnum max \n " ) ;
2008-07-09 14:56:51 -06:00
priv - > vdev = vdev ;
priv - > urb = urb ;
urb - > hcpriv = ( void * ) priv ;
list_add_tail ( & priv - > list , & vdev - > priv_tx ) ;
wake_up ( & vdev - > waitq_tx ) ;
2016-02-02 17:36:39 +00:00
spin_unlock_irqrestore ( & vdev - > priv_lock , flags ) ;
2008-07-09 14:56:51 -06:00
}
2017-06-08 13:04:10 +08:00
static int vhci_urb_enqueue ( struct usb_hcd * hcd , struct urb * urb , gfp_t mem_flags )
2008-07-09 14:56:51 -06:00
{
2017-06-08 13:04:09 +08:00
struct vhci_hcd * vhci_hcd = hcd_to_vhci_hcd ( hcd ) ;
struct vhci * vhci = vhci_hcd - > vhci ;
2008-07-09 14:56:51 -06:00
struct device * dev = & urb - > dev - > dev ;
2016-06-13 11:33:40 +09:00
u8 portnum = urb - > dev - > portnum ;
2008-07-09 14:56:51 -06:00
int ret = 0 ;
2011-01-12 15:02:02 +02:00
struct vhci_device * vdev ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
if ( portnum > VHCI_HC_PORTS ) {
pr_err ( " invalid port number %d \n " , portnum ) ;
return - ENODEV ;
}
2017-06-08 13:04:09 +08:00
vdev = & vhci_hcd - > vdev [ portnum - 1 ] ;
2016-06-13 11:33:40 +09:00
2019-01-24 14:46:42 -07:00
if ( ! urb - > transfer_buffer & & urb - > transfer_buffer_length ) {
dev_dbg ( dev , " Null URB transfer buffer \n " ) ;
return - EINVAL ;
}
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
spin_lock_irqsave ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
if ( urb - > status ! = - EINPROGRESS ) {
dev_err ( dev , " URB already unlinked!, status %d \n " , urb - > status ) ;
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
return urb - > status ;
}
2011-01-12 15:02:02 +02:00
/* refuse enqueue for dead connection */
spin_lock ( & vdev - > ud . lock ) ;
2011-05-06 03:47:50 -07:00
if ( vdev - > ud . status = = VDEV_ST_NULL | |
vdev - > ud . status = = VDEV_ST_ERROR ) {
2011-05-19 16:47:32 -07:00
dev_err ( dev , " enqueue for inactive port %d \n " , vdev - > rhport ) ;
2011-01-12 15:02:02 +02:00
spin_unlock ( & vdev - > ud . lock ) ;
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2011-01-12 15:02:02 +02:00
return - ENODEV ;
}
spin_unlock ( & vdev - > ud . lock ) ;
2008-07-09 14:56:51 -06:00
ret = usb_hcd_link_urb_to_ep ( hcd , urb ) ;
if ( ret )
goto no_need_unlink ;
/*
2009-07-21 00:46:13 -06:00
* The enumeration process is as follows ;
2008-07-09 14:56:51 -06:00
*
* 1. Get_Descriptor request to DevAddrs ( 0 ) EndPoint ( 0 )
* to get max packet length of default pipe
*
* 2. Set_Address request to DevAddr ( 0 ) EndPoint ( 0 )
*
*/
if ( usb_pipedevice ( urb - > pipe ) = = 0 ) {
__u8 type = usb_pipetype ( urb - > pipe ) ;
struct usb_ctrlrequest * ctrlreq =
2011-05-06 03:47:50 -07:00
( struct usb_ctrlrequest * ) urb - > setup_packet ;
2008-07-09 14:56:51 -06:00
if ( type ! = PIPE_CONTROL | | ! ctrlreq ) {
dev_err ( dev , " invalid request to devnum 0 \n " ) ;
2009-07-24 16:57:35 +08:00
ret = - EINVAL ;
2008-07-09 14:56:51 -06:00
goto no_need_xmit ;
}
switch ( ctrlreq - > bRequest ) {
case USB_REQ_SET_ADDRESS :
/* set_address may come when a device is reset */
dev_info ( dev , " SetAddress Request (%d) to port %d \n " ,
ctrlreq - > wValue , vdev - > rhport ) ;
2014-11-21 16:33:18 +01:00
usb_put_dev ( vdev - > udev ) ;
2011-01-12 15:02:00 +02:00
vdev - > udev = usb_get_dev ( urb - > dev ) ;
2008-07-09 14:56:51 -06:00
spin_lock ( & vdev - > ud . lock ) ;
vdev - > ud . status = VDEV_ST_USED ;
spin_unlock ( & vdev - > ud . lock ) ;
if ( urb - > status = = - EINPROGRESS ) {
/* This request is successfully completed. */
/* If not -EINPROGRESS, possibly unlinked. */
urb - > status = 0 ;
}
goto no_need_xmit ;
case USB_REQ_GET_DESCRIPTOR :
2013-11-04 13:12:12 +02:00
if ( ctrlreq - > wValue = = cpu_to_le16 ( USB_DT_DEVICE < < 8 ) )
2014-03-19 23:04:57 +01:00
usbip_dbg_vhci_hc (
" Not yet?:Get_Descriptor to device 0 (get max pipe size) \n " ) ;
2008-07-09 14:56:51 -06:00
2014-11-21 16:33:18 +01:00
usb_put_dev ( vdev - > udev ) ;
2011-01-12 15:02:00 +02:00
vdev - > udev = usb_get_dev ( urb - > dev ) ;
2008-07-09 14:56:51 -06:00
goto out ;
default :
/* NOT REACHED */
2014-03-19 23:04:57 +01:00
dev_err ( dev ,
" invalid request to devnum 0 bRequest %u, wValue %u \n " ,
ctrlreq - > bRequest ,
2008-07-09 14:56:51 -06:00
ctrlreq - > wValue ) ;
ret = - EINVAL ;
goto no_need_xmit ;
}
}
out :
2017-04-06 06:03:22 +08:00
vhci_tx_urb ( urb , vdev ) ;
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
return 0 ;
no_need_xmit :
usb_hcd_unlink_urb_from_ep ( hcd , urb ) ;
no_need_unlink :
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2015-09-21 11:30:44 +02:00
if ( ! ret )
2016-06-13 11:33:40 +09:00
usb_hcd_giveback_urb ( hcd , urb , urb - > status ) ;
2009-07-24 16:57:35 +08:00
return ret ;
2008-07-09 14:56:51 -06:00
}
/*
* vhci_rx gives back the urb after receiving the reply of the urb . If an
* unlink pdu is sent or not , vhci_rx receives a normal return pdu and gives
* back its urb . For the driver unlinking the urb , the content of the urb is
* not important , but the calling to its completion handler is important ; the
* completion of unlinking is notified by the completion handler .
*
*
* CLIENT SIDE
*
* - When vhci_hcd receives RET_SUBMIT ,
*
* - case 1 a ) . the urb of the pdu is not unlinking .
* - normal case
* = > just give back the urb
*
* - case 1 b ) . the urb of the pdu is unlinking .
* - usbip . ko will return a reply of the unlinking request .
* = > give back the urb now and go to case 2 b ) .
*
* - When vhci_hcd receives RET_UNLINK ,
*
* - case 2 a ) . a submit request is still pending in vhci_hcd .
* - urb was really pending in usbip . ko and urb_unlink_urb ( ) was
* completed there .
* = > free a pending submit request
* = > notify unlink completeness by giving back the urb
*
* - case 2 b ) . a submit request is * not * pending in vhci_hcd .
* - urb was already given back to the core driver .
* = > do not give back the urb
*
*
* SERVER SIDE
*
* - When usbip receives CMD_UNLINK ,
*
* - case 3 a ) . the urb of the unlink request is now in submission .
* = > do usb_unlink_urb ( ) .
* = > after the unlink is completed , send RET_UNLINK .
*
* - case 3 b ) . the urb of the unlink request is not in submission .
* - may be already completed or never be received
* = > send RET_UNLINK
*
*/
static int vhci_urb_dequeue ( struct usb_hcd * hcd , struct urb * urb , int status )
{
2017-06-08 13:04:09 +08:00
struct vhci_hcd * vhci_hcd = hcd_to_vhci_hcd ( hcd ) ;
struct vhci * vhci = vhci_hcd - > vhci ;
2008-07-09 14:56:51 -06:00
struct vhci_priv * priv ;
struct vhci_device * vdev ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
spin_lock_irqsave ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
priv = urb - > hcpriv ;
if ( ! priv ) {
/* URB was never linked! or will be soon given back by
* vhci_rx . */
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2015-09-21 11:30:45 +02:00
return - EIDRM ;
2008-07-09 14:56:51 -06:00
}
{
int ret = 0 ;
2014-05-14 19:20:27 +02:00
2008-07-09 14:56:51 -06:00
ret = usb_hcd_check_unlink_urb ( hcd , urb , status ) ;
if ( ret ) {
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2009-07-21 00:46:13 -06:00
return ret ;
2008-07-09 14:56:51 -06:00
}
}
/* send unlink request here? */
vdev = priv - > vdev ;
if ( ! vdev - > ud . tcp_socket ) {
/* tcp connection is closed */
2013-01-22 13:31:31 +08:00
spin_lock ( & vdev - > priv_lock ) ;
2008-07-09 14:56:51 -06:00
list_del ( & priv - > list ) ;
kfree ( priv ) ;
urb - > hcpriv = NULL ;
2013-01-22 13:31:31 +08:00
spin_unlock ( & vdev - > priv_lock ) ;
2008-07-09 14:56:51 -06:00
2009-07-21 00:46:13 -06:00
/*
* If tcp connection is alive , we have sent CMD_UNLINK .
* vhci_rx will receive RET_UNLINK and give back the URB .
* Otherwise , we give back it here .
*/
usb_hcd_unlink_urb_from_ep ( hcd , urb ) ;
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2017-06-08 13:04:09 +08:00
usb_hcd_giveback_urb ( hcd , urb , urb - > status ) ;
2016-06-13 11:33:40 +09:00
spin_lock_irqsave ( & vhci - > lock , flags ) ;
2009-07-21 00:46:13 -06:00
2008-07-09 14:56:51 -06:00
} else {
/* tcp connection is alive */
struct vhci_unlink * unlink ;
2013-01-22 13:31:31 +08:00
spin_lock ( & vdev - > priv_lock ) ;
2008-07-09 14:56:51 -06:00
/* setup CMD_UNLINK pdu */
unlink = kzalloc ( sizeof ( struct vhci_unlink ) , GFP_ATOMIC ) ;
if ( ! unlink ) {
2013-01-22 13:31:31 +08:00
spin_unlock ( & vdev - > priv_lock ) ;
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
usbip_event_add ( & vdev - > ud , VDEV_EVENT_ERROR_MALLOC ) ;
return - ENOMEM ;
}
2017-06-08 13:04:09 +08:00
unlink - > seqnum = atomic_inc_return ( & vhci_hcd - > seqnum ) ;
2008-07-09 14:56:51 -06:00
if ( unlink - > seqnum = = 0xffff )
2011-05-19 16:47:32 -07:00
pr_info ( " seqnum max \n " ) ;
2008-07-09 14:56:51 -06:00
unlink - > unlink_seqnum = priv - > seqnum ;
/* send cmd_unlink and try to cancel the pending URB in the
* peer */
list_add_tail ( & unlink - > list , & vdev - > unlink_tx ) ;
wake_up ( & vdev - > waitq_tx ) ;
2013-01-22 13:31:31 +08:00
spin_unlock ( & vdev - > priv_lock ) ;
2008-07-09 14:56:51 -06:00
}
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_hc ( " leave \n " ) ;
2008-07-09 14:56:51 -06:00
return 0 ;
}
static void vhci_device_unlink_cleanup ( struct vhci_device * vdev )
{
2017-06-08 13:04:09 +08:00
struct vhci_hcd * vhci_hcd = vdev_to_vhci_hcd ( vdev ) ;
struct usb_hcd * hcd = vhci_hcd_to_hcd ( vhci_hcd ) ;
struct vhci * vhci = vhci_hcd - > vhci ;
2008-07-09 14:56:51 -06:00
struct vhci_unlink * unlink , * tmp ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
spin_lock_irqsave ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
spin_lock ( & vdev - > priv_lock ) ;
list_for_each_entry_safe ( unlink , tmp , & vdev - > unlink_tx , list ) {
2011-05-19 16:47:32 -07:00
pr_info ( " unlink cleanup tx %lu \n " , unlink - > unlink_seqnum ) ;
2008-07-09 14:56:51 -06:00
list_del ( & unlink - > list ) ;
kfree ( unlink ) ;
}
2012-09-06 20:25:04 +10:00
while ( ! list_empty ( & vdev - > unlink_rx ) ) {
2011-01-12 15:02:01 +02:00
struct urb * urb ;
2012-09-06 20:25:04 +10:00
unlink = list_first_entry ( & vdev - > unlink_rx , struct vhci_unlink ,
list ) ;
2011-01-12 15:02:01 +02:00
/* give back URB of unanswered unlink request */
2011-05-19 16:47:32 -07:00
pr_info ( " unlink cleanup rx %lu \n " , unlink - > unlink_seqnum ) ;
2011-01-12 15:02:01 +02:00
urb = pickup_urb_and_free_priv ( vdev , unlink - > unlink_seqnum ) ;
if ( ! urb ) {
2011-05-19 16:47:32 -07:00
pr_info ( " the urb (seqnum %lu) was already given back \n " ,
unlink - > unlink_seqnum ) ;
2011-01-12 15:02:01 +02:00
list_del ( & unlink - > list ) ;
kfree ( unlink ) ;
continue ;
}
urb - > status = - ENODEV ;
2016-06-13 11:33:40 +09:00
usb_hcd_unlink_urb_from_ep ( hcd , urb ) ;
2012-09-06 20:25:04 +10:00
list_del ( & unlink - > list ) ;
spin_unlock ( & vdev - > priv_lock ) ;
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2011-01-12 15:02:01 +02:00
2016-06-13 11:33:40 +09:00
usb_hcd_giveback_urb ( hcd , urb , urb - > status ) ;
2011-01-12 15:02:01 +02:00
2016-06-13 11:33:40 +09:00
spin_lock_irqsave ( & vhci - > lock , flags ) ;
2012-09-06 20:25:04 +10:00
spin_lock ( & vdev - > priv_lock ) ;
2008-07-09 14:56:51 -06:00
kfree ( unlink ) ;
}
spin_unlock ( & vdev - > priv_lock ) ;
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
}
/*
* The important thing is that only one context begins cleanup .
* This is why error handling and cleanup become simple .
* We do not want to consider race condition as possible .
*/
static void vhci_shutdown_connection ( struct usbip_device * ud )
{
struct vhci_device * vdev = container_of ( ud , struct vhci_device , ud ) ;
/* need this? see stub_dev.c */
if ( ud - > tcp_socket ) {
2017-12-15 10:50:09 -07:00
pr_debug ( " shutdown tcp_socket %d \n " , ud - > sockfd ) ;
2008-07-09 14:56:51 -06:00
kernel_sock_shutdown ( ud - > tcp_socket , SHUT_RDWR ) ;
}
2012-10-10 13:34:27 -04:00
/* kill threads related to this sdev */
2012-09-19 17:04:51 +05:30
if ( vdev - > ud . tcp_rx ) {
2012-03-13 19:07:18 +01:00
kthread_stop_put ( vdev - > ud . tcp_rx ) ;
2012-09-19 17:04:51 +05:30
vdev - > ud . tcp_rx = NULL ;
}
if ( vdev - > ud . tcp_tx ) {
2012-03-13 19:07:18 +01:00
kthread_stop_put ( vdev - > ud . tcp_tx ) ;
2012-09-19 17:04:51 +05:30
vdev - > ud . tcp_tx = NULL ;
}
2011-05-19 16:47:32 -07:00
pr_info ( " stop threads \n " ) ;
2008-07-09 14:56:51 -06:00
/* active connection is closed */
2012-10-22 06:45:00 +11:00
if ( vdev - > ud . tcp_socket ) {
2014-03-05 20:33:08 -05:00
sockfd_put ( vdev - > ud . tcp_socket ) ;
2008-07-09 14:56:51 -06:00
vdev - > ud . tcp_socket = NULL ;
2018-01-26 11:56:50 -07:00
vdev - > ud . sockfd = - 1 ;
2008-07-09 14:56:51 -06:00
}
2011-05-19 16:47:32 -07:00
pr_info ( " release socket \n " ) ;
2008-07-09 14:56:51 -06:00
vhci_device_unlink_cleanup ( vdev ) ;
/*
* rh_port_disconnect ( ) is a trigger of . . .
* usb_disable_device ( ) :
* disable all the endpoints for a USB device .
* usb_disable_endpoint ( ) :
* disable endpoints . pending urbs are unlinked ( dequeued ) .
*
* NOTE : After calling rh_port_disconnect ( ) , the USB device drivers of a
2012-08-06 08:00:27 -07:00
* detached device should release used urbs in a cleanup function ( i . e .
2008-07-09 14:56:51 -06:00
* xxx_disconnect ( ) ) . Therefore , vhci_hcd does not need to release
* pushed urbs and their private data in this function .
*
2012-08-06 08:00:27 -07:00
* NOTE : vhci_dequeue ( ) must be considered carefully . When shutting down
2008-07-09 14:56:51 -06:00
* a connection , vhci_shutdown_connection ( ) expects vhci_dequeue ( )
* gives back pushed urbs and frees their private data by request of
* the cleanup function of a USB driver . When unlinking a urb with an
* active connection , vhci_dequeue ( ) does not give back the urb which
* is actually given back by vhci_rx after receiving its return pdu .
*
*/
2016-06-13 11:33:40 +09:00
rh_port_disconnect ( vdev ) ;
2008-07-09 14:56:51 -06:00
2011-05-19 16:47:32 -07:00
pr_info ( " disconnect device \n " ) ;
2008-07-09 14:56:51 -06:00
}
static void vhci_device_reset ( struct usbip_device * ud )
{
struct vhci_device * vdev = container_of ( ud , struct vhci_device , ud ) ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2008-07-09 14:56:51 -06:00
2016-02-02 17:36:39 +00:00
spin_lock_irqsave ( & ud - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
vdev - > speed = 0 ;
vdev - > devid = 0 ;
2014-11-21 16:33:18 +01:00
usb_put_dev ( vdev - > udev ) ;
2011-01-12 15:02:00 +02:00
vdev - > udev = NULL ;
2012-10-22 06:45:00 +11:00
if ( ud - > tcp_socket ) {
2014-03-05 20:33:08 -05:00
sockfd_put ( ud - > tcp_socket ) ;
2012-10-22 06:45:00 +11:00
ud - > tcp_socket = NULL ;
2018-01-26 11:56:50 -07:00
ud - > sockfd = - 1 ;
2012-10-22 06:45:00 +11:00
}
2008-07-09 14:56:51 -06:00
ud - > status = VDEV_ST_NULL ;
2016-02-02 17:36:39 +00:00
spin_unlock_irqrestore ( & ud - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
}
static void vhci_device_unusable ( struct usbip_device * ud )
{
2016-02-02 17:36:39 +00:00
unsigned long flags ;
spin_lock_irqsave ( & ud - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
ud - > status = VDEV_ST_ERROR ;
2016-02-02 17:36:39 +00:00
spin_unlock_irqrestore ( & ud - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
}
static void vhci_device_init ( struct vhci_device * vdev )
{
2016-06-13 11:33:40 +09:00
memset ( vdev , 0 , sizeof ( struct vhci_device ) ) ;
2008-07-09 14:56:51 -06:00
vdev - > ud . side = USBIP_VHCI ;
vdev - > ud . status = VDEV_ST_NULL ;
spin_lock_init ( & vdev - > ud . lock ) ;
INIT_LIST_HEAD ( & vdev - > priv_rx ) ;
INIT_LIST_HEAD ( & vdev - > priv_tx ) ;
INIT_LIST_HEAD ( & vdev - > unlink_tx ) ;
INIT_LIST_HEAD ( & vdev - > unlink_rx ) ;
spin_lock_init ( & vdev - > priv_lock ) ;
init_waitqueue_head ( & vdev - > waitq_tx ) ;
vdev - > ud . eh_ops . shutdown = vhci_shutdown_connection ;
vdev - > ud . eh_ops . reset = vhci_device_reset ;
vdev - > ud . eh_ops . unusable = vhci_device_unusable ;
usbip_start_eh ( & vdev - > ud ) ;
}
2016-06-13 11:33:40 +09:00
static int hcd_name_to_id ( const char * name )
{
char * c ;
long val ;
int ret ;
c = strchr ( name , ' . ' ) ;
if ( c = = NULL )
return 0 ;
ret = kstrtol ( c + 1 , 10 , & val ) ;
if ( ret < 0 )
return ret ;
return val ;
}
2017-06-08 13:04:09 +08:00
static int vhci_setup ( struct usb_hcd * hcd )
{
struct vhci * vhci = * ( ( void * * ) dev_get_platdata ( hcd - > self . controller ) ) ;
2017-06-08 13:04:10 +08:00
if ( usb_hcd_is_primary_hcd ( hcd ) ) {
vhci - > vhci_hcd_hs = hcd_to_vhci_hcd ( hcd ) ;
vhci - > vhci_hcd_hs - > vhci = vhci ;
/*
* Mark the first roothub as being USB 2.0 .
* The USB 3.0 roothub will be registered later by
* vhci_hcd_probe ( )
*/
hcd - > speed = HCD_USB2 ;
hcd - > self . root_hub - > speed = USB_SPEED_HIGH ;
} else {
vhci - > vhci_hcd_ss = hcd_to_vhci_hcd ( hcd ) ;
vhci - > vhci_hcd_ss - > vhci = vhci ;
hcd - > speed = HCD_USB3 ;
hcd - > self . root_hub - > speed = USB_SPEED_SUPER ;
}
2017-06-08 13:04:09 +08:00
return 0 ;
}
2008-07-09 14:56:51 -06:00
static int vhci_start ( struct usb_hcd * hcd )
{
2017-06-08 13:04:09 +08:00
struct vhci_hcd * vhci_hcd = hcd_to_vhci_hcd ( hcd ) ;
2016-06-13 11:33:40 +09:00
int id , rhport ;
2017-06-08 13:04:09 +08:00
int err ;
2008-07-09 14:56:51 -06:00
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_hc ( " enter vhci_start \n " ) ;
2008-07-09 14:56:51 -06:00
2017-06-08 13:04:10 +08:00
if ( usb_hcd_is_primary_hcd ( hcd ) )
spin_lock_init ( & vhci_hcd - > vhci - > lock ) ;
2017-06-08 13:04:09 +08:00
2008-07-09 14:56:51 -06:00
/* initialize private data of usb_hcd */
2016-06-13 11:33:40 +09:00
for ( rhport = 0 ; rhport < VHCI_HC_PORTS ; rhport + + ) {
2017-06-08 13:04:09 +08:00
struct vhci_device * vdev = & vhci_hcd - > vdev [ rhport ] ;
2014-05-14 19:20:27 +02:00
2008-07-09 14:56:51 -06:00
vhci_device_init ( vdev ) ;
vdev - > rhport = rhport ;
}
2017-06-08 13:04:09 +08:00
atomic_set ( & vhci_hcd - > seqnum , 0 ) ;
2008-07-09 14:56:51 -06:00
hcd - > power_budget = 0 ; /* no limit */
hcd - > uses_new_polling = 1 ;
2017-06-08 13:04:09 +08:00
# ifdef CONFIG_USB_OTG
hcd - > self . otg_port = 1 ;
# endif
2016-06-13 11:33:40 +09:00
id = hcd_name_to_id ( hcd_name ( hcd ) ) ;
if ( id < 0 ) {
pr_err ( " invalid vhci name %s \n " , hcd_name ( hcd ) ) ;
return - EINVAL ;
}
2008-07-09 14:56:51 -06:00
/* vhci_hcd is now ready to be controlled through sysfs */
2017-06-08 13:04:10 +08:00
if ( id = = 0 & & usb_hcd_is_primary_hcd ( hcd ) ) {
2016-06-13 11:33:40 +09:00
err = vhci_init_attr_group ( ) ;
if ( err ) {
pr_err ( " init attr group \n " ) ;
return err ;
}
err = sysfs_create_group ( & hcd_dev ( hcd ) - > kobj , & vhci_attr_group ) ;
if ( err ) {
pr_err ( " create sysfs files \n " ) ;
vhci_finish_attr_group ( ) ;
return err ;
}
pr_info ( " created sysfs %s \n " , hcd_name ( hcd ) ) ;
2008-07-09 14:56:51 -06:00
}
return 0 ;
}
static void vhci_stop ( struct usb_hcd * hcd )
{
2017-06-08 13:04:09 +08:00
struct vhci_hcd * vhci_hcd = hcd_to_vhci_hcd ( hcd ) ;
2016-06-13 11:33:40 +09:00
int id , rhport ;
2008-07-09 14:56:51 -06:00
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_hc ( " stop VHCI controller \n " ) ;
2008-07-09 14:56:51 -06:00
/* 1. remove the userland interface of vhci_hcd */
2016-06-13 11:33:40 +09:00
id = hcd_name_to_id ( hcd_name ( hcd ) ) ;
2017-06-08 13:04:10 +08:00
if ( id = = 0 & & usb_hcd_is_primary_hcd ( hcd ) ) {
2016-06-13 11:33:40 +09:00
sysfs_remove_group ( & hcd_dev ( hcd ) - > kobj , & vhci_attr_group ) ;
vhci_finish_attr_group ( ) ;
}
2008-07-09 14:56:51 -06:00
/* 2. shutdown all the ports of vhci_hcd */
2016-06-13 11:33:40 +09:00
for ( rhport = 0 ; rhport < VHCI_HC_PORTS ; rhport + + ) {
2017-06-08 13:04:09 +08:00
struct vhci_device * vdev = & vhci_hcd - > vdev [ rhport ] ;
2008-07-09 14:56:51 -06:00
usbip_event_add ( & vdev - > ud , VDEV_EVENT_REMOVED ) ;
usbip_stop_eh ( & vdev - > ud ) ;
}
}
static int vhci_get_frame_number ( struct usb_hcd * hcd )
{
2016-06-14 13:41:07 -07:00
dev_err_ratelimited ( & hcd - > self . root_hub - > dev , " Not yet implemented \n " ) ;
2008-07-09 14:56:51 -06:00
return 0 ;
}
# ifdef CONFIG_PM
/* FIXME: suspend/resume */
static int vhci_bus_suspend ( struct usb_hcd * hcd )
{
2017-06-08 13:04:09 +08:00
struct vhci * vhci = * ( ( void * * ) dev_get_platdata ( hcd - > self . controller ) ) ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2008-07-09 14:56:51 -06:00
dev_dbg ( & hcd - > self . root_hub - > dev , " %s \n " , __func__ ) ;
2016-02-02 17:36:39 +00:00
spin_lock_irqsave ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
hcd - > state = HC_STATE_SUSPENDED ;
2016-02-02 17:36:39 +00:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
return 0 ;
}
static int vhci_bus_resume ( struct usb_hcd * hcd )
{
2017-06-08 13:04:09 +08:00
struct vhci * vhci = * ( ( void * * ) dev_get_platdata ( hcd - > self . controller ) ) ;
2008-07-09 14:56:51 -06:00
int rc = 0 ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2008-07-09 14:56:51 -06:00
dev_dbg ( & hcd - > self . root_hub - > dev , " %s \n " , __func__ ) ;
2016-02-02 17:36:39 +00:00
spin_lock_irqsave ( & vhci - > lock , flags ) ;
2013-02-22 12:13:32 +01:00
if ( ! HCD_HW_ACCESSIBLE ( hcd ) )
2008-07-09 14:56:51 -06:00
rc = - ESHUTDOWN ;
2013-02-22 12:13:32 +01:00
else
2008-07-09 14:56:51 -06:00
hcd - > state = HC_STATE_RUNNING ;
2016-02-02 17:36:39 +00:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
2011-05-19 21:36:56 -07:00
return rc ;
2008-07-09 14:56:51 -06:00
}
# else
# define vhci_bus_suspend NULL
# define vhci_bus_resume NULL
# endif
2017-06-08 13:04:10 +08:00
/* Change a group of bulk endpoints to support multiple stream IDs */
static int vhci_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 )
{
dev_dbg ( & hcd - > self . root_hub - > dev , " vhci_alloc_streams not implemented \n " ) ;
return 0 ;
}
/* Reverts a group of bulk endpoints back to not using stream IDs. */
static int vhci_free_streams ( struct usb_hcd * hcd , struct usb_device * udev ,
struct usb_host_endpoint * * eps , unsigned int num_eps ,
gfp_t mem_flags )
{
dev_dbg ( & hcd - > self . root_hub - > dev , " vhci_free_streams not implemented \n " ) ;
return 0 ;
}
2017-08-30 18:22:23 +05:30
static const struct hc_driver vhci_hc_driver = {
2008-07-09 14:56:51 -06:00
. description = driver_name ,
. product_desc = driver_desc ,
. hcd_priv_size = sizeof ( struct vhci_hcd ) ,
2017-06-08 13:04:10 +08:00
. flags = HCD_USB3 | HCD_SHARED ,
2008-07-09 14:56:51 -06:00
2017-06-08 13:04:09 +08:00
. reset = vhci_setup ,
2008-07-09 14:56:51 -06:00
. start = vhci_start ,
2010-03-15 17:06:44 +02:00
. stop = vhci_stop ,
2008-07-09 14:56:51 -06:00
. urb_enqueue = vhci_urb_enqueue ,
. urb_dequeue = vhci_urb_dequeue ,
. get_frame_number = vhci_get_frame_number ,
. hub_status_data = vhci_hub_status ,
. hub_control = vhci_hub_control ,
. bus_suspend = vhci_bus_suspend ,
. bus_resume = vhci_bus_resume ,
2017-06-08 13:04:10 +08:00
. alloc_streams = vhci_alloc_streams ,
. free_streams = vhci_free_streams ,
2008-07-09 14:56:51 -06:00
} ;
static int vhci_hcd_probe ( struct platform_device * pdev )
{
2017-06-27 09:44:26 -06:00
struct vhci * vhci = * ( ( void * * ) dev_get_platdata ( & pdev - > dev ) ) ;
2017-06-08 13:04:09 +08:00
struct usb_hcd * hcd_hs ;
2017-06-08 13:04:10 +08:00
struct usb_hcd * hcd_ss ;
2008-07-09 14:56:51 -06:00
int ret ;
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_hc ( " name %s id %d \n " , pdev - > name , pdev - > id ) ;
2008-07-09 14:56:51 -06:00
/*
* Allocate and initialize hcd .
* Our private data is also allocated automatically .
*/
2017-06-08 13:04:09 +08:00
hcd_hs = usb_create_hcd ( & vhci_hc_driver , & pdev - > dev , dev_name ( & pdev - > dev ) ) ;
if ( ! hcd_hs ) {
2017-06-08 13:04:10 +08:00
pr_err ( " create primary hcd failed \n " ) ;
2008-07-09 14:56:51 -06:00
return - ENOMEM ;
}
2017-06-08 13:04:09 +08:00
hcd_hs - > has_tt = 1 ;
2008-07-09 14:56:51 -06:00
/*
* Finish generic HCD structure initialization and register .
* Call the driver ' s reset ( ) and start ( ) routines .
*/
2017-06-08 13:04:09 +08:00
ret = usb_add_hcd ( hcd_hs , 0 , 0 ) ;
2008-07-09 14:56:51 -06:00
if ( ret ! = 0 ) {
2017-06-08 13:04:09 +08:00
pr_err ( " usb_add_hcd hs failed %d \n " , ret ) ;
goto put_usb2_hcd ;
2008-07-09 14:56:51 -06:00
}
2017-06-08 13:04:10 +08:00
hcd_ss = usb_create_shared_hcd ( & vhci_hc_driver , & pdev - > dev ,
dev_name ( & pdev - > dev ) , hcd_hs ) ;
if ( ! hcd_ss ) {
ret = - ENOMEM ;
pr_err ( " create shared hcd failed \n " ) ;
goto remove_usb2_hcd ;
}
ret = usb_add_hcd ( hcd_ss , 0 , 0 ) ;
if ( ret ) {
pr_err ( " usb_add_hcd ss failed %d \n " , ret ) ;
goto put_usb3_hcd ;
}
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_hc ( " bye \n " ) ;
2008-07-09 14:56:51 -06:00
return 0 ;
2017-06-08 13:04:09 +08:00
2017-06-08 13:04:10 +08:00
put_usb3_hcd :
usb_put_hcd ( hcd_ss ) ;
remove_usb2_hcd :
usb_remove_hcd ( hcd_hs ) ;
2017-06-08 13:04:09 +08:00
put_usb2_hcd :
usb_put_hcd ( hcd_hs ) ;
vhci - > vhci_hcd_hs = NULL ;
2017-06-08 13:04:10 +08:00
vhci - > vhci_hcd_ss = NULL ;
2017-06-08 13:04:09 +08:00
return ret ;
2008-07-09 14:56:51 -06:00
}
static int vhci_hcd_remove ( struct platform_device * pdev )
{
2017-06-08 13:04:09 +08:00
struct vhci * vhci = * ( ( void * * ) dev_get_platdata ( & pdev - > dev ) ) ;
2008-07-09 14:56:51 -06:00
/*
* Disconnects the root hub ,
* then reverses the effects of usb_add_hcd ( ) ,
* invoking the HCD ' s stop ( ) methods .
*/
2017-06-08 13:04:10 +08:00
usb_remove_hcd ( vhci_hcd_to_hcd ( vhci - > vhci_hcd_ss ) ) ;
usb_put_hcd ( vhci_hcd_to_hcd ( vhci - > vhci_hcd_ss ) ) ;
2017-06-08 13:04:09 +08:00
usb_remove_hcd ( vhci_hcd_to_hcd ( vhci - > vhci_hcd_hs ) ) ;
usb_put_hcd ( vhci_hcd_to_hcd ( vhci - > vhci_hcd_hs ) ) ;
vhci - > vhci_hcd_hs = NULL ;
2017-06-08 13:04:10 +08:00
vhci - > vhci_hcd_ss = NULL ;
2008-07-09 14:56:51 -06:00
return 0 ;
}
# ifdef CONFIG_PM
/* what should happen for USB/IP under suspend/resume? */
static int vhci_hcd_suspend ( struct platform_device * pdev , pm_message_t state )
{
struct usb_hcd * hcd ;
2017-06-08 13:04:09 +08:00
struct vhci * vhci ;
2017-06-08 13:04:10 +08:00
int rhport ;
2008-07-09 14:56:51 -06:00
int connected = 0 ;
int ret = 0 ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2008-07-09 14:56:51 -06:00
2017-06-08 13:04:09 +08:00
dev_dbg ( & pdev - > dev , " %s \n " , __func__ ) ;
2008-07-09 14:56:51 -06:00
hcd = platform_get_drvdata ( pdev ) ;
2016-06-13 11:33:40 +09:00
if ( ! hcd )
return 0 ;
2017-06-08 13:04:09 +08:00
vhci = * ( ( void * * ) dev_get_platdata ( hcd - > self . controller ) ) ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
spin_lock_irqsave ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
2017-06-08 13:04:09 +08:00
for ( rhport = 0 ; rhport < VHCI_HC_PORTS ; rhport + + ) {
if ( vhci - > vhci_hcd_hs - > port_status [ rhport ] &
USB_PORT_STAT_CONNECTION )
2008-07-09 14:56:51 -06:00
connected + = 1 ;
2017-06-08 13:04:10 +08:00
if ( vhci - > vhci_hcd_ss - > port_status [ rhport ] &
USB_PORT_STAT_CONNECTION )
connected + = 1 ;
2017-06-08 13:04:09 +08:00
}
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
if ( connected > 0 ) {
2014-03-19 23:04:57 +01:00
dev_info ( & pdev - > dev ,
" We have %d active connection%s. Do not suspend. \n " ,
connected , ( connected = = 1 ? " " : " s " ) ) ;
2008-07-09 14:56:51 -06:00
ret = - EBUSY ;
} else {
2011-05-19 16:47:32 -07:00
dev_info ( & pdev - > dev , " suspend vhci_hcd " ) ;
2008-07-09 14:56:51 -06:00
clear_bit ( HCD_FLAG_HW_ACCESSIBLE , & hcd - > flags ) ;
}
return ret ;
}
static int vhci_hcd_resume ( struct platform_device * pdev )
{
struct usb_hcd * hcd ;
dev_dbg ( & pdev - > dev , " %s \n " , __func__ ) ;
hcd = platform_get_drvdata ( pdev ) ;
2016-06-13 11:33:40 +09:00
if ( ! hcd )
return 0 ;
2008-07-09 14:56:51 -06:00
set_bit ( HCD_FLAG_HW_ACCESSIBLE , & hcd - > flags ) ;
usb_hcd_poll_rh_status ( hcd ) ;
return 0 ;
}
# else
# define vhci_hcd_suspend NULL
# define vhci_hcd_resume NULL
# endif
static struct platform_driver vhci_driver = {
. probe = vhci_hcd_probe ,
2012-11-19 13:21:02 -05:00
. remove = vhci_hcd_remove ,
2008-07-09 14:56:51 -06:00
. suspend = vhci_hcd_suspend ,
. resume = vhci_hcd_resume ,
. driver = {
2013-11-12 20:07:19 +01:00
. name = driver_name ,
2008-07-09 14:56:51 -06:00
} ,
} ;
2016-06-13 11:33:40 +09:00
static void del_platform_devices ( void )
{
struct platform_device * pdev ;
int i ;
for ( i = 0 ; i < vhci_num_controllers ; i + + ) {
2017-06-08 13:04:07 +08:00
pdev = vhcis [ i ] . pdev ;
2016-06-13 11:33:40 +09:00
if ( pdev ! = NULL )
platform_device_unregister ( pdev ) ;
2017-06-08 13:04:07 +08:00
vhcis [ i ] . pdev = NULL ;
2016-06-13 11:33:40 +09:00
}
sysfs_remove_link ( & platform_bus . kobj , driver_name ) ;
}
2008-07-09 14:56:51 -06:00
2011-05-19 21:37:06 -07:00
static int __init vhci_hcd_init ( void )
2008-07-09 14:56:51 -06:00
{
2016-06-13 11:33:40 +09:00
int i , ret ;
2008-07-09 14:56:51 -06:00
if ( usb_disabled ( ) )
return - ENODEV ;
2016-06-13 11:33:40 +09:00
if ( vhci_num_controllers < 1 )
vhci_num_controllers = 1 ;
2017-06-08 13:04:07 +08:00
vhcis = kcalloc ( vhci_num_controllers , sizeof ( struct vhci ) , GFP_KERNEL ) ;
if ( vhcis = = NULL )
2016-06-13 11:33:40 +09:00
return - ENOMEM ;
2017-06-08 13:04:08 +08:00
for ( i = 0 ; i < vhci_num_controllers ; i + + ) {
vhcis [ i ] . pdev = platform_device_alloc ( driver_name , i ) ;
if ( ! vhcis [ i ] . pdev ) {
i - - ;
while ( i > = 0 )
platform_device_put ( vhcis [ i - - ] . pdev ) ;
ret = - ENOMEM ;
goto err_device_alloc ;
}
}
for ( i = 0 ; i < vhci_num_controllers ; i + + ) {
void * vhci = & vhcis [ i ] ;
ret = platform_device_add_data ( vhcis [ i ] . pdev , & vhci , sizeof ( void * ) ) ;
if ( ret )
goto err_driver_register ;
}
2008-07-09 14:56:51 -06:00
ret = platform_driver_register ( & vhci_driver ) ;
2013-09-10 10:43:39 +05:30
if ( ret )
2008-07-09 14:56:51 -06:00
goto err_driver_register ;
2016-06-13 11:33:40 +09:00
for ( i = 0 ; i < vhci_num_controllers ; i + + ) {
2017-06-08 13:04:08 +08:00
ret = platform_device_add ( vhcis [ i ] . pdev ) ;
if ( ret < 0 ) {
i - - ;
while ( i > = 0 )
platform_device_del ( vhcis [ i - - ] . pdev ) ;
goto err_add_hcd ;
}
2016-06-13 11:33:40 +09:00
}
2008-07-09 14:56:51 -06:00
return ret ;
2017-06-08 13:04:08 +08:00
err_add_hcd :
2008-07-09 14:56:51 -06:00
platform_driver_unregister ( & vhci_driver ) ;
err_driver_register :
2017-06-08 13:04:08 +08:00
for ( i = 0 ; i < vhci_num_controllers ; i + + )
platform_device_put ( vhcis [ i ] . pdev ) ;
err_device_alloc :
2017-06-08 13:04:07 +08:00
kfree ( vhcis ) ;
2008-07-09 14:56:51 -06:00
return ret ;
}
2011-05-19 21:37:06 -07:00
static void __exit vhci_hcd_exit ( void )
2008-07-09 14:56:51 -06:00
{
2016-06-13 11:33:40 +09:00
del_platform_devices ( ) ;
2008-07-09 14:56:51 -06:00
platform_driver_unregister ( & vhci_driver ) ;
2017-06-08 13:04:07 +08:00
kfree ( vhcis ) ;
2008-07-09 14:56:51 -06:00
}
2011-05-06 03:47:50 -07:00
2011-05-19 21:37:06 -07:00
module_init ( vhci_hcd_init ) ;
module_exit ( vhci_hcd_exit ) ;
2011-05-06 03:47:50 -07:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
2011-05-06 03:47:56 -07:00
MODULE_LICENSE ( " GPL " ) ;