2004-10-19 15:37:22 +04:00
/*
* udev_sysfs . c - sysfs linux kernel specific knowledge
*
* Copyright ( C ) 2004 Kay Sievers < kay . sievers @ vrfy . org >
* Copyright ( C ) 2004 Greg Kroah - Hartman < greg @ kroah . com >
*
* 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 version 2 of the License .
*
* 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 , write to the Free Software Foundation , Inc . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
# include <stdio.h>
# include <stddef.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <ctype.h>
# include <errno.h>
# include <sys/stat.h>
2004-11-13 14:36:47 +03:00
# include "libsysfs/sysfs/libsysfs.h"
2004-10-19 15:37:22 +04:00
# include "udev_version.h"
# include "udev_sysfs.h"
2004-11-25 04:44:38 +03:00
# include "udev_utils.h"
2004-11-13 14:36:47 +03:00
# include "logging.h"
2004-10-19 15:37:22 +04:00
2004-11-13 14:36:47 +03:00
/* list of subsystem specific files, NULL if there is no file to wait for */
2004-11-23 05:43:37 +03:00
static const struct subsystem_file {
const char * subsystem ;
const char * file ;
2004-10-19 15:37:22 +04:00
} subsystem_files [ ] = {
2004-12-12 04:03:14 +03:00
{ . subsystem = " class " , . file = NULL } ,
2004-10-19 15:37:22 +04:00
{ . subsystem = " net " , . file = " ifindex " } ,
{ . subsystem = " scsi_host " , . file = " unique_id " } ,
{ . subsystem = " scsi_device " , . file = NULL } ,
{ . subsystem = " pcmcia_socket " , . file = " card_type " } ,
{ . subsystem = " usb_host " , . file = NULL } ,
{ . subsystem = " bluetooth " , . file = " address " } ,
{ . subsystem = " firmware " , . file = " data " } ,
{ . subsystem = " i2c-adapter " , . file = NULL } ,
{ . subsystem = " pci_bus " , . file = NULL } ,
{ . subsystem = " ieee1394 " , . file = NULL } ,
{ . subsystem = " ieee1394_host " , . file = NULL } ,
{ . subsystem = " ieee1394_node " , . file = NULL } ,
2004-11-30 13:36:36 +03:00
{ . subsystem = " fc_transport " , . file = " port_id " } ,
2004-12-11 08:07:31 +03:00
{ . subsystem = " fc_host " , . file = " port_id " } ,
2004-12-12 10:34:46 +03:00
{ . subsystem = " spi_transport " , . file = " width " } ,
{ . subsystem = " spi_host " , . file = " width " } ,
2004-10-19 15:37:22 +04:00
{ NULL , NULL }
} ;
2005-02-21 15:44:39 +03:00
dev_t get_devt ( struct sysfs_class_device * class_dev )
{
struct sysfs_attribute * attr = NULL ;
unsigned int major , minor ;
attr = sysfs_get_classdev_attr ( class_dev , " dev " ) ;
if ( attr = = NULL )
return 0 ;
dbg ( " dev='%s' " , attr - > value ) ;
if ( sscanf ( attr - > value , " %u:%u " , & major , & minor ) ! = 2 )
return 0 ;
dbg ( " found major=%d, minor=%d " , major , minor ) ;
return makedev ( major , minor ) ;
}
2004-10-19 15:37:22 +04:00
int subsystem_expect_no_dev ( const char * subsystem )
{
2004-11-23 05:43:37 +03:00
const struct subsystem_file * file ;
2004-10-19 15:37:22 +04:00
for ( file = subsystem_files ; file - > subsystem ! = NULL ; file + + )
if ( strcmp ( subsystem , file - > subsystem ) = = 0 )
return 1 ;
return 0 ;
}
/* get subsystem specific files, returns "dev" if no other found */
2004-11-23 05:43:37 +03:00
static const char * get_subsystem_specific_file ( const char * subsystem )
2004-10-19 15:37:22 +04:00
{
2004-11-23 05:43:37 +03:00
const struct subsystem_file * file ;
2004-10-19 15:37:22 +04:00
/* look if we want to look for another file instead of "dev" */
for ( file = subsystem_files ; file - > subsystem ! = NULL ; file + + )
if ( strcmp ( subsystem , file - > subsystem ) = = 0 )
return file - > file ;
return " dev " ;
}
/* wait for class pecific file to show up */
static int wait_for_class_device_attributes ( struct sysfs_class_device * class_dev ,
const char * * error )
{
const char * file ;
2005-03-07 06:29:43 +03:00
char filename [ PATH_SIZE ] ;
2004-10-19 15:37:22 +04:00
int loop ;
file = get_subsystem_specific_file ( class_dev - > classname ) ;
if ( file = = NULL ) {
dbg ( " class '%s' has no file to wait for " , class_dev - > classname ) ;
return 0 ;
}
2005-03-07 06:29:43 +03:00
snprintf ( filename , sizeof ( filename ) , " %s/%s " , class_dev - > path , file ) ;
filename [ sizeof ( filename ) - 1 ] = ' \0 ' ;
2004-10-23 14:12:28 +04:00
dbg ( " looking at class '%s' for specific file '%s' " , class_dev - > classname , filename ) ;
2004-10-19 15:37:22 +04:00
loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND ;
while ( - - loop ) {
struct stat stats ;
if ( stat ( class_dev - > path , & stats ) = = - 1 ) {
dbg ( " '%s' now disappeared (probably remove has beaten us) " , class_dev - > path ) ;
return - ENODEV ;
}
if ( stat ( filename , & stats ) = = 0 ) {
dbg ( " class '%s' specific file '%s' found " , class_dev - > classname , file ) ;
return 0 ;
}
usleep ( 1000 * 1000 / WAIT_LOOP_PER_SECOND ) ;
}
dbg ( " error: getting class '%s' specific file '%s' " , class_dev - > classname , file ) ;
if ( error )
* error = " class specific file unavailable " ;
return - ENOENT ;
}
/* check if we need to wait for a physical device */
static int class_device_expect_no_device_link ( struct sysfs_class_device * class_dev )
{
/* list of devices without a "device" symlink to the physical device
* if device is set to NULL , no devices in that subsystem has a link */
2004-11-23 05:43:37 +03:00
static const struct class_device {
const char * subsystem ;
const char * device ;
2004-10-19 15:37:22 +04:00
} class_device [ ] = {
{ . subsystem = " block " , . device = " double " } ,
{ . subsystem = " block " , . device = " nb " } ,
{ . subsystem = " block " , . device = " ram " } ,
{ . subsystem = " block " , . device = " loop " } ,
{ . subsystem = " block " , . device = " fd " } ,
{ . subsystem = " block " , . device = " md " } ,
{ . subsystem = " block " , . device = " dos_cd " } ,
{ . subsystem = " block " , . device = " rflash " } ,
{ . subsystem = " block " , . device = " rom " } ,
{ . subsystem = " block " , . device = " rrom " } ,
{ . subsystem = " block " , . device = " flash " } ,
{ . subsystem = " block " , . device = " msd " } ,
{ . subsystem = " block " , . device = " sbpcd " } ,
{ . subsystem = " block " , . device = " pcd " } ,
{ . subsystem = " block " , . device = " pf " } ,
{ . subsystem = " block " , . device = " scd " } ,
{ . subsystem = " block " , . device = " ubd " } ,
{ . subsystem = " block " , . device = " dm- " } ,
2004-11-19 05:43:43 +03:00
{ . subsystem = " block " , . device = " bcrypt " } ,
2004-10-19 15:37:22 +04:00
{ . subsystem = " input " , . device = " event " } ,
{ . subsystem = " input " , . device = " mice " } ,
{ . subsystem = " input " , . device = " mouse " } ,
{ . subsystem = " input " , . device = " ts " } ,
2005-02-26 03:08:44 +03:00
{ . subsystem = " input " , . device = " js " } ,
2004-10-19 15:37:22 +04:00
{ . subsystem = " vc " , . device = NULL } ,
{ . subsystem = " tty " , . device = NULL } ,
{ . subsystem = " cpuid " , . device = " cpu " } ,
{ . subsystem = " graphics " , . device = " fb " } ,
{ . subsystem = " mem " , . device = NULL } ,
{ . subsystem = " misc " , . device = NULL } ,
{ . subsystem = " msr " , . device = NULL } ,
{ . subsystem = " netlink " , . device = NULL } ,
{ . subsystem = " net " , . device = " sit " } ,
{ . subsystem = " net " , . device = " lo " } ,
{ . subsystem = " net " , . device = " tap " } ,
{ . subsystem = " net " , . device = " ipsec " } ,
{ . subsystem = " net " , . device = " dummy " } ,
{ . subsystem = " net " , . device = " irda " } ,
{ . subsystem = " net " , . device = " ppp " } ,
2004-10-23 14:12:28 +04:00
{ . subsystem = " net " , . device = " tun " } ,
2004-10-23 14:13:01 +04:00
{ . subsystem = " net " , . device = " pan " } ,
{ . subsystem = " net " , . device = " bnep " } ,
2004-10-30 15:27:36 +04:00
{ . subsystem = " net " , . device = " vmnet " } ,
2004-11-19 05:43:43 +03:00
{ . subsystem = " net " , . device = " ippp " } ,
2004-12-02 21:30:23 +03:00
{ . subsystem = " net " , . device = " nlv " } ,
2004-12-05 17:03:43 +03:00
{ . subsystem = " net " , . device = " atml " } ,
2004-10-19 15:37:22 +04:00
{ . subsystem = " ppp " , . device = NULL } ,
{ . subsystem = " sound " , . device = NULL } ,
{ . subsystem = " printer " , . device = " lp " } ,
2004-12-05 17:03:43 +03:00
{ . subsystem = " ppdev " , . device = NULL } ,
2004-10-19 15:37:22 +04:00
{ . subsystem = " nvidia " , . device = NULL } ,
{ . subsystem = " video4linux " , . device = " vbi " } ,
2004-11-02 12:20:31 +03:00
{ . subsystem = " dvb " , . device = NULL } ,
2004-10-19 15:37:22 +04:00
{ . subsystem = " lirc " , . device = NULL } ,
{ . subsystem = " firmware " , . device = NULL } ,
{ . subsystem = " drm " , . device = NULL } ,
{ . subsystem = " pci_bus " , . device = NULL } ,
{ . subsystem = " ieee1394 " , . device = NULL } ,
{ . subsystem = " ieee1394_host " , . device = NULL } ,
{ . subsystem = " ieee1394_node " , . device = NULL } ,
{ . subsystem = " raw " , . device = NULL } ,
2004-10-30 15:27:36 +04:00
{ . subsystem = " zaptel " , . device = NULL } ,
2004-11-02 12:20:31 +03:00
{ . subsystem = " tiglusb " , . device = NULL } ,
{ . subsystem = " ppdev " , . device = NULL } ,
{ . subsystem = " ticables " , . device = NULL } ,
{ . subsystem = " snsc " , . device = NULL } ,
{ . subsystem = " staliomem " , . device = NULL } ,
{ . subsystem = " tape " , . device = NULL } ,
{ . subsystem = " ip2 " , . device = NULL } ,
{ . subsystem = " tpqic02 " , . device = NULL } ,
{ . subsystem = " dsp56k " , . device = NULL } ,
{ . subsystem = " zft " , . device = NULL } ,
{ . subsystem = " adb " , . device = NULL } ,
{ . subsystem = " cosa " , . device = NULL } ,
{ . subsystem = " pg " , . device = NULL } ,
{ . subsystem = " pt " , . device = NULL } ,
{ . subsystem = " capi " , . device = NULL } ,
2004-10-19 15:37:22 +04:00
{ NULL , NULL }
} ;
2004-11-23 05:43:37 +03:00
const struct class_device * classdevice ;
2005-02-05 04:38:56 +03:00
unsigned int len ;
2004-10-19 15:37:22 +04:00
2004-11-13 14:36:47 +03:00
/* the kernel may tell us what to wait for */
if ( kernel_release_satisfactory ( 2 , 6 , 10 ) > 0 )
if ( getenv ( " PHYSDEVPATH " ) = = NULL ) {
dbg ( " the kernel says, that there is no physical device for '%s' " , class_dev - > path ) ;
return 1 ;
}
2004-10-19 15:37:22 +04:00
for ( classdevice = class_device ; classdevice - > subsystem ! = NULL ; classdevice + + ) {
if ( strcmp ( class_dev - > classname , classdevice - > subsystem ) = = 0 ) {
/* see if no device in this class is expected to have a device-link */
if ( classdevice - > device = = NULL )
return 1 ;
len = strlen ( classdevice - > device ) ;
/* see if device name matches */
if ( strncmp ( class_dev - > name , classdevice - > device , len ) ! = 0 )
continue ;
/* exact name match */
if ( strlen ( class_dev - > name ) = = len )
return 1 ;
/* name match with instance number */
if ( isdigit ( class_dev - > name [ len ] ) )
return 1 ;
}
}
return 0 ;
}
2004-11-13 14:36:47 +03:00
/* skip waiting for the bus of the devices device */
2004-10-19 15:37:22 +04:00
static int class_device_expect_no_bus ( struct sysfs_class_device * class_dev )
{
2004-11-23 05:43:37 +03:00
static const char * devices_without_bus [ ] = {
2004-10-19 15:37:22 +04:00
" scsi_host " ,
" i2c-adapter " ,
2004-11-11 06:04:38 +03:00
" i2c-dev " ,
2004-10-19 15:37:22 +04:00
NULL
} ;
2004-11-23 05:43:37 +03:00
const char * * device ;
2004-10-19 15:37:22 +04:00
for ( device = devices_without_bus ; * device ! = NULL ; device + + ) {
int len = strlen ( * device ) ;
if ( strncmp ( class_dev - > classname , * device , len ) = = 0 )
return 1 ;
}
return 0 ;
}
2004-11-13 14:36:47 +03:00
/* wait for a devices device specific file to show up */
int wait_for_devices_device ( struct sysfs_device * devices_dev ,
2004-10-19 15:37:22 +04:00
const char * * error )
{
2004-11-23 05:43:37 +03:00
static const struct device_file {
const char * bus ;
const char * file ;
2004-11-13 14:36:47 +03:00
} device_files [ ] = {
2004-10-19 15:37:22 +04:00
{ . bus = " scsi " , . file = " vendor " } ,
{ . bus = " usb " , . file = " idVendor " } ,
{ . bus = " usb " , . file = " iInterface " } ,
{ . bus = " usb " , . file = " bNumEndpoints " } ,
2005-06-22 04:11:59 +04:00
{ . bus = " usb-serial " , . file = " bus " } ,
{ . bus = " ide " , . file = " bus " } ,
2004-10-19 15:37:22 +04:00
{ . bus = " pci " , . file = " vendor " } ,
2005-06-22 04:11:59 +04:00
{ . bus = " platform " , . file = " bus " } ,
{ . bus = " pcmcia " , . file = " bus " } ,
{ . bus = " i2c " , . file = " bus " } ,
2004-10-23 14:12:28 +04:00
{ . bus = " ieee1394 " , . file = " node_count " } ,
{ . bus = " ieee1394 " , . file = " nodeid " } ,
{ . bus = " ieee1394 " , . file = " address " } ,
2004-11-23 05:55:24 +03:00
{ . bus = " bttv-sub " , . file = NULL } ,
2005-06-22 04:11:59 +04:00
{ . bus = " pnp " , . file = " bus " } ,
{ . bus = " eisa " , . file = " bus " } ,
{ . bus = " serio " , . file = " bus " } ,
{ . bus = " pseudo " , . file = " bus " } ,
{ . bus = " mmc " , . file = " bus " } ,
{ . bus = " macio " , . file = " bus " } ,
{ . bus = " of_platform " , . file = " bus " } ,
{ . bus = " vio " , . file = " bus " } ,
{ . bus = " ecard " , . file = " bus " } ,
{ . bus = " sa1111-rab " , . file = " bus " } ,
{ . bus = " amba " , . file = " bus " } ,
{ . bus = " locomo-bus " , . file = " bus " } ,
{ . bus = " logicmodule " , . file = " bus " } ,
{ . bus = " parisc " , . file = " bus " } ,
{ . bus = " ocp " , . file = " bus " } ,
{ . bus = " dio " , . file = " bus " } ,
{ . bus = " MCA " , . file = " bus " } ,
{ . bus = " wl " , . file = " bus " } ,
{ . bus = " ccwgroup " , . file = " bus " } ,
{ . bus = " css " , . file = " bus " } ,
{ . bus = " ccw " , . file = " bus " } ,
{ . bus = " iucv " , . file = " bus " } ,
2004-10-23 14:12:28 +04:00
{ NULL , NULL }
2004-10-19 15:37:22 +04:00
} ;
2005-03-04 22:56:51 +03:00
const struct device_file * devicefile = NULL ;
2004-10-19 15:37:22 +04:00
int loop ;
2004-11-13 14:36:47 +03:00
/* the kernel may tell us what to wait for */
if ( kernel_release_satisfactory ( 2 , 6 , 10 ) > 0 )
if ( getenv ( " PHYSDEVBUS " ) = = NULL ) {
dbg ( " the kernel says, that there is no bus for '%s' " , devices_dev - > path ) ;
return 0 ;
}
2004-10-19 15:37:22 +04:00
/* wait for the bus device link to the devices device */
loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND ;
while ( - - loop ) {
if ( sysfs_get_device_bus ( devices_dev ) = = 0 )
break ;
usleep ( 1000 * 1000 / WAIT_LOOP_PER_SECOND ) ;
}
if ( loop = = 0 ) {
dbg ( " error: getting bus device link " ) ;
if ( error )
* error = " no bus device link " ;
return - 1 ;
}
dbg ( " bus device link found for bus '%s' " , devices_dev - > bus ) ;
2004-11-13 14:36:47 +03:00
/* wait for a bus device specific file to show up */
2004-10-19 15:37:22 +04:00
loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND ;
while ( - - loop ) {
2004-10-23 14:12:28 +04:00
int found_bus_type = 0 ;
2004-10-19 15:37:22 +04:00
2004-11-13 14:36:47 +03:00
for ( devicefile = device_files ; devicefile - > bus ! = NULL ; devicefile + + ) {
if ( strcmp ( devices_dev - > bus , devicefile - > bus ) = = 0 ) {
2005-03-07 06:29:43 +03:00
char filename [ PATH_SIZE ] ;
2004-10-23 14:12:28 +04:00
struct stat stats ;
2004-11-23 05:55:24 +03:00
if ( devicefile - > file = = NULL ) {
dbg ( " bus '%s' has no file to wait for " , devices_dev - > bus ) ;
return 0 ;
}
2004-10-23 14:12:28 +04:00
found_bus_type = 1 ;
2005-03-07 06:29:43 +03:00
snprintf ( filename , sizeof ( filename ) , " %s/%s " , devices_dev - > path , devicefile - > file ) ;
filename [ sizeof ( filename ) - 1 ] = ' \0 ' ;
2004-11-13 14:36:47 +03:00
dbg ( " looking at bus '%s' device for specific file '%s' " , devices_dev - > bus , filename ) ;
2004-10-23 14:12:28 +04:00
if ( stat ( filename , & stats ) = = 0 ) {
2004-11-13 14:36:47 +03:00
dbg ( " bus '%s' device specific file '%s' found " , devices_dev - > bus , devicefile - > file ) ;
2004-10-19 15:37:22 +04:00
return 0 ;
}
}
}
2004-10-23 14:12:28 +04:00
if ( found_bus_type = = 0 ) {
2004-10-19 15:37:22 +04:00
if ( error )
* error = " unknown bus " ;
info ( " error: unknown bus, please report to "
" <linux-hotplug-devel@lists.sourceforge.net> '%s' " , devices_dev - > bus ) ;
return - 1 ;
}
usleep ( 1000 * 1000 / WAIT_LOOP_PER_SECOND ) ;
}
2004-11-13 14:36:47 +03:00
dbg ( " error: getting '%s' device specific file '%s' " , devices_dev - > bus , devicefile - > file ) ;
2004-10-19 15:37:22 +04:00
if ( error )
2004-11-13 14:36:47 +03:00
* error = " bus device specific file unavailable " ;
2004-10-19 15:37:22 +04:00
return - 1 ;
}
2004-11-13 14:36:47 +03:00
struct sysfs_class_device * wait_class_device_open ( const char * path )
2004-10-19 15:37:22 +04:00
{
2005-01-17 05:54:26 +03:00
struct sysfs_class_device * class_dev = NULL ;
2004-10-19 15:37:22 +04:00
int loop ;
loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND ;
while ( - - loop ) {
class_dev = sysfs_open_class_device_path ( path ) ;
if ( class_dev )
break ;
usleep ( 1000 * 1000 / WAIT_LOOP_PER_SECOND ) ;
}
2005-01-17 05:54:26 +03:00
return class_dev ;
2004-10-19 15:37:22 +04:00
}
int wait_for_class_device ( struct sysfs_class_device * class_dev ,
const char * * error )
{
struct sysfs_class_device * class_dev_parent ;
struct sysfs_device * devices_dev = NULL ;
int loop ;
if ( wait_for_class_device_attributes ( class_dev , error ) ! = 0 )
return - ENOENT ;
/* skip devices without devices-link */
if ( class_device_expect_no_device_link ( class_dev ) ) {
dbg ( " no device symlink expected for '%s', " , class_dev - > name ) ;
2004-11-13 14:36:47 +03:00
return 0 ;
2004-10-19 15:37:22 +04:00
}
/* the symlink may be on the parent device */
class_dev_parent = sysfs_get_classdev_parent ( class_dev ) ;
if ( class_dev_parent )
dbg ( " looking at parent device for device link '%s' " , class_dev_parent - > path ) ;
/* wait for the symlink to the devices device */
dbg ( " waiting for symlink to devices device " ) ;
loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND ;
while ( - - loop ) {
if ( class_dev_parent )
devices_dev = sysfs_get_classdev_device ( class_dev_parent ) ;
else
devices_dev = sysfs_get_classdev_device ( class_dev ) ;
if ( devices_dev )
break ;
usleep ( 1000 * 1000 / WAIT_LOOP_PER_SECOND ) ;
}
if ( ! devices_dev ) {
dbg ( " error: no devices device symlink found " ) ;
if ( error )
* error = " no device symlink " ;
return - ENODEV ;
}
dbg ( " device symlink found pointing to '%s' " , devices_dev - > path ) ;
2004-11-13 14:36:47 +03:00
/* wait for the devices device */
2004-10-19 15:37:22 +04:00
if ( class_device_expect_no_bus ( class_dev ) ) {
dbg ( " no bus device expected for '%s', " , class_dev - > classname ) ;
return 0 ;
}
2004-11-13 14:36:47 +03:00
return wait_for_devices_device ( devices_dev , error ) ;
2004-10-19 15:37:22 +04:00
}
2004-11-13 14:36:47 +03:00
struct sysfs_device * wait_devices_device_open ( const char * path )
2004-10-19 15:37:22 +04:00
{
2005-01-17 05:54:26 +03:00
struct sysfs_device * devices_dev = NULL ;
2004-10-19 15:37:22 +04:00
int loop ;
loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND ;
while ( - - loop ) {
devices_dev = sysfs_open_device_path ( path ) ;
if ( devices_dev )
break ;
usleep ( 1000 * 1000 / WAIT_LOOP_PER_SECOND ) ;
}
2005-01-17 05:54:26 +03:00
return devices_dev ;
2004-10-19 15:37:22 +04:00
}