2005-09-09 18:22:50 +04:00
/*
2006-02-16 15:31:47 +03:00
* Copyright ( C ) 2005 - 2006 Dell Inc .
2005-09-09 18:22:50 +04:00
* Released under GPL v2 .
*
* Serial Attached SCSI ( SAS ) transport class .
*
* The SAS transport class contains common code to deal with SAS HBAs ,
* an aproximated representation of SAS topologies in the driver model ,
* and various sysfs attributes to expose these topologies and managment
* interfaces to userspace .
*
* In addition to the basic SCSI core objects this transport class
* introduces two additional intermediate objects : The SAS PHY
* as represented by struct sas_phy defines an " outgoing " PHY on
* a SAS HBA or Expander , and the SAS remote PHY represented by
* struct sas_rphy defines an " incoming " PHY on a SAS Expander or
* end device . Note that this is purely a software concept , the
* underlying hardware for a PHY and a remote PHY is the exactly
* the same .
*
* There is no concept of a SAS port in this code , users can see
* what PHYs form a wide port based on the port_identifier attribute ,
* which is the same for all PHYs in a port .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/err.h>
2005-11-07 11:59:43 +03:00
# include <linux/slab.h>
# include <linux/string.h>
2005-09-09 18:22:50 +04:00
2006-01-13 21:04:00 +03:00
# include <scsi/scsi.h>
2005-09-09 18:22:50 +04:00
# include <scsi/scsi_device.h>
# include <scsi/scsi_host.h>
# include <scsi/scsi_transport.h>
# include <scsi/scsi_transport_sas.h>
2006-03-28 02:45:34 +04:00
# include "scsi_sas_internal.h"
2005-09-09 18:22:50 +04:00
struct sas_host_attrs {
struct list_head rphy_list ;
2006-01-13 21:04:00 +03:00
struct mutex lock ;
2005-09-09 18:22:50 +04:00
u32 next_target_id ;
2006-03-13 22:50:04 +03:00
u32 next_expander_id ;
2006-07-02 20:10:18 +04:00
int next_port_id ;
2005-09-09 18:22:50 +04:00
} ;
# define to_sas_host_attrs(host) ((struct sas_host_attrs *)(host)->shost_data)
/*
* Hack to allow attributes of the same name in different objects .
*/
# define SAS_CLASS_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \
struct class_device_attribute class_device_attr_ # # _prefix # # _ # # _name = \
__ATTR ( _name , _mode , _show , _store )
/*
* Pretty printing helpers
*/
# define sas_bitfield_name_match(title, table) \
static ssize_t \
get_sas_ # # title # # _names ( u32 table_key , char * buf ) \
{ \
char * prefix = " " ; \
ssize_t len = 0 ; \
int i ; \
\
2006-06-09 09:23:48 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( table ) ; i + + ) { \
2005-09-09 18:22:50 +04:00
if ( table [ i ] . value & table_key ) { \
len + = sprintf ( buf + len , " %s%s " , \
prefix , table [ i ] . name ) ; \
prefix = " , " ; \
} \
} \
len + = sprintf ( buf + len , " \n " ) ; \
return len ; \
}
# define sas_bitfield_name_search(title, table) \
static ssize_t \
get_sas_ # # title # # _names ( u32 table_key , char * buf ) \
{ \
ssize_t len = 0 ; \
int i ; \
\
2006-06-09 09:23:48 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( table ) ; i + + ) { \
2005-09-09 18:22:50 +04:00
if ( table [ i ] . value = = table_key ) { \
len + = sprintf ( buf + len , " %s " , \
table [ i ] . name ) ; \
break ; \
} \
} \
len + = sprintf ( buf + len , " \n " ) ; \
return len ; \
}
static struct {
u32 value ;
char * name ;
} sas_device_type_names [ ] = {
{ SAS_PHY_UNUSED , " unused " } ,
{ SAS_END_DEVICE , " end device " } ,
{ SAS_EDGE_EXPANDER_DEVICE , " edge expander " } ,
{ SAS_FANOUT_EXPANDER_DEVICE , " fanout expander " } ,
} ;
sas_bitfield_name_search ( device_type , sas_device_type_names )
static struct {
u32 value ;
char * name ;
} sas_protocol_names [ ] = {
{ SAS_PROTOCOL_SATA , " sata " } ,
{ SAS_PROTOCOL_SMP , " smp " } ,
{ SAS_PROTOCOL_STP , " stp " } ,
{ SAS_PROTOCOL_SSP , " ssp " } ,
} ;
sas_bitfield_name_match ( protocol , sas_protocol_names )
static struct {
u32 value ;
char * name ;
} sas_linkspeed_names [ ] = {
{ SAS_LINK_RATE_UNKNOWN , " Unknown " } ,
{ SAS_PHY_DISABLED , " Phy disabled " } ,
{ SAS_LINK_RATE_FAILED , " Link Rate failed " } ,
{ SAS_SATA_SPINUP_HOLD , " Spin-up hold " } ,
{ SAS_LINK_RATE_1_5_GBPS , " 1.5 Gbit " } ,
{ SAS_LINK_RATE_3_0_GBPS , " 3.0 Gbit " } ,
2006-03-02 23:12:56 +03:00
{ SAS_LINK_RATE_6_0_GBPS , " 6.0 Gbit " } ,
2005-09-09 18:22:50 +04:00
} ;
sas_bitfield_name_search ( linkspeed , sas_linkspeed_names )
/*
* SAS host attributes
*/
2005-09-10 03:38:27 +04:00
static int sas_host_setup ( struct transport_container * tc , struct device * dev ,
struct class_device * cdev )
2005-09-09 18:22:50 +04:00
{
struct Scsi_Host * shost = dev_to_shost ( dev ) ;
struct sas_host_attrs * sas_host = to_sas_host_attrs ( shost ) ;
INIT_LIST_HEAD ( & sas_host - > rphy_list ) ;
2006-01-13 21:04:00 +03:00
mutex_init ( & sas_host - > lock ) ;
2005-09-09 18:22:50 +04:00
sas_host - > next_target_id = 0 ;
2006-03-13 22:50:04 +03:00
sas_host - > next_expander_id = 0 ;
2006-07-02 20:10:18 +04:00
sas_host - > next_port_id = 0 ;
2005-09-09 18:22:50 +04:00
return 0 ;
}
static DECLARE_TRANSPORT_CLASS ( sas_host_class ,
" sas_host " , sas_host_setup , NULL , NULL ) ;
static int sas_host_match ( struct attribute_container * cont ,
struct device * dev )
{
struct Scsi_Host * shost ;
struct sas_internal * i ;
if ( ! scsi_is_host_device ( dev ) )
return 0 ;
shost = dev_to_shost ( dev ) ;
if ( ! shost - > transportt )
return 0 ;
if ( shost - > transportt - > host_attrs . ac . class ! =
& sas_host_class . class )
return 0 ;
i = to_sas_internal ( shost - > transportt ) ;
return & i - > t . host_attrs . ac = = cont ;
}
static int do_sas_phy_delete ( struct device * dev , void * data )
{
2006-06-28 20:22:50 +04:00
int pass = ( int ) ( unsigned long ) data ;
if ( pass = = 0 & & scsi_is_sas_port ( dev ) )
sas_port_delete ( dev_to_sas_port ( dev ) ) ;
else if ( pass = = 1 & & scsi_is_sas_phy ( dev ) )
2005-09-09 18:22:50 +04:00
sas_phy_delete ( dev_to_phy ( dev ) ) ;
return 0 ;
}
2006-06-28 20:22:50 +04:00
/**
* sas_remove_children - - tear down a devices SAS data structures
* @ dev : device belonging to the sas object
*
* Removes all SAS PHYs and remote PHYs for a given object
*/
void sas_remove_children ( struct device * dev )
{
device_for_each_child ( dev , ( void * ) 0 , do_sas_phy_delete ) ;
device_for_each_child ( dev , ( void * ) 1 , do_sas_phy_delete ) ;
}
EXPORT_SYMBOL ( sas_remove_children ) ;
2005-09-09 18:22:50 +04:00
/**
* sas_remove_host - - tear down a Scsi_Host ' s SAS data structures
* @ shost : Scsi Host that is torn down
*
* Removes all SAS PHYs and remote PHYs for a given Scsi_Host .
* Must be called just before scsi_remove_host for SAS HBAs .
*/
void sas_remove_host ( struct Scsi_Host * shost )
{
2006-06-28 20:22:50 +04:00
sas_remove_children ( & shost - > shost_gendev ) ;
2005-09-09 18:22:50 +04:00
}
EXPORT_SYMBOL ( sas_remove_host ) ;
/*
2006-06-28 20:22:50 +04:00
* SAS Phy attributes
2005-09-09 18:22:50 +04:00
*/
# define sas_phy_show_simple(field, name, format_string, cast) \
static ssize_t \
show_sas_phy_ # # name ( struct class_device * cdev , char * buf ) \
{ \
struct sas_phy * phy = transport_class_to_phy ( cdev ) ; \
\
return snprintf ( buf , 20 , format_string , cast phy - > field ) ; \
}
# define sas_phy_simple_attr(field, name, format_string, type) \
sas_phy_show_simple ( field , name , format_string , ( type ) ) \
static CLASS_DEVICE_ATTR ( name , S_IRUGO , show_sas_phy_ # # name , NULL )
# define sas_phy_show_protocol(field, name) \
static ssize_t \
show_sas_phy_ # # name ( struct class_device * cdev , char * buf ) \
{ \
struct sas_phy * phy = transport_class_to_phy ( cdev ) ; \
\
if ( ! phy - > field ) \
return snprintf ( buf , 20 , " none \n " ) ; \
return get_sas_protocol_names ( phy - > field , buf ) ; \
}
# define sas_phy_protocol_attr(field, name) \
sas_phy_show_protocol ( field , name ) \
static CLASS_DEVICE_ATTR ( name , S_IRUGO , show_sas_phy_ # # name , NULL )
# define sas_phy_show_linkspeed(field) \
static ssize_t \
show_sas_phy_ # # field ( struct class_device * cdev , char * buf ) \
{ \
struct sas_phy * phy = transport_class_to_phy ( cdev ) ; \
\
return get_sas_linkspeed_names ( phy - > field , buf ) ; \
}
# define sas_phy_linkspeed_attr(field) \
sas_phy_show_linkspeed ( field ) \
static CLASS_DEVICE_ATTR ( field , S_IRUGO , show_sas_phy_ # # field , NULL )
2005-09-19 23:59:42 +04:00
# define sas_phy_show_linkerror(field) \
static ssize_t \
show_sas_phy_ # # field ( struct class_device * cdev , char * buf ) \
{ \
struct sas_phy * phy = transport_class_to_phy ( cdev ) ; \
struct Scsi_Host * shost = dev_to_shost ( phy - > dev . parent ) ; \
struct sas_internal * i = to_sas_internal ( shost - > transportt ) ; \
int error ; \
\
2005-10-19 22:01:17 +04:00
if ( ! phy - > local_attached ) \
return - EINVAL ; \
\
2006-03-03 01:01:31 +03:00
error = i - > f - > get_linkerrors ? i - > f - > get_linkerrors ( phy ) : 0 ; \
2005-09-19 23:59:42 +04:00
if ( error ) \
return error ; \
return snprintf ( buf , 20 , " %u \n " , phy - > field ) ; \
}
# define sas_phy_linkerror_attr(field) \
sas_phy_show_linkerror ( field ) \
static CLASS_DEVICE_ATTR ( field , S_IRUGO , show_sas_phy_ # # field , NULL )
2005-09-09 18:22:50 +04:00
static ssize_t
show_sas_device_type ( struct class_device * cdev , char * buf )
{
struct sas_phy * phy = transport_class_to_phy ( cdev ) ;
if ( ! phy - > identify . device_type )
return snprintf ( buf , 20 , " none \n " ) ;
return get_sas_device_type_names ( phy - > identify . device_type , buf ) ;
}
static CLASS_DEVICE_ATTR ( device_type , S_IRUGO , show_sas_device_type , NULL ) ;
2005-10-19 22:01:31 +04:00
static ssize_t do_sas_phy_reset ( struct class_device * cdev ,
size_t count , int hard_reset )
{
struct sas_phy * phy = transport_class_to_phy ( cdev ) ;
struct Scsi_Host * shost = dev_to_shost ( phy - > dev . parent ) ;
struct sas_internal * i = to_sas_internal ( shost - > transportt ) ;
int error ;
if ( ! phy - > local_attached )
return - EINVAL ;
error = i - > f - > phy_reset ( phy , hard_reset ) ;
if ( error )
return error ;
return count ;
} ;
static ssize_t store_sas_link_reset ( struct class_device * cdev ,
const char * buf , size_t count )
{
return do_sas_phy_reset ( cdev , count , 0 ) ;
}
static CLASS_DEVICE_ATTR ( link_reset , S_IWUSR , NULL , store_sas_link_reset ) ;
static ssize_t store_sas_hard_reset ( struct class_device * cdev ,
const char * buf , size_t count )
{
return do_sas_phy_reset ( cdev , count , 1 ) ;
}
static CLASS_DEVICE_ATTR ( hard_reset , S_IWUSR , NULL , store_sas_hard_reset ) ;
2005-09-09 18:22:50 +04:00
sas_phy_protocol_attr ( identify . initiator_port_protocols ,
initiator_port_protocols ) ;
sas_phy_protocol_attr ( identify . target_port_protocols ,
target_port_protocols ) ;
sas_phy_simple_attr ( identify . sas_address , sas_address , " 0x%016llx \n " ,
unsigned long long ) ;
sas_phy_simple_attr ( identify . phy_identifier , phy_identifier , " %d \n " , u8 ) ;
2006-07-02 20:10:18 +04:00
//sas_phy_simple_attr(port_identifier, port_identifier, "%d\n", int);
2005-09-09 18:22:50 +04:00
sas_phy_linkspeed_attr ( negotiated_linkrate ) ;
sas_phy_linkspeed_attr ( minimum_linkrate_hw ) ;
sas_phy_linkspeed_attr ( minimum_linkrate ) ;
sas_phy_linkspeed_attr ( maximum_linkrate_hw ) ;
sas_phy_linkspeed_attr ( maximum_linkrate ) ;
2005-09-19 23:59:42 +04:00
sas_phy_linkerror_attr ( invalid_dword_count ) ;
sas_phy_linkerror_attr ( running_disparity_error_count ) ;
sas_phy_linkerror_attr ( loss_of_dword_sync_count ) ;
sas_phy_linkerror_attr ( phy_reset_problem_count ) ;
2005-09-09 18:22:50 +04:00
static DECLARE_TRANSPORT_CLASS ( sas_phy_class ,
" sas_phy " , NULL , NULL , NULL ) ;
static int sas_phy_match ( struct attribute_container * cont , struct device * dev )
{
struct Scsi_Host * shost ;
struct sas_internal * i ;
if ( ! scsi_is_sas_phy ( dev ) )
return 0 ;
shost = dev_to_shost ( dev - > parent ) ;
if ( ! shost - > transportt )
return 0 ;
if ( shost - > transportt - > host_attrs . ac . class ! =
& sas_host_class . class )
return 0 ;
i = to_sas_internal ( shost - > transportt ) ;
return & i - > phy_attr_cont . ac = = cont ;
}
static void sas_phy_release ( struct device * dev )
{
struct sas_phy * phy = dev_to_phy ( dev ) ;
put_device ( dev - > parent ) ;
kfree ( phy ) ;
}
/**
* sas_phy_alloc - - allocates and initialize a SAS PHY structure
* @ parent : Parent device
2006-01-27 02:20:02 +03:00
* @ number : Phy index
2005-09-09 18:22:50 +04:00
*
* Allocates an SAS PHY structure . It will be added in the device tree
* below the device specified by @ parent , which has to be either a Scsi_Host
* or sas_rphy .
*
* Returns :
* SAS PHY allocated or % NULL if the allocation failed .
*/
struct sas_phy * sas_phy_alloc ( struct device * parent , int number )
{
struct Scsi_Host * shost = dev_to_shost ( parent ) ;
struct sas_phy * phy ;
2006-01-16 18:31:18 +03:00
phy = kzalloc ( sizeof ( * phy ) , GFP_KERNEL ) ;
2005-09-09 18:22:50 +04:00
if ( ! phy )
return NULL ;
phy - > number = number ;
device_initialize ( & phy - > dev ) ;
phy - > dev . parent = get_device ( parent ) ;
phy - > dev . release = sas_phy_release ;
2006-06-28 20:22:50 +04:00
INIT_LIST_HEAD ( & phy - > port_siblings ) ;
2006-03-13 22:50:04 +03:00
if ( scsi_is_sas_expander_device ( parent ) ) {
struct sas_rphy * rphy = dev_to_rphy ( parent ) ;
2006-06-28 20:22:50 +04:00
sprintf ( phy - > dev . bus_id , " phy-%d:%d:%d " , shost - > host_no ,
2006-03-13 22:50:04 +03:00
rphy - > scsi_target_id , number ) ;
} else
sprintf ( phy - > dev . bus_id , " phy-%d:%d " , shost - > host_no , number ) ;
2005-09-09 18:22:50 +04:00
transport_setup_device ( & phy - > dev ) ;
return phy ;
}
EXPORT_SYMBOL ( sas_phy_alloc ) ;
/**
* sas_phy_add - - add a SAS PHY to the device hierachy
* @ phy : The PHY to be added
*
* Publishes a SAS PHY to the rest of the system .
*/
int sas_phy_add ( struct sas_phy * phy )
{
int error ;
error = device_add ( & phy - > dev ) ;
if ( ! error ) {
transport_add_device ( & phy - > dev ) ;
transport_configure_device ( & phy - > dev ) ;
}
return error ;
}
EXPORT_SYMBOL ( sas_phy_add ) ;
/**
* sas_phy_free - - free a SAS PHY
* @ phy : SAS PHY to free
*
* Frees the specified SAS PHY .
*
* Note :
* This function must only be called on a PHY that has not
* sucessfully been added using sas_phy_add ( ) .
*/
void sas_phy_free ( struct sas_phy * phy )
{
transport_destroy_device ( & phy - > dev ) ;
2006-03-27 21:37:28 +04:00
put_device ( & phy - > dev ) ;
2005-09-09 18:22:50 +04:00
}
EXPORT_SYMBOL ( sas_phy_free ) ;
/**
* sas_phy_delete - - remove SAS PHY
* @ phy : SAS PHY to remove
*
* Removes the specified SAS PHY . If the SAS PHY has an
* associated remote PHY it is removed before .
*/
void
sas_phy_delete ( struct sas_phy * phy )
{
struct device * dev = & phy - > dev ;
2006-06-28 20:22:50 +04:00
/* this happens if the phy is still part of a port when deleted */
BUG_ON ( ! list_empty ( & phy - > port_siblings ) ) ;
2005-09-09 18:22:50 +04:00
transport_remove_device ( dev ) ;
device_del ( dev ) ;
transport_destroy_device ( dev ) ;
2006-03-27 21:37:28 +04:00
put_device ( dev ) ;
2005-09-09 18:22:50 +04:00
}
EXPORT_SYMBOL ( sas_phy_delete ) ;
/**
* scsi_is_sas_phy - - check if a struct device represents a SAS PHY
* @ dev : device to check
*
* Returns :
* % 1 if the device represents a SAS PHY , % 0 else
*/
int scsi_is_sas_phy ( const struct device * dev )
{
return dev - > release = = sas_phy_release ;
}
EXPORT_SYMBOL ( scsi_is_sas_phy ) ;
2006-06-28 20:22:50 +04:00
/*
* SAS Port attributes
*/
# define sas_port_show_simple(field, name, format_string, cast) \
static ssize_t \
show_sas_port_ # # name ( struct class_device * cdev , char * buf ) \
{ \
struct sas_port * port = transport_class_to_sas_port ( cdev ) ; \
\
return snprintf ( buf , 20 , format_string , cast port - > field ) ; \
}
# define sas_port_simple_attr(field, name, format_string, type) \
sas_port_show_simple ( field , name , format_string , ( type ) ) \
static CLASS_DEVICE_ATTR ( name , S_IRUGO , show_sas_port_ # # name , NULL )
sas_port_simple_attr ( num_phys , num_phys , " %d \n " , int ) ;
static DECLARE_TRANSPORT_CLASS ( sas_port_class ,
" sas_port " , NULL , NULL , NULL ) ;
static int sas_port_match ( struct attribute_container * cont , struct device * dev )
{
struct Scsi_Host * shost ;
struct sas_internal * i ;
if ( ! scsi_is_sas_port ( dev ) )
return 0 ;
shost = dev_to_shost ( dev - > parent ) ;
if ( ! shost - > transportt )
return 0 ;
if ( shost - > transportt - > host_attrs . ac . class ! =
& sas_host_class . class )
return 0 ;
i = to_sas_internal ( shost - > transportt ) ;
return & i - > port_attr_cont . ac = = cont ;
}
static void sas_port_release ( struct device * dev )
{
struct sas_port * port = dev_to_sas_port ( dev ) ;
BUG_ON ( ! list_empty ( & port - > phy_list ) ) ;
put_device ( dev - > parent ) ;
kfree ( port ) ;
}
static void sas_port_create_link ( struct sas_port * port ,
struct sas_phy * phy )
{
sysfs_create_link ( & port - > dev . kobj , & phy - > dev . kobj , phy - > dev . bus_id ) ;
sysfs_create_link ( & phy - > dev . kobj , & port - > dev . kobj , " port " ) ;
}
static void sas_port_delete_link ( struct sas_port * port ,
struct sas_phy * phy )
{
sysfs_remove_link ( & port - > dev . kobj , phy - > dev . bus_id ) ;
sysfs_remove_link ( & phy - > dev . kobj , " port " ) ;
}
/** sas_port_alloc - allocate and initialize a SAS port structure
*
* @ parent : parent device
* @ port_id : port number
*
* Allocates a SAS port structure . It will be added to the device tree
* below the device specified by @ parent which must be either a Scsi_Host
* or a sas_expander_device .
*
* Returns % NULL on error
*/
struct sas_port * sas_port_alloc ( struct device * parent , int port_id )
{
struct Scsi_Host * shost = dev_to_shost ( parent ) ;
struct sas_port * port ;
port = kzalloc ( sizeof ( * port ) , GFP_KERNEL ) ;
if ( ! port )
return NULL ;
port - > port_identifier = port_id ;
device_initialize ( & port - > dev ) ;
port - > dev . parent = get_device ( parent ) ;
port - > dev . release = sas_port_release ;
mutex_init ( & port - > phy_list_mutex ) ;
INIT_LIST_HEAD ( & port - > phy_list ) ;
if ( scsi_is_sas_expander_device ( parent ) ) {
struct sas_rphy * rphy = dev_to_rphy ( parent ) ;
sprintf ( port - > dev . bus_id , " port-%d:%d:%d " , shost - > host_no ,
rphy - > scsi_target_id , port - > port_identifier ) ;
} else
sprintf ( port - > dev . bus_id , " port-%d:%d " , shost - > host_no ,
port - > port_identifier ) ;
transport_setup_device ( & port - > dev ) ;
return port ;
}
EXPORT_SYMBOL ( sas_port_alloc ) ;
2006-07-02 20:10:18 +04:00
/** sas_port_alloc_num - allocate and initialize a SAS port structure
*
* @ parent : parent device
*
* Allocates a SAS port structure and a number to go with it . This
* interface is really for adapters where the port number has no
* meansing , so the sas class should manage them . It will be added to
* the device tree below the device specified by @ parent which must be
* either a Scsi_Host or a sas_expander_device .
*
* Returns % NULL on error
*/
struct sas_port * sas_port_alloc_num ( struct device * parent )
{
int index ;
struct Scsi_Host * shost = dev_to_shost ( parent ) ;
struct sas_host_attrs * sas_host = to_sas_host_attrs ( shost ) ;
/* FIXME: use idr for this eventually */
mutex_lock ( & sas_host - > lock ) ;
if ( scsi_is_sas_expander_device ( parent ) ) {
struct sas_rphy * rphy = dev_to_rphy ( parent ) ;
struct sas_expander_device * exp = rphy_to_expander_device ( rphy ) ;
index = exp - > next_port_id + + ;
} else
index = sas_host - > next_port_id + + ;
mutex_unlock ( & sas_host - > lock ) ;
return sas_port_alloc ( parent , index ) ;
}
EXPORT_SYMBOL ( sas_port_alloc_num ) ;
2006-06-28 20:22:50 +04:00
/**
* sas_port_add - add a SAS port to the device hierarchy
*
* @ port : port to be added
*
* publishes a port to the rest of the system
*/
int sas_port_add ( struct sas_port * port )
{
int error ;
/* No phys should be added until this is made visible */
BUG_ON ( ! list_empty ( & port - > phy_list ) ) ;
error = device_add ( & port - > dev ) ;
if ( error )
return error ;
transport_add_device ( & port - > dev ) ;
transport_configure_device ( & port - > dev ) ;
return 0 ;
}
EXPORT_SYMBOL ( sas_port_add ) ;
/**
* sas_port_free - - free a SAS PORT
* @ port : SAS PORT to free
*
* Frees the specified SAS PORT .
*
* Note :
* This function must only be called on a PORT that has not
* sucessfully been added using sas_port_add ( ) .
*/
void sas_port_free ( struct sas_port * port )
{
transport_destroy_device ( & port - > dev ) ;
put_device ( & port - > dev ) ;
}
EXPORT_SYMBOL ( sas_port_free ) ;
/**
* sas_port_delete - - remove SAS PORT
* @ port : SAS PORT to remove
*
* Removes the specified SAS PORT . If the SAS PORT has an
* associated phys , unlink them from the port as well .
*/
void sas_port_delete ( struct sas_port * port )
{
struct device * dev = & port - > dev ;
struct sas_phy * phy , * tmp_phy ;
if ( port - > rphy ) {
sas_rphy_delete ( port - > rphy ) ;
port - > rphy = NULL ;
}
mutex_lock ( & port - > phy_list_mutex ) ;
list_for_each_entry_safe ( phy , tmp_phy , & port - > phy_list ,
port_siblings ) {
sas_port_delete_link ( port , phy ) ;
list_del_init ( & phy - > port_siblings ) ;
}
mutex_unlock ( & port - > phy_list_mutex ) ;
2006-07-09 21:38:19 +04:00
if ( port - > is_backlink ) {
struct device * parent = port - > dev . parent ;
sysfs_remove_link ( & port - > dev . kobj , parent - > bus_id ) ;
port - > is_backlink = 0 ;
}
2006-06-28 20:22:50 +04:00
transport_remove_device ( dev ) ;
device_del ( dev ) ;
transport_destroy_device ( dev ) ;
put_device ( dev ) ;
}
EXPORT_SYMBOL ( sas_port_delete ) ;
/**
* scsi_is_sas_port - - check if a struct device represents a SAS port
* @ dev : device to check
*
* Returns :
* % 1 if the device represents a SAS Port , % 0 else
*/
int scsi_is_sas_port ( const struct device * dev )
{
return dev - > release = = sas_port_release ;
}
EXPORT_SYMBOL ( scsi_is_sas_port ) ;
/**
* sas_port_add_phy - add another phy to a port to form a wide port
* @ port : port to add the phy to
* @ phy : phy to add
*
* When a port is initially created , it is empty ( has no phys ) . All
* ports must have at least one phy to operated , and all wide ports
* must have at least two . The current code makes no difference
* between ports and wide ports , but the only object that can be
* connected to a remote device is a port , so ports must be formed on
* all devices with phys if they ' re connected to anything .
*/
void sas_port_add_phy ( struct sas_port * port , struct sas_phy * phy )
{
mutex_lock ( & port - > phy_list_mutex ) ;
if ( unlikely ( ! list_empty ( & phy - > port_siblings ) ) ) {
/* make sure we're already on this port */
struct sas_phy * tmp ;
list_for_each_entry ( tmp , & port - > phy_list , port_siblings )
if ( tmp = = phy )
break ;
/* If this trips, you added a phy that was already
* part of a different port */
if ( unlikely ( tmp ! = phy ) ) {
dev_printk ( KERN_ERR , & port - > dev , " trying to add phy %s fails: it's already part of another port \n " , phy - > dev . bus_id ) ;
BUG ( ) ;
}
} else {
sas_port_create_link ( port , phy ) ;
list_add_tail ( & phy - > port_siblings , & port - > phy_list ) ;
port - > num_phys + + ;
}
mutex_unlock ( & port - > phy_list_mutex ) ;
}
EXPORT_SYMBOL ( sas_port_add_phy ) ;
/**
* sas_port_delete_phy - remove a phy from a port or wide port
* @ port : port to remove the phy from
* @ phy : phy to remove
*
* This operation is used for tearing down ports again . It must be
* done to every port or wide port before calling sas_port_delete .
*/
void sas_port_delete_phy ( struct sas_port * port , struct sas_phy * phy )
{
mutex_lock ( & port - > phy_list_mutex ) ;
sas_port_delete_link ( port , phy ) ;
list_del_init ( & phy - > port_siblings ) ;
port - > num_phys - - ;
mutex_unlock ( & port - > phy_list_mutex ) ;
}
EXPORT_SYMBOL ( sas_port_delete_phy ) ;
2006-07-09 21:38:19 +04:00
void sas_port_mark_backlink ( struct sas_port * port )
{
struct device * parent = port - > dev . parent - > parent - > parent ;
if ( port - > is_backlink )
return ;
port - > is_backlink = 1 ;
sysfs_create_link ( & port - > dev . kobj , & parent - > kobj ,
parent - > bus_id ) ;
}
EXPORT_SYMBOL ( sas_port_mark_backlink ) ;
2005-09-09 18:22:50 +04:00
/*
* SAS remote PHY attributes .
*/
# define sas_rphy_show_simple(field, name, format_string, cast) \
static ssize_t \
show_sas_rphy_ # # name ( struct class_device * cdev , char * buf ) \
{ \
struct sas_rphy * rphy = transport_class_to_rphy ( cdev ) ; \
\
return snprintf ( buf , 20 , format_string , cast rphy - > field ) ; \
}
# define sas_rphy_simple_attr(field, name, format_string, type) \
sas_rphy_show_simple ( field , name , format_string , ( type ) ) \
static SAS_CLASS_DEVICE_ATTR ( rphy , name , S_IRUGO , \
show_sas_rphy_ # # name , NULL )
# define sas_rphy_show_protocol(field, name) \
static ssize_t \
show_sas_rphy_ # # name ( struct class_device * cdev , char * buf ) \
{ \
struct sas_rphy * rphy = transport_class_to_rphy ( cdev ) ; \
\
if ( ! rphy - > field ) \
return snprintf ( buf , 20 , " none \n " ) ; \
return get_sas_protocol_names ( rphy - > field , buf ) ; \
}
# define sas_rphy_protocol_attr(field, name) \
sas_rphy_show_protocol ( field , name ) \
static SAS_CLASS_DEVICE_ATTR ( rphy , name , S_IRUGO , \
show_sas_rphy_ # # name , NULL )
static ssize_t
show_sas_rphy_device_type ( struct class_device * cdev , char * buf )
{
struct sas_rphy * rphy = transport_class_to_rphy ( cdev ) ;
if ( ! rphy - > identify . device_type )
return snprintf ( buf , 20 , " none \n " ) ;
return get_sas_device_type_names (
rphy - > identify . device_type , buf ) ;
}
static SAS_CLASS_DEVICE_ATTR ( rphy , device_type , S_IRUGO ,
show_sas_rphy_device_type , NULL ) ;
2006-02-16 15:31:47 +03:00
static ssize_t
show_sas_rphy_enclosure_identifier ( struct class_device * cdev , char * buf )
{
struct sas_rphy * rphy = transport_class_to_rphy ( cdev ) ;
struct sas_phy * phy = dev_to_phy ( rphy - > dev . parent ) ;
struct Scsi_Host * shost = dev_to_shost ( phy - > dev . parent ) ;
struct sas_internal * i = to_sas_internal ( shost - > transportt ) ;
u64 identifier ;
int error ;
/*
* Only devices behind an expander are supported , because the
* enclosure identifier is a SMP feature .
*/
if ( phy - > local_attached )
return - EINVAL ;
error = i - > f - > get_enclosure_identifier ( rphy , & identifier ) ;
if ( error )
return error ;
return sprintf ( buf , " 0x%llx \n " , ( unsigned long long ) identifier ) ;
}
static SAS_CLASS_DEVICE_ATTR ( rphy , enclosure_identifier , S_IRUGO ,
show_sas_rphy_enclosure_identifier , NULL ) ;
static ssize_t
show_sas_rphy_bay_identifier ( struct class_device * cdev , char * buf )
{
struct sas_rphy * rphy = transport_class_to_rphy ( cdev ) ;
struct sas_phy * phy = dev_to_phy ( rphy - > dev . parent ) ;
struct Scsi_Host * shost = dev_to_shost ( phy - > dev . parent ) ;
struct sas_internal * i = to_sas_internal ( shost - > transportt ) ;
int val ;
if ( phy - > local_attached )
return - EINVAL ;
val = i - > f - > get_bay_identifier ( rphy ) ;
if ( val < 0 )
return val ;
return sprintf ( buf , " %d \n " , val ) ;
}
static SAS_CLASS_DEVICE_ATTR ( rphy , bay_identifier , S_IRUGO ,
show_sas_rphy_bay_identifier , NULL ) ;
2005-09-09 18:22:50 +04:00
sas_rphy_protocol_attr ( identify . initiator_port_protocols ,
initiator_port_protocols ) ;
sas_rphy_protocol_attr ( identify . target_port_protocols , target_port_protocols ) ;
sas_rphy_simple_attr ( identify . sas_address , sas_address , " 0x%016llx \n " ,
unsigned long long ) ;
sas_rphy_simple_attr ( identify . phy_identifier , phy_identifier , " %d \n " , u8 ) ;
2006-03-04 18:10:18 +03:00
/* only need 8 bytes of data plus header (4 or 8) */
# define BUF_SIZE 64
int sas_read_port_mode_page ( struct scsi_device * sdev )
{
char * buffer = kzalloc ( BUF_SIZE , GFP_KERNEL ) , * msdata ;
struct sas_rphy * rphy = target_to_rphy ( sdev - > sdev_target ) ;
struct sas_end_device * rdev ;
struct scsi_mode_data mode_data ;
int res , error ;
BUG_ON ( rphy - > identify . device_type ! = SAS_END_DEVICE ) ;
rdev = rphy_to_end_device ( rphy ) ;
if ( ! buffer )
return - ENOMEM ;
res = scsi_mode_sense ( sdev , 1 , 0x19 , buffer , BUF_SIZE , 30 * HZ , 3 ,
& mode_data , NULL ) ;
error = - EINVAL ;
if ( ! scsi_status_is_good ( res ) )
goto out ;
msdata = buffer + mode_data . header_length +
mode_data . block_descriptor_length ;
if ( msdata - buffer > BUF_SIZE - 8 )
goto out ;
error = 0 ;
rdev - > ready_led_meaning = msdata [ 2 ] & 0x10 ? 1 : 0 ;
rdev - > I_T_nexus_loss_timeout = ( msdata [ 4 ] < < 8 ) + msdata [ 5 ] ;
rdev - > initiator_response_timeout = ( msdata [ 6 ] < < 8 ) + msdata [ 7 ] ;
out :
kfree ( buffer ) ;
return error ;
}
EXPORT_SYMBOL ( sas_read_port_mode_page ) ;
2006-03-13 22:50:04 +03:00
static DECLARE_TRANSPORT_CLASS ( sas_end_dev_class ,
" sas_end_device " , NULL , NULL , NULL ) ;
2006-03-04 18:10:18 +03:00
# define sas_end_dev_show_simple(field, name, format_string, cast) \
static ssize_t \
show_sas_end_dev_ # # name ( struct class_device * cdev , char * buf ) \
{ \
struct sas_rphy * rphy = transport_class_to_rphy ( cdev ) ; \
struct sas_end_device * rdev = rphy_to_end_device ( rphy ) ; \
\
return snprintf ( buf , 20 , format_string , cast rdev - > field ) ; \
}
# define sas_end_dev_simple_attr(field, name, format_string, type) \
sas_end_dev_show_simple ( field , name , format_string , ( type ) ) \
static SAS_CLASS_DEVICE_ATTR ( end_dev , name , S_IRUGO , \
show_sas_end_dev_ # # name , NULL )
sas_end_dev_simple_attr ( ready_led_meaning , ready_led_meaning , " %d \n " , int ) ;
sas_end_dev_simple_attr ( I_T_nexus_loss_timeout , I_T_nexus_loss_timeout ,
" %d \n " , int ) ;
sas_end_dev_simple_attr ( initiator_response_timeout , initiator_response_timeout ,
" %d \n " , int ) ;
2006-03-13 22:50:04 +03:00
static DECLARE_TRANSPORT_CLASS ( sas_expander_class ,
" sas_expander " , NULL , NULL , NULL ) ;
# define sas_expander_show_simple(field, name, format_string, cast) \
static ssize_t \
show_sas_expander_ # # name ( struct class_device * cdev , char * buf ) \
{ \
struct sas_rphy * rphy = transport_class_to_rphy ( cdev ) ; \
struct sas_expander_device * edev = rphy_to_expander_device ( rphy ) ; \
\
return snprintf ( buf , 20 , format_string , cast edev - > field ) ; \
}
# define sas_expander_simple_attr(field, name, format_string, type) \
sas_expander_show_simple ( field , name , format_string , ( type ) ) \
static SAS_CLASS_DEVICE_ATTR ( expander , name , S_IRUGO , \
show_sas_expander_ # # name , NULL )
sas_expander_simple_attr ( vendor_id , vendor_id , " %s \n " , char * ) ;
sas_expander_simple_attr ( product_id , product_id , " %s \n " , char * ) ;
sas_expander_simple_attr ( product_rev , product_rev , " %s \n " , char * ) ;
sas_expander_simple_attr ( component_vendor_id , component_vendor_id ,
" %s \n " , char * ) ;
sas_expander_simple_attr ( component_id , component_id , " %u \n " , unsigned int ) ;
sas_expander_simple_attr ( component_revision_id , component_revision_id , " %u \n " ,
unsigned int ) ;
sas_expander_simple_attr ( level , level , " %d \n " , int ) ;
2006-03-04 18:10:18 +03:00
2005-09-09 18:22:50 +04:00
static DECLARE_TRANSPORT_CLASS ( sas_rphy_class ,
2006-03-19 00:00:50 +03:00
" sas_device " , NULL , NULL , NULL ) ;
2005-09-09 18:22:50 +04:00
static int sas_rphy_match ( struct attribute_container * cont , struct device * dev )
{
struct Scsi_Host * shost ;
struct sas_internal * i ;
if ( ! scsi_is_sas_rphy ( dev ) )
return 0 ;
shost = dev_to_shost ( dev - > parent - > parent ) ;
if ( ! shost - > transportt )
return 0 ;
if ( shost - > transportt - > host_attrs . ac . class ! =
& sas_host_class . class )
return 0 ;
i = to_sas_internal ( shost - > transportt ) ;
return & i - > rphy_attr_cont . ac = = cont ;
}
2006-03-04 18:10:18 +03:00
static int sas_end_dev_match ( struct attribute_container * cont ,
struct device * dev )
{
struct Scsi_Host * shost ;
struct sas_internal * i ;
struct sas_rphy * rphy ;
if ( ! scsi_is_sas_rphy ( dev ) )
return 0 ;
shost = dev_to_shost ( dev - > parent - > parent ) ;
rphy = dev_to_rphy ( dev ) ;
if ( ! shost - > transportt )
return 0 ;
if ( shost - > transportt - > host_attrs . ac . class ! =
& sas_host_class . class )
return 0 ;
i = to_sas_internal ( shost - > transportt ) ;
return & i - > end_dev_attr_cont . ac = = cont & &
2006-03-19 00:00:50 +03:00
rphy - > identify . device_type = = SAS_END_DEVICE ;
2006-03-04 18:10:18 +03:00
}
2006-03-13 22:50:04 +03:00
static int sas_expander_match ( struct attribute_container * cont ,
struct device * dev )
{
struct Scsi_Host * shost ;
struct sas_internal * i ;
struct sas_rphy * rphy ;
if ( ! scsi_is_sas_rphy ( dev ) )
return 0 ;
shost = dev_to_shost ( dev - > parent - > parent ) ;
rphy = dev_to_rphy ( dev ) ;
if ( ! shost - > transportt )
return 0 ;
if ( shost - > transportt - > host_attrs . ac . class ! =
& sas_host_class . class )
return 0 ;
i = to_sas_internal ( shost - > transportt ) ;
return & i - > expander_attr_cont . ac = = cont & &
( rphy - > identify . device_type = = SAS_EDGE_EXPANDER_DEVICE | |
2006-03-19 00:00:50 +03:00
rphy - > identify . device_type = = SAS_FANOUT_EXPANDER_DEVICE ) ;
2006-03-13 22:50:04 +03:00
}
2006-03-19 00:00:50 +03:00
static void sas_expander_release ( struct device * dev )
2005-09-09 18:22:50 +04:00
{
struct sas_rphy * rphy = dev_to_rphy ( dev ) ;
2006-03-19 00:00:50 +03:00
struct sas_expander_device * edev = rphy_to_expander_device ( rphy ) ;
2005-09-09 18:22:50 +04:00
put_device ( dev - > parent ) ;
2006-03-19 00:00:50 +03:00
kfree ( edev ) ;
2005-09-09 18:22:50 +04:00
}
2006-03-19 00:00:50 +03:00
static void sas_end_device_release ( struct device * dev )
2005-09-09 18:22:50 +04:00
{
2006-03-19 00:00:50 +03:00
struct sas_rphy * rphy = dev_to_rphy ( dev ) ;
struct sas_end_device * edev = rphy_to_end_device ( rphy ) ;
2005-09-09 18:22:50 +04:00
2006-03-19 00:00:50 +03:00
put_device ( dev - > parent ) ;
kfree ( edev ) ;
2005-09-09 18:22:50 +04:00
}
2006-06-12 18:09:18 +04:00
/**
* sas_rphy_initialize - common rphy intialization
* @ rphy : rphy to initialise
*
* Used by both sas_end_device_alloc ( ) and sas_expander_alloc ( ) to
* initialise the common rphy component of each .
*/
static void sas_rphy_initialize ( struct sas_rphy * rphy )
{
INIT_LIST_HEAD ( & rphy - > list ) ;
}
2006-03-04 18:10:18 +03:00
/**
* sas_end_device_alloc - allocate an rphy for an end device
*
* Allocates an SAS remote PHY structure , connected to @ parent .
*
* Returns :
* SAS PHY allocated or % NULL if the allocation failed .
*/
2006-06-28 20:22:50 +04:00
struct sas_rphy * sas_end_device_alloc ( struct sas_port * parent )
2006-03-04 18:10:18 +03:00
{
struct Scsi_Host * shost = dev_to_shost ( & parent - > dev ) ;
struct sas_end_device * rdev ;
rdev = kzalloc ( sizeof ( * rdev ) , GFP_KERNEL ) ;
if ( ! rdev ) {
return NULL ;
}
device_initialize ( & rdev - > rphy . dev ) ;
rdev - > rphy . dev . parent = get_device ( & parent - > dev ) ;
2006-03-19 00:00:50 +03:00
rdev - > rphy . dev . release = sas_end_device_release ;
2006-06-28 20:22:50 +04:00
if ( scsi_is_sas_expander_device ( parent - > dev . parent ) ) {
struct sas_rphy * rphy = dev_to_rphy ( parent - > dev . parent ) ;
sprintf ( rdev - > rphy . dev . bus_id , " end_device-%d:%d:%d " ,
shost - > host_no , rphy - > scsi_target_id , parent - > port_identifier ) ;
} else
sprintf ( rdev - > rphy . dev . bus_id , " end_device-%d:%d " ,
shost - > host_no , parent - > port_identifier ) ;
2006-03-04 18:10:18 +03:00
rdev - > rphy . identify . device_type = SAS_END_DEVICE ;
2006-06-12 18:09:18 +04:00
sas_rphy_initialize ( & rdev - > rphy ) ;
2006-03-04 18:10:18 +03:00
transport_setup_device ( & rdev - > rphy . dev ) ;
return & rdev - > rphy ;
}
EXPORT_SYMBOL ( sas_end_device_alloc ) ;
2006-03-13 22:50:04 +03:00
/**
* sas_expander_alloc - allocate an rphy for an end device
*
* Allocates an SAS remote PHY structure , connected to @ parent .
*
* Returns :
* SAS PHY allocated or % NULL if the allocation failed .
*/
2006-06-28 20:22:50 +04:00
struct sas_rphy * sas_expander_alloc ( struct sas_port * parent ,
2006-03-13 22:50:04 +03:00
enum sas_device_type type )
{
struct Scsi_Host * shost = dev_to_shost ( & parent - > dev ) ;
struct sas_expander_device * rdev ;
struct sas_host_attrs * sas_host = to_sas_host_attrs ( shost ) ;
BUG_ON ( type ! = SAS_EDGE_EXPANDER_DEVICE & &
type ! = SAS_FANOUT_EXPANDER_DEVICE ) ;
rdev = kzalloc ( sizeof ( * rdev ) , GFP_KERNEL ) ;
if ( ! rdev ) {
return NULL ;
}
device_initialize ( & rdev - > rphy . dev ) ;
rdev - > rphy . dev . parent = get_device ( & parent - > dev ) ;
2006-03-19 00:00:50 +03:00
rdev - > rphy . dev . release = sas_expander_release ;
2006-03-13 22:50:04 +03:00
mutex_lock ( & sas_host - > lock ) ;
rdev - > rphy . scsi_target_id = sas_host - > next_expander_id + + ;
mutex_unlock ( & sas_host - > lock ) ;
sprintf ( rdev - > rphy . dev . bus_id , " expander-%d:%d " ,
shost - > host_no , rdev - > rphy . scsi_target_id ) ;
rdev - > rphy . identify . device_type = type ;
2006-06-12 18:09:18 +04:00
sas_rphy_initialize ( & rdev - > rphy ) ;
2006-03-13 22:50:04 +03:00
transport_setup_device ( & rdev - > rphy . dev ) ;
return & rdev - > rphy ;
}
EXPORT_SYMBOL ( sas_expander_alloc ) ;
2006-03-04 18:10:18 +03:00
2005-09-09 18:22:50 +04:00
/**
* sas_rphy_add - - add a SAS remote PHY to the device hierachy
* @ rphy : The remote PHY to be added
*
* Publishes a SAS remote PHY to the rest of the system .
*/
int sas_rphy_add ( struct sas_rphy * rphy )
{
2006-06-28 20:22:50 +04:00
struct sas_port * parent = dev_to_sas_port ( rphy - > dev . parent ) ;
2005-09-09 18:22:50 +04:00
struct Scsi_Host * shost = dev_to_shost ( parent - > dev . parent ) ;
struct sas_host_attrs * sas_host = to_sas_host_attrs ( shost ) ;
struct sas_identify * identify = & rphy - > identify ;
int error ;
if ( parent - > rphy )
return - ENXIO ;
parent - > rphy = rphy ;
error = device_add ( & rphy - > dev ) ;
if ( error )
return error ;
transport_add_device ( & rphy - > dev ) ;
transport_configure_device ( & rphy - > dev ) ;
2006-01-13 21:04:00 +03:00
mutex_lock ( & sas_host - > lock ) ;
2005-09-09 18:22:50 +04:00
list_add_tail ( & rphy - > list , & sas_host - > rphy_list ) ;
if ( identify - > device_type = = SAS_END_DEVICE & &
( identify - > target_port_protocols &
( SAS_PROTOCOL_SSP | SAS_PROTOCOL_STP | SAS_PROTOCOL_SATA ) ) )
rphy - > scsi_target_id = sas_host - > next_target_id + + ;
2006-04-14 18:47:59 +04:00
else if ( identify - > device_type = = SAS_END_DEVICE )
rphy - > scsi_target_id = - 1 ;
2006-01-13 21:04:00 +03:00
mutex_unlock ( & sas_host - > lock ) ;
2005-09-09 18:22:50 +04:00
2006-03-13 22:50:04 +03:00
if ( identify - > device_type = = SAS_END_DEVICE & &
rphy - > scsi_target_id ! = - 1 ) {
2006-07-12 01:49:34 +04:00
scsi_scan_target ( & rphy - > dev , 0 ,
2005-09-09 18:22:50 +04:00
rphy - > scsi_target_id , ~ 0 , 0 ) ;
}
return 0 ;
}
EXPORT_SYMBOL ( sas_rphy_add ) ;
/**
* sas_rphy_free - - free a SAS remote PHY
* @ rphy SAS remote PHY to free
*
* Frees the specified SAS remote PHY .
*
* Note :
* This function must only be called on a remote
* PHY that has not sucessfully been added using
* sas_rphy_add ( ) .
*/
void sas_rphy_free ( struct sas_rphy * rphy )
{
2006-03-27 21:37:28 +04:00
struct device * dev = & rphy - > dev ;
2005-09-09 18:22:50 +04:00
struct Scsi_Host * shost = dev_to_shost ( rphy - > dev . parent - > parent ) ;
struct sas_host_attrs * sas_host = to_sas_host_attrs ( shost ) ;
2006-01-13 21:04:00 +03:00
mutex_lock ( & sas_host - > lock ) ;
2005-09-09 18:22:50 +04:00
list_del ( & rphy - > list ) ;
2006-01-13 21:04:00 +03:00
mutex_unlock ( & sas_host - > lock ) ;
2005-09-09 18:22:50 +04:00
2006-03-27 21:37:28 +04:00
transport_destroy_device ( dev ) ;
2006-03-19 00:00:50 +03:00
2006-03-27 21:37:28 +04:00
put_device ( dev ) ;
2005-09-09 18:22:50 +04:00
}
EXPORT_SYMBOL ( sas_rphy_free ) ;
/**
* sas_rphy_delete - - remove SAS remote PHY
* @ rphy : SAS remote PHY to remove
*
* Removes the specified SAS remote PHY .
*/
void
sas_rphy_delete ( struct sas_rphy * rphy )
{
struct device * dev = & rphy - > dev ;
2006-06-28 20:22:50 +04:00
struct sas_port * parent = dev_to_sas_port ( dev - > parent ) ;
2005-09-09 18:22:50 +04:00
struct Scsi_Host * shost = dev_to_shost ( parent - > dev . parent ) ;
struct sas_host_attrs * sas_host = to_sas_host_attrs ( shost ) ;
2006-01-04 15:45:20 +03:00
switch ( rphy - > identify . device_type ) {
case SAS_END_DEVICE :
scsi_remove_target ( dev ) ;
break ;
case SAS_EDGE_EXPANDER_DEVICE :
case SAS_FANOUT_EXPANDER_DEVICE :
2006-06-28 20:22:50 +04:00
sas_remove_children ( dev ) ;
2006-01-04 15:45:20 +03:00
break ;
default :
break ;
}
2005-09-09 18:22:50 +04:00
2005-09-26 01:10:33 +04:00
transport_remove_device ( dev ) ;
device_del ( dev ) ;
transport_destroy_device ( dev ) ;
2005-09-09 18:22:50 +04:00
2006-01-13 21:04:00 +03:00
mutex_lock ( & sas_host - > lock ) ;
2005-09-09 18:22:50 +04:00
list_del ( & rphy - > list ) ;
2006-01-13 21:04:00 +03:00
mutex_unlock ( & sas_host - > lock ) ;
2005-09-09 18:22:50 +04:00
2006-01-11 16:20:43 +03:00
parent - > rphy = NULL ;
2006-03-27 21:37:28 +04:00
put_device ( dev ) ;
2005-09-09 18:22:50 +04:00
}
EXPORT_SYMBOL ( sas_rphy_delete ) ;
/**
* scsi_is_sas_rphy - - check if a struct device represents a SAS remote PHY
* @ dev : device to check
*
* Returns :
* % 1 if the device represents a SAS remote PHY , % 0 else
*/
int scsi_is_sas_rphy ( const struct device * dev )
{
2006-03-19 00:00:50 +03:00
return dev - > release = = sas_end_device_release | |
dev - > release = = sas_expander_release ;
2005-09-09 18:22:50 +04:00
}
EXPORT_SYMBOL ( scsi_is_sas_rphy ) ;
/*
* SCSI scan helper
*/
2006-01-13 21:04:00 +03:00
static int sas_user_scan ( struct Scsi_Host * shost , uint channel ,
uint id , uint lun )
2005-09-09 18:22:50 +04:00
{
struct sas_host_attrs * sas_host = to_sas_host_attrs ( shost ) ;
struct sas_rphy * rphy ;
2006-01-13 21:04:00 +03:00
mutex_lock ( & sas_host - > lock ) ;
2005-09-09 18:22:50 +04:00
list_for_each_entry ( rphy , & sas_host - > rphy_list , list ) {
2006-05-19 19:49:37 +04:00
if ( rphy - > identify . device_type ! = SAS_END_DEVICE | |
rphy - > scsi_target_id = = - 1 )
2006-01-13 21:04:00 +03:00
continue ;
2006-07-12 01:49:34 +04:00
if ( ( channel = = SCAN_WILD_CARD | | channel = = 0 ) & &
2006-01-13 21:04:00 +03:00
( id = = SCAN_WILD_CARD | | id = = rphy - > scsi_target_id ) ) {
2006-07-12 01:49:34 +04:00
scsi_scan_target ( & rphy - > dev , 0 ,
2006-01-13 21:04:00 +03:00
rphy - > scsi_target_id , lun , 1 ) ;
}
2005-09-09 18:22:50 +04:00
}
2006-01-13 21:04:00 +03:00
mutex_unlock ( & sas_host - > lock ) ;
2005-09-09 18:22:50 +04:00
2006-01-13 21:04:00 +03:00
return 0 ;
2005-09-09 18:22:50 +04:00
}
/*
* Setup / Teardown code
*/
2006-03-04 18:10:18 +03:00
# define SETUP_TEMPLATE(attrb, field, perm, test) \
i - > private_ # # attrb [ count ] = class_device_attr_ # # field ; \
i - > private_ # # attrb [ count ] . attr . mode = perm ; \
i - > attrb [ count ] = & i - > private_ # # attrb [ count ] ; \
if ( test ) \
count + +
# define SETUP_RPORT_ATTRIBUTE(field) \
SETUP_TEMPLATE ( rphy_attrs , field , S_IRUGO , 1 )
2005-09-09 18:22:50 +04:00
2006-03-03 01:01:31 +03:00
# define SETUP_OPTIONAL_RPORT_ATTRIBUTE(field, func) \
2006-03-04 18:10:18 +03:00
SETUP_TEMPLATE ( rphy_attrs , field , S_IRUGO , i - > f - > func )
2006-03-03 01:01:31 +03:00
2006-06-28 20:22:50 +04:00
# define SETUP_PHY_ATTRIBUTE(field) \
2006-03-04 18:10:18 +03:00
SETUP_TEMPLATE ( phy_attrs , field , S_IRUGO , 1 )
2005-09-09 18:22:50 +04:00
2006-06-28 20:22:50 +04:00
# define SETUP_PORT_ATTRIBUTE(field) \
SETUP_TEMPLATE ( port_attrs , field , S_IRUGO , 1 )
# define SETUP_OPTIONAL_PHY_ATTRIBUTE(field, func) \
2006-03-04 18:10:18 +03:00
SETUP_TEMPLATE ( phy_attrs , field , S_IRUGO , i - > f - > func )
2006-03-03 01:01:31 +03:00
2006-06-28 20:22:50 +04:00
# define SETUP_PHY_ATTRIBUTE_WRONLY(field) \
2006-03-04 18:10:18 +03:00
SETUP_TEMPLATE ( phy_attrs , field , S_IWUGO , 1 )
2005-10-19 22:01:31 +04:00
2006-06-28 20:22:50 +04:00
# define SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(field, func) \
2006-03-04 18:10:18 +03:00
SETUP_TEMPLATE ( phy_attrs , field , S_IWUGO , i - > f - > func )
2006-03-03 01:01:31 +03:00
2006-03-04 18:10:18 +03:00
# define SETUP_END_DEV_ATTRIBUTE(field) \
SETUP_TEMPLATE ( end_dev_attrs , field , S_IRUGO , 1 )
2005-09-09 18:22:50 +04:00
2006-03-13 22:50:04 +03:00
# define SETUP_EXPANDER_ATTRIBUTE(field) \
SETUP_TEMPLATE ( expander_attrs , expander_ # # field , S_IRUGO , 1 )
2005-09-09 18:22:50 +04:00
/**
* sas_attach_transport - - instantiate SAS transport template
* @ ft : SAS transport class function template
*/
struct scsi_transport_template *
sas_attach_transport ( struct sas_function_template * ft )
{
struct sas_internal * i ;
int count ;
2006-01-16 18:31:18 +03:00
i = kzalloc ( sizeof ( struct sas_internal ) , GFP_KERNEL ) ;
2005-09-09 18:22:50 +04:00
if ( ! i )
return NULL ;
2006-01-13 21:04:00 +03:00
i - > t . user_scan = sas_user_scan ;
2005-09-09 18:22:50 +04:00
i - > t . host_attrs . ac . attrs = & i - > host_attrs [ 0 ] ;
i - > t . host_attrs . ac . class = & sas_host_class . class ;
i - > t . host_attrs . ac . match = sas_host_match ;
transport_container_register ( & i - > t . host_attrs ) ;
i - > t . host_size = sizeof ( struct sas_host_attrs ) ;
i - > phy_attr_cont . ac . class = & sas_phy_class . class ;
i - > phy_attr_cont . ac . attrs = & i - > phy_attrs [ 0 ] ;
i - > phy_attr_cont . ac . match = sas_phy_match ;
transport_container_register ( & i - > phy_attr_cont ) ;
2006-06-28 20:22:50 +04:00
i - > port_attr_cont . ac . class = & sas_port_class . class ;
i - > port_attr_cont . ac . attrs = & i - > port_attrs [ 0 ] ;
i - > port_attr_cont . ac . match = sas_port_match ;
transport_container_register ( & i - > port_attr_cont ) ;
2005-09-09 18:22:50 +04:00
i - > rphy_attr_cont . ac . class = & sas_rphy_class . class ;
i - > rphy_attr_cont . ac . attrs = & i - > rphy_attrs [ 0 ] ;
i - > rphy_attr_cont . ac . match = sas_rphy_match ;
transport_container_register ( & i - > rphy_attr_cont ) ;
2006-03-04 18:10:18 +03:00
i - > end_dev_attr_cont . ac . class = & sas_end_dev_class . class ;
i - > end_dev_attr_cont . ac . attrs = & i - > end_dev_attrs [ 0 ] ;
i - > end_dev_attr_cont . ac . match = sas_end_dev_match ;
transport_container_register ( & i - > end_dev_attr_cont ) ;
2006-03-13 22:50:04 +03:00
i - > expander_attr_cont . ac . class = & sas_expander_class . class ;
i - > expander_attr_cont . ac . attrs = & i - > expander_attrs [ 0 ] ;
i - > expander_attr_cont . ac . match = sas_expander_match ;
transport_container_register ( & i - > expander_attr_cont ) ;
2005-09-09 18:22:50 +04:00
i - > f = ft ;
count = 0 ;
2006-06-28 20:22:50 +04:00
SETUP_PORT_ATTRIBUTE ( num_phys ) ;
2005-09-09 18:22:50 +04:00
i - > host_attrs [ count ] = NULL ;
count = 0 ;
2006-06-28 20:22:50 +04:00
SETUP_PHY_ATTRIBUTE ( initiator_port_protocols ) ;
SETUP_PHY_ATTRIBUTE ( target_port_protocols ) ;
SETUP_PHY_ATTRIBUTE ( device_type ) ;
SETUP_PHY_ATTRIBUTE ( sas_address ) ;
SETUP_PHY_ATTRIBUTE ( phy_identifier ) ;
//SETUP_PHY_ATTRIBUTE(port_identifier);
SETUP_PHY_ATTRIBUTE ( negotiated_linkrate ) ;
SETUP_PHY_ATTRIBUTE ( minimum_linkrate_hw ) ;
SETUP_PHY_ATTRIBUTE ( minimum_linkrate ) ;
SETUP_PHY_ATTRIBUTE ( maximum_linkrate_hw ) ;
SETUP_PHY_ATTRIBUTE ( maximum_linkrate ) ;
SETUP_PHY_ATTRIBUTE ( invalid_dword_count ) ;
SETUP_PHY_ATTRIBUTE ( running_disparity_error_count ) ;
SETUP_PHY_ATTRIBUTE ( loss_of_dword_sync_count ) ;
SETUP_PHY_ATTRIBUTE ( phy_reset_problem_count ) ;
SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY ( link_reset , phy_reset ) ;
SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY ( hard_reset , phy_reset ) ;
2005-09-09 18:22:50 +04:00
i - > phy_attrs [ count ] = NULL ;
2006-06-28 20:22:50 +04:00
count = 0 ;
SETUP_PORT_ATTRIBUTE ( num_phys ) ;
i - > port_attrs [ count ] = NULL ;
2005-09-09 18:22:50 +04:00
count = 0 ;
SETUP_RPORT_ATTRIBUTE ( rphy_initiator_port_protocols ) ;
SETUP_RPORT_ATTRIBUTE ( rphy_target_port_protocols ) ;
SETUP_RPORT_ATTRIBUTE ( rphy_device_type ) ;
SETUP_RPORT_ATTRIBUTE ( rphy_sas_address ) ;
SETUP_RPORT_ATTRIBUTE ( rphy_phy_identifier ) ;
2006-03-03 01:01:31 +03:00
SETUP_OPTIONAL_RPORT_ATTRIBUTE ( rphy_enclosure_identifier ,
get_enclosure_identifier ) ;
SETUP_OPTIONAL_RPORT_ATTRIBUTE ( rphy_bay_identifier ,
get_bay_identifier ) ;
2005-09-09 18:22:50 +04:00
i - > rphy_attrs [ count ] = NULL ;
2006-03-04 18:10:18 +03:00
count = 0 ;
SETUP_END_DEV_ATTRIBUTE ( end_dev_ready_led_meaning ) ;
SETUP_END_DEV_ATTRIBUTE ( end_dev_I_T_nexus_loss_timeout ) ;
SETUP_END_DEV_ATTRIBUTE ( end_dev_initiator_response_timeout ) ;
i - > end_dev_attrs [ count ] = NULL ;
2006-03-13 22:50:04 +03:00
count = 0 ;
SETUP_EXPANDER_ATTRIBUTE ( vendor_id ) ;
SETUP_EXPANDER_ATTRIBUTE ( product_id ) ;
SETUP_EXPANDER_ATTRIBUTE ( product_rev ) ;
SETUP_EXPANDER_ATTRIBUTE ( component_vendor_id ) ;
SETUP_EXPANDER_ATTRIBUTE ( component_id ) ;
SETUP_EXPANDER_ATTRIBUTE ( component_revision_id ) ;
SETUP_EXPANDER_ATTRIBUTE ( level ) ;
i - > expander_attrs [ count ] = NULL ;
2005-09-09 18:22:50 +04:00
return & i - > t ;
}
EXPORT_SYMBOL ( sas_attach_transport ) ;
/**
* sas_release_transport - - release SAS transport template instance
* @ t : transport template instance
*/
void sas_release_transport ( struct scsi_transport_template * t )
{
struct sas_internal * i = to_sas_internal ( t ) ;
transport_container_unregister ( & i - > t . host_attrs ) ;
transport_container_unregister ( & i - > phy_attr_cont ) ;
2006-06-28 20:22:50 +04:00
transport_container_unregister ( & i - > port_attr_cont ) ;
2005-09-09 18:22:50 +04:00
transport_container_unregister ( & i - > rphy_attr_cont ) ;
2006-03-10 06:06:36 +03:00
transport_container_unregister ( & i - > end_dev_attr_cont ) ;
2006-03-13 22:50:04 +03:00
transport_container_unregister ( & i - > expander_attr_cont ) ;
2005-09-09 18:22:50 +04:00
kfree ( i ) ;
}
EXPORT_SYMBOL ( sas_release_transport ) ;
static __init int sas_transport_init ( void )
{
int error ;
error = transport_class_register ( & sas_host_class ) ;
if ( error )
goto out ;
error = transport_class_register ( & sas_phy_class ) ;
if ( error )
goto out_unregister_transport ;
2006-06-28 20:22:50 +04:00
error = transport_class_register ( & sas_port_class ) ;
2005-09-09 18:22:50 +04:00
if ( error )
goto out_unregister_phy ;
2006-06-28 20:22:50 +04:00
error = transport_class_register ( & sas_rphy_class ) ;
if ( error )
goto out_unregister_port ;
2006-03-04 18:10:18 +03:00
error = transport_class_register ( & sas_end_dev_class ) ;
if ( error )
goto out_unregister_rphy ;
2006-03-13 22:50:04 +03:00
error = transport_class_register ( & sas_expander_class ) ;
if ( error )
goto out_unregister_end_dev ;
2005-09-09 18:22:50 +04:00
return 0 ;
2006-03-13 22:50:04 +03:00
out_unregister_end_dev :
transport_class_unregister ( & sas_end_dev_class ) ;
2006-03-04 18:10:18 +03:00
out_unregister_rphy :
transport_class_unregister ( & sas_rphy_class ) ;
2006-06-28 20:22:50 +04:00
out_unregister_port :
transport_class_unregister ( & sas_port_class ) ;
2005-09-09 18:22:50 +04:00
out_unregister_phy :
transport_class_unregister ( & sas_phy_class ) ;
out_unregister_transport :
transport_class_unregister ( & sas_host_class ) ;
out :
return error ;
}
static void __exit sas_transport_exit ( void )
{
transport_class_unregister ( & sas_host_class ) ;
transport_class_unregister ( & sas_phy_class ) ;
2006-06-28 20:22:50 +04:00
transport_class_unregister ( & sas_port_class ) ;
2005-09-09 18:22:50 +04:00
transport_class_unregister ( & sas_rphy_class ) ;
2006-03-04 18:10:18 +03:00
transport_class_unregister ( & sas_end_dev_class ) ;
2006-03-13 22:50:04 +03:00
transport_class_unregister ( & sas_expander_class ) ;
2005-09-09 18:22:50 +04:00
}
MODULE_AUTHOR ( " Christoph Hellwig " ) ;
MODULE_DESCRIPTION ( " SAS Transphy Attributes " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( sas_transport_init ) ;
module_exit ( sas_transport_exit ) ;