2005-04-17 02:20:36 +04:00
/*
* driver . c - centralized device driver management
*
* Copyright ( c ) 2002 - 3 Patrick Mochel
* Copyright ( c ) 2002 - 3 Open Source Development Labs
*
* This file is released under the GPLv2
*
*/
# include <linux/config.h>
# include <linux/device.h>
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/string.h>
# include "base.h"
# define to_dev(node) container_of(node, struct device, driver_list)
# define to_drv(obj) container_of(obj, struct device_driver, kobj)
2005-03-21 21:59:56 +03:00
2005-03-21 23:25:36 +03:00
static struct device * next_device ( struct klist_iter * i )
{
struct klist_node * n = klist_next ( i ) ;
return n ? container_of ( n , struct device , knode_driver ) : NULL ;
}
2005-03-21 21:59:56 +03:00
/**
* driver_for_each_device - Iterator for devices bound to a driver .
* @ drv : Driver we ' re iterating .
* @ data : Data to pass to the callback .
* @ fn : Function to call for each device .
*
* Take the bus ' s rwsem and iterate over the @ drv ' s list of devices ,
* calling @ fn for each one .
*/
int driver_for_each_device ( struct device_driver * drv , struct device * start ,
void * data , int ( * fn ) ( struct device * , void * ) )
{
2005-03-21 23:25:36 +03:00
struct klist_iter i ;
2005-03-21 21:59:56 +03:00
struct device * dev ;
int error = 0 ;
2005-03-21 23:25:36 +03:00
if ( ! drv )
return - EINVAL ;
klist_iter_init_node ( & drv - > klist_devices , & i ,
start ? & start - > knode_driver : NULL ) ;
while ( ( dev = next_device ( & i ) ) & & ! error )
2005-03-21 21:59:56 +03:00
error = fn ( dev , data ) ;
2005-03-21 23:25:36 +03:00
klist_iter_exit ( & i ) ;
2005-03-21 21:59:56 +03:00
return error ;
}
EXPORT_SYMBOL ( driver_for_each_device ) ;
2005-04-17 02:20:36 +04:00
/**
* driver_create_file - create sysfs file for driver .
* @ drv : driver .
* @ attr : driver attribute descriptor .
*/
int driver_create_file ( struct device_driver * drv , struct driver_attribute * attr )
{
int error ;
if ( get_driver ( drv ) ) {
error = sysfs_create_file ( & drv - > kobj , & attr - > attr ) ;
put_driver ( drv ) ;
} else
error = - EINVAL ;
return error ;
}
/**
* driver_remove_file - remove sysfs file for driver .
* @ drv : driver .
* @ attr : driver attribute descriptor .
*/
void driver_remove_file ( struct device_driver * drv , struct driver_attribute * attr )
{
if ( get_driver ( drv ) ) {
sysfs_remove_file ( & drv - > kobj , & attr - > attr ) ;
put_driver ( drv ) ;
}
}
/**
* get_driver - increment driver reference count .
* @ drv : driver .
*/
struct device_driver * get_driver ( struct device_driver * drv )
{
return drv ? to_drv ( kobject_get ( & drv - > kobj ) ) : NULL ;
}
/**
* put_driver - decrement driver ' s refcount .
* @ drv : driver .
*/
void put_driver ( struct device_driver * drv )
{
kobject_put ( & drv - > kobj ) ;
}
/**
* driver_register - register driver with bus
* @ drv : driver to register
*
* We pass off most of the work to the bus_add_driver ( ) call ,
* since most of the things we have to do deal with the bus
* structures .
*
* The one interesting aspect is that we setup @ drv - > unloaded
* as a completion that gets complete when the driver reference
* count reaches 0.
*/
int driver_register ( struct device_driver * drv )
{
2005-03-21 23:25:36 +03:00
klist_init ( & drv - > klist_devices ) ;
2005-04-17 02:20:36 +04:00
init_completion ( & drv - > unloaded ) ;
return bus_add_driver ( drv ) ;
}
/**
* driver_unregister - remove driver from system .
* @ drv : driver .
*
* Again , we pass off most of the work to the bus - level call .
*
* Though , once that is done , we wait until @ drv - > unloaded is completed .
* This will block until the driver refcount reaches 0 , and it is
* released . Only modular drivers will call this function , and we
* have to guarantee that it won ' t complete , letting the driver
* unload until all references are gone .
*/
void driver_unregister ( struct device_driver * drv )
{
bus_remove_driver ( drv ) ;
wait_for_completion ( & drv - > unloaded ) ;
}
/**
* driver_find - locate driver on a bus by its name .
* @ name : name of the driver .
* @ bus : bus to scan for the driver .
*
* Call kset_find_obj ( ) to iterate over list of drivers on
* a bus to find driver by name . Return driver if found .
*
* Note that kset_find_obj increments driver ' s reference count .
*/
struct device_driver * driver_find ( const char * name , struct bus_type * bus )
{
struct kobject * k = kset_find_obj ( & bus - > drivers , name ) ;
if ( k )
return to_drv ( k ) ;
return NULL ;
}
EXPORT_SYMBOL_GPL ( driver_register ) ;
EXPORT_SYMBOL_GPL ( driver_unregister ) ;
EXPORT_SYMBOL_GPL ( get_driver ) ;
EXPORT_SYMBOL_GPL ( put_driver ) ;
EXPORT_SYMBOL_GPL ( driver_find ) ;
EXPORT_SYMBOL_GPL ( driver_create_file ) ;
EXPORT_SYMBOL_GPL ( driver_remove_file ) ;