2008-07-09 14:56:51 -06:00
/*
* Copyright ( C ) 2003 - 2008 Takahiro Hirofuchi
*
* 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>
2008-07-09 14:56:51 -06:00
# include "usbip_common.h"
# include "vhci.h"
/* TODO: refine locking ?*/
/* Sysfs entry to show port status */
2013-08-26 12:02:54 -07:00
static ssize_t status_show ( struct device * dev , struct device_attribute * attr ,
2008-07-09 14:56:51 -06:00
char * out )
{
char * s = out ;
int i = 0 ;
2009-03-10 00:10:27 -05:00
BUG_ON ( ! the_controller | | ! out ) ;
2008-07-09 14:56:51 -06:00
spin_lock ( & the_controller - > lock ) ;
/*
* output example :
* prt sta spd dev socket local_busid
* 000 004 000 000 c5a7bb80 1 - 2.3
* 001 004 000 000 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 .
*/
2014-03-19 23:04:56 +01:00
out + = sprintf ( out ,
" prt sta spd bus dev socket local_busid \n " ) ;
2008-07-09 14:56:51 -06:00
for ( i = 0 ; i < VHCI_NPORTS ; i + + ) {
struct vhci_device * vdev = port_to_vdev ( i ) ;
spin_lock ( & vdev - > ud . lock ) ;
out + = sprintf ( out , " %03u %03u " , i , vdev - > ud . status ) ;
if ( vdev - > ud . status = = VDEV_ST_USED ) {
out + = sprintf ( out , " %03u %08x " ,
2011-05-06 03:47:52 -07:00
vdev - > speed , vdev - > devid ) ;
2008-07-09 14:56:51 -06:00
out + = sprintf ( out , " %16p " , vdev - > ud . tcp_socket ) ;
2008-10-30 01:59:32 +01:00
out + = sprintf ( out , " %s " , dev_name ( & vdev - > udev - > dev ) ) ;
2008-07-09 14:56:51 -06:00
2011-05-06 03:47:52 -07:00
} else {
2008-07-09 14:56:51 -06:00
out + = sprintf ( out , " 000 000 000 0000000000000000 0-0 " ) ;
2011-05-06 03:47:52 -07:00
}
2008-07-09 14:56:51 -06:00
out + = sprintf ( out , " \n " ) ;
spin_unlock ( & vdev - > ud . lock ) ;
}
spin_unlock ( & the_controller - > lock ) ;
return out - s ;
}
2013-08-26 12:02:54 -07:00
static DEVICE_ATTR_RO ( status ) ;
2008-07-09 14:56:51 -06:00
/* Sysfs entry to shutdown a virtual connection */
static int vhci_port_disconnect ( __u32 rhport )
{
struct vhci_device * vdev ;
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_sysfs ( " enter \n " ) ;
2008-07-09 14:56:51 -06:00
/* lock */
spin_lock ( & the_controller - > lock ) ;
vdev = port_to_vdev ( rhport ) ;
spin_lock ( & vdev - > ud . lock ) ;
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 ) ;
spin_unlock ( & the_controller - > lock ) ;
return - EINVAL ;
}
/* unlock */
spin_unlock ( & vdev - > ud . lock ) ;
spin_unlock ( & the_controller - > lock ) ;
usbip_event_add ( & vdev - > ud , VDEV_EVENT_DOWN ) ;
return 0 ;
}
static ssize_t store_detach ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
int err ;
__u32 rhport = 0 ;
2014-03-06 10:36:34 -08:00
if ( sscanf ( buf , " %u " , & rhport ) ! = 1 )
return - EINVAL ;
2008-07-09 14:56:51 -06:00
/* check rhport */
if ( rhport > = VHCI_NPORTS ) {
2011-05-19 16:47:32 -07:00
dev_err ( dev , " invalid port %u \n " , rhport ) ;
2008-07-09 14:56:51 -06:00
return - EINVAL ;
}
err = vhci_port_disconnect ( rhport ) ;
if ( err < 0 )
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 ) ;
/* Sysfs entry to establish a virtual connection */
static int valid_args ( __u32 rhport , enum usb_device_speed speed )
{
/* check rhport */
2011-05-26 09:24:45 +02:00
if ( rhport > = VHCI_NPORTS ) {
2011-05-19 16:47:32 -07:00
pr_err ( " port %u \n " , rhport ) ;
2008-07-09 14:56:51 -06:00
return - EINVAL ;
}
/* check speed */
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 :
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 ) ) ;
2008-07-09 14:56:51 -06:00
return - EINVAL ;
}
return 0 ;
}
/*
* 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 vhci_device * vdev ;
struct socket * socket ;
int sockfd = 0 ;
__u32 rhport = 0 , devid = 0 , speed = 0 ;
2014-03-05 20:33:08 -05:00
int err ;
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
*/
2014-03-25 06:47:13 -06:00
if ( sscanf ( buf , " %u %u %u %u " , & rhport , & sockfd , & devid , & speed ) ! = 4 )
2014-03-06 10:36:34 -08:00
return - EINVAL ;
2008-07-09 14:56:51 -06:00
2009-07-21 00:46:13 -06:00
usbip_dbg_vhci_sysfs ( " rhport(%u) sockfd(%u) devid(%u) speed(%u) \n " ,
2011-05-06 03:47:52 -07:00
rhport , sockfd , devid , speed ) ;
2008-07-09 14:56:51 -06:00
/* check received parameters */
if ( valid_args ( rhport , speed ) < 0 )
return - EINVAL ;
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 */
spin_lock ( & the_controller - > lock ) ;
vdev = port_to_vdev ( rhport ) ;
spin_lock ( & vdev - > ud . lock ) ;
if ( vdev - > ud . status ! = VDEV_ST_NULL ) {
/* end of the lock */
spin_unlock ( & vdev - > ud . lock ) ;
spin_unlock ( & the_controller - > lock ) ;
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 ;
}
2014-01-22 14:24:29 -07:00
dev_info ( dev ,
" rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s) \n " ,
rhport , sockfd , 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 ) ;
spin_unlock ( & the_controller - > lock ) ;
/* 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
2008-07-09 14:56:51 -06:00
rh_port_connect ( rhport , speed ) ;
return count ;
}
static DEVICE_ATTR ( attach , S_IWUSR , NULL , store_attach ) ;
static struct attribute * dev_attrs [ ] = {
& dev_attr_status . attr ,
& dev_attr_detach . attr ,
& dev_attr_attach . attr ,
& dev_attr_usbip_debug . attr ,
NULL ,
} ;
2011-05-30 21:50:26 +02:00
const struct attribute_group dev_attr_group = {
2008-07-09 14:56:51 -06:00
. attrs = dev_attrs ,
} ;