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
2007-11-29 02:59:15 +03:00
* Copyright ( c ) 2007 Greg Kroah - Hartman < gregkh @ suse . de >
* Copyright ( c ) 2007 Novell Inc .
2005-04-17 02:20:36 +04:00
*
* This file is released under the GPLv2
*
*/
# 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)
2005-03-21 21:59:56 +03:00
2008-01-25 09:50:12 +03:00
static struct device * next_device ( struct klist_iter * i )
2005-03-21 23:25:36 +03:00
{
2008-01-25 09:50:12 +03:00
struct klist_node * n = klist_next ( i ) ;
2005-03-21 23:25:36 +03:00
return n ? container_of ( n , struct device , knode_driver ) : NULL ;
}
2005-03-21 21:59:56 +03:00
/**
2008-01-25 09:50:12 +03:00
* driver_for_each_device - Iterator for devices bound to a driver .
* @ drv : Driver we ' re iterating .
* @ start : Device to begin with
* @ data : Data to pass to the callback .
* @ fn : Function to call for each device .
2005-03-21 21:59:56 +03:00
*
2008-01-25 09:50:12 +03:00
* Iterate over the @ drv ' s list of devices calling @ fn for each one .
2005-03-21 21:59:56 +03:00
*/
2008-01-25 09:50:12 +03:00
int driver_for_each_device ( struct device_driver * drv , struct device * start ,
void * data , int ( * fn ) ( struct device * , void * ) )
2005-03-21 21:59:56 +03:00
{
2005-03-21 23:25:36 +03:00
struct klist_iter i ;
2008-01-25 09:50:12 +03:00
struct device * dev ;
2005-03-21 21:59:56 +03:00
int error = 0 ;
2005-03-21 23:25:36 +03:00
if ( ! drv )
return - EINVAL ;
2007-11-29 02:59:15 +03:00
klist_iter_init_node ( & drv - > p - > klist_devices , & i ,
2005-03-21 23:25:36 +03:00
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 ;
}
2005-03-22 23:17:13 +03:00
EXPORT_SYMBOL_GPL ( driver_for_each_device ) ;
2005-03-21 21:59:56 +03:00
2005-06-22 18:59:51 +04:00
/**
* driver_find_device - device iterator for locating a particular device .
2005-10-23 22:59:14 +04:00
* @ drv : The device ' s driver
2005-06-22 18:59:51 +04:00
* @ start : Device to begin with
* @ data : Data to pass to match function
* @ match : Callback function to check device
*
* This is similar to the driver_for_each_device ( ) function above , but
* it returns a reference to a device that is ' found ' for later use , as
* determined by the @ match callback .
*
* The callback should return 0 if the device doesn ' t match and non - zero
* if it does . If the callback returns non - zero , this function will
* return to the caller and not iterate over any more devices .
*/
2008-01-25 09:50:12 +03:00
struct device * driver_find_device ( struct device_driver * drv ,
struct device * start , void * data ,
int ( * match ) ( struct device * dev , void * data ) )
2005-06-22 18:59:51 +04:00
{
struct klist_iter i ;
struct device * dev ;
if ( ! drv )
return NULL ;
2007-11-29 02:59:15 +03:00
klist_iter_init_node ( & drv - > p - > klist_devices , & i ,
2005-06-22 18:59:51 +04:00
( start ? & start - > knode_driver : NULL ) ) ;
while ( ( dev = next_device ( & i ) ) )
if ( match ( dev , data ) & & get_device ( dev ) )
break ;
klist_iter_exit ( & i ) ;
return dev ;
}
EXPORT_SYMBOL_GPL ( driver_find_device ) ;
2005-04-17 02:20:36 +04:00
/**
2008-01-25 09:50:12 +03:00
* driver_create_file - create sysfs file for driver .
* @ drv : driver .
* @ attr : driver attribute descriptor .
2005-04-17 02:20:36 +04:00
*/
2008-01-25 09:50:12 +03:00
int driver_create_file ( struct device_driver * drv ,
struct driver_attribute * attr )
2005-04-17 02:20:36 +04:00
{
int error ;
if ( get_driver ( drv ) ) {
2007-11-29 02:59:15 +03:00
error = sysfs_create_file ( & drv - > p - > kobj , & attr - > attr ) ;
2005-04-17 02:20:36 +04:00
put_driver ( drv ) ;
} else
error = - EINVAL ;
return error ;
}
2008-01-25 09:50:12 +03:00
EXPORT_SYMBOL_GPL ( driver_create_file ) ;
2005-04-17 02:20:36 +04:00
/**
2008-01-25 09:50:12 +03:00
* driver_remove_file - remove sysfs file for driver .
* @ drv : driver .
* @ attr : driver attribute descriptor .
2005-04-17 02:20:36 +04:00
*/
2008-01-25 09:50:12 +03:00
void driver_remove_file ( struct device_driver * drv ,
struct driver_attribute * attr )
2005-04-17 02:20:36 +04:00
{
if ( get_driver ( drv ) ) {
2007-11-29 02:59:15 +03:00
sysfs_remove_file ( & drv - > p - > kobj , & attr - > attr ) ;
2005-04-17 02:20:36 +04:00
put_driver ( drv ) ;
}
}
2008-01-25 09:50:12 +03:00
EXPORT_SYMBOL_GPL ( driver_remove_file ) ;
2005-04-17 02:20:36 +04:00
2007-12-19 22:54:39 +03:00
/**
* driver_add_kobj - add a kobject below the specified driver
*
* You really don ' t want to do this , this is only here due to one looney
* iseries driver , go poke those developers if you are annoyed about
* this . . .
*/
int driver_add_kobj ( struct device_driver * drv , struct kobject * kobj ,
const char * fmt , . . . )
{
va_list args ;
char * name ;
va_start ( args , fmt ) ;
name = kvasprintf ( GFP_KERNEL , fmt , args ) ;
va_end ( args ) ;
if ( ! name )
return - ENOMEM ;
2007-12-18 09:05:35 +03:00
return kobject_add ( kobj , & drv - > p - > kobj , " %s " , name ) ;
2007-12-19 22:54:39 +03:00
}
EXPORT_SYMBOL_GPL ( driver_add_kobj ) ;
2005-04-17 02:20:36 +04:00
/**
2008-01-25 09:50:12 +03:00
* get_driver - increment driver reference count .
* @ drv : driver .
2005-04-17 02:20:36 +04:00
*/
2008-01-25 09:50:12 +03:00
struct device_driver * get_driver ( struct device_driver * drv )
2005-04-17 02:20:36 +04:00
{
2007-11-29 02:59:15 +03:00
if ( drv ) {
struct driver_private * priv ;
struct kobject * kobj ;
kobj = kobject_get ( & drv - > p - > kobj ) ;
priv = to_driver ( kobj ) ;
return priv - > driver ;
}
return NULL ;
2005-04-17 02:20:36 +04:00
}
2008-01-25 09:50:12 +03:00
EXPORT_SYMBOL_GPL ( get_driver ) ;
2005-04-17 02:20:36 +04:00
/**
2008-01-25 09:50:12 +03:00
* put_driver - decrement driver ' s refcount .
* @ drv : driver .
2005-04-17 02:20:36 +04:00
*/
2008-01-25 09:50:12 +03:00
void put_driver ( struct device_driver * drv )
2005-04-17 02:20:36 +04:00
{
2007-11-29 02:59:15 +03:00
kobject_put ( & drv - > p - > kobj ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-25 09:50:12 +03:00
EXPORT_SYMBOL_GPL ( put_driver ) ;
2005-04-17 02:20:36 +04:00
2007-12-05 14:50:23 +03:00
static int driver_add_groups ( struct device_driver * drv ,
struct attribute_group * * groups )
{
int error = 0 ;
int i ;
if ( groups ) {
for ( i = 0 ; groups [ i ] ; i + + ) {
2007-11-29 02:59:15 +03:00
error = sysfs_create_group ( & drv - > p - > kobj , groups [ i ] ) ;
2007-12-05 14:50:23 +03:00
if ( error ) {
while ( - - i > = 0 )
2007-11-29 02:59:15 +03:00
sysfs_remove_group ( & drv - > p - > kobj ,
2007-12-05 14:50:23 +03:00
groups [ i ] ) ;
break ;
}
}
}
return error ;
}
static void driver_remove_groups ( struct device_driver * drv ,
struct attribute_group * * groups )
{
int i ;
if ( groups )
for ( i = 0 ; groups [ i ] ; i + + )
2007-11-29 02:59:15 +03:00
sysfs_remove_group ( & drv - > p - > kobj , groups [ i ] ) ;
2007-12-05 14:50:23 +03:00
}
2005-04-17 02:20:36 +04:00
/**
2008-01-25 09:50:12 +03:00
* driver_register - register driver with bus
* @ drv : driver to register
2005-04-17 02:20:36 +04:00
*
2008-01-25 09:50:12 +03:00
* 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 .
2005-04-17 02:20:36 +04:00
*/
2008-01-25 09:50:12 +03:00
int driver_register ( struct device_driver * drv )
2005-04-17 02:20:36 +04:00
{
2007-12-05 14:50:23 +03:00
int ret ;
2006-01-05 17:29:51 +03:00
if ( ( drv - > bus - > probe & & drv - > probe ) | |
( drv - > bus - > remove & & drv - > remove ) | |
2008-01-25 09:50:12 +03:00
( drv - > bus - > shutdown & & drv - > shutdown ) )
printk ( KERN_WARNING " Driver '%s' needs updating - please use "
" bus_type methods \n " , drv - > name ) ;
2007-12-05 14:50:23 +03:00
ret = bus_add_driver ( drv ) ;
if ( ret )
return ret ;
ret = driver_add_groups ( drv , drv - > groups ) ;
if ( ret )
bus_remove_driver ( drv ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2008-01-25 09:50:12 +03:00
EXPORT_SYMBOL_GPL ( driver_register ) ;
2005-04-17 02:20:36 +04:00
/**
2008-01-25 09:50:12 +03:00
* driver_unregister - remove driver from system .
* @ drv : driver .
2005-04-17 02:20:36 +04:00
*
2008-01-25 09:50:12 +03:00
* Again , we pass off most of the work to the bus - level call .
2005-04-17 02:20:36 +04:00
*/
2008-01-25 09:50:12 +03:00
void driver_unregister ( struct device_driver * drv )
2005-04-17 02:20:36 +04:00
{
2007-12-05 14:50:23 +03:00
driver_remove_groups ( drv , drv - > groups ) ;
2005-04-17 02:20:36 +04:00
bus_remove_driver ( drv ) ;
}
2008-01-25 09:50:12 +03:00
EXPORT_SYMBOL_GPL ( driver_unregister ) ;
2005-04-17 02:20:36 +04:00
/**
2008-01-25 09:50:12 +03:00
* driver_find - locate driver on a bus by its name .
* @ name : name of the driver .
* @ bus : bus to scan for the driver .
2005-04-17 02:20:36 +04:00
*
2008-01-25 09:50:12 +03:00
* Call kset_find_obj ( ) to iterate over list of drivers on
* a bus to find driver by name . Return driver if found .
2005-04-17 02:20:36 +04:00
*
2008-01-25 09:50:12 +03:00
* Note that kset_find_obj increments driver ' s reference count .
2005-04-17 02:20:36 +04:00
*/
struct device_driver * driver_find ( const char * name , struct bus_type * bus )
{
2007-11-02 05:41:16 +03:00
struct kobject * k = kset_find_obj ( bus - > p - > drivers_kset , name ) ;
2007-11-29 02:59:15 +03:00
struct driver_private * priv ;
if ( k ) {
priv = to_driver ( k ) ;
return priv - > driver ;
}
2005-04-17 02:20:36 +04:00
return NULL ;
}
EXPORT_SYMBOL_GPL ( driver_find ) ;