2005-04-17 02:20:36 +04:00
/*
* Functions to handle I2O drivers ( OSMs ) and I2O bus type for sysfs
*
* Copyright ( C ) 2004 Markus Lidel < Markus . Lidel @ shadowconnect . 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 ; either version 2 of the License , or ( at your
* option ) any later version .
*
* Fixes / additions :
* Markus Lidel < Markus . Lidel @ shadowconnect . com >
* initial version .
*/
# include <linux/device.h>
# include <linux/module.h>
# include <linux/rwsem.h>
# include <linux/i2o.h>
2005-10-31 02:03:48 +03:00
# include <linux/workqueue.h>
# include <linux/string.h>
# include <linux/slab.h>
2005-06-24 09:02:21 +04:00
# include "core.h"
2005-04-17 02:20:36 +04:00
2005-06-24 09:02:14 +04:00
# define OSM_NAME "i2o"
2005-06-24 09:02:11 +04:00
2005-04-17 02:20:36 +04:00
/* max_drivers - Maximum I2O drivers (OSMs) which could be registered */
2005-06-24 09:02:21 +04:00
static unsigned int i2o_max_drivers = I2O_MAX_DRIVERS ;
2005-04-17 02:20:36 +04:00
module_param_named ( max_drivers , i2o_max_drivers , uint , 0 ) ;
MODULE_PARM_DESC ( max_drivers , " maximum number of OSM's to support " ) ;
/* I2O drivers lock and array */
static spinlock_t i2o_drivers_lock ;
static struct i2o_driver * * i2o_drivers ;
/**
2006-12-07 07:38:43 +03:00
* i2o_bus_match - Tell if I2O device class id matches the class ids of the I2O driver ( OSM )
2005-04-17 02:20:36 +04:00
* @ dev : device which should be verified
* @ drv : the driver to match against
*
* Used by the bus to check if the driver wants to handle the device .
*
* Returns 1 if the class ids of the driver match the class id of the
* device , otherwise 0.
*/
static int i2o_bus_match ( struct device * dev , struct device_driver * drv )
{
struct i2o_device * i2o_dev = to_i2o_device ( dev ) ;
struct i2o_driver * i2o_drv = to_i2o_driver ( drv ) ;
struct i2o_class_id * ids = i2o_drv - > classes ;
if ( ids )
while ( ids - > class_id ! = I2O_CLASS_END ) {
if ( ids - > class_id = = i2o_dev - > lct_data . class_id )
return 1 ;
ids + + ;
}
return 0 ;
} ;
/* I2O bus type */
struct bus_type i2o_bus_type = {
. name = " i2o " ,
. match = i2o_bus_match ,
2006-01-06 11:19:32 +03:00
. dev_attrs = i2o_device_attrs
2005-04-17 02:20:36 +04:00
} ;
/**
* i2o_driver_register - Register a I2O driver ( OSM ) in the I2O core
* @ drv : I2O driver which should be registered
*
* Registers the OSM drv in the I2O core and creates an event queues if
* necessary .
*
* Returns 0 on success or negative error code on failure .
*/
int i2o_driver_register ( struct i2o_driver * drv )
{
struct i2o_controller * c ;
int i ;
int rc = 0 ;
unsigned long flags ;
2005-06-24 09:02:14 +04:00
osm_debug ( " Register driver %s \n " , drv - > name ) ;
2005-04-17 02:20:36 +04:00
if ( drv - > event ) {
2011-02-01 13:42:42 +03:00
drv - > event_queue = alloc_workqueue ( drv - > name ,
WQ_MEM_RECLAIM , 1 ) ;
2005-04-17 02:20:36 +04:00
if ( ! drv - > event_queue ) {
2005-06-24 09:02:14 +04:00
osm_err ( " Could not initialize event queue for driver "
" %s \n " , drv - > name ) ;
2005-04-17 02:20:36 +04:00
return - EFAULT ;
}
2005-06-24 09:02:14 +04:00
osm_debug ( " Event queue initialized for driver %s \n " , drv - > name ) ;
2005-04-17 02:20:36 +04:00
} else
drv - > event_queue = NULL ;
drv - > driver . name = drv - > name ;
drv - > driver . bus = & i2o_bus_type ;
spin_lock_irqsave ( & i2o_drivers_lock , flags ) ;
for ( i = 0 ; i2o_drivers [ i ] ; i + + )
if ( i > = i2o_max_drivers ) {
2005-06-24 09:02:14 +04:00
osm_err ( " too many drivers registered, increase "
" max_drivers \n " ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & i2o_drivers_lock , flags ) ;
return - EFAULT ;
}
drv - > context = i ;
i2o_drivers [ i ] = drv ;
spin_unlock_irqrestore ( & i2o_drivers_lock , flags ) ;
2005-06-24 09:02:14 +04:00
osm_debug ( " driver %s gets context id %d \n " , drv - > name , drv - > context ) ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( c , & i2o_controllers , list ) {
struct i2o_device * i2o_dev ;
i2o_driver_notify_controller_add ( drv , c ) ;
list_for_each_entry ( i2o_dev , & c - > devices , list )
2005-06-24 09:02:23 +04:00
i2o_driver_notify_device_add ( drv , i2o_dev ) ;
2005-04-17 02:20:36 +04:00
}
rc = driver_register ( & drv - > driver ) ;
2007-05-24 00:58:05 +04:00
if ( rc ) {
if ( drv - > event ) {
destroy_workqueue ( drv - > event_queue ) ;
drv - > event_queue = NULL ;
}
}
2005-04-17 02:20:36 +04:00
return rc ;
} ;
/**
* i2o_driver_unregister - Unregister a I2O driver ( OSM ) from the I2O core
* @ drv : I2O driver which should be unregistered
*
* Unregisters the OSM drv from the I2O core and cleanup event queues if
* necessary .
*/
void i2o_driver_unregister ( struct i2o_driver * drv )
{
struct i2o_controller * c ;
unsigned long flags ;
2005-06-24 09:02:14 +04:00
osm_debug ( " unregister driver %s \n " , drv - > name ) ;
2005-04-17 02:20:36 +04:00
driver_unregister ( & drv - > driver ) ;
list_for_each_entry ( c , & i2o_controllers , list ) {
struct i2o_device * i2o_dev ;
list_for_each_entry ( i2o_dev , & c - > devices , list )
i2o_driver_notify_device_remove ( drv , i2o_dev ) ;
i2o_driver_notify_controller_remove ( drv , c ) ;
}
spin_lock_irqsave ( & i2o_drivers_lock , flags ) ;
i2o_drivers [ drv - > context ] = NULL ;
spin_unlock_irqrestore ( & i2o_drivers_lock , flags ) ;
if ( drv - > event_queue ) {
destroy_workqueue ( drv - > event_queue ) ;
drv - > event_queue = NULL ;
2005-06-24 09:02:14 +04:00
osm_debug ( " event queue removed for %s \n " , drv - > name ) ;
2005-04-17 02:20:36 +04:00
}
} ;
/**
* i2o_driver_dispatch - dispatch an I2O reply message
* @ c : I2O controller of the message
* @ m : I2O message number
*
* The reply is delivered to the driver from which the original message
* was . This function is only called from interrupt context .
*
* Returns 0 on success and the message should not be flushed . Returns > 0
* on success and if the message should be flushed afterwords . Returns
* negative error code on failure ( the message will be flushed too ) .
*/
2005-06-24 09:02:14 +04:00
int i2o_driver_dispatch ( struct i2o_controller * c , u32 m )
2005-04-17 02:20:36 +04:00
{
struct i2o_driver * drv ;
2005-06-24 09:02:21 +04:00
struct i2o_message * msg = i2o_msg_out_to_virt ( c , m ) ;
u32 context = le32_to_cpu ( msg - > u . s . icntxt ) ;
2005-06-24 09:02:16 +04:00
unsigned long flags ;
2005-06-24 09:02:11 +04:00
if ( unlikely ( context > = i2o_max_drivers ) ) {
2005-06-24 09:02:14 +04:00
osm_warn ( " %s: Spurious reply to unknown driver %d \n " , c - > name ,
context ) ;
2005-06-24 09:02:11 +04:00
return - EIO ;
}
2005-04-17 02:20:36 +04:00
2005-06-24 09:02:16 +04:00
spin_lock_irqsave ( & i2o_drivers_lock , flags ) ;
2005-06-24 09:02:11 +04:00
drv = i2o_drivers [ context ] ;
2005-06-24 09:02:16 +04:00
spin_unlock_irqrestore ( & i2o_drivers_lock , flags ) ;
2005-04-17 02:20:36 +04:00
2005-06-24 09:02:11 +04:00
if ( unlikely ( ! drv ) ) {
2005-06-24 09:02:14 +04:00
osm_warn ( " %s: Spurious reply to unknown driver %d \n " , c - > name ,
context ) ;
2005-06-24 09:02:11 +04:00
return - EIO ;
}
2005-04-17 02:20:36 +04:00
2005-06-24 09:02:21 +04:00
if ( ( le32_to_cpu ( msg - > u . head [ 1 ] ) > > 24 ) = = I2O_CMD_UTIL_EVT_REGISTER ) {
2005-06-24 09:02:11 +04:00
struct i2o_device * dev , * tmp ;
struct i2o_event * evt ;
u16 size ;
2005-06-24 09:02:21 +04:00
u16 tid = le32_to_cpu ( msg - > u . head [ 1 ] ) & 0xfff ;
2005-04-17 02:20:36 +04:00
2005-06-24 09:02:11 +04:00
osm_debug ( " event received from device %d \n " , tid ) ;
2005-04-17 02:20:36 +04:00
2005-06-24 09:02:14 +04:00
if ( ! drv - > event )
return - EIO ;
2005-06-24 09:02:11 +04:00
/* cut of header from message size (in 32-bit words) */
2005-06-24 09:02:21 +04:00
size = ( le32_to_cpu ( msg - > u . head [ 0 ] ) > > 16 ) - 5 ;
2005-04-17 02:20:36 +04:00
2006-01-06 11:19:33 +03:00
evt = kzalloc ( size * 4 + sizeof ( * evt ) , GFP_ATOMIC ) ;
2005-06-24 09:02:11 +04:00
if ( ! evt )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2005-06-24 09:02:11 +04:00
evt - > size = size ;
2005-06-24 09:02:21 +04:00
evt - > tcntxt = le32_to_cpu ( msg - > u . s . tcntxt ) ;
evt - > event_indicator = le32_to_cpu ( msg - > body [ 0 ] ) ;
2006-01-06 11:19:32 +03:00
memcpy ( & evt - > data , & msg - > body [ 1 ] , size * 4 ) ;
2005-04-17 02:20:36 +04:00
2005-06-24 09:02:11 +04:00
list_for_each_entry_safe ( dev , tmp , & c - > devices , list )
if ( dev - > lct_data . tid = = tid ) {
evt - > i2o_dev = dev ;
break ;
2005-04-17 02:20:36 +04:00
}
2006-11-22 17:57:56 +03:00
INIT_WORK ( & evt - > work , drv - > event ) ;
2005-06-24 09:02:11 +04:00
queue_work ( drv - > event_queue , & evt - > work ) ;
return 1 ;
}
if ( unlikely ( ! drv - > reply ) ) {
2005-06-24 09:02:14 +04:00
osm_debug ( " %s: Reply to driver %s, but no reply function "
" defined! \n " , c - > name , drv - > name ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
2005-06-24 09:02:11 +04:00
}
return drv - > reply ( c , m , msg ) ;
2005-04-17 02:20:36 +04:00
}
/**
* i2o_driver_notify_controller_add_all - Send notify of added controller
2006-12-07 07:38:43 +03:00
* @ c : newly added controller
2005-04-17 02:20:36 +04:00
*
* Send notifications to all registered drivers that a new controller was
* added .
*/
void i2o_driver_notify_controller_add_all ( struct i2o_controller * c )
{
int i ;
struct i2o_driver * drv ;
2007-05-24 00:58:06 +04:00
for ( i = 0 ; i < i2o_max_drivers ; i + + ) {
2005-04-17 02:20:36 +04:00
drv = i2o_drivers [ i ] ;
if ( drv )
i2o_driver_notify_controller_add ( drv , c ) ;
}
}
/**
2006-12-07 07:38:43 +03:00
* i2o_driver_notify_controller_remove_all - Send notify of removed controller
* @ c : controller that is being removed
2005-04-17 02:20:36 +04:00
*
* Send notifications to all registered drivers that a controller was
* removed .
*/
void i2o_driver_notify_controller_remove_all ( struct i2o_controller * c )
{
int i ;
struct i2o_driver * drv ;
2007-05-24 00:58:06 +04:00
for ( i = 0 ; i < i2o_max_drivers ; i + + ) {
2005-04-17 02:20:36 +04:00
drv = i2o_drivers [ i ] ;
if ( drv )
i2o_driver_notify_controller_remove ( drv , c ) ;
}
}
/**
2006-12-07 07:38:43 +03:00
* i2o_driver_notify_device_add_all - Send notify of added device
* @ i2o_dev : newly added I2O device
2005-04-17 02:20:36 +04:00
*
* Send notifications to all registered drivers that a device was added .
*/
void i2o_driver_notify_device_add_all ( struct i2o_device * i2o_dev )
{
int i ;
struct i2o_driver * drv ;
2007-05-24 00:58:06 +04:00
for ( i = 0 ; i < i2o_max_drivers ; i + + ) {
2005-04-17 02:20:36 +04:00
drv = i2o_drivers [ i ] ;
if ( drv )
i2o_driver_notify_device_add ( drv , i2o_dev ) ;
}
}
/**
2006-12-07 07:38:43 +03:00
* i2o_driver_notify_device_remove_all - Send notify of removed device
* @ i2o_dev : device that is being removed
2005-04-17 02:20:36 +04:00
*
* Send notifications to all registered drivers that a device was removed .
*/
void i2o_driver_notify_device_remove_all ( struct i2o_device * i2o_dev )
{
int i ;
struct i2o_driver * drv ;
2007-05-24 00:58:06 +04:00
for ( i = 0 ; i < i2o_max_drivers ; i + + ) {
2005-04-17 02:20:36 +04:00
drv = i2o_drivers [ i ] ;
if ( drv )
i2o_driver_notify_device_remove ( drv , i2o_dev ) ;
}
}
/**
* i2o_driver_init - initialize I2O drivers ( OSMs )
*
* Registers the I2O bus and allocate memory for the array of OSMs .
*
* Returns 0 on success or negative error code on failure .
*/
int __init i2o_driver_init ( void )
{
int rc = 0 ;
spin_lock_init ( & i2o_drivers_lock ) ;
2007-05-24 00:58:07 +04:00
if ( ( i2o_max_drivers < 2 ) | | ( i2o_max_drivers > 64 ) ) {
osm_warn ( " max_drivers set to %d, but must be >=2 and <= 64 \n " ,
i2o_max_drivers ) ;
2005-04-17 02:20:36 +04:00
i2o_max_drivers = I2O_MAX_DRIVERS ;
}
2005-06-24 09:02:14 +04:00
osm_info ( " max drivers = %d \n " , i2o_max_drivers ) ;
2005-04-17 02:20:36 +04:00
i2o_drivers =
2007-05-24 00:58:06 +04:00
kcalloc ( i2o_max_drivers , sizeof ( * i2o_drivers ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! i2o_drivers )
return - ENOMEM ;
rc = bus_register ( & i2o_bus_type ) ;
if ( rc < 0 )
kfree ( i2o_drivers ) ;
return rc ;
} ;
/**
* i2o_driver_exit - clean up I2O drivers ( OSMs )
*
2006-12-07 07:38:43 +03:00
* Unregisters the I2O bus and frees driver array .
2005-04-17 02:20:36 +04:00
*/
2006-12-13 11:33:53 +03:00
void i2o_driver_exit ( void )
2005-04-17 02:20:36 +04:00
{
bus_unregister ( & i2o_bus_type ) ;
kfree ( i2o_drivers ) ;
} ;
EXPORT_SYMBOL ( i2o_driver_register ) ;
EXPORT_SYMBOL ( i2o_driver_unregister ) ;
EXPORT_SYMBOL ( i2o_driver_notify_controller_add_all ) ;
EXPORT_SYMBOL ( i2o_driver_notify_controller_remove_all ) ;
EXPORT_SYMBOL ( i2o_driver_notify_device_add_all ) ;
EXPORT_SYMBOL ( i2o_driver_notify_device_remove_all ) ;