2003-07-19 07:08:00 +04:00
/*
* sysfs_device . c
*
* Generic device utility functions for libsysfs
*
2005-02-23 14:21:39 +03:00
* Copyright ( C ) IBM Corp . 2003 - 2005
2003-07-19 07:08:00 +04:00
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* This library 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
2004-10-20 15:15:26 +04:00
# include "libsysfs.h"
2003-07-19 07:08:00 +04:00
# include "sysfs.h"
2004-03-12 11:57:30 +03:00
/**
2005-02-23 14:21:39 +03:00
* get_dev_driver : fills in the dev - > driver_name field
* Returns 0 on SUCCESS and - 1 on error
2004-03-12 11:57:30 +03:00
*/
static int get_dev_driver ( struct sysfs_device * dev )
{
2005-02-23 14:21:39 +03:00
struct dlist * drvlist ;
char path [ SYSFS_PATH_MAX ] ;
char devpath [ SYSFS_PATH_MAX ] ;
char * drv = NULL , * c ;
if ( ! dev ) {
2004-03-12 11:57:30 +03:00
errno = EINVAL ;
2005-02-23 14:21:39 +03:00
return - 1 ;
}
memset ( path , 0 , SYSFS_PATH_MAX ) ;
memset ( devpath , 0 , SYSFS_PATH_MAX ) ;
safestrcpymax ( path , dev - > path , SYSFS_PATH_MAX ) ;
safestrcatmax ( path , " /driver " , SYSFS_PATH_MAX ) ;
if ( ! sysfs_path_is_link ( path ) ) {
if ( ! sysfs_get_link ( path , devpath , SYSFS_PATH_MAX ) ) {
if ( sysfs_get_name_from_path ( devpath ,
dev - > driver_name , SYSFS_NAME_LEN ) )
return - 1 ;
}
return 0 ;
2004-03-12 11:57:30 +03:00
}
2005-02-23 14:21:39 +03:00
/*
* Devices on on earlier kernels do not have the " driver " link .
* Look it up in the bus directory .
*/
2004-03-12 11:57:30 +03:00
if ( dev - > bus [ 0 ] = = ' \0 ' )
2005-02-23 14:21:39 +03:00
return - 1 ;
2004-03-12 11:57:30 +03:00
memset ( path , 0 , SYSFS_PATH_MAX ) ;
memset ( devpath , 0 , SYSFS_PATH_MAX ) ;
2005-02-23 14:21:39 +03:00
safestrcpy ( path , dev - > path ) ;
c = strstr ( path , SYSFS_DEVICES_NAME ) ;
if ( c = = NULL ) {
dprintf ( " Invalid path to device - %s \n " , dev - > path ) ;
return - 1 ;
} else
* c = ' \0 ' ;
safestrcat ( path , SYSFS_BUS_NAME ) ;
2004-03-12 11:57:30 +03:00
safestrcat ( path , " / " ) ;
safestrcat ( path , dev - > bus ) ;
safestrcat ( path , " / " ) ;
safestrcat ( path , SYSFS_DRIVERS_NAME ) ;
2005-02-23 14:21:39 +03:00
drvlist = sysfs_open_directory_list ( path ) ;
if ( drvlist ) {
2004-03-12 11:57:30 +03:00
dlist_for_each_data ( drvlist , drv , char ) {
2005-02-23 14:21:39 +03:00
safestrcpy ( devpath , path ) ;
safestrcat ( devpath , " / " ) ;
safestrcat ( devpath , drv ) ;
safestrcat ( devpath , " / " ) ;
safestrcat ( devpath , dev - > bus_id ) ;
if ( sysfs_path_is_link ( devpath ) = = 0 ) {
2004-03-12 11:57:30 +03:00
safestrcpy ( dev - > driver_name , drv ) ;
sysfs_close_list ( drvlist ) ;
return 0 ;
}
}
sysfs_close_list ( drvlist ) ;
}
2005-02-23 14:21:39 +03:00
return - 1 ;
2004-03-12 11:57:30 +03:00
}
2004-12-16 18:05:01 +03:00
2003-11-25 10:47:43 +03:00
/**
2004-01-15 09:21:38 +03:00
* sysfs_get_device_bus : retrieves the bus name the device is on , checks path
* to bus ' link to make sure it has correct device .
2003-11-25 10:47:43 +03:00
* @ dev : device to get busname .
* returns 0 with success and - 1 with error .
*/
2004-01-15 09:21:38 +03:00
int sysfs_get_device_bus ( struct sysfs_device * dev )
2003-11-25 10:47:43 +03:00
{
2005-02-23 14:21:39 +03:00
char devpath [ SYSFS_PATH_MAX ] ;
char subsys [ SYSFS_NAME_LEN ] ;
char path [ SYSFS_PATH_MAX ] ;
char target [ SYSFS_PATH_MAX ] ;
char * bus = NULL , * c ;
struct dlist * buslist ;
2003-11-25 10:47:43 +03:00
2005-02-23 14:21:39 +03:00
if ( ! dev ) {
2003-11-25 10:47:43 +03:00
errno = EINVAL ;
return - 1 ;
}
2005-02-23 14:21:39 +03:00
memset ( path , 0 , SYSFS_PATH_MAX ) ;
memset ( devpath , 0 , SYSFS_PATH_MAX ) ;
safestrcpymax ( path , dev - > path , SYSFS_PATH_MAX ) ;
safestrcatmax ( path , " /bus " , SYSFS_PATH_MAX ) ;
if ( ! sysfs_path_is_link ( path ) ) {
if ( ! sysfs_get_link ( path , devpath , SYSFS_PATH_MAX ) ) {
if ( sysfs_get_name_from_path ( devpath ,
dev - > bus , SYSFS_NAME_LEN ) )
return - 1 ;
}
return 0 ;
}
/*
* Devices on on earlier kernels do not have the " bus " link .
* Look it up in the bus directory .
*/
2003-12-16 08:53:28 +03:00
memset ( subsys , 0 , SYSFS_NAME_LEN ) ;
2005-02-23 14:21:39 +03:00
safestrcpy ( subsys , dev - > path ) ;
c = strstr ( subsys , SYSFS_DEVICES_NAME ) ;
if ( c = = NULL ) {
dprintf ( " Invalid path to device - %s \n " , dev - > path ) ;
return - 1 ;
} else
* c = ' \0 ' ;
safestrcat ( subsys , SYSFS_BUS_NAME ) ;
buslist = sysfs_open_directory_list ( subsys ) ;
if ( buslist ) {
2003-11-25 10:47:43 +03:00
dlist_for_each_data ( buslist , bus , char ) {
2003-12-16 08:53:28 +03:00
memset ( path , 0 , SYSFS_PATH_MAX ) ;
2005-02-23 14:21:39 +03:00
safestrcpy ( path , subsys ) ;
2004-03-12 11:57:30 +03:00
safestrcat ( path , " / " ) ;
safestrcat ( path , bus ) ;
safestrcat ( path , " / " ) ;
safestrcat ( path , SYSFS_DEVICES_NAME ) ;
safestrcat ( path , " / " ) ;
safestrcat ( path , dev - > bus_id ) ;
2003-12-16 08:53:28 +03:00
if ( ( sysfs_path_is_link ( path ) ) = = 0 ) {
memset ( target , 0 , SYSFS_PATH_MAX ) ;
2005-02-23 14:21:39 +03:00
if ( sysfs_get_link ( path , target ,
SYSFS_PATH_MAX ) ) {
2003-12-16 08:53:28 +03:00
dprintf ( " Error getting link target \n " ) ;
sysfs_close_list ( buslist ) ;
return - 1 ;
}
if ( ! ( strncmp ( target , dev - > path ,
SYSFS_PATH_MAX ) ) ) {
2004-03-12 11:57:30 +03:00
safestrcpy ( dev - > bus , bus ) ;
2003-12-16 08:53:28 +03:00
sysfs_close_list ( buslist ) ;
return 0 ;
}
}
2005-02-23 14:21:39 +03:00
}
sysfs_close_list ( buslist ) ;
}
return - 1 ;
2003-11-25 10:47:43 +03:00
}
2003-10-21 12:19:14 +04:00
/**
* sysfs_close_device_tree : closes every device in the supplied tree ,
* closing children only .
* @ devroot : device root of tree .
*/
2004-03-12 11:57:30 +03:00
void sysfs_close_device_tree ( struct sysfs_device * devroot )
2003-10-21 12:19:14 +04:00
{
2005-02-23 14:21:39 +03:00
if ( devroot ) {
if ( devroot - > children ) {
2003-10-21 12:19:14 +04:00
struct sysfs_device * child = NULL ;
dlist_for_each_data ( devroot - > children , child ,
struct sysfs_device ) {
sysfs_close_device_tree ( child ) ;
}
}
sysfs_close_device ( devroot ) ;
}
}
2003-07-19 07:08:00 +04:00
/**
* sysfs_close_device : closes and cleans up a device
* @ dev = device to clean up
*/
void sysfs_close_device ( struct sysfs_device * dev )
{
2005-02-23 14:21:39 +03:00
if ( dev ) {
if ( dev - > parent )
2003-12-20 05:29:10 +03:00
sysfs_close_device ( dev - > parent ) ;
2005-02-23 14:21:39 +03:00
if ( dev - > children & & dev - > children - > count )
2003-10-21 12:19:14 +04:00
dlist_destroy ( dev - > children ) ;
2005-02-23 14:21:39 +03:00
if ( dev - > attrlist )
dlist_destroy ( dev - > attrlist ) ;
2003-07-19 07:08:00 +04:00
free ( dev ) ;
}
}
/**
* alloc_device : allocates and initializes device structure
* returns struct sysfs_device
*/
static struct sysfs_device * alloc_device ( void )
{
2005-02-23 14:21:39 +03:00
return ( struct sysfs_device * ) calloc ( 1 , sizeof ( struct sysfs_device ) ) ;
2003-07-19 07:08:00 +04:00
}
/**
2003-12-20 05:29:10 +03:00
* sysfs_open_device_path : opens and populates device structure
2003-07-19 07:08:00 +04:00
* @ path : path to device , this is the / sys / devices / path
* returns sysfs_device structure with success or NULL with error
*/
2004-03-12 11:57:30 +03:00
struct sysfs_device * sysfs_open_device_path ( const char * path )
2003-07-19 07:08:00 +04:00
{
2005-02-23 14:21:39 +03:00
struct sysfs_device * dev ;
2003-07-19 07:08:00 +04:00
2005-02-23 14:21:39 +03:00
if ( ! path ) {
2003-07-19 07:08:00 +04:00
errno = EINVAL ;
return NULL ;
}
2005-02-23 14:21:39 +03:00
if ( sysfs_path_is_dir ( path ) ) {
2003-12-16 08:53:28 +03:00
dprintf ( " Incorrect path to device: %s \n " , path ) ;
return NULL ;
}
2005-02-23 14:21:39 +03:00
dev = alloc_device ( ) ;
if ( ! dev ) {
2003-10-21 12:19:14 +04:00
dprintf ( " Error allocating device at %s \n " , path ) ;
2003-07-19 07:08:00 +04:00
return NULL ;
}
2005-02-23 14:21:39 +03:00
if ( sysfs_get_name_from_path ( path , dev - > bus_id , SYSFS_NAME_LEN ) ) {
2003-07-19 07:08:00 +04:00
errno = EINVAL ;
2003-12-16 08:53:28 +03:00
dprintf ( " Error getting device bus_id \n " ) ;
2003-07-19 07:08:00 +04:00
sysfs_close_device ( dev ) ;
return NULL ;
}
2004-03-12 11:57:30 +03:00
safestrcpy ( dev - > path , path ) ;
2005-02-23 14:21:39 +03:00
if ( sysfs_remove_trailing_slash ( dev - > path ) ) {
2004-01-15 09:21:38 +03:00
dprintf ( " Invalid path to device %s \n " , dev - > path ) ;
sysfs_close_device ( dev ) ;
return NULL ;
}
2005-02-23 14:21:39 +03:00
/*
2003-10-21 12:19:14 +04:00
* The " name " attribute no longer exists . . . return the device ' s
* sysfs representation instead , in the " dev->name " field , which
* implies that the dev - > name and dev - > bus_id contain same data .
*/
2004-03-12 11:57:30 +03:00
safestrcpy ( dev - > name , dev - > bus_id ) ;
2005-02-23 14:21:39 +03:00
if ( sysfs_get_device_bus ( dev ) )
2004-01-15 09:21:38 +03:00
dprintf ( " Could not get device bus \n " ) ;
2005-02-23 14:21:39 +03:00
if ( get_dev_driver ( dev ) ) {
2004-03-12 11:57:30 +03:00
dprintf ( " Could not get device %s's driver \n " , dev - > bus_id ) ;
safestrcpy ( dev - > driver_name , SYSFS_UNKNOWN ) ;
}
2003-07-19 07:08:00 +04:00
return dev ;
}
/**
2005-02-23 14:21:39 +03:00
* sysfs_get_device_attr : searches dev ' s attributes by name
* @ dev : device to look through
* @ name : attribute name to get
* returns sysfs_attribute reference with success or NULL with error .
2004-01-15 09:21:38 +03:00
*/
2005-02-23 14:21:39 +03:00
struct sysfs_attribute * sysfs_get_device_attr ( struct sysfs_device * dev ,
const char * name )
2004-01-15 09:21:38 +03:00
{
2005-02-23 14:21:39 +03:00
if ( ! dev | | ! name ) {
2004-01-15 09:21:38 +03:00
errno = EINVAL ;
return NULL ;
}
2005-02-23 14:21:39 +03:00
return get_attribute ( dev , ( char * ) name ) ;
2004-01-15 09:21:38 +03:00
}
2003-12-16 08:53:28 +03:00
/**
2005-02-23 14:21:39 +03:00
* sysfs_get_device_attributes : gets list of device attributes
* @ dev : device whose attributes list is needed
* returns dlist of attributes on success or NULL on error
2003-12-16 08:53:28 +03:00
*/
2005-02-23 14:21:39 +03:00
struct dlist * sysfs_get_device_attributes ( struct sysfs_device * dev )
2003-12-16 08:53:28 +03:00
{
2005-02-23 14:21:39 +03:00
if ( ! dev ) {
2003-12-16 08:53:28 +03:00
errno = EINVAL ;
return NULL ;
}
2005-02-23 14:21:39 +03:00
return get_attributes_list ( dev ) ;
2003-12-16 08:53:28 +03:00
}
2003-10-21 12:19:14 +04:00
/**
* get_device_absolute_path : looks up the bus the device is on , gets
* absolute path to the device
* @ device : device for which path is needed
* @ path : buffer to store absolute path
* @ psize : size of " path "
* Returns 0 on success - 1 on failure
*/
2004-10-20 15:15:26 +04:00
static int get_device_absolute_path ( const char * device , const char * bus ,
2004-03-12 11:57:30 +03:00
char * path , size_t psize )
2003-10-21 12:19:14 +04:00
{
2004-03-12 11:57:30 +03:00
char bus_path [ SYSFS_PATH_MAX ] ;
2003-10-21 12:19:14 +04:00
2005-02-23 14:21:39 +03:00
if ( ! device | | ! path ) {
2003-10-21 12:19:14 +04:00
errno = EINVAL ;
return - 1 ;
}
2003-12-16 08:53:28 +03:00
memset ( bus_path , 0 , SYSFS_PATH_MAX ) ;
2005-02-23 14:21:39 +03:00
if ( sysfs_get_mnt_path ( bus_path , SYSFS_PATH_MAX ) ) {
2003-10-21 12:19:14 +04:00
dprintf ( " Sysfs not supported on this system \n " ) ;
return - 1 ;
}
2004-03-12 11:57:30 +03:00
safestrcat ( bus_path , " / " ) ;
safestrcat ( bus_path , SYSFS_BUS_NAME ) ;
safestrcat ( bus_path , " / " ) ;
safestrcat ( bus_path , bus ) ;
safestrcat ( bus_path , " / " ) ;
safestrcat ( bus_path , SYSFS_DEVICES_NAME ) ;
safestrcat ( bus_path , " / " ) ;
safestrcat ( bus_path , device ) ;
2003-10-21 12:19:14 +04:00
/*
* We now are at / sys / bus / " bus_name " / devices / " device " which is a link .
* Now read this link to reach to the device .
*/
2005-02-23 14:21:39 +03:00
if ( sysfs_get_link ( bus_path , path , psize ) ) {
2003-10-21 12:19:14 +04:00
dprintf ( " Error getting to device %s \n " , device ) ;
return - 1 ;
}
return 0 ;
}
/**
2003-12-20 05:29:10 +03:00
* sysfs_open_device : open a device by id ( use the " bus " subsystem )
2004-03-12 11:57:30 +03:00
* @ bus : bus the device belongs to
2003-11-25 10:47:43 +03:00
* @ bus_id : bus_id of the device to open - has to be the " bus_id " in
* / sys / bus / xxx / devices
* returns struct sysfs_device if found , NULL otherwise
* NOTE :
* 1. Use sysfs_close_device to close the device
* 2. Bus the device is on must be supplied
* Use sysfs_find_device_bus to get the bus name
*/
2004-10-20 15:15:26 +04:00
struct sysfs_device * sysfs_open_device ( const char * bus , const char * bus_id )
2003-10-21 12:19:14 +04:00
{
2003-11-25 10:47:43 +03:00
char sysfs_path [ SYSFS_PATH_MAX ] ;
2005-02-23 14:21:39 +03:00
struct sysfs_device * device ;
2003-10-21 12:19:14 +04:00
2005-02-23 14:21:39 +03:00
if ( ! bus_id | | ! bus ) {
2003-10-21 12:19:14 +04:00
errno = EINVAL ;
2003-11-25 10:47:43 +03:00
return NULL ;
2003-10-21 12:19:14 +04:00
}
2003-11-25 10:47:43 +03:00
memset ( sysfs_path , 0 , SYSFS_PATH_MAX ) ;
2005-02-23 14:21:39 +03:00
if ( get_device_absolute_path ( bus_id , bus , sysfs_path ,
SYSFS_PATH_MAX ) ) {
2003-11-25 10:47:43 +03:00
dprintf ( " Error getting to device %s \n " , bus_id ) ;
return NULL ;
2003-07-19 07:08:00 +04:00
}
2003-11-25 10:47:43 +03:00
2003-12-20 05:29:10 +03:00
device = sysfs_open_device_path ( sysfs_path ) ;
2005-02-23 14:21:39 +03:00
if ( ! device ) {
2003-11-25 10:47:43 +03:00
dprintf ( " Error opening device %s \n " , bus_id ) ;
return NULL ;
2003-10-21 12:19:14 +04:00
}
2003-11-25 10:47:43 +03:00
return device ;
2003-10-21 12:19:14 +04:00
}
2003-07-19 07:08:00 +04:00
2003-12-20 05:29:10 +03:00
/**
* sysfs_get_device_parent : opens up given device ' s parent and returns a
* reference to its sysfs_device
* @ dev : sysfs_device whose parent is requested
* Returns sysfs_device of the parent on success and NULL on failure
*/
struct sysfs_device * sysfs_get_device_parent ( struct sysfs_device * dev )
{
2005-02-23 14:21:39 +03:00
char ppath [ SYSFS_PATH_MAX ] , * tmp ;
2003-12-20 05:29:10 +03:00
2005-02-23 14:21:39 +03:00
if ( ! dev ) {
2003-12-20 05:29:10 +03:00
errno = EINVAL ;
return NULL ;
}
2005-02-23 14:21:39 +03:00
if ( dev - > parent )
2003-12-20 05:29:10 +03:00
return ( dev - > parent ) ;
memset ( ppath , 0 , SYSFS_PATH_MAX ) ;
2004-03-12 11:57:30 +03:00
safestrcpy ( ppath , dev - > path ) ;
2003-12-20 05:29:10 +03:00
tmp = strrchr ( ppath , ' / ' ) ;
2005-02-23 14:21:39 +03:00
if ( ! tmp ) {
2003-12-20 05:29:10 +03:00
dprintf ( " Invalid path to device %s \n " , ppath ) ;
return NULL ;
}
2004-03-12 11:57:30 +03:00
if ( * ( tmp + 1 ) = = ' \0 ' ) {
2003-12-20 05:29:10 +03:00
* tmp = ' \0 ' ;
tmp = strrchr ( tmp , ' / ' ) ;
if ( tmp = = NULL ) {
dprintf ( " Invalid path to device %s \n " , ppath ) ;
return NULL ;
}
}
* tmp = ' \0 ' ;
2005-02-23 14:21:39 +03:00
2003-12-20 05:29:10 +03:00
/*
* All " devices " have the " detach_state " attribute - validate here
*/
2004-03-12 11:57:30 +03:00
safestrcat ( ppath , " /detach_state " ) ;
2005-02-23 14:21:39 +03:00
if ( sysfs_path_is_file ( ppath ) ) {
2003-12-20 05:29:10 +03:00
dprintf ( " Device at %s does not have a parent \n " , dev - > path ) ;
return NULL ;
}
tmp = strrchr ( ppath , ' / ' ) ;
* tmp = ' \0 ' ;
dev - > parent = sysfs_open_device_path ( ppath ) ;
2005-02-23 14:21:39 +03:00
if ( ! dev - > parent ) {
2003-12-20 05:29:10 +03:00
dprintf ( " Error opening device %s's parent at %s \n " ,
dev - > bus_id , ppath ) ;
return NULL ;
}
return ( dev - > parent ) ;
}