2005-04-17 02:20:36 +04:00
/*
* File . . . . . . . . . . . : linux / drivers / s390 / block / dasd_devmap . c
* Author ( s ) . . . . . . : Holger Smolinski < Holger . Smolinski @ de . ibm . com >
* Horst Hummel < Horst . Hummel @ de . ibm . com >
* Carsten Otte < Cotte @ de . ibm . com >
* Martin Schwidefsky < schwidefsky @ de . ibm . com >
* Bugreports . to . . : < Linux390 @ de . ibm . com >
* ( C ) IBM Corporation , IBM Deutschland Entwicklung GmbH , 1999 - 2001
*
* Device mapping and dasd = parameter parsing functions . All devmap
* functions may not be called from interrupt context . In particular
* dasd_get_device is a no - no from interrupt context .
*
2005-05-01 19:58:59 +04:00
* $ Revision : 1.40 $
2005-04-17 02:20:36 +04:00
*/
# include <linux/config.h>
# include <linux/ctype.h>
# include <linux/init.h>
# include <asm/debug.h>
# include <asm/uaccess.h>
/* This is ugly... */
# define PRINTK_HEADER "dasd_devmap:"
# include "dasd_int.h"
kmem_cache_t * dasd_page_cache ;
EXPORT_SYMBOL ( dasd_page_cache ) ;
/*
* dasd_devmap_t is used to store the features and the relation
* between device number and device index . To find a dasd_devmap_t
* that corresponds to a device number of a device index each
* dasd_devmap_t is added to two linked lists , one to search by
* the device number and one to search by the device index . As
* soon as big minor numbers are available the device index list
* can be removed since the device number will then be identical
* to the device index .
*/
struct dasd_devmap {
struct list_head list ;
char bus_id [ BUS_ID_SIZE ] ;
unsigned int devindex ;
unsigned short features ;
struct dasd_device * device ;
} ;
/*
* Parameter parsing functions for dasd = parameter . The syntax is :
* < devno > : ( 0 x ) ? [ 0 - 9 a - fA - F ] +
* < busid > : [ 0 - 0 a - f ] \ . [ 0 - 9 a - f ] \ . ( 0 x ) ? [ 0 - 9 a - fA - F ] +
* < feature > : ro
* < feature_list > : \ ( < feature > ( : < feature > ) * \ )
* < devno - range > : < devno > ( - < devno > ) ? < feature_list > ?
* < busid - range > : < busid > ( - < busid > ) ? < feature_list > ?
* < devices > : < devno - range > | < busid - range >
* < dasd_module > : dasd_diag_mod | dasd_eckd_mod | dasd_fba_mod
*
* < dasd > : autodetect | probeonly | < devices > ( , < devices > ) *
*/
int dasd_probeonly = 0 ; /* is true, when probeonly mode is active */
int dasd_autodetect = 0 ; /* is true, when autodetection is active */
/*
* char * dasd [ ] is intended to hold the ranges supplied by the dasd = statement
* it is named ' dasd ' to directly be filled by insmod with the comma separated
* strings when running as a module .
*/
static char * dasd [ 256 ] ;
/*
* Single spinlock to protect devmap structures and lists .
*/
static DEFINE_SPINLOCK ( dasd_devmap_lock ) ;
/*
* Hash lists for devmap structures .
*/
static struct list_head dasd_hashlists [ 256 ] ;
int dasd_max_devindex ;
static struct dasd_devmap * dasd_add_busid ( char * , int ) ;
static inline int
dasd_hash_busid ( char * bus_id )
{
int hash , i ;
hash = 0 ;
for ( i = 0 ; ( i < BUS_ID_SIZE ) & & * bus_id ; i + + , bus_id + + )
hash + = * bus_id ;
return hash & 0xff ;
}
# ifndef MODULE
/*
* The parameter parsing functions for builtin - drivers are called
* before kmalloc works . Store the pointers to the parameters strings
* into dasd [ ] for later processing .
*/
static int __init
dasd_call_setup ( char * str )
{
static int count = 0 ;
if ( count < 256 )
dasd [ count + + ] = str ;
return 1 ;
}
__setup ( " dasd= " , dasd_call_setup ) ;
# endif /* #ifndef MODULE */
/*
* Read a device busid / devno from a string .
*/
static inline int
dasd_busid ( char * * str , int * id0 , int * id1 , int * devno )
{
int val , old_style ;
/* check for leading '0x' */
old_style = 0 ;
if ( ( * str ) [ 0 ] = = ' 0 ' & & ( * str ) [ 1 ] = = ' x ' ) {
* str + = 2 ;
old_style = 1 ;
}
if ( ! isxdigit ( ( * str ) [ 0 ] ) ) /* We require at least one hex digit */
return - EINVAL ;
val = simple_strtoul ( * str , str , 16 ) ;
if ( old_style | | ( * str ) [ 0 ] ! = ' . ' ) {
* id0 = * id1 = 0 ;
if ( val < 0 | | val > 0xffff )
return - EINVAL ;
* devno = val ;
return 0 ;
}
/* New style x.y.z busid */
if ( val < 0 | | val > 0xff )
return - EINVAL ;
* id0 = val ;
( * str ) + + ;
if ( ! isxdigit ( ( * str ) [ 0 ] ) ) /* We require at least one hex digit */
return - EINVAL ;
val = simple_strtoul ( * str , str , 16 ) ;
if ( val < 0 | | val > 0xff | | ( * str ) + + [ 0 ] ! = ' . ' )
return - EINVAL ;
* id1 = val ;
if ( ! isxdigit ( ( * str ) [ 0 ] ) ) /* We require at least one hex digit */
return - EINVAL ;
val = simple_strtoul ( * str , str , 16 ) ;
if ( val < 0 | | val > 0xffff )
return - EINVAL ;
* devno = val ;
return 0 ;
}
/*
* Read colon separated list of dasd features . Currently there is
* only one : " ro " for read - only devices . The default feature set
* is empty ( value 0 ) .
*/
static inline int
dasd_feature_list ( char * str , char * * endp )
{
int features , len , rc ;
rc = 0 ;
if ( * str ! = ' ( ' ) {
* endp = str ;
return DASD_FEATURE_DEFAULT ;
}
str + + ;
features = 0 ;
while ( 1 ) {
for ( len = 0 ;
str [ len ] & & str [ len ] ! = ' : ' & & str [ len ] ! = ' ) ' ; len + + ) ;
if ( len = = 2 & & ! strncmp ( str , " ro " , 2 ) )
features | = DASD_FEATURE_READONLY ;
else if ( len = = 4 & & ! strncmp ( str , " diag " , 4 ) )
features | = DASD_FEATURE_USEDIAG ;
else {
MESSAGE ( KERN_WARNING ,
" unsupported feature: %*s, "
" ignoring setting " , len , str ) ;
rc = - EINVAL ;
}
str + = len ;
if ( * str ! = ' : ' )
break ;
str + + ;
}
if ( * str ! = ' ) ' ) {
MESSAGE ( KERN_WARNING , " %s " ,
" missing ')' in dasd parameter string \n " ) ;
rc = - EINVAL ;
} else
str + + ;
* endp = str ;
if ( rc ! = 0 )
return rc ;
return features ;
}
/*
* Try to match the first element on the comma separated parse string
* with one of the known keywords . If a keyword is found , take the approprate
* action and return a pointer to the residual string . If the first element
* could not be matched to any keyword then return an error code .
*/
static char *
dasd_parse_keyword ( char * parsestring ) {
char * nextcomma , * residual_str ;
int length ;
nextcomma = strchr ( parsestring , ' , ' ) ;
if ( nextcomma ) {
length = nextcomma - parsestring ;
residual_str = nextcomma + 1 ;
} else {
length = strlen ( parsestring ) ;
residual_str = parsestring + length ;
}
if ( strncmp ( " autodetect " , parsestring , length ) = = 0 ) {
dasd_autodetect = 1 ;
MESSAGE ( KERN_INFO , " %s " ,
" turning to autodetection mode " ) ;
return residual_str ;
}
if ( strncmp ( " probeonly " , parsestring , length ) = = 0 ) {
dasd_probeonly = 1 ;
MESSAGE ( KERN_INFO , " %s " ,
" turning to probeonly mode " ) ;
return residual_str ;
}
if ( strncmp ( " fixedbuffers " , parsestring , length ) = = 0 ) {
if ( dasd_page_cache )
return residual_str ;
dasd_page_cache =
kmem_cache_create ( " dasd_page_cache " , PAGE_SIZE , 0 ,
SLAB_CACHE_DMA , NULL , NULL ) ;
if ( ! dasd_page_cache )
MESSAGE ( KERN_WARNING , " %s " , " Failed to create slab, "
" fixed buffer mode disabled. " ) ;
else
MESSAGE ( KERN_INFO , " %s " ,
" turning on fixed buffer mode " ) ;
return residual_str ;
}
return ERR_PTR ( - EINVAL ) ;
}
/*
* Try to interprete the first element on the comma separated parse string
* as a device number or a range of devices . If the interpretation is
* successfull , create the matching dasd_devmap entries and return a pointer
* to the residual string .
* If interpretation fails or in case of an error , return an error code .
*/
static char *
dasd_parse_range ( char * parsestring ) {
struct dasd_devmap * devmap ;
int from , from_id0 , from_id1 ;
int to , to_id0 , to_id1 ;
int features , rc ;
char bus_id [ BUS_ID_SIZE + 1 ] , * str ;
str = parsestring ;
rc = dasd_busid ( & str , & from_id0 , & from_id1 , & from ) ;
if ( rc = = 0 ) {
to = from ;
to_id0 = from_id0 ;
to_id1 = from_id1 ;
if ( * str = = ' - ' ) {
str + + ;
rc = dasd_busid ( & str , & to_id0 , & to_id1 , & to ) ;
}
}
if ( rc = = 0 & &
( from_id0 ! = to_id0 | | from_id1 ! = to_id1 | | from > to ) )
rc = - EINVAL ;
if ( rc ) {
MESSAGE ( KERN_ERR , " Invalid device range %s " , parsestring ) ;
return ERR_PTR ( rc ) ;
}
features = dasd_feature_list ( str , & str ) ;
if ( features < 0 )
return ERR_PTR ( - EINVAL ) ;
while ( from < = to ) {
sprintf ( bus_id , " %01x.%01x.%04x " ,
from_id0 , from_id1 , from + + ) ;
devmap = dasd_add_busid ( bus_id , features ) ;
if ( IS_ERR ( devmap ) )
return ( char * ) devmap ;
}
if ( * str = = ' , ' )
return str + 1 ;
if ( * str = = ' \0 ' )
return str ;
MESSAGE ( KERN_WARNING ,
" junk at end of dasd parameter string: %s \n " , str ) ;
return ERR_PTR ( - EINVAL ) ;
}
static inline char *
dasd_parse_next_element ( char * parsestring ) {
char * residual_str ;
residual_str = dasd_parse_keyword ( parsestring ) ;
if ( ! IS_ERR ( residual_str ) )
return residual_str ;
residual_str = dasd_parse_range ( parsestring ) ;
return residual_str ;
}
/*
* Parse parameters stored in dasd [ ]
* The ' dasd = . . . ' parameter allows to specify a comma separated list of
* keywords and device ranges . When the dasd driver is build into the kernel ,
* the complete list will be stored as one element of the dasd [ ] array .
* When the dasd driver is build as a module , then the list is broken into
* it ' s elements and each dasd [ ] entry contains one element .
*/
int
dasd_parse ( void )
{
int rc , i ;
char * parsestring ;
rc = 0 ;
for ( i = 0 ; i < 256 ; i + + ) {
if ( dasd [ i ] = = NULL )
break ;
parsestring = dasd [ i ] ;
/* loop over the comma separated list in the parsestring */
while ( * parsestring ) {
parsestring = dasd_parse_next_element ( parsestring ) ;
if ( IS_ERR ( parsestring ) ) {
rc = PTR_ERR ( parsestring ) ;
break ;
}
}
if ( rc ) {
DBF_EVENT ( DBF_ALERT , " %s " , " invalid range found " ) ;
break ;
}
}
return rc ;
}
/*
* Add a devmap for the device specified by busid . It is possible that
* the devmap already exists ( dasd = parameter ) . The order of the devices
* added through this function will define the kdevs for the individual
* devices .
*/
static struct dasd_devmap *
dasd_add_busid ( char * bus_id , int features )
{
struct dasd_devmap * devmap , * new , * tmp ;
int hash ;
new = ( struct dasd_devmap * )
kmalloc ( sizeof ( struct dasd_devmap ) , GFP_KERNEL ) ;
if ( ! new )
return ERR_PTR ( - ENOMEM ) ;
spin_lock ( & dasd_devmap_lock ) ;
devmap = 0 ;
hash = dasd_hash_busid ( bus_id ) ;
list_for_each_entry ( tmp , & dasd_hashlists [ hash ] , list )
if ( strncmp ( tmp - > bus_id , bus_id , BUS_ID_SIZE ) = = 0 ) {
devmap = tmp ;
break ;
}
if ( ! devmap ) {
/* This bus_id is new. */
new - > devindex = dasd_max_devindex + + ;
strncpy ( new - > bus_id , bus_id , BUS_ID_SIZE ) ;
new - > features = features ;
new - > device = 0 ;
list_add ( & new - > list , & dasd_hashlists [ hash ] ) ;
devmap = new ;
new = 0 ;
}
spin_unlock ( & dasd_devmap_lock ) ;
if ( new )
kfree ( new ) ;
return devmap ;
}
/*
* Find devmap for device with given bus_id .
*/
static struct dasd_devmap *
dasd_find_busid ( char * bus_id )
{
struct dasd_devmap * devmap , * tmp ;
int hash ;
spin_lock ( & dasd_devmap_lock ) ;
devmap = ERR_PTR ( - ENODEV ) ;
hash = dasd_hash_busid ( bus_id ) ;
list_for_each_entry ( tmp , & dasd_hashlists [ hash ] , list ) {
if ( strncmp ( tmp - > bus_id , bus_id , BUS_ID_SIZE ) = = 0 ) {
devmap = tmp ;
break ;
}
}
spin_unlock ( & dasd_devmap_lock ) ;
return devmap ;
}
/*
* Check if busid has been added to the list of dasd ranges .
*/
int
dasd_busid_known ( char * bus_id )
{
return IS_ERR ( dasd_find_busid ( bus_id ) ) ? - ENOENT : 0 ;
}
/*
* Forget all about the device numbers added so far .
* This may only be called at module unload or system shutdown .
*/
static void
dasd_forget_ranges ( void )
{
struct dasd_devmap * devmap , * n ;
int i ;
spin_lock ( & dasd_devmap_lock ) ;
for ( i = 0 ; i < 256 ; i + + ) {
list_for_each_entry_safe ( devmap , n , & dasd_hashlists [ i ] , list ) {
if ( devmap - > device ! = NULL )
BUG ( ) ;
list_del ( & devmap - > list ) ;
kfree ( devmap ) ;
}
}
spin_unlock ( & dasd_devmap_lock ) ;
}
/*
* Find the device struct by its device index .
*/
struct dasd_device *
dasd_device_from_devindex ( int devindex )
{
struct dasd_devmap * devmap , * tmp ;
struct dasd_device * device ;
int i ;
spin_lock ( & dasd_devmap_lock ) ;
devmap = 0 ;
for ( i = 0 ; ( i < 256 ) & & ! devmap ; i + + )
list_for_each_entry ( tmp , & dasd_hashlists [ i ] , list )
if ( tmp - > devindex = = devindex ) {
/* Found the devmap for the device. */
devmap = tmp ;
break ;
}
if ( devmap & & devmap - > device ) {
device = devmap - > device ;
dasd_get_device ( device ) ;
} else
device = ERR_PTR ( - ENODEV ) ;
spin_unlock ( & dasd_devmap_lock ) ;
return device ;
}
/*
* Return devmap for cdev . If no devmap exists yet , create one and
* connect it to the cdev .
*/
static struct dasd_devmap *
dasd_devmap_from_cdev ( struct ccw_device * cdev )
{
struct dasd_devmap * devmap ;
devmap = dasd_find_busid ( cdev - > dev . bus_id ) ;
if ( IS_ERR ( devmap ) )
devmap = dasd_add_busid ( cdev - > dev . bus_id ,
DASD_FEATURE_DEFAULT ) ;
return devmap ;
}
/*
* Create a dasd device structure for cdev .
*/
struct dasd_device *
dasd_create_device ( struct ccw_device * cdev )
{
struct dasd_devmap * devmap ;
struct dasd_device * device ;
int rc ;
devmap = dasd_devmap_from_cdev ( cdev ) ;
if ( IS_ERR ( devmap ) )
return ( void * ) devmap ;
cdev - > dev . driver_data = devmap ;
device = dasd_alloc_device ( ) ;
if ( IS_ERR ( device ) )
return device ;
atomic_set ( & device - > ref_count , 2 ) ;
spin_lock ( & dasd_devmap_lock ) ;
if ( ! devmap - > device ) {
devmap - > device = device ;
device - > devindex = devmap - > devindex ;
get_device ( & cdev - > dev ) ;
device - > cdev = cdev ;
rc = 0 ;
} else
/* Someone else was faster. */
rc = - EBUSY ;
spin_unlock ( & dasd_devmap_lock ) ;
if ( rc ) {
dasd_free_device ( device ) ;
return ERR_PTR ( rc ) ;
}
return device ;
}
/*
* Wait queue for dasd_delete_device waits .
*/
static DECLARE_WAIT_QUEUE_HEAD ( dasd_delete_wq ) ;
/*
* Remove a dasd device structure . The passed referenced
* is destroyed .
*/
void
dasd_delete_device ( struct dasd_device * device )
{
struct ccw_device * cdev ;
struct dasd_devmap * devmap ;
/* First remove device pointer from devmap. */
devmap = dasd_find_busid ( device - > cdev - > dev . bus_id ) ;
if ( IS_ERR ( devmap ) )
BUG ( ) ;
spin_lock ( & dasd_devmap_lock ) ;
if ( devmap - > device ! = device ) {
spin_unlock ( & dasd_devmap_lock ) ;
dasd_put_device ( device ) ;
return ;
}
devmap - > device = NULL ;
spin_unlock ( & dasd_devmap_lock ) ;
/* Drop ref_count by 2, one for the devmap reference and
* one for the passed reference . */
atomic_sub ( 2 , & device - > ref_count ) ;
/* Wait for reference counter to drop to zero. */
wait_event ( dasd_delete_wq , atomic_read ( & device - > ref_count ) = = 0 ) ;
/* Disconnect dasd_device structure from ccw_device structure. */
cdev = device - > cdev ;
device - > cdev = NULL ;
/* Disconnect dasd_devmap structure from ccw_device structure. */
cdev - > dev . driver_data = NULL ;
/* Put ccw_device structure. */
put_device ( & cdev - > dev ) ;
/* Now the device structure can be freed. */
dasd_free_device ( device ) ;
}
/*
* Reference counter dropped to zero . Wake up waiter
* in dasd_delete_device .
*/
void
dasd_put_device_wake ( struct dasd_device * device )
{
wake_up ( & dasd_delete_wq ) ;
}
/*
* Return dasd_device structure associated with cdev .
*/
struct dasd_device *
dasd_device_from_cdev ( struct ccw_device * cdev )
{
struct dasd_devmap * devmap ;
struct dasd_device * device ;
device = ERR_PTR ( - ENODEV ) ;
spin_lock ( & dasd_devmap_lock ) ;
devmap = cdev - > dev . driver_data ;
if ( devmap & & devmap - > device ) {
device = devmap - > device ;
dasd_get_device ( device ) ;
}
spin_unlock ( & dasd_devmap_lock ) ;
return device ;
}
/*
* SECTION : files in sysfs
*/
/*
* readonly controls the readonly status of a dasd
*/
static ssize_t
2005-05-17 14:42:58 +04:00
dasd_ro_show ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct dasd_devmap * devmap ;
int ro_flag ;
devmap = dasd_find_busid ( dev - > bus_id ) ;
if ( ! IS_ERR ( devmap ) )
ro_flag = ( devmap - > features & DASD_FEATURE_READONLY ) ! = 0 ;
else
ro_flag = ( DASD_FEATURE_DEFAULT & DASD_FEATURE_READONLY ) ! = 0 ;
return snprintf ( buf , PAGE_SIZE , ro_flag ? " 1 \n " : " 0 \n " ) ;
}
static ssize_t
2005-05-17 14:42:58 +04:00
dasd_ro_store ( struct device * dev , struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
struct dasd_devmap * devmap ;
int ro_flag ;
devmap = dasd_devmap_from_cdev ( to_ccwdev ( dev ) ) ;
if ( IS_ERR ( devmap ) )
return PTR_ERR ( devmap ) ;
ro_flag = buf [ 0 ] = = ' 1 ' ;
spin_lock ( & dasd_devmap_lock ) ;
if ( ro_flag )
devmap - > features | = DASD_FEATURE_READONLY ;
else
devmap - > features & = ~ DASD_FEATURE_READONLY ;
2005-05-01 19:58:59 +04:00
if ( devmap - > device & & devmap - > device - > gdp )
set_disk_ro ( devmap - > device - > gdp , ro_flag ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & dasd_devmap_lock ) ;
return count ;
}
static DEVICE_ATTR ( readonly , 0644 , dasd_ro_show , dasd_ro_store ) ;
/*
* use_diag controls whether the driver should use diag rather than ssch
* to talk to the device
*/
static ssize_t
2005-05-17 14:42:58 +04:00
dasd_use_diag_show ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct dasd_devmap * devmap ;
int use_diag ;
devmap = dasd_find_busid ( dev - > bus_id ) ;
if ( ! IS_ERR ( devmap ) )
use_diag = ( devmap - > features & DASD_FEATURE_USEDIAG ) ! = 0 ;
else
use_diag = ( DASD_FEATURE_DEFAULT & DASD_FEATURE_USEDIAG ) ! = 0 ;
return sprintf ( buf , use_diag ? " 1 \n " : " 0 \n " ) ;
}
static ssize_t
2005-05-17 14:42:58 +04:00
dasd_use_diag_store ( struct device * dev , struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
struct dasd_devmap * devmap ;
ssize_t rc ;
int use_diag ;
devmap = dasd_devmap_from_cdev ( to_ccwdev ( dev ) ) ;
if ( IS_ERR ( devmap ) )
return PTR_ERR ( devmap ) ;
use_diag = buf [ 0 ] = = ' 1 ' ;
spin_lock ( & dasd_devmap_lock ) ;
/* Changing diag discipline flag is only allowed in offline state. */
rc = count ;
if ( ! devmap - > device ) {
if ( use_diag )
devmap - > features | = DASD_FEATURE_USEDIAG ;
else
devmap - > features & = ~ DASD_FEATURE_USEDIAG ;
} else
rc = - EPERM ;
spin_unlock ( & dasd_devmap_lock ) ;
return rc ;
}
static
DEVICE_ATTR ( use_diag , 0644 , dasd_use_diag_show , dasd_use_diag_store ) ;
static ssize_t
2005-05-17 14:42:58 +04:00
dasd_discipline_show ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct dasd_devmap * devmap ;
char * dname ;
spin_lock ( & dasd_devmap_lock ) ;
dname = " none " ;
devmap = dev - > driver_data ;
if ( devmap & & devmap - > device & & devmap - > device - > discipline )
dname = devmap - > device - > discipline - > name ;
spin_unlock ( & dasd_devmap_lock ) ;
return snprintf ( buf , PAGE_SIZE , " %s \n " , dname ) ;
}
static DEVICE_ATTR ( discipline , 0444 , dasd_discipline_show , NULL ) ;
static struct attribute * dasd_attrs [ ] = {
& dev_attr_readonly . attr ,
& dev_attr_discipline . attr ,
& dev_attr_use_diag . attr ,
NULL ,
} ;
static struct attribute_group dasd_attr_group = {
. attrs = dasd_attrs ,
} ;
2005-05-01 19:58:59 +04:00
/*
* Return value of the specified feature .
*/
int
dasd_get_feature ( struct ccw_device * cdev , int feature )
{
struct dasd_devmap * devmap ;
devmap = dasd_find_busid ( cdev - > dev . bus_id ) ;
if ( IS_ERR ( devmap ) )
return ( int ) PTR_ERR ( devmap ) ;
return ( ( devmap - > features & feature ) ! = 0 ) ;
}
/*
* Set / reset given feature .
* Flag indicates wether to set ( ! = 0 ) or the reset ( = 0 ) the feature .
*/
int
dasd_set_feature ( struct ccw_device * cdev , int feature , int flag )
{
struct dasd_devmap * devmap ;
devmap = dasd_find_busid ( cdev - > dev . bus_id ) ;
if ( IS_ERR ( devmap ) )
return ( int ) PTR_ERR ( devmap ) ;
spin_lock ( & dasd_devmap_lock ) ;
if ( flag )
devmap - > features | = feature ;
else
devmap - > features & = ~ feature ;
spin_unlock ( & dasd_devmap_lock ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
int
dasd_add_sysfs_files ( struct ccw_device * cdev )
{
return sysfs_create_group ( & cdev - > dev . kobj , & dasd_attr_group ) ;
}
void
dasd_remove_sysfs_files ( struct ccw_device * cdev )
{
sysfs_remove_group ( & cdev - > dev . kobj , & dasd_attr_group ) ;
}
int
dasd_devmap_init ( void )
{
int i ;
/* Initialize devmap structures. */
dasd_max_devindex = 0 ;
for ( i = 0 ; i < 256 ; i + + )
INIT_LIST_HEAD ( & dasd_hashlists [ i ] ) ;
return 0 ;
}
void
dasd_devmap_exit ( void )
{
dasd_forget_ranges ( ) ;
}