2007-05-08 04:33:32 +04:00
/*
* Device probing and sysfs code .
2006-12-20 03:58:31 +03:00
*
* Copyright ( C ) 2005 - 2006 Kristian Hoegsberg < krh @ bitplanet . net >
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
# include <linux/module.h>
# include <linux/wait.h>
# include <linux/errno.h>
# include <linux/kthread.h>
# include <linux/device.h>
# include <linux/delay.h>
2007-03-07 20:12:44 +03:00
# include <linux/idr.h>
2008-03-24 22:54:28 +03:00
# include <linux/string.h>
2008-04-19 06:21:05 +04:00
# include <linux/rwsem.h>
# include <linux/semaphore.h>
2008-01-25 20:57:41 +03:00
# include <asm/system.h>
2007-03-21 17:55:19 +03:00
# include <linux/ctype.h>
2006-12-20 03:58:31 +03:00
# include "fw-transaction.h"
# include "fw-topology.h"
# include "fw-device.h"
void fw_csr_iterator_init ( struct fw_csr_iterator * ci , u32 * p )
{
ci - > p = p + 1 ;
ci - > end = ci - > p + ( p [ 0 ] > > 16 ) ;
}
EXPORT_SYMBOL ( fw_csr_iterator_init ) ;
int fw_csr_iterator_next ( struct fw_csr_iterator * ci , int * key , int * value )
{
* key = * ci - > p > > 24 ;
* value = * ci - > p & 0xffffff ;
return ci - > p + + < ci - > end ;
}
EXPORT_SYMBOL ( fw_csr_iterator_next ) ;
static int is_fw_unit ( struct device * dev ) ;
2007-01-14 17:29:07 +03:00
static int match_unit_directory ( u32 * directory , const struct fw_device_id * id )
2006-12-20 03:58:31 +03:00
{
struct fw_csr_iterator ci ;
int key , value , match ;
match = 0 ;
fw_csr_iterator_init ( & ci , directory ) ;
while ( fw_csr_iterator_next ( & ci , & key , & value ) ) {
if ( key = = CSR_VENDOR & & value = = id - > vendor )
match | = FW_MATCH_VENDOR ;
if ( key = = CSR_MODEL & & value = = id - > model )
match | = FW_MATCH_MODEL ;
if ( key = = CSR_SPECIFIER_ID & & value = = id - > specifier_id )
match | = FW_MATCH_SPECIFIER_ID ;
if ( key = = CSR_VERSION & & value = = id - > version )
match | = FW_MATCH_VERSION ;
}
return ( match & id - > match_flags ) = = id - > match_flags ;
}
static int fw_unit_match ( struct device * dev , struct device_driver * drv )
{
struct fw_unit * unit = fw_unit ( dev ) ;
struct fw_driver * driver = fw_driver ( drv ) ;
int i ;
/* We only allow binding to fw_units. */
if ( ! is_fw_unit ( dev ) )
return 0 ;
for ( i = 0 ; driver - > id_table [ i ] . match_flags ! = 0 ; i + + ) {
if ( match_unit_directory ( unit - > directory , & driver - > id_table [ i ] ) )
return 1 ;
}
return 0 ;
}
static int get_modalias ( struct fw_unit * unit , char * buffer , size_t buffer_size )
{
struct fw_device * device = fw_device ( unit - > device . parent ) ;
struct fw_csr_iterator ci ;
int key , value ;
int vendor = 0 ;
int model = 0 ;
int specifier_id = 0 ;
int version = 0 ;
fw_csr_iterator_init ( & ci , & device - > config_rom [ 5 ] ) ;
while ( fw_csr_iterator_next ( & ci , & key , & value ) ) {
switch ( key ) {
case CSR_VENDOR :
vendor = value ;
break ;
case CSR_MODEL :
model = value ;
break ;
}
}
fw_csr_iterator_init ( & ci , unit - > directory ) ;
while ( fw_csr_iterator_next ( & ci , & key , & value ) ) {
switch ( key ) {
case CSR_SPECIFIER_ID :
specifier_id = value ;
break ;
case CSR_VERSION :
version = value ;
break ;
}
}
return snprintf ( buffer , buffer_size ,
" ieee1394:ven%08Xmo%08Xsp%08Xver%08X " ,
vendor , model , specifier_id , version ) ;
}
static int
2007-08-14 17:15:12 +04:00
fw_unit_uevent ( struct device * dev , struct kobj_uevent_env * env )
2006-12-20 03:58:31 +03:00
{
struct fw_unit * unit = fw_unit ( dev ) ;
char modalias [ 64 ] ;
2007-05-10 03:23:14 +04:00
get_modalias ( unit , modalias , sizeof ( modalias ) ) ;
2006-12-20 03:58:31 +03:00
2007-08-14 17:15:12 +04:00
if ( add_uevent_var ( env , " MODALIAS=%s " , modalias ) )
2006-12-20 03:58:31 +03:00
return - ENOMEM ;
return 0 ;
}
struct bus_type fw_bus_type = {
2007-02-06 22:49:37 +03:00
. name = " firewire " ,
2006-12-20 03:58:31 +03:00
. match = fw_unit_match ,
} ;
EXPORT_SYMBOL ( fw_bus_type ) ;
static void fw_device_release ( struct device * dev )
{
struct fw_device * device = fw_device ( dev ) ;
2008-02-28 00:14:27 +03:00
struct fw_card * card = device - > card ;
2006-12-20 03:58:31 +03:00
unsigned long flags ;
2007-05-08 04:33:32 +04:00
/*
* Take the card lock so we don ' t set this to NULL while a
2009-01-09 22:49:37 +03:00
* FW_NODE_UPDATED callback is being handled or while the
* bus manager work looks at this node .
2007-05-08 04:33:32 +04:00
*/
2008-03-24 22:54:28 +03:00
spin_lock_irqsave ( & card - > lock , flags ) ;
2006-12-20 03:58:31 +03:00
device - > node - > data = NULL ;
2008-03-24 22:54:28 +03:00
spin_unlock_irqrestore ( & card - > lock , flags ) ;
2006-12-20 03:58:31 +03:00
fw_node_put ( device - > node ) ;
kfree ( device - > config_rom ) ;
kfree ( device ) ;
2008-05-24 18:50:22 +04:00
fw_card_put ( card ) ;
2006-12-20 03:58:31 +03:00
}
int fw_device_enable_phys_dma ( struct fw_device * device )
{
2008-01-25 20:57:41 +03:00
int generation = device - > generation ;
/* device->node_id, accessed below, must not be older than generation */
smp_rmb ( ) ;
2006-12-20 03:58:31 +03:00
return device - > card - > driver - > enable_phys_dma ( device - > card ,
device - > node_id ,
2008-01-25 20:57:41 +03:00
generation ) ;
2006-12-20 03:58:31 +03:00
}
EXPORT_SYMBOL ( fw_device_enable_phys_dma ) ;
2007-03-21 17:55:19 +03:00
struct config_rom_attribute {
struct device_attribute attr ;
u32 key ;
} ;
static ssize_t
show_immediate ( struct device * dev , struct device_attribute * dattr , char * buf )
{
struct config_rom_attribute * attr =
container_of ( dattr , struct config_rom_attribute , attr ) ;
struct fw_csr_iterator ci ;
u32 * dir ;
2008-03-24 22:54:28 +03:00
int key , value , ret = - ENOENT ;
down_read ( & fw_device_rwsem ) ;
2007-03-21 17:55:19 +03:00
if ( is_fw_unit ( dev ) )
dir = fw_unit ( dev ) - > directory ;
else
dir = fw_device ( dev ) - > config_rom + 5 ;
fw_csr_iterator_init ( & ci , dir ) ;
while ( fw_csr_iterator_next ( & ci , & key , & value ) )
2008-03-24 22:54:28 +03:00
if ( attr - > key = = key ) {
ret = snprintf ( buf , buf ? PAGE_SIZE : 0 ,
" 0x%06x \n " , value ) ;
break ;
}
up_read ( & fw_device_rwsem ) ;
2007-03-21 17:55:19 +03:00
2008-03-24 22:54:28 +03:00
return ret ;
2007-03-21 17:55:19 +03:00
}
# define IMMEDIATE_ATTR(name, key) \
{ __ATTR ( name , S_IRUGO , show_immediate , NULL ) , key }
static ssize_t
show_text_leaf ( struct device * dev , struct device_attribute * dattr , char * buf )
{
struct config_rom_attribute * attr =
container_of ( dattr , struct config_rom_attribute , attr ) ;
struct fw_csr_iterator ci ;
u32 * dir , * block = NULL , * p , * end ;
2008-03-24 22:54:28 +03:00
int length , key , value , last_key = 0 , ret = - ENOENT ;
2007-03-21 17:55:19 +03:00
char * b ;
2008-03-24 22:54:28 +03:00
down_read ( & fw_device_rwsem ) ;
2007-03-21 17:55:19 +03:00
if ( is_fw_unit ( dev ) )
dir = fw_unit ( dev ) - > directory ;
else
dir = fw_device ( dev ) - > config_rom + 5 ;
fw_csr_iterator_init ( & ci , dir ) ;
while ( fw_csr_iterator_next ( & ci , & key , & value ) ) {
if ( attr - > key = = last_key & &
key = = ( CSR_DESCRIPTOR | CSR_LEAF ) )
block = ci . p - 1 + value ;
last_key = key ;
}
if ( block = = NULL )
2008-03-24 22:54:28 +03:00
goto out ;
2007-03-21 17:55:19 +03:00
length = min ( block [ 0 ] > > 16 , 256U ) ;
if ( length < 3 )
2008-03-24 22:54:28 +03:00
goto out ;
2007-03-21 17:55:19 +03:00
if ( block [ 1 ] ! = 0 | | block [ 2 ] ! = 0 )
/* Unknown encoding. */
2008-03-24 22:54:28 +03:00
goto out ;
2007-03-21 17:55:19 +03:00
2008-03-24 22:54:28 +03:00
if ( buf = = NULL ) {
ret = length * 4 ;
goto out ;
}
2007-03-21 17:55:19 +03:00
b = buf ;
end = & block [ length + 1 ] ;
for ( p = & block [ 3 ] ; p < end ; p + + , b + = 4 )
* ( u32 * ) b = ( __force u32 ) __cpu_to_be32 ( * p ) ;
/* Strip trailing whitespace and add newline. */
while ( b - - , ( isspace ( * b ) | | * b = = ' \0 ' ) & & b > buf ) ;
strcpy ( b + 1 , " \n " ) ;
2008-03-24 22:54:28 +03:00
ret = b + 2 - buf ;
out :
up_read ( & fw_device_rwsem ) ;
2007-03-21 17:55:19 +03:00
2008-03-24 22:54:28 +03:00
return ret ;
2007-03-21 17:55:19 +03:00
}
# define TEXT_LEAF_ATTR(name, key) \
{ __ATTR ( name , S_IRUGO , show_text_leaf , NULL ) , key }
static struct config_rom_attribute config_rom_attributes [ ] = {
IMMEDIATE_ATTR ( vendor , CSR_VENDOR ) ,
IMMEDIATE_ATTR ( hardware_version , CSR_HARDWARE_VERSION ) ,
IMMEDIATE_ATTR ( specifier_id , CSR_SPECIFIER_ID ) ,
IMMEDIATE_ATTR ( version , CSR_VERSION ) ,
IMMEDIATE_ATTR ( model , CSR_MODEL ) ,
TEXT_LEAF_ATTR ( vendor_name , CSR_VENDOR ) ,
TEXT_LEAF_ATTR ( model_name , CSR_MODEL ) ,
TEXT_LEAF_ATTR ( hardware_version_name , CSR_HARDWARE_VERSION ) ,
} ;
static void
2007-03-28 03:35:13 +04:00
init_fw_attribute_group ( struct device * dev ,
struct device_attribute * attrs ,
struct fw_attribute_group * group )
2007-03-21 17:55:19 +03:00
{
struct device_attribute * attr ;
2007-03-28 03:35:13 +04:00
int i , j ;
for ( j = 0 ; attrs [ j ] . attr . name ! = NULL ; j + + )
group - > attrs [ j ] = & attrs [ j ] . attr ;
2007-03-21 17:55:19 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( config_rom_attributes ) ; i + + ) {
attr = & config_rom_attributes [ i ] . attr ;
if ( attr - > show ( dev , attr , NULL ) < 0 )
continue ;
2007-03-28 03:35:13 +04:00
group - > attrs [ j + + ] = & attr - > attr ;
2007-03-21 17:55:19 +03:00
}
2007-03-28 03:35:13 +04:00
BUG_ON ( j > = ARRAY_SIZE ( group - > attrs ) ) ;
group - > attrs [ j + + ] = NULL ;
group - > groups [ 0 ] = & group - > group ;
group - > groups [ 1 ] = NULL ;
group - > group . attrs = group - > attrs ;
dev - > groups = group - > groups ;
2007-03-21 17:55:19 +03:00
}
2006-12-20 03:58:31 +03:00
static ssize_t
2007-03-21 03:58:33 +03:00
modalias_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2006-12-20 03:58:31 +03:00
{
struct fw_unit * unit = fw_unit ( dev ) ;
int length ;
length = get_modalias ( unit , buf , PAGE_SIZE ) ;
strcpy ( buf + length , " \n " ) ;
return length + 1 ;
}
static ssize_t
2007-03-21 03:58:33 +03:00
rom_index_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2006-12-20 03:58:31 +03:00
{
2007-03-21 03:58:33 +03:00
struct fw_device * device = fw_device ( dev - > parent ) ;
struct fw_unit * unit = fw_unit ( dev ) ;
2006-12-20 03:58:31 +03:00
2007-03-21 03:58:33 +03:00
return snprintf ( buf , PAGE_SIZE , " %d \n " ,
( int ) ( unit - > directory - device - > config_rom ) ) ;
2006-12-20 03:58:31 +03:00
}
2007-03-21 03:58:33 +03:00
static struct device_attribute fw_unit_attributes [ ] = {
__ATTR_RO ( modalias ) ,
__ATTR_RO ( rom_index ) ,
__ATTR_NULL ,
2006-12-20 03:58:31 +03:00
} ;
2007-03-07 20:12:46 +03:00
static ssize_t
2007-03-21 03:58:35 +03:00
config_rom_show ( struct device * dev , struct device_attribute * attr , char * buf )
2007-03-07 20:12:46 +03:00
{
2007-03-21 03:58:33 +03:00
struct fw_device * device = fw_device ( dev ) ;
2008-03-24 22:54:28 +03:00
size_t length ;
2007-03-07 20:12:46 +03:00
2008-03-24 22:54:28 +03:00
down_read ( & fw_device_rwsem ) ;
length = device - > config_rom_length * 4 ;
memcpy ( buf , device - > config_rom , length ) ;
up_read ( & fw_device_rwsem ) ;
2007-03-21 03:58:33 +03:00
2008-03-24 22:54:28 +03:00
return length ;
2007-03-07 20:12:46 +03:00
}
2007-03-21 03:58:35 +03:00
static ssize_t
guid_show ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct fw_device * device = fw_device ( dev ) ;
2008-03-24 22:54:28 +03:00
int ret ;
down_read ( & fw_device_rwsem ) ;
ret = snprintf ( buf , PAGE_SIZE , " 0x%08x%08x \n " ,
device - > config_rom [ 3 ] , device - > config_rom [ 4 ] ) ;
up_read ( & fw_device_rwsem ) ;
2007-03-21 03:58:35 +03:00
2008-03-24 22:54:28 +03:00
return ret ;
2007-03-21 03:58:35 +03:00
}
2007-03-21 03:58:33 +03:00
static struct device_attribute fw_device_attributes [ ] = {
__ATTR_RO ( config_rom ) ,
2007-03-21 03:58:35 +03:00
__ATTR_RO ( guid ) ,
2007-03-21 03:58:33 +03:00
__ATTR_NULL ,
2007-03-07 20:12:46 +03:00
} ;
2008-01-25 19:53:49 +03:00
static int
read_rom ( struct fw_device * device , int generation , int index , u32 * data )
2006-12-20 03:58:31 +03:00
{
2008-07-20 16:20:53 +04:00
int rcode ;
2008-01-25 20:57:41 +03:00
/* device->node_id, accessed below, must not be older than generation */
smp_rmb ( ) ;
2006-12-20 03:58:31 +03:00
2008-07-20 16:20:53 +04:00
rcode = fw_run_transaction ( device - > card , TCODE_READ_QUADLET_REQUEST ,
2008-01-25 20:57:41 +03:00
device - > node_id , generation , device - > max_speed ,
2008-07-20 16:20:53 +04:00
( CSR_REGISTER_BASE | CSR_CONFIG_ROM ) + index * 4 ,
data , 4 ) ;
be32_to_cpus ( data ) ;
2006-12-20 03:58:31 +03:00
2008-07-20 16:20:53 +04:00
return rcode ;
2006-12-20 03:58:31 +03:00
}
2008-03-02 21:35:42 +03:00
# define READ_BIB_ROM_SIZE 256
# define READ_BIB_STACK_SIZE 16
2008-01-25 19:53:49 +03:00
/*
* Read the bus info block , perform a speed probe , and read all of the rest of
* the config ROM . We do all this with a cached bus generation . If the bus
* generation changes under us , read_bus_info_block will fail and get retried .
* It ' s better to start all over in this case because the node from which we
* are reading the ROM may have changed the ROM during the reset .
*/
static int read_bus_info_block ( struct fw_device * device , int generation )
2006-12-20 03:58:31 +03:00
{
2008-03-24 22:54:28 +03:00
u32 * rom , * stack , * old_rom , * new_rom ;
2008-03-02 21:35:42 +03:00
u32 sp , key ;
int i , end , length , ret = - 1 ;
rom = kmalloc ( sizeof ( * rom ) * READ_BIB_ROM_SIZE +
sizeof ( * stack ) * READ_BIB_STACK_SIZE , GFP_KERNEL ) ;
if ( rom = = NULL )
return - ENOMEM ;
stack = & rom [ READ_BIB_ROM_SIZE ] ;
2006-12-20 03:58:31 +03:00
2007-06-10 23:31:36 +04:00
device - > max_speed = SCODE_100 ;
2006-12-20 03:58:31 +03:00
/* First read the bus info block. */
for ( i = 0 ; i < 5 ; i + + ) {
2008-01-25 19:53:49 +03:00
if ( read_rom ( device , generation , i , & rom [ i ] ) ! = RCODE_COMPLETE )
2008-03-02 21:35:42 +03:00
goto out ;
2007-05-08 04:33:32 +04:00
/*
* As per IEEE1212 7.2 , during power - up , devices can
2006-12-20 03:58:31 +03:00
* reply with a 0 for the first quadlet of the config
* rom to indicate that they are booting ( for example ,
* if the firmware is on the disk of a external
* harddisk ) . In that case we just fail , and the
2007-05-08 04:33:32 +04:00
* retry mechanism will try again later .
*/
2006-12-20 03:58:31 +03:00
if ( i = = 0 & & rom [ i ] = = 0 )
2008-03-02 21:35:42 +03:00
goto out ;
2006-12-20 03:58:31 +03:00
}
2007-06-10 23:31:36 +04:00
device - > max_speed = device - > node - > max_speed ;
/*
* Determine the speed of
* - devices with link speed less than PHY speed ,
* - devices with 1394 b PHY ( unless only connected to 1394 a PHYs ) ,
* - all devices if there are 1394 b repeaters .
* Note , we cannot use the bus info block ' s link_spd as starting point
* because some buggy firmwares set it lower than necessary and because
* 1394 - 1995 nodes do not have the field .
*/
if ( ( rom [ 2 ] & 0x7 ) < device - > max_speed | |
device - > max_speed = = SCODE_BETA | |
device - > card - > beta_repeaters_present ) {
u32 dummy ;
/* for S1600 and S3200 */
if ( device - > max_speed = = SCODE_BETA )
device - > max_speed = device - > card - > link_speed ;
while ( device - > max_speed > SCODE_100 ) {
2008-01-25 19:53:49 +03:00
if ( read_rom ( device , generation , 0 , & dummy ) = =
RCODE_COMPLETE )
2007-06-10 23:31:36 +04:00
break ;
device - > max_speed - - ;
}
}
2007-05-08 04:33:32 +04:00
/*
* Now parse the config rom . The config rom is a recursive
2006-12-20 03:58:31 +03:00
* directory structure so we parse it using a stack of
* references to the blocks that make up the structure . We
* push a reference to the root directory on the stack to
2007-05-08 04:33:32 +04:00
* start things off .
*/
2006-12-20 03:58:31 +03:00
length = i ;
sp = 0 ;
stack [ sp + + ] = 0xc0000005 ;
while ( sp > 0 ) {
2007-05-08 04:33:32 +04:00
/*
* Pop the next block reference of the stack . The
2006-12-20 03:58:31 +03:00
* lower 24 bits is the offset into the config rom ,
* the upper 8 bits are the type of the reference the
2007-05-08 04:33:32 +04:00
* block .
*/
2006-12-20 03:58:31 +03:00
key = stack [ - - sp ] ;
i = key & 0xffffff ;
2008-03-02 21:35:42 +03:00
if ( i > = READ_BIB_ROM_SIZE )
2007-05-08 04:33:32 +04:00
/*
* The reference points outside the standard
* config rom area , something ' s fishy .
*/
2008-03-02 21:35:42 +03:00
goto out ;
2006-12-20 03:58:31 +03:00
/* Read header quadlet for the block to get the length. */
2008-01-25 19:53:49 +03:00
if ( read_rom ( device , generation , i , & rom [ i ] ) ! = RCODE_COMPLETE )
2008-03-02 21:35:42 +03:00
goto out ;
2006-12-20 03:58:31 +03:00
end = i + ( rom [ i ] > > 16 ) + 1 ;
i + + ;
2008-03-02 21:35:42 +03:00
if ( end > READ_BIB_ROM_SIZE )
2007-05-08 04:33:32 +04:00
/*
* This block extends outside standard config
2006-12-20 03:58:31 +03:00
* area ( and the array we ' re reading it
* into ) . That ' s broken , so ignore this
2007-05-08 04:33:32 +04:00
* device .
*/
2008-03-02 21:35:42 +03:00
goto out ;
2006-12-20 03:58:31 +03:00
2007-05-08 04:33:32 +04:00
/*
* Now read in the block . If this is a directory
2006-12-20 03:58:31 +03:00
* block , check the entries as we read them to see if
2007-05-08 04:33:32 +04:00
* it references another block , and push it in that case .
*/
2006-12-20 03:58:31 +03:00
while ( i < end ) {
2008-01-25 19:53:49 +03:00
if ( read_rom ( device , generation , i , & rom [ i ] ) ! =
RCODE_COMPLETE )
2008-03-02 21:35:42 +03:00
goto out ;
2006-12-20 03:58:31 +03:00
if ( ( key > > 30 ) = = 3 & & ( rom [ i ] > > 30 ) > 1 & &
2008-03-02 21:35:42 +03:00
sp < READ_BIB_STACK_SIZE )
2006-12-20 03:58:31 +03:00
stack [ sp + + ] = i + rom [ i ] ;
i + + ;
}
if ( length < i )
length = i ;
}
2008-03-24 22:54:28 +03:00
old_rom = device - > config_rom ;
new_rom = kmemdup ( rom , length * 4 , GFP_KERNEL ) ;
if ( new_rom = = NULL )
2008-03-02 21:35:42 +03:00
goto out ;
2008-03-24 22:54:28 +03:00
down_write ( & fw_device_rwsem ) ;
device - > config_rom = new_rom ;
2006-12-20 03:58:31 +03:00
device - > config_rom_length = length ;
2008-03-24 22:54:28 +03:00
up_write ( & fw_device_rwsem ) ;
kfree ( old_rom ) ;
2008-03-02 21:35:42 +03:00
ret = 0 ;
2008-03-24 22:54:28 +03:00
device - > cmc = rom [ 2 ] & 1 < < 30 ;
2008-03-02 21:35:42 +03:00
out :
kfree ( rom ) ;
2006-12-20 03:58:31 +03:00
2008-03-02 21:35:42 +03:00
return ret ;
2006-12-20 03:58:31 +03:00
}
static void fw_unit_release ( struct device * dev )
{
struct fw_unit * unit = fw_unit ( dev ) ;
kfree ( unit ) ;
}
2007-03-21 03:58:33 +03:00
static struct device_type fw_unit_type = {
. uevent = fw_unit_uevent ,
. release = fw_unit_release ,
} ;
2006-12-20 03:58:31 +03:00
static int is_fw_unit ( struct device * dev )
{
2007-03-21 03:58:33 +03:00
return dev - > type = = & fw_unit_type ;
2006-12-20 03:58:31 +03:00
}
static void create_units ( struct fw_device * device )
{
struct fw_csr_iterator ci ;
struct fw_unit * unit ;
int key , value , i ;
i = 0 ;
fw_csr_iterator_init ( & ci , & device - > config_rom [ 5 ] ) ;
while ( fw_csr_iterator_next ( & ci , & key , & value ) ) {
if ( key ! = ( CSR_UNIT | CSR_DIRECTORY ) )
continue ;
2007-05-08 04:33:32 +04:00
/*
* Get the address of the unit directory and try to
* match the drivers id_tables against it .
*/
2007-05-10 03:23:14 +04:00
unit = kzalloc ( sizeof ( * unit ) , GFP_KERNEL ) ;
2006-12-20 03:58:31 +03:00
if ( unit = = NULL ) {
fw_error ( " failed to allocate memory for unit \n " ) ;
continue ;
}
unit - > directory = ci . p + value - 1 ;
unit - > device . bus = & fw_bus_type ;
2007-03-21 03:58:33 +03:00
unit - > device . type = & fw_unit_type ;
2006-12-20 03:58:31 +03:00
unit - > device . parent = & device - > device ;
2008-10-30 03:41:56 +03:00
dev_set_name ( & unit - > device , " %s.%d " , dev_name ( & device - > device ) , i + + ) ;
2006-12-20 03:58:31 +03:00
2007-03-28 03:35:13 +04:00
init_fw_attribute_group ( & unit - > device ,
fw_unit_attributes ,
& unit - > attribute_group ) ;
2007-03-21 17:55:19 +03:00
if ( device_register ( & unit - > device ) < 0 )
goto skip_unit ;
continue ;
skip_unit :
kfree ( unit ) ;
2006-12-20 03:58:31 +03:00
}
}
static int shutdown_unit ( struct device * device , void * data )
{
2007-03-21 03:58:33 +03:00
device_unregister ( device ) ;
2006-12-20 03:58:31 +03:00
return 0 ;
}
2008-03-24 22:54:28 +03:00
/*
* fw_device_rwsem acts as dual purpose mutex :
* - serializes accesses to fw_device_idr ,
* - serializes accesses to fw_device . config_rom / . config_rom_length and
* fw_unit . directory , unless those accesses happen at safe occasions
*/
DECLARE_RWSEM ( fw_device_rwsem ) ;
2008-11-24 22:40:00 +03:00
DEFINE_IDR ( fw_device_idr ) ;
2007-03-07 20:12:44 +03:00
int fw_cdev_major ;
2008-02-02 17:01:09 +03:00
struct fw_device * fw_device_get_by_devt ( dev_t devt )
2007-03-07 20:12:44 +03:00
{
struct fw_device * device ;
2008-03-24 22:54:28 +03:00
down_read ( & fw_device_rwsem ) ;
2007-03-07 20:12:44 +03:00
device = idr_find ( & fw_device_idr , MINOR ( devt ) ) ;
2008-02-02 17:01:09 +03:00
if ( device )
fw_device_get ( device ) ;
2008-03-24 22:54:28 +03:00
up_read ( & fw_device_rwsem ) ;
2007-03-07 20:12:44 +03:00
return device ;
}
2006-12-20 03:58:31 +03:00
static void fw_device_shutdown ( struct work_struct * work )
{
struct fw_device * device =
container_of ( work , struct fw_device , work . work ) ;
2007-03-07 20:12:44 +03:00
int minor = MINOR ( device - > device . devt ) ;
2007-03-07 20:12:48 +03:00
fw_device_cdev_remove ( device ) ;
2006-12-20 03:58:31 +03:00
device_for_each_child ( & device - > device , NULL , shutdown_unit ) ;
device_unregister ( & device - > device ) ;
2008-02-02 17:01:09 +03:00
2008-03-24 22:54:28 +03:00
down_write ( & fw_device_rwsem ) ;
2008-02-02 17:01:09 +03:00
idr_remove ( & fw_device_idr , minor ) ;
2008-03-24 22:54:28 +03:00
up_write ( & fw_device_rwsem ) ;
2008-02-02 17:01:09 +03:00
fw_device_put ( device ) ;
2006-12-20 03:58:31 +03:00
}
2007-03-21 03:58:33 +03:00
static struct device_type fw_device_type = {
. release = fw_device_release ,
} ;
2007-05-08 04:33:32 +04:00
/*
* These defines control the retry behavior for reading the config
2006-12-20 03:58:31 +03:00
* rom . It shouldn ' t be necessary to tweak these ; if the device
* doesn ' t respond to a config rom read within 10 seconds , it ' s not
* going to respond at all . As for the initial delay , a lot of
* devices will be able to respond within half a second after bus
* reset . On the other hand , it ' s not really worth being more
* aggressive than that , since it scales pretty well ; if 10 devices
2007-05-08 04:33:32 +04:00
* are plugged in , they ' re all getting read within one second .
*/
2006-12-20 03:58:31 +03:00
2007-03-27 09:43:43 +04:00
# define MAX_RETRIES 10
# define RETRY_DELAY (3 * HZ)
2006-12-20 03:58:31 +03:00
# define INITIAL_DELAY (HZ / 2)
static void fw_device_init ( struct work_struct * work )
{
struct fw_device * device =
container_of ( work , struct fw_device , work . work ) ;
2007-03-07 20:12:44 +03:00
int minor , err ;
2006-12-20 03:58:31 +03:00
2007-05-08 04:33:32 +04:00
/*
* All failure paths here set node - > data to NULL , so that we
2006-12-20 03:58:31 +03:00
* don ' t try to do device_for_each_child ( ) on a kfree ( ) ' d
2007-05-08 04:33:32 +04:00
* device .
*/
2006-12-20 03:58:31 +03:00
2008-01-25 19:53:49 +03:00
if ( read_bus_info_block ( device , device - > generation ) < 0 ) {
2008-02-28 00:14:27 +03:00
if ( device - > config_rom_retries < MAX_RETRIES & &
atomic_read ( & device - > state ) = = FW_DEVICE_INITIALIZING ) {
2006-12-20 03:58:31 +03:00
device - > config_rom_retries + + ;
schedule_delayed_work ( & device - > work , RETRY_DELAY ) ;
} else {
2007-01-23 23:11:43 +03:00
fw_notify ( " giving up on config rom for node id %x \n " ,
2006-12-20 03:58:31 +03:00
device - > node_id ) ;
2007-01-26 08:38:45 +03:00
if ( device - > node = = device - > card - > root_node )
2008-11-29 19:44:57 +03:00
fw_schedule_bm_work ( device - > card , 0 ) ;
2006-12-20 03:58:31 +03:00
fw_device_release ( & device - > device ) ;
}
return ;
}
2009-01-09 22:49:37 +03:00
device_initialize ( & device - > device ) ;
2008-02-02 17:01:09 +03:00
fw_device_get ( device ) ;
2008-03-24 22:54:28 +03:00
down_write ( & fw_device_rwsem ) ;
2009-01-09 22:49:37 +03:00
err = idr_pre_get ( & fw_device_idr , GFP_KERNEL ) ?
idr_get_new ( & fw_device_idr , device , & minor ) :
- ENOMEM ;
2008-03-24 22:54:28 +03:00
up_write ( & fw_device_rwsem ) ;
2008-02-02 17:01:09 +03:00
2007-03-07 20:12:44 +03:00
if ( err < 0 )
goto error ;
2006-12-20 03:58:31 +03:00
device - > device . bus = & fw_bus_type ;
2007-03-21 03:58:33 +03:00
device - > device . type = & fw_device_type ;
2006-12-20 03:58:31 +03:00
device - > device . parent = device - > card - > device ;
2007-03-07 20:12:44 +03:00
device - > device . devt = MKDEV ( fw_cdev_major , minor ) ;
2008-10-30 03:41:56 +03:00
dev_set_name ( & device - > device , " fw%d " , minor ) ;
2006-12-20 03:58:31 +03:00
2007-03-28 03:35:13 +04:00
init_fw_attribute_group ( & device - > device ,
fw_device_attributes ,
& device - > attribute_group ) ;
2006-12-20 03:58:31 +03:00
if ( device_add ( & device - > device ) ) {
fw_error ( " Failed to add device. \n " ) ;
2007-03-07 20:12:44 +03:00
goto error_with_cdev ;
2006-12-20 03:58:31 +03:00
}
create_units ( device ) ;
2007-05-08 04:33:32 +04:00
/*
* Transition the device to running state . If it got pulled
2006-12-20 03:58:31 +03:00
* out from under us while we did the intialization work , we
* have to shut down the device again here . Normally , though ,
* fw_node_event will be responsible for shutting it down when
* necessary . We have to use the atomic cmpxchg here to avoid
* racing with the FW_NODE_DESTROYED case in
2007-05-08 04:33:32 +04:00
* fw_node_event ( ) .
*/
2007-01-27 12:34:55 +03:00
if ( atomic_cmpxchg ( & device - > state ,
2006-12-20 03:58:31 +03:00
FW_DEVICE_INITIALIZING ,
2008-02-04 01:03:00 +03:00
FW_DEVICE_RUNNING ) = = FW_DEVICE_SHUTDOWN ) {
2008-03-24 22:54:28 +03:00
fw_device_shutdown ( work ) ;
2008-02-04 01:03:00 +03:00
} else {
if ( device - > config_rom_retries )
fw_notify ( " created device %s: GUID %08x%08x, S%d00, "
" %d config ROM retries \n " ,
2008-10-30 03:41:56 +03:00
dev_name ( & device - > device ) ,
2008-02-04 01:03:00 +03:00
device - > config_rom [ 3 ] , device - > config_rom [ 4 ] ,
1 < < device - > max_speed ,
device - > config_rom_retries ) ;
else
fw_notify ( " created device %s: GUID %08x%08x, S%d00 \n " ,
2008-10-30 03:41:56 +03:00
dev_name ( & device - > device ) ,
2008-02-04 01:03:00 +03:00
device - > config_rom [ 3 ] , device - > config_rom [ 4 ] ,
1 < < device - > max_speed ) ;
2008-03-24 22:54:28 +03:00
device - > config_rom_retries = 0 ;
2008-02-04 01:03:00 +03:00
}
2006-12-20 03:58:31 +03:00
2007-05-08 04:33:32 +04:00
/*
* Reschedule the IRM work if we just finished reading the
2006-12-20 03:58:31 +03:00
* root node config rom . If this races with a bus reset we
* just end up running the IRM work a couple of extra times -
2007-05-08 04:33:32 +04:00
* pretty harmless .
*/
2006-12-20 03:58:31 +03:00
if ( device - > node = = device - > card - > root_node )
2008-11-29 19:44:57 +03:00
fw_schedule_bm_work ( device - > card , 0 ) ;
2006-12-20 03:58:31 +03:00
return ;
2007-03-07 20:12:44 +03:00
error_with_cdev :
2008-03-24 22:54:28 +03:00
down_write ( & fw_device_rwsem ) ;
2007-03-07 20:12:44 +03:00
idr_remove ( & fw_device_idr , minor ) ;
2008-03-24 22:54:28 +03:00
up_write ( & fw_device_rwsem ) ;
2007-03-04 16:45:18 +03:00
error :
2008-02-02 17:01:09 +03:00
fw_device_put ( device ) ; /* fw_device_idr's reference */
put_device ( & device - > device ) ; /* our reference */
2006-12-20 03:58:31 +03:00
}
static int update_unit ( struct device * dev , void * data )
{
struct fw_unit * unit = fw_unit ( dev ) ;
struct fw_driver * driver = ( struct fw_driver * ) dev - > driver ;
2007-03-19 18:37:16 +03:00
if ( is_fw_unit ( dev ) & & driver ! = NULL & & driver - > update ! = NULL ) {
down ( & dev - > sem ) ;
2006-12-20 03:58:31 +03:00
driver - > update ( unit ) ;
2007-03-19 18:37:16 +03:00
up ( & dev - > sem ) ;
}
2006-12-20 03:58:31 +03:00
return 0 ;
}
2007-03-07 20:12:39 +03:00
static void fw_device_update ( struct work_struct * work )
{
struct fw_device * device =
container_of ( work , struct fw_device , work . work ) ;
2007-03-07 20:12:41 +03:00
fw_device_cdev_update ( device ) ;
2007-03-07 20:12:39 +03:00
device_for_each_child ( & device - > device , NULL , update_unit ) ;
}
2008-03-24 22:54:28 +03:00
enum {
REREAD_BIB_ERROR ,
REREAD_BIB_GONE ,
REREAD_BIB_UNCHANGED ,
REREAD_BIB_CHANGED ,
} ;
/* Reread and compare bus info block and header of root directory */
static int reread_bus_info_block ( struct fw_device * device , int generation )
{
u32 q ;
int i ;
for ( i = 0 ; i < 6 ; i + + ) {
if ( read_rom ( device , generation , i , & q ) ! = RCODE_COMPLETE )
return REREAD_BIB_ERROR ;
if ( i = = 0 & & q = = 0 )
return REREAD_BIB_GONE ;
if ( i > device - > config_rom_length | | q ! = device - > config_rom [ i ] )
return REREAD_BIB_CHANGED ;
}
return REREAD_BIB_UNCHANGED ;
}
static void fw_device_refresh ( struct work_struct * work )
{
struct fw_device * device =
container_of ( work , struct fw_device , work . work ) ;
struct fw_card * card = device - > card ;
int node_id = device - > node_id ;
switch ( reread_bus_info_block ( device , device - > generation ) ) {
case REREAD_BIB_ERROR :
if ( device - > config_rom_retries < MAX_RETRIES / 2 & &
atomic_read ( & device - > state ) = = FW_DEVICE_INITIALIZING ) {
device - > config_rom_retries + + ;
schedule_delayed_work ( & device - > work , RETRY_DELAY / 2 ) ;
return ;
}
goto give_up ;
case REREAD_BIB_GONE :
goto gone ;
case REREAD_BIB_UNCHANGED :
if ( atomic_cmpxchg ( & device - > state ,
FW_DEVICE_INITIALIZING ,
FW_DEVICE_RUNNING ) = = FW_DEVICE_SHUTDOWN )
goto gone ;
fw_device_update ( work ) ;
device - > config_rom_retries = 0 ;
goto out ;
case REREAD_BIB_CHANGED :
break ;
}
/*
* Something changed . We keep things simple and don ' t investigate
* further . We just destroy all previous units and create new ones .
*/
device_for_each_child ( & device - > device , NULL , shutdown_unit ) ;
if ( read_bus_info_block ( device , device - > generation ) < 0 ) {
if ( device - > config_rom_retries < MAX_RETRIES & &
atomic_read ( & device - > state ) = = FW_DEVICE_INITIALIZING ) {
device - > config_rom_retries + + ;
schedule_delayed_work ( & device - > work , RETRY_DELAY ) ;
return ;
}
goto give_up ;
}
create_units ( device ) ;
if ( atomic_cmpxchg ( & device - > state ,
FW_DEVICE_INITIALIZING ,
FW_DEVICE_RUNNING ) = = FW_DEVICE_SHUTDOWN )
goto gone ;
2008-10-30 03:41:56 +03:00
fw_notify ( " refreshed device %s \n " , dev_name ( & device - > device ) ) ;
2008-03-24 22:54:28 +03:00
device - > config_rom_retries = 0 ;
goto out ;
give_up :
2008-10-30 03:41:56 +03:00
fw_notify ( " giving up on refresh of device %s \n " , dev_name ( & device - > device ) ) ;
2008-03-24 22:54:28 +03:00
gone :
atomic_set ( & device - > state , FW_DEVICE_SHUTDOWN ) ;
fw_device_shutdown ( work ) ;
out :
if ( node_id = = card - > root_node - > node_id )
2008-11-29 19:44:57 +03:00
fw_schedule_bm_work ( card , 0 ) ;
2008-03-24 22:54:28 +03:00
}
2006-12-20 03:58:31 +03:00
void fw_node_event ( struct fw_card * card , struct fw_node * node , int event )
{
struct fw_device * device ;
switch ( event ) {
case FW_NODE_CREATED :
case FW_NODE_LINK_ON :
if ( ! node - > link_on )
break ;
2008-03-24 22:54:28 +03:00
create :
2006-12-20 03:58:31 +03:00
device = kzalloc ( sizeof ( * device ) , GFP_ATOMIC ) ;
if ( device = = NULL )
break ;
2007-05-08 04:33:32 +04:00
/*
* Do minimal intialization of the device here , the
2009-01-09 22:49:37 +03:00
* rest will happen in fw_device_init ( ) .
*
* Attention : A lot of things , even fw_device_get ( ) ,
* cannot be done before fw_device_init ( ) finished !
* You can basically just check device - > state and
* schedule work until then , but only while holding
* card - > lock .
2007-05-08 04:33:32 +04:00
*/
2007-01-27 12:34:55 +03:00
atomic_set ( & device - > state , FW_DEVICE_INITIALIZING ) ;
2008-05-24 18:50:22 +04:00
device - > card = fw_card_get ( card ) ;
2006-12-20 03:58:31 +03:00
device - > node = fw_node_get ( node ) ;
device - > node_id = node - > node_id ;
device - > generation = card - > generation ;
2007-03-07 20:12:41 +03:00
INIT_LIST_HEAD ( & device - > client_list ) ;
2006-12-20 03:58:31 +03:00
2007-05-08 04:33:32 +04:00
/*
* Set the node data to point back to this device so
2006-12-20 03:58:31 +03:00
* FW_NODE_UPDATED callbacks can update the node_id
2007-05-08 04:33:32 +04:00
* and generation for the device .
*/
2006-12-20 03:58:31 +03:00
node - > data = device ;
2007-05-08 04:33:32 +04:00
/*
* Many devices are slow to respond after bus resets ,
2006-12-20 03:58:31 +03:00
* especially if they are bus powered and go through
* power - up after getting plugged in . We schedule the
2007-05-08 04:33:32 +04:00
* first config rom scan half a second after bus reset .
*/
2006-12-20 03:58:31 +03:00
INIT_DELAYED_WORK ( & device - > work , fw_device_init ) ;
schedule_delayed_work ( & device - > work , INITIAL_DELAY ) ;
break ;
2008-03-24 22:54:28 +03:00
case FW_NODE_INITIATED_RESET :
device = node - > data ;
if ( device = = NULL )
goto create ;
device - > node_id = node - > node_id ;
smp_wmb ( ) ; /* update node_id before generation */
device - > generation = card - > generation ;
if ( atomic_cmpxchg ( & device - > state ,
FW_DEVICE_RUNNING ,
FW_DEVICE_INITIALIZING ) = = FW_DEVICE_RUNNING ) {
PREPARE_DELAYED_WORK ( & device - > work , fw_device_refresh ) ;
schedule_delayed_work ( & device - > work ,
node = = card - > local_node ? 0 : INITIAL_DELAY ) ;
}
break ;
2006-12-20 03:58:31 +03:00
case FW_NODE_UPDATED :
if ( ! node - > link_on | | node - > data = = NULL )
break ;
device = node - > data ;
device - > node_id = node - > node_id ;
2008-01-25 20:57:41 +03:00
smp_wmb ( ) ; /* update node_id before generation */
2006-12-20 03:58:31 +03:00
device - > generation = card - > generation ;
2007-03-07 20:12:39 +03:00
if ( atomic_read ( & device - > state ) = = FW_DEVICE_RUNNING ) {
PREPARE_DELAYED_WORK ( & device - > work , fw_device_update ) ;
schedule_delayed_work ( & device - > work , 0 ) ;
}
2006-12-20 03:58:31 +03:00
break ;
case FW_NODE_DESTROYED :
case FW_NODE_LINK_OFF :
if ( ! node - > data )
break ;
2007-05-08 04:33:32 +04:00
/*
* Destroy the device associated with the node . There
2006-12-20 03:58:31 +03:00
* are two cases here : either the device is fully
* initialized ( FW_DEVICE_RUNNING ) or we ' re in the
* process of reading its config rom
* ( FW_DEVICE_INITIALIZING ) . If it is fully
* initialized we can reuse device - > work to schedule a
* full fw_device_shutdown ( ) . If not , there ' s work
* scheduled to read it ' s config rom , and we just put
* the device in shutdown state to have that code fail
2007-05-08 04:33:32 +04:00
* to create the device .
*/
2006-12-20 03:58:31 +03:00
device = node - > data ;
2007-01-27 12:34:55 +03:00
if ( atomic_xchg ( & device - > state ,
2007-03-07 20:12:39 +03:00
FW_DEVICE_SHUTDOWN ) = = FW_DEVICE_RUNNING ) {
PREPARE_DELAYED_WORK ( & device - > work , fw_device_shutdown ) ;
2006-12-20 03:58:31 +03:00
schedule_delayed_work ( & device - > work , 0 ) ;
}
break ;
}
}