2011-05-14 03:55:07 -07:00
/*
2011-06-19 22:44:47 -07:00
* Copyright ( C ) 2011 matt mooney < mfm @ muteddisk . com >
* 2005 - 2007 Takahiro Hirofuchi
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
*
* This program 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 , see < http : //www.gnu.org/licenses/>.
2011-05-14 03:55:07 -07:00
*/
# include <sys/types.h>
# include <sys/stat.h>
2014-03-08 14:53:26 +02:00
# include <fcntl.h>
2011-06-19 22:44:34 -07:00
# include <errno.h>
2011-05-14 03:55:07 -07:00
# include <unistd.h>
2014-03-08 14:53:26 +02:00
# include <libudev.h>
2011-06-19 22:44:37 -07:00
# include "usbip_common.h"
2011-06-19 22:44:47 -07:00
# include "usbip_host_driver.h"
2014-03-08 14:53:26 +02:00
# include "list.h"
# include "sysfs_utils.h"
2011-05-14 03:55:07 -07:00
2011-06-19 22:44:35 -07:00
# undef PROGNAME
# define PROGNAME "libusbip"
2011-06-19 22:44:47 -07:00
struct usbip_host_driver * host_driver ;
2014-03-08 14:53:26 +02:00
struct udev * udev_context ;
2011-05-14 03:55:07 -07:00
2011-05-27 01:44:14 -07:00
static int32_t read_attr_usbip_status ( struct usbip_usb_device * udev )
2011-05-14 03:55:07 -07:00
{
2014-03-08 14:53:26 +02:00
char status_attr_path [ SYSFS_PATH_MAX ] ;
int fd ;
int length ;
char status ;
2011-05-14 03:55:07 -07:00
int value = 0 ;
2014-03-08 14:53:26 +02:00
snprintf ( status_attr_path , SYSFS_PATH_MAX , " %s/usbip_status " ,
udev - > path ) ;
2011-05-14 03:55:07 -07:00
2014-06-12 23:40:18 +04:00
fd = open ( status_attr_path , O_RDONLY ) ;
if ( fd < 0 ) {
2014-03-08 14:53:26 +02:00
err ( " error opening attribute %s " , status_attr_path ) ;
2011-05-14 03:55:07 -07:00
return - 1 ;
}
2014-03-08 14:53:26 +02:00
length = read ( fd , & status , 1 ) ;
if ( length < 0 ) {
err ( " error reading attribute %s " , status_attr_path ) ;
close ( fd ) ;
2011-05-14 03:55:07 -07:00
return - 1 ;
}
2014-03-08 14:53:26 +02:00
value = atoi ( & status ) ;
2011-05-14 03:55:07 -07:00
return value ;
}
2014-03-08 14:53:26 +02:00
static
struct usbip_exported_device * usbip_exported_device_new ( const char * sdevpath )
2011-05-14 03:55:07 -07:00
{
struct usbip_exported_device * edev = NULL ;
2014-02-27 14:25:19 +01:00
struct usbip_exported_device * edev_old ;
2011-06-19 22:44:47 -07:00
size_t size ;
int i ;
2011-05-14 03:55:07 -07:00
2014-03-08 14:53:26 +02:00
edev = calloc ( 1 , sizeof ( struct usbip_exported_device ) ) ;
2011-05-14 03:55:07 -07:00
2014-03-08 14:53:26 +02:00
edev - > sudev = udev_device_new_from_syspath ( udev_context , sdevpath ) ;
2011-05-14 03:55:07 -07:00
if ( ! edev - > sudev ) {
2014-03-08 14:53:26 +02:00
err ( " udev_device_new_from_syspath: %s " , sdevpath ) ;
2011-05-14 03:55:07 -07:00
goto err ;
}
read_usb_device ( edev - > sudev , & edev - > udev ) ;
edev - > status = read_attr_usbip_status ( & edev - > udev ) ;
if ( edev - > status < 0 )
goto err ;
/* reallocate buffer to include usb interface data */
2014-06-12 23:40:19 +04:00
size = sizeof ( struct usbip_exported_device ) +
edev - > udev . bNumInterfaces * sizeof ( struct usbip_usb_interface ) ;
2011-06-19 22:44:47 -07:00
2014-02-27 14:25:19 +01:00
edev_old = edev ;
2011-06-19 22:44:47 -07:00
edev = realloc ( edev , size ) ;
2011-05-14 03:55:07 -07:00
if ( ! edev ) {
2014-02-27 14:25:19 +01:00
edev = edev_old ;
2011-06-19 22:44:38 -07:00
dbg ( " realloc failed " ) ;
2011-05-14 03:55:07 -07:00
goto err ;
}
2011-06-19 22:44:47 -07:00
for ( i = 0 ; i < edev - > udev . bNumInterfaces ; i + + )
2011-05-14 03:55:07 -07:00
read_usb_interface ( & edev - > udev , i , & edev - > uinf [ i ] ) ;
return edev ;
err :
2014-03-08 14:53:26 +02:00
if ( edev - > sudev )
udev_device_unref ( edev - > sudev ) ;
2011-05-14 03:55:07 -07:00
if ( edev )
free ( edev ) ;
2011-06-19 22:44:47 -07:00
2011-05-14 03:55:07 -07:00
return NULL ;
}
static int refresh_exported_devices ( void )
{
2011-06-19 22:44:47 -07:00
struct usbip_exported_device * edev ;
2014-03-08 14:53:26 +02:00
struct udev_enumerate * enumerate ;
struct udev_list_entry * devices , * dev_list_entry ;
struct udev_device * dev ;
const char * path ;
2014-03-24 13:45:13 -06:00
const char * driver ;
2014-03-08 14:53:26 +02:00
enumerate = udev_enumerate_new ( udev_context ) ;
udev_enumerate_add_match_subsystem ( enumerate , " usb " ) ;
udev_enumerate_scan_devices ( enumerate ) ;
devices = udev_enumerate_get_list_entry ( enumerate ) ;
udev_list_entry_foreach ( dev_list_entry , devices ) {
path = udev_list_entry_get_name ( dev_list_entry ) ;
dev = udev_device_new_from_syspath ( udev_context , path ) ;
2014-03-24 13:45:13 -06:00
if ( dev = = NULL )
continue ;
2014-03-08 14:53:26 +02:00
/* Check whether device uses usbip-host driver. */
2014-03-24 13:45:13 -06:00
driver = udev_device_get_driver ( dev ) ;
if ( driver ! = NULL & & ! strcmp ( driver , USBIP_HOST_DRV_NAME ) ) {
2014-03-08 14:53:26 +02:00
edev = usbip_exported_device_new ( path ) ;
if ( ! edev ) {
dbg ( " usbip_exported_device_new failed " ) ;
continue ;
}
list_add ( & edev - > node , & host_driver - > edev_list ) ;
host_driver - > ndevs + + ;
2011-05-14 03:55:07 -07:00
}
}
return 0 ;
}
2014-03-08 14:53:26 +02:00
static void usbip_exported_device_destroy ( void )
2011-05-14 03:55:07 -07:00
{
2014-03-08 14:53:26 +02:00
struct list_head * i , * tmp ;
struct usbip_exported_device * edev ;
2011-05-14 03:55:07 -07:00
2014-03-08 14:53:26 +02:00
list_for_each_safe ( i , tmp , & host_driver - > edev_list ) {
edev = list_entry ( i , struct usbip_exported_device , node ) ;
list_del ( i ) ;
free ( edev ) ;
2011-05-14 03:55:07 -07:00
}
2011-06-19 22:44:47 -07:00
}
2011-05-14 03:55:07 -07:00
2011-06-19 22:44:47 -07:00
int usbip_host_driver_open ( void )
{
int rc ;
2011-05-14 03:55:07 -07:00
2014-03-08 14:53:26 +02:00
udev_context = udev_new ( ) ;
if ( ! udev_context ) {
err ( " udev_new failed " ) ;
2011-05-14 03:55:07 -07:00
return - 1 ;
}
2014-03-08 14:53:26 +02:00
host_driver = calloc ( 1 , sizeof ( * host_driver ) ) ;
2011-05-14 03:55:07 -07:00
2014-03-08 14:53:26 +02:00
host_driver - > ndevs = 0 ;
INIT_LIST_HEAD ( & host_driver - > edev_list ) ;
2011-05-14 03:55:07 -07:00
2011-06-19 22:44:47 -07:00
rc = refresh_exported_devices ( ) ;
if ( rc < 0 )
2014-03-08 14:53:26 +02:00
goto err_free_host_driver ;
2011-05-14 03:55:07 -07:00
return 0 ;
2011-06-19 22:44:47 -07:00
err_free_host_driver :
free ( host_driver ) ;
host_driver = NULL ;
2011-05-14 03:55:07 -07:00
2014-03-08 14:53:26 +02:00
udev_unref ( udev_context ) ;
2011-05-14 03:55:07 -07:00
return - 1 ;
}
2011-06-19 22:44:47 -07:00
void usbip_host_driver_close ( void )
2011-05-14 03:55:07 -07:00
{
2011-06-19 22:44:47 -07:00
if ( ! host_driver )
2011-05-14 03:55:07 -07:00
return ;
2014-03-08 14:53:26 +02:00
usbip_exported_device_destroy ( ) ;
2011-05-14 03:55:07 -07:00
2011-06-19 22:44:47 -07:00
free ( host_driver ) ;
host_driver = NULL ;
2014-03-08 14:53:26 +02:00
udev_unref ( udev_context ) ;
2011-05-14 03:55:07 -07:00
}
2011-06-19 22:44:47 -07:00
int usbip_host_refresh_device_list ( void )
2011-05-14 03:55:07 -07:00
{
2011-06-19 22:44:47 -07:00
int rc ;
2014-03-08 14:53:26 +02:00
usbip_exported_device_destroy ( ) ;
2011-06-19 22:44:47 -07:00
host_driver - > ndevs = 0 ;
2014-03-08 14:53:26 +02:00
INIT_LIST_HEAD ( & host_driver - > edev_list ) ;
2011-06-19 22:44:47 -07:00
rc = refresh_exported_devices ( ) ;
if ( rc < 0 )
return - 1 ;
return 0 ;
}
int usbip_host_export_device ( struct usbip_exported_device * edev , int sockfd )
{
char attr_name [ ] = " usbip_sockfd " ;
2014-03-08 14:53:26 +02:00
char sockfd_attr_path [ SYSFS_PATH_MAX ] ;
2011-05-14 03:55:07 -07:00
char sockfd_buff [ 30 ] ;
int ret ;
if ( edev - > status ! = SDEV_ST_AVAILABLE ) {
2011-06-19 22:44:38 -07:00
dbg ( " device not available: %s " , edev - > udev . busid ) ;
2011-06-19 22:44:47 -07:00
switch ( edev - > status ) {
case SDEV_ST_ERROR :
dbg ( " status SDEV_ST_ERROR " ) ;
break ;
case SDEV_ST_USED :
dbg ( " status SDEV_ST_USED " ) ;
break ;
default :
dbg ( " status unknown: 0x%x " , edev - > status ) ;
2011-05-14 03:55:07 -07:00
}
return - 1 ;
}
/* only the first interface is true */
2014-03-08 14:53:26 +02:00
snprintf ( sockfd_attr_path , sizeof ( sockfd_attr_path ) , " %s/%s " ,
2014-01-23 23:12:29 +02:00
edev - > udev . path , attr_name ) ;
2011-05-14 03:55:07 -07:00
snprintf ( sockfd_buff , sizeof ( sockfd_buff ) , " %d \n " , sockfd ) ;
2011-06-19 22:44:47 -07:00
2014-03-08 14:53:26 +02:00
ret = write_sysfs_attribute ( sockfd_attr_path , sockfd_buff ,
strlen ( sockfd_buff ) ) ;
2011-05-14 03:55:07 -07:00
if ( ret < 0 ) {
2014-03-08 14:53:26 +02:00
err ( " write_sysfs_attribute failed: sockfd %s to %s " ,
sockfd_buff , sockfd_attr_path ) ;
return ret ;
2011-05-14 03:55:07 -07:00
}
2014-03-08 14:53:26 +02:00
info ( " connect: %s " , edev - > udev . busid ) ;
2011-05-14 03:55:07 -07:00
return ret ;
}
2011-06-19 22:44:47 -07:00
struct usbip_exported_device * usbip_host_get_device ( int num )
2011-05-14 03:55:07 -07:00
{
2014-03-08 14:53:26 +02:00
struct list_head * i ;
2011-05-14 03:55:07 -07:00
struct usbip_exported_device * edev ;
2011-06-19 22:44:47 -07:00
int cnt = 0 ;
2011-05-14 03:55:07 -07:00
2014-03-08 14:53:26 +02:00
list_for_each ( i , & host_driver - > edev_list ) {
edev = list_entry ( i , struct usbip_exported_device , node ) ;
2011-06-19 22:44:47 -07:00
if ( num = = cnt )
2011-05-14 03:55:07 -07:00
return edev ;
else
2011-06-19 22:44:47 -07:00
cnt + + ;
2011-05-14 03:55:07 -07:00
}
return NULL ;
}