2017-11-03 13:28:30 +03:00
// SPDX-License-Identifier: GPL-2.0+
2008-07-10 00:56:51 +04:00
/*
* Copyright ( C ) 2003 - 2008 Takahiro Hirofuchi
*/
2011-05-12 09:33:43 +04:00
# include <linux/string.h>
2011-07-03 23:49:50 +04:00
# include <linux/module.h>
2014-03-08 16:53:33 +04:00
# include <linux/device.h>
2008-07-10 00:56:51 +04:00
# include "usbip_common.h"
# include "stub.h"
# define DRIVER_AUTHOR "Takahiro Hirofuchi"
2011-05-12 09:33:44 +04:00
# define DRIVER_DESC "USB / IP Host Driver"
2008-07-10 00:56:51 +04:00
struct kmem_cache * stub_priv_cache ;
2018-05-01 01:17:20 +03:00
2008-07-10 00:56:51 +04:00
/*
* busid_tables defines matching busids that usbip can grab . A user can change
* dynamically what device is locally used and what device is exported to a
* remote host .
*/
# define MAX_BUSID 16
2010-07-27 14:39:30 +04:00
static struct bus_id_priv busid_table [ MAX_BUSID ] ;
2008-07-10 00:56:51 +04:00
static spinlock_t busid_table_lock ;
2011-05-20 08:36:57 +04:00
static void init_busid_table ( void )
{
2018-05-15 05:49:58 +03:00
int i ;
2013-04-04 18:03:10 +04:00
/*
* This also sets the bus_table [ i ] . status to
* STUB_BUSID_OTHER , which is 0.
*/
2011-05-27 12:49:24 +04:00
memset ( busid_table , 0 , sizeof ( busid_table ) ) ;
2011-05-20 08:36:57 +04:00
spin_lock_init ( & busid_table_lock ) ;
2018-05-15 05:49:58 +03:00
for ( i = 0 ; i < MAX_BUSID ; i + + )
spin_lock_init ( & busid_table [ i ] . busid_lock ) ;
2011-05-20 08:36:57 +04:00
}
2011-05-20 08:36:58 +04:00
/*
* Find the index of the busid by name .
* Must be called with busid_table_lock held .
*/
static int get_busid_idx ( const char * busid )
2008-07-10 00:56:51 +04:00
{
int i ;
2011-05-20 08:36:58 +04:00
int idx = - 1 ;
2008-07-10 00:56:51 +04:00
2018-05-15 05:49:58 +03:00
for ( i = 0 ; i < MAX_BUSID ; i + + ) {
spin_lock ( & busid_table [ i ] . busid_lock ) ;
2010-07-27 14:39:30 +04:00
if ( busid_table [ i ] . name [ 0 ] )
if ( ! strncmp ( busid_table [ i ] . name , busid , BUSID_SIZE ) ) {
2011-05-20 08:36:58 +04:00
idx = i ;
2018-05-15 05:49:58 +03:00
spin_unlock ( & busid_table [ i ] . busid_lock ) ;
2011-05-20 08:36:58 +04:00
break ;
2008-07-10 00:56:51 +04:00
}
2018-05-15 05:49:58 +03:00
spin_unlock ( & busid_table [ i ] . busid_lock ) ;
}
2011-05-20 08:36:58 +04:00
return idx ;
2008-07-10 00:56:51 +04:00
}
2018-05-15 05:49:58 +03:00
/* Returns holding busid_lock. Should call put_busid_priv() to unlock */
2010-07-27 14:39:30 +04:00
struct bus_id_priv * get_busid_priv ( const char * busid )
{
2011-05-20 08:36:58 +04:00
int idx ;
struct bus_id_priv * bid = NULL ;
2010-07-27 14:39:30 +04:00
spin_lock ( & busid_table_lock ) ;
2011-05-20 08:36:58 +04:00
idx = get_busid_idx ( busid ) ;
2018-05-15 05:49:58 +03:00
if ( idx > = 0 ) {
2011-05-20 08:36:58 +04:00
bid = & ( busid_table [ idx ] ) ;
2018-05-15 05:49:58 +03:00
/* get busid_lock before returning */
spin_lock ( & bid - > busid_lock ) ;
}
2010-07-27 14:39:30 +04:00
spin_unlock ( & busid_table_lock ) ;
2011-05-20 08:36:58 +04:00
return bid ;
2010-07-27 14:39:30 +04:00
}
2018-05-15 05:49:58 +03:00
void put_busid_priv ( struct bus_id_priv * bid )
{
spin_unlock ( & bid - > busid_lock ) ;
}
2008-07-10 00:56:51 +04:00
static int add_match_busid ( char * busid )
{
int i ;
2011-05-20 08:36:58 +04:00
int ret = - 1 ;
2008-07-10 00:56:51 +04:00
spin_lock ( & busid_table_lock ) ;
2011-05-20 08:36:58 +04:00
/* already registered? */
if ( get_busid_idx ( busid ) > = 0 ) {
ret = 0 ;
goto out ;
}
2018-05-15 05:49:58 +03:00
for ( i = 0 ; i < MAX_BUSID ; i + + ) {
spin_lock ( & busid_table [ i ] . busid_lock ) ;
2010-07-27 14:39:30 +04:00
if ( ! busid_table [ i ] . name [ 0 ] ) {
2014-07-26 18:43:20 +04:00
strlcpy ( busid_table [ i ] . name , busid , BUSID_SIZE ) ;
2010-07-27 14:39:30 +04:00
if ( ( busid_table [ i ] . status ! = STUB_BUSID_ALLOC ) & &
( busid_table [ i ] . status ! = STUB_BUSID_REMOV ) )
busid_table [ i ] . status = STUB_BUSID_ADDED ;
2011-05-20 08:36:58 +04:00
ret = 0 ;
2018-05-15 05:49:58 +03:00
spin_unlock ( & busid_table [ i ] . busid_lock ) ;
2011-05-20 08:36:58 +04:00
break ;
2008-07-10 00:56:51 +04:00
}
2018-05-15 05:49:58 +03:00
spin_unlock ( & busid_table [ i ] . busid_lock ) ;
}
2011-05-20 08:36:58 +04:00
out :
2008-07-10 00:56:51 +04:00
spin_unlock ( & busid_table_lock ) ;
2011-05-20 08:36:58 +04:00
return ret ;
2008-07-10 00:56:51 +04:00
}
2010-07-27 14:39:30 +04:00
int del_match_busid ( char * busid )
2008-07-10 00:56:51 +04:00
{
2011-05-20 08:36:58 +04:00
int idx ;
int ret = - 1 ;
2008-07-10 00:56:51 +04:00
spin_lock ( & busid_table_lock ) ;
2011-05-20 08:36:58 +04:00
idx = get_busid_idx ( busid ) ;
if ( idx < 0 )
goto out ;
/* found */
ret = 0 ;
2018-05-15 05:49:58 +03:00
spin_lock ( & busid_table [ idx ] . busid_lock ) ;
2011-05-20 08:36:58 +04:00
if ( busid_table [ idx ] . status = = STUB_BUSID_OTHER )
memset ( busid_table [ idx ] . name , 0 , BUSID_SIZE ) ;
if ( ( busid_table [ idx ] . status ! = STUB_BUSID_OTHER ) & &
( busid_table [ idx ] . status ! = STUB_BUSID_ADDED ) )
busid_table [ idx ] . status = STUB_BUSID_REMOV ;
2018-05-15 05:49:58 +03:00
spin_unlock ( & busid_table [ idx ] . busid_lock ) ;
2011-05-20 08:36:58 +04:00
out :
2008-07-10 00:56:51 +04:00
spin_unlock ( & busid_table_lock ) ;
2011-05-20 08:36:58 +04:00
return ret ;
2008-07-10 00:56:51 +04:00
}
2011-05-06 14:47:42 +04:00
2017-06-09 12:03:14 +03:00
static ssize_t match_busid_show ( struct device_driver * drv , char * buf )
2010-07-27 14:39:30 +04:00
{
int i ;
2011-05-20 08:36:57 +04:00
char * out = buf ;
2010-07-27 14:39:30 +04:00
2011-05-20 08:36:57 +04:00
spin_lock ( & busid_table_lock ) ;
2018-05-15 05:49:58 +03:00
for ( i = 0 ; i < MAX_BUSID ; i + + ) {
spin_lock ( & busid_table [ i ] . busid_lock ) ;
2011-05-20 08:36:57 +04:00
if ( busid_table [ i ] . name [ 0 ] )
out + = sprintf ( out , " %s " , busid_table [ i ] . name ) ;
2018-05-15 05:49:58 +03:00
spin_unlock ( & busid_table [ i ] . busid_lock ) ;
}
2011-05-20 08:36:57 +04:00
spin_unlock ( & busid_table_lock ) ;
out + = sprintf ( out , " \n " ) ;
2011-05-20 08:36:58 +04:00
2011-05-20 08:36:57 +04:00
return out - buf ;
2010-07-27 14:39:30 +04:00
}
2008-07-10 00:56:51 +04:00
2017-06-09 12:03:14 +03:00
static ssize_t match_busid_store ( struct device_driver * dev , const char * buf ,
2011-05-06 14:47:42 +04:00
size_t count )
2008-07-10 00:56:51 +04:00
{
int len ;
2008-10-30 03:59:32 +03:00
char busid [ BUSID_SIZE ] ;
2008-07-10 00:56:51 +04:00
if ( count < 5 )
return - EINVAL ;
/* busid needs to include \0 termination */
2014-07-26 18:43:20 +04:00
len = strlcpy ( busid , buf + 4 , BUSID_SIZE ) ;
if ( sizeof ( busid ) < = len )
2008-07-10 00:56:51 +04:00
return - EINVAL ;
if ( ! strncmp ( buf , " add " , 4 ) ) {
2013-04-04 18:03:09 +04:00
if ( add_match_busid ( busid ) < 0 )
2008-07-10 00:56:51 +04:00
return - ENOMEM ;
2013-04-04 18:03:09 +04:00
pr_debug ( " add busid %s \n " , busid ) ;
return count ;
}
if ( ! strncmp ( buf , " del " , 4 ) ) {
if ( del_match_busid ( busid ) < 0 )
2008-07-10 00:56:51 +04:00
return - ENODEV ;
2013-04-04 18:03:09 +04:00
pr_debug ( " del busid %s \n " , busid ) ;
return count ;
2011-05-20 08:36:58 +04:00
}
2013-04-04 18:03:09 +04:00
return - EINVAL ;
2008-07-10 00:56:51 +04:00
}
2017-06-09 12:03:14 +03:00
static DRIVER_ATTR_RW ( match_busid ) ;
2008-07-10 00:56:51 +04:00
2018-05-01 01:17:20 +03:00
static int do_rebind ( char * busid , struct bus_id_priv * busid_priv )
{
int ret ;
/* device_attach() callers should hold parent lock for USB */
if ( busid_priv - > udev - > dev . parent )
device_lock ( busid_priv - > udev - > dev . parent ) ;
ret = device_attach ( & busid_priv - > udev - > dev ) ;
if ( busid_priv - > udev - > dev . parent )
device_unlock ( busid_priv - > udev - > dev . parent ) ;
if ( ret < 0 ) {
dev_err ( & busid_priv - > udev - > dev , " rebind failed \n " ) ;
return ret ;
}
return 0 ;
}
static void stub_device_rebind ( void )
{
# if IS_MODULE(CONFIG_USBIP_HOST)
struct bus_id_priv * busid_priv ;
int i ;
/* update status to STUB_BUSID_OTHER so probe ignores the device */
spin_lock ( & busid_table_lock ) ;
for ( i = 0 ; i < MAX_BUSID ; i + + ) {
if ( busid_table [ i ] . name [ 0 ] & &
busid_table [ i ] . shutdown_busid ) {
busid_priv = & ( busid_table [ i ] ) ;
busid_priv - > status = STUB_BUSID_OTHER ;
}
}
spin_unlock ( & busid_table_lock ) ;
2018-05-15 05:49:58 +03:00
/* now run rebind - no need to hold locks. driver files are removed */
2018-05-01 01:17:20 +03:00
for ( i = 0 ; i < MAX_BUSID ; i + + ) {
if ( busid_table [ i ] . name [ 0 ] & &
busid_table [ i ] . shutdown_busid ) {
busid_priv = & ( busid_table [ i ] ) ;
do_rebind ( busid_table [ i ] . name , busid_priv ) ;
}
}
# endif
}
2014-03-08 16:53:33 +04:00
static ssize_t rebind_store ( struct device_driver * dev , const char * buf ,
size_t count )
{
int ret ;
int len ;
struct bus_id_priv * bid ;
/* buf length should be less that BUSID_SIZE */
len = strnlen ( buf , BUSID_SIZE ) ;
if ( ! ( len < BUSID_SIZE ) )
return - EINVAL ;
bid = get_busid_priv ( buf ) ;
if ( ! bid )
return - ENODEV ;
2018-05-01 01:17:19 +03:00
/* mark the device for deletion so probe ignores it during rescan */
bid - > status = STUB_BUSID_OTHER ;
2018-05-15 05:49:58 +03:00
/* release the busid lock */
put_busid_priv ( bid ) ;
2018-05-01 01:17:19 +03:00
2018-05-01 01:17:20 +03:00
ret = do_rebind ( ( char * ) buf , bid ) ;
if ( ret < 0 )
2014-03-08 16:53:33 +04:00
return ret ;
2018-05-01 01:17:19 +03:00
/* delete device from busid_table */
del_match_busid ( ( char * ) buf ) ;
2014-03-08 16:53:33 +04:00
return count ;
}
static DRIVER_ATTR_WO ( rebind ) ;
2008-07-10 00:56:51 +04:00
static struct stub_priv * stub_priv_pop_from_listhead ( struct list_head * listhead )
{
struct stub_priv * priv , * tmp ;
list_for_each_entry_safe ( priv , tmp , listhead , list ) {
list_del ( & priv - > list ) ;
return priv ;
}
return NULL ;
}
static struct stub_priv * stub_priv_pop ( struct stub_device * sdev )
{
unsigned long flags ;
struct stub_priv * priv ;
spin_lock_irqsave ( & sdev - > priv_lock , flags ) ;
priv = stub_priv_pop_from_listhead ( & sdev - > priv_init ) ;
2011-05-20 08:36:58 +04:00
if ( priv )
goto done ;
2008-07-10 00:56:51 +04:00
priv = stub_priv_pop_from_listhead ( & sdev - > priv_tx ) ;
2011-05-20 08:36:58 +04:00
if ( priv )
goto done ;
2008-07-10 00:56:51 +04:00
priv = stub_priv_pop_from_listhead ( & sdev - > priv_free ) ;
2011-05-20 08:36:58 +04:00
done :
2008-07-10 00:56:51 +04:00
spin_unlock_irqrestore ( & sdev - > priv_lock , flags ) ;
2011-05-20 08:36:58 +04:00
return priv ;
2008-07-10 00:56:51 +04:00
}
void stub_device_cleanup_urbs ( struct stub_device * sdev )
{
struct stub_priv * priv ;
2011-05-20 08:36:58 +04:00
struct urb * urb ;
2008-07-10 00:56:51 +04:00
2017-12-19 03:23:37 +03:00
dev_dbg ( & sdev - > udev - > dev , " Stub device cleaning up urbs \n " ) ;
2008-07-10 00:56:51 +04:00
while ( ( priv = stub_priv_pop ( sdev ) ) ) {
2011-05-20 08:36:58 +04:00
urb = priv - > urb ;
2017-12-19 03:23:37 +03:00
dev_dbg ( & sdev - > udev - > dev , " free urb seqnum %lu \n " ,
priv - > seqnum ) ;
2008-07-10 00:56:51 +04:00
usb_kill_urb ( urb ) ;
kmem_cache_free ( stub_priv_cache , priv ) ;
2011-03-13 08:29:12 +03:00
kfree ( urb - > transfer_buffer ) ;
2017-05-22 14:02:44 +03:00
urb - > transfer_buffer = NULL ;
2011-03-13 08:29:12 +03:00
kfree ( urb - > setup_packet ) ;
2017-05-22 14:02:44 +03:00
urb - > setup_packet = NULL ;
2008-07-10 00:56:51 +04:00
usb_free_urb ( urb ) ;
}
}
2011-05-20 08:36:59 +04:00
static int __init usbip_host_init ( void )
2008-07-10 00:56:51 +04:00
{
int ret ;
2012-01-25 22:46:32 +04:00
init_busid_table ( ) ;
2011-05-20 08:37:00 +04:00
2012-01-25 22:46:32 +04:00
stub_priv_cache = KMEM_CACHE ( stub_priv , SLAB_HWCACHE_ALIGN ) ;
2008-07-10 00:56:51 +04:00
if ( ! stub_priv_cache ) {
2011-05-20 08:36:58 +04:00
pr_err ( " kmem_cache_create failed \n " ) ;
2008-07-10 00:56:51 +04:00
return - ENOMEM ;
}
2014-01-24 01:12:29 +04:00
ret = usb_register_device_driver ( & stub_driver , THIS_MODULE ) ;
2013-09-10 09:14:07 +04:00
if ( ret ) {
2011-05-20 03:47:32 +04:00
pr_err ( " usb_register failed %d \n " , ret ) ;
2011-05-20 08:36:58 +04:00
goto err_usb_register ;
2008-07-10 00:56:51 +04:00
}
ret = driver_create_file ( & stub_driver . drvwrap . driver ,
& driver_attr_match_busid ) ;
2013-09-10 09:14:07 +04:00
if ( ret ) {
2011-05-20 08:36:58 +04:00
pr_err ( " driver_create_file failed \n " ) ;
goto err_create_file ;
2008-07-10 00:56:51 +04:00
}
2014-03-08 16:53:33 +04:00
ret = driver_create_file ( & stub_driver . drvwrap . driver ,
& driver_attr_rebind ) ;
if ( ret ) {
pr_err ( " driver_create_file failed \n " ) ;
goto err_create_file ;
}
2008-07-10 00:56:51 +04:00
return ret ;
2011-05-20 08:36:58 +04:00
err_create_file :
2014-01-24 01:12:29 +04:00
usb_deregister_device_driver ( & stub_driver ) ;
2011-05-20 08:36:58 +04:00
err_usb_register :
2008-07-10 00:56:51 +04:00
kmem_cache_destroy ( stub_priv_cache ) ;
return ret ;
}
2011-05-20 08:36:59 +04:00
static void __exit usbip_host_exit ( void )
2008-07-10 00:56:51 +04:00
{
driver_remove_file ( & stub_driver . drvwrap . driver ,
& driver_attr_match_busid ) ;
2014-03-08 16:53:33 +04:00
driver_remove_file ( & stub_driver . drvwrap . driver ,
& driver_attr_rebind ) ;
2008-07-10 00:56:51 +04:00
/*
* deregister ( ) calls stub_disconnect ( ) for all devices . Device
* specific data is cleared in stub_disconnect ( ) .
*/
2014-01-24 01:12:29 +04:00
usb_deregister_device_driver ( & stub_driver ) ;
2008-07-10 00:56:51 +04:00
2018-05-01 01:17:20 +03:00
/* initiate scan to attach devices */
stub_device_rebind ( ) ;
2008-07-10 00:56:51 +04:00
kmem_cache_destroy ( stub_priv_cache ) ;
}
2011-05-20 08:36:59 +04:00
module_init ( usbip_host_init ) ;
module_exit ( usbip_host_exit ) ;
2008-07-10 00:56:51 +04:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;