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
*
* This 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 is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 ,
* USA .
*/
2011-05-11 22:33:43 -07:00
# include <linux/kthread.h>
2012-10-22 06:45:00 +11:00
# include <linux/file.h>
2011-05-11 22:33:43 -07:00
# include <linux/net.h>
2016-06-13 11:33:40 +09:00
# include <linux/platform_device.h>
# include <linux/slab.h>
2011-05-11 22:33:43 -07:00
2008-07-09 14:56:51 -06:00
# include "usbip_common.h"
# include "vhci.h"
/* TODO: refine locking ?*/
2017-06-08 13:04:10 +08:00
/*
* output example :
* hub port sta spd dev socket local_busid
* hs 0000 004 000 00000000 c5a7bb80 1 - 2.3
* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
* ss 000 8 004 000 00000000 d8cee980 2 - 3.4
* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
*
* IP address can be retrieved from a socket pointer address by looking
* up / proc / net / { tcp , tcp6 } . Also , a userland program may remember a
* port number and its peer IP address .
*/
static void port_show_vhci ( char * * out , int hub , int port , struct vhci_device * vdev )
{
if ( hub = = HUB_SPEED_HIGH )
* out + = sprintf ( * out , " hs %04u %03u " ,
port , vdev - > ud . status ) ;
else /* hub == HUB_SPEED_SUPER */
* out + = sprintf ( * out , " ss %04u %03u " ,
port , vdev - > ud . status ) ;
if ( vdev - > ud . status = = VDEV_ST_USED ) {
* out + = sprintf ( * out , " %03u %08x " ,
vdev - > speed , vdev - > devid ) ;
* out + = sprintf ( * out , " %16p %s " ,
vdev - > ud . tcp_socket ,
dev_name ( & vdev - > udev - > dev ) ) ;
} else {
* out + = sprintf ( * out , " 000 00000000 " ) ;
* out + = sprintf ( * out , " 0000000000000000 0-0 " ) ;
}
* out + = sprintf ( * out , " \n " ) ;
}
2008-07-09 14:56:51 -06:00
/* Sysfs entry to show port status */
2016-06-13 11:33:40 +09:00
static ssize_t status_show_vhci ( int pdev_nr , char * out )
2008-07-09 14:56:51 -06:00
{
2017-06-08 13:04:07 +08:00
struct platform_device * pdev = vhcis [ pdev_nr ] . pdev ;
2017-06-08 13:04:09 +08:00
struct vhci * vhci ;
struct usb_hcd * hcd ;
struct vhci_hcd * vhci_hcd ;
2008-07-09 14:56:51 -06:00
char * s = out ;
2017-06-08 13:04:09 +08:00
int i ;
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 ( ! pdev | | ! out ) {
usbip_dbg_vhci_sysfs ( " show status error \n " ) ;
return 0 ;
}
2017-06-08 13:04:09 +08:00
hcd = platform_get_drvdata ( pdev ) ;
vhci_hcd = hcd_to_vhci_hcd ( hcd ) ;
vhci = vhci_hcd - > vhci ;
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
2016-06-13 11:33:40 +09:00
for ( i = 0 ; i < VHCI_HC_PORTS ; i + + ) {
2017-06-08 13:04:10 +08:00
struct vhci_device * vdev = & vhci - > vhci_hcd_hs - > vdev [ i ] ;
2008-07-09 14:56:51 -06:00
spin_lock ( & vdev - > ud . lock ) ;
2017-06-08 13:04:10 +08:00
port_show_vhci ( & out , HUB_SPEED_HIGH ,
2017-06-08 13:04:13 +08:00
pdev_nr * VHCI_PORTS + i , vdev ) ;
2017-06-08 13:04:10 +08:00
spin_unlock ( & vdev - > ud . lock ) ;
}
2008-07-09 14:56:51 -06:00
2017-06-08 13:04:10 +08:00
for ( i = 0 ; i < VHCI_HC_PORTS ; i + + ) {
struct vhci_device * vdev = & vhci - > vhci_hcd_ss - > vdev [ i ] ;
spin_lock ( & vdev - > ud . lock ) ;
port_show_vhci ( & out , HUB_SPEED_SUPER ,
2017-06-08 13:04:13 +08:00
pdev_nr * VHCI_PORTS + VHCI_HC_PORTS + i , vdev ) ;
2008-07-09 14:56:51 -06:00
spin_unlock ( & vdev - > ud . lock ) ;
}
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
return out - s ;
}
static ssize_t status_show_not_ready ( int pdev_nr , char * out )
{
char * s = out ;
int i = 0 ;
for ( i = 0 ; i < VHCI_HC_PORTS ; i + + ) {
2017-06-08 13:04:10 +08:00
out + = sprintf ( out , " hs %04u %03u " ,
2017-06-08 13:04:13 +08:00
( pdev_nr * VHCI_PORTS ) + i ,
2017-06-08 13:04:10 +08:00
VDEV_ST_NOTASSIGNED ) ;
out + = sprintf ( out , " 000 00000000 0000000000000000 0-0 " ) ;
out + = sprintf ( out , " \n " ) ;
}
for ( i = 0 ; i < VHCI_HC_PORTS ; i + + ) {
out + = sprintf ( out , " ss %04u %03u " ,
2017-06-08 13:04:13 +08:00
( pdev_nr * VHCI_PORTS ) + VHCI_HC_PORTS + i ,
2016-06-13 11:33:40 +09:00
VDEV_ST_NOTASSIGNED ) ;
out + = sprintf ( out , " 000 00000000 0000000000000000 0-0 " ) ;
out + = sprintf ( out , " \n " ) ;
}
return out - s ;
}
static int status_name_to_id ( const char * name )
{
char * c ;
long val ;
int ret ;
c = strchr ( name , ' . ' ) ;
if ( c = = NULL )
return 0 ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
ret = kstrtol ( c + 1 , 10 , & val ) ;
if ( ret < 0 )
return ret ;
return val ;
}
static ssize_t status_show ( struct device * dev ,
struct device_attribute * attr , char * out )
{
char * s = out ;
int pdev_nr ;
out + = sprintf ( out ,
2017-06-08 13:04:10 +08:00
" hub port sta spd dev socket local_busid \n " ) ;
2016-06-13 11:33:40 +09:00
pdev_nr = status_name_to_id ( attr - > attr . name ) ;
if ( pdev_nr < 0 )
out + = status_show_not_ready ( pdev_nr , out ) ;
else
out + = status_show_vhci ( pdev_nr , out ) ;
return out - s ;
}
static ssize_t nports_show ( struct device * dev , struct device_attribute * attr ,
char * out )
{
char * s = out ;
2017-06-08 13:04:10 +08:00
/*
* Half the ports are for SPEED_HIGH and half for SPEED_SUPER , thus the * 2.
*/
2017-06-08 13:04:13 +08:00
out + = sprintf ( out , " %d \n " , VHCI_PORTS * vhci_num_controllers ) ;
2008-07-09 14:56:51 -06:00
return out - s ;
}
2016-06-13 11:33:40 +09:00
static DEVICE_ATTR_RO ( nports ) ;
2008-07-09 14:56:51 -06:00
/* Sysfs entry to shutdown a virtual connection */
2017-06-08 13:04:09 +08:00
static int vhci_port_disconnect ( struct vhci_hcd * vhci_hcd , __u32 rhport )
2008-07-09 14:56:51 -06:00
{
2017-06-08 13:04:09 +08:00
struct vhci_device * vdev = & vhci_hcd - > vdev [ rhport ] ;
struct vhci * vhci = vhci_hcd - > vhci ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2008-07-09 14:56:51 -06:00
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_sysfs ( " enter \n " ) ;
2008-07-09 14:56:51 -06:00
/* lock */
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 - > ud . lock ) ;
2016-06-13 11:33:40 +09:00
2008-07-09 14:56:51 -06:00
if ( vdev - > ud . status = = VDEV_ST_NULL ) {
2011-05-19 16:47:32 -07:00
pr_err ( " not connected %d \n " , vdev - > ud . status ) ;
2008-07-09 14:56:51 -06:00
/* unlock */
spin_unlock ( & vdev - > ud . lock ) ;
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
return - EINVAL ;
}
/* unlock */
spin_unlock ( & vdev - > ud . 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_DOWN ) ;
return 0 ;
}
2016-06-13 11:33:40 +09:00
static int valid_port ( __u32 pdev_nr , __u32 rhport )
{
if ( pdev_nr > = vhci_num_controllers ) {
pr_err ( " pdev %u \n " , pdev_nr ) ;
return 0 ;
}
if ( rhport > = VHCI_HC_PORTS ) {
pr_err ( " rhport %u \n " , rhport ) ;
return 0 ;
}
return 1 ;
}
2008-07-09 14:56:51 -06:00
static ssize_t store_detach ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
2016-06-13 11:33:40 +09:00
__u32 port = 0 , pdev_nr = 0 , rhport = 0 ;
struct usb_hcd * hcd ;
2017-06-08 13:04:10 +08:00
struct vhci_hcd * vhci_hcd ;
2016-06-13 11:33:40 +09:00
int ret ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
if ( kstrtoint ( buf , 10 , & port ) < 0 )
2014-03-06 10:36:34 -08:00
return - EINVAL ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
pdev_nr = port_to_pdev_nr ( port ) ;
rhport = port_to_rhport ( port ) ;
if ( ! valid_port ( pdev_nr , rhport ) )
2008-07-09 14:56:51 -06:00
return - EINVAL ;
2016-06-13 11:33:40 +09:00
2017-06-08 13:04:07 +08:00
hcd = platform_get_drvdata ( vhcis [ pdev_nr ] . pdev ) ;
2016-06-13 11:33:40 +09:00
if ( hcd = = NULL ) {
dev_err ( dev , " port is not ready %u \n " , port ) ;
return - EAGAIN ;
2008-07-09 14:56:51 -06:00
}
2017-06-08 13:04:10 +08:00
usbip_dbg_vhci_sysfs ( " rhport %d \n " , rhport ) ;
if ( ( port / VHCI_HC_PORTS ) % 2 )
vhci_hcd = hcd_to_vhci_hcd ( hcd ) - > vhci - > vhci_hcd_ss ;
else
vhci_hcd = hcd_to_vhci_hcd ( hcd ) - > vhci - > vhci_hcd_hs ;
ret = vhci_port_disconnect ( vhci_hcd , rhport ) ;
2016-06-13 11:33:40 +09:00
if ( ret < 0 )
2008-07-09 14:56:51 -06:00
return - EINVAL ;
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_sysfs ( " Leave \n " ) ;
2011-05-06 03:47:52 -07:00
2008-07-09 14:56:51 -06:00
return count ;
}
static DEVICE_ATTR ( detach , S_IWUSR , NULL , store_detach ) ;
2016-06-13 11:33:40 +09:00
static int valid_args ( __u32 pdev_nr , __u32 rhport , enum usb_device_speed speed )
2008-07-09 14:56:51 -06:00
{
2016-06-13 11:33:40 +09:00
if ( ! valid_port ( pdev_nr , rhport ) ) {
return 0 ;
2008-07-09 14:56:51 -06:00
}
switch ( speed ) {
case USB_SPEED_LOW :
case USB_SPEED_FULL :
case USB_SPEED_HIGH :
2010-01-14 11:08:04 -08:00
case USB_SPEED_WIRELESS :
2017-06-08 13:04:11 +08:00
case USB_SPEED_SUPER :
2008-07-09 14:56:51 -06:00
break ;
default :
2014-01-22 09:38:14 -07:00
pr_err ( " Failed attach request for unsupported USB speed: %s \n " ,
usb_speed_string ( speed ) ) ;
2016-06-13 11:33:40 +09:00
return 0 ;
2008-07-09 14:56:51 -06:00
}
2016-06-13 11:33:40 +09:00
return 1 ;
2008-07-09 14:56:51 -06:00
}
2016-06-13 11:33:40 +09:00
/* Sysfs entry to establish a virtual connection */
2008-07-09 14:56:51 -06:00
/*
* To start a new USB / IP attachment , a userland program needs to setup a TCP
* connection and then write its socket descriptor with remote device
* information into this sysfs file .
*
* A remote device is virtually attached to the root - hub port of @ rhport with
* @ speed . @ devid is embedded into a request to specify the remote device in a
* server host .
*
* write ( ) returns 0 on success , else negative errno .
*/
static ssize_t store_attach ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct socket * socket ;
int sockfd = 0 ;
2016-06-13 11:33:40 +09:00
__u32 port = 0 , pdev_nr = 0 , rhport = 0 , devid = 0 , speed = 0 ;
struct usb_hcd * hcd ;
2017-06-08 13:04:09 +08:00
struct vhci_hcd * vhci_hcd ;
2016-06-13 11:33:40 +09:00
struct vhci_device * vdev ;
2017-06-08 13:04:09 +08:00
struct vhci * vhci ;
2014-03-05 20:33:08 -05:00
int err ;
2016-02-02 17:36:39 +00:00
unsigned long flags ;
2008-07-09 14:56:51 -06:00
/*
* @ rhport : port number of vhci_hcd
* @ sockfd : socket descriptor of an established TCP connection
* @ devid : unique device identifier in a remote host
* @ speed : usb device speed in a remote host
*/
2016-06-13 11:33:40 +09:00
if ( sscanf ( buf , " %u %u %u %u " , & port , & sockfd , & devid , & speed ) ! = 4 )
2014-03-06 10:36:34 -08:00
return - EINVAL ;
2016-06-13 11:33:40 +09:00
pdev_nr = port_to_pdev_nr ( port ) ;
rhport = port_to_rhport ( port ) ;
2008-07-09 14:56:51 -06:00
2016-06-13 11:33:40 +09:00
usbip_dbg_vhci_sysfs ( " port(%u) pdev(%d) rhport(%u) \n " ,
port , pdev_nr , rhport ) ;
usbip_dbg_vhci_sysfs ( " sockfd(%u) devid(%u) speed(%u) \n " ,
sockfd , devid , speed ) ;
2008-07-09 14:56:51 -06:00
/* check received parameters */
2016-06-13 11:33:40 +09:00
if ( ! valid_args ( pdev_nr , rhport , speed ) )
2008-07-09 14:56:51 -06:00
return - EINVAL ;
2017-06-08 13:04:07 +08:00
hcd = platform_get_drvdata ( vhcis [ pdev_nr ] . pdev ) ;
2016-06-13 11:33:40 +09:00
if ( hcd = = NULL ) {
dev_err ( dev , " port %d is not ready \n " , port ) ;
return - EAGAIN ;
}
2017-06-08 13:04:09 +08:00
vhci_hcd = hcd_to_vhci_hcd ( hcd ) ;
vhci = vhci_hcd - > vhci ;
2017-06-08 13:04:10 +08:00
if ( speed = = USB_SPEED_SUPER )
vdev = & vhci - > vhci_hcd_ss - > vdev [ rhport ] ;
else
vdev = & vhci - > vhci_hcd_hs - > vdev [ rhport ] ;
2016-06-13 11:33:40 +09:00
2012-10-22 06:45:00 +11:00
/* Extract socket from fd. */
2014-03-05 20:33:08 -05:00
socket = sockfd_lookup ( sockfd , & err ) ;
2008-07-09 14:56:51 -06:00
if ( ! socket )
2011-05-27 06:18:48 +02:00
return - EINVAL ;
2008-07-09 14:56:51 -06:00
/* now need lock until setting vdev status as used */
/* begin a lock */
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 - > ud . lock ) ;
if ( vdev - > ud . status ! = VDEV_ST_NULL ) {
/* end of the lock */
spin_unlock ( & vdev - > ud . lock ) ;
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
2014-03-05 20:33:08 -05:00
sockfd_put ( socket ) ;
2012-10-22 06:45:00 +11:00
2011-05-19 16:47:32 -07:00
dev_err ( dev , " port %d already used \n " , rhport ) ;
2008-07-09 14:56:51 -06:00
return - EINVAL ;
}
2016-06-13 11:33:40 +09:00
dev_info ( dev , " pdev(%u) rhport(%u) sockfd(%d) \n " ,
pdev_nr , rhport , sockfd ) ;
dev_info ( dev , " devid(%u) speed(%u) speed_str(%s) \n " ,
devid , speed , usb_speed_string ( speed ) ) ;
2008-07-09 14:56:51 -06:00
vdev - > devid = devid ;
vdev - > speed = speed ;
vdev - > ud . tcp_socket = socket ;
vdev - > ud . status = VDEV_ST_NOTASSIGNED ;
spin_unlock ( & vdev - > ud . lock ) ;
2016-06-13 11:33:40 +09:00
spin_unlock_irqrestore ( & vhci - > lock , flags ) ;
2008-07-09 14:56:51 -06:00
/* end the lock */
2012-03-13 19:07:18 +01:00
vdev - > ud . tcp_rx = kthread_get_run ( vhci_rx_loop , & vdev - > ud , " vhci_rx " ) ;
vdev - > ud . tcp_tx = kthread_get_run ( vhci_tx_loop , & vdev - > ud , " vhci_tx " ) ;
2011-04-18 21:44:10 +02:00
2016-06-13 11:33:40 +09:00
rh_port_connect ( vdev , speed ) ;
2008-07-09 14:56:51 -06:00
return count ;
}
static DEVICE_ATTR ( attach , S_IWUSR , NULL , store_attach ) ;
2016-06-13 11:33:40 +09:00
# define MAX_STATUS_NAME 16
struct status_attr {
struct device_attribute attr ;
char name [ MAX_STATUS_NAME + 1 ] ;
2008-07-09 14:56:51 -06:00
} ;
2016-06-13 11:33:40 +09:00
static struct status_attr * status_attrs ;
static void set_status_attr ( int id )
{
struct status_attr * status ;
status = status_attrs + id ;
if ( id = = 0 )
strcpy ( status - > name , " status " ) ;
else
snprintf ( status - > name , MAX_STATUS_NAME + 1 , " status.%d " , id ) ;
status - > attr . attr . name = status - > name ;
status - > attr . attr . mode = S_IRUGO ;
status - > attr . show = status_show ;
2016-12-05 12:56:38 -07:00
sysfs_attr_init ( & status - > attr . attr ) ;
2016-06-13 11:33:40 +09:00
}
static int init_status_attrs ( void )
{
int id ;
status_attrs = kcalloc ( vhci_num_controllers , sizeof ( struct status_attr ) ,
GFP_KERNEL ) ;
if ( status_attrs = = NULL )
return - ENOMEM ;
for ( id = 0 ; id < vhci_num_controllers ; id + + )
set_status_attr ( id ) ;
return 0 ;
}
static void finish_status_attrs ( void )
{
kfree ( status_attrs ) ;
}
struct attribute_group vhci_attr_group = {
. attrs = NULL ,
2008-07-09 14:56:51 -06:00
} ;
2016-06-13 11:33:40 +09:00
int vhci_init_attr_group ( void )
{
struct attribute * * attrs ;
int ret , i ;
attrs = kcalloc ( ( vhci_num_controllers + 5 ) , sizeof ( struct attribute * ) ,
GFP_KERNEL ) ;
if ( attrs = = NULL )
return - ENOMEM ;
ret = init_status_attrs ( ) ;
if ( ret ) {
kfree ( attrs ) ;
return ret ;
}
* attrs = & dev_attr_nports . attr ;
* ( attrs + 1 ) = & dev_attr_detach . attr ;
* ( attrs + 2 ) = & dev_attr_attach . attr ;
* ( attrs + 3 ) = & dev_attr_usbip_debug . attr ;
for ( i = 0 ; i < vhci_num_controllers ; i + + )
* ( attrs + i + 4 ) = & ( ( status_attrs + i ) - > attr . attr ) ;
vhci_attr_group . attrs = attrs ;
return 0 ;
}
void vhci_finish_attr_group ( void )
{
finish_status_attrs ( ) ;
kfree ( vhci_attr_group . attrs ) ;
}