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/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 .
2005-10-23 22:59:14 +04:00
* @ start : Device to begin with
2005-03-21 21:59:56 +03:00
* @ data : Data to pass to the callback .
* @ fn : Function to call for each device .
*
2005-03-25 07:08:04 +03:00
* Iterate over the @ drv ' s list of devices calling @ fn for each one .
2005-03-21 21:59:56 +03:00
*/
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 ;
}
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 .
*/
struct device * driver_find_device ( struct device_driver * drv ,
struct device * start , void * data ,
int ( * match ) ( struct device * , void * ) )
{
struct klist_iter i ;
struct device * dev ;
if ( ! drv )
return NULL ;
klist_iter_init_node ( & drv - > klist_devices , & i ,
( 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
/**
* 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 .
*/
int driver_register ( struct device_driver * drv )
{
2006-01-05 17:29:51 +03:00
if ( ( drv - > bus - > probe & & drv - > probe ) | |
( drv - > bus - > remove & & drv - > remove ) | |
( drv - > bus - > shutdown & & drv - > shutdown ) ) {
printk ( KERN_WARNING " Driver '%s' needs updating - please use bus_type methods \n " , drv - > name ) ;
}
2006-09-19 00:24:28 +04:00
klist_init ( & drv - > klist_devices , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
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 .
*/
void driver_unregister ( struct device_driver * drv )
{
bus_remove_driver ( drv ) ;
}
/**
* 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 )
{
2007-11-01 23:31:26 +03:00
struct kobject * k = kset_find_obj ( bus - > drivers_kset , name ) ;
2005-04-17 02:20:36 +04:00
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 ) ;