2005-04-17 02:20:36 +04:00
/*
* IEEE 1394 for Linux
*
* Copyright ( C ) 1999 Andreas E . Bombe
*
* This code is licensed under the GPL . See the file COPYING in the root
* directory of the kernel sources for details .
*
*
* Contributions :
*
* Christian Toegel < christian . toegel @ gmx . at >
* unregister address space
*
* Manfred Weihs < weihs @ ict . tuwien . ac . at >
* unregister address space
*
*/
# include <linux/slab.h>
# include <linux/list.h>
# include <linux/bitops.h>
# include "ieee1394.h"
# include "ieee1394_types.h"
# include "hosts.h"
# include "ieee1394_core.h"
# include "highlevel.h"
# include "nodemgr.h"
struct hl_host_info {
struct list_head list ;
struct hpsb_host * host ;
size_t size ;
unsigned long key ;
void * data ;
} ;
static LIST_HEAD ( hl_drivers ) ;
static DECLARE_RWSEM ( hl_drivers_sem ) ;
static LIST_HEAD ( hl_irqs ) ;
static DEFINE_RWLOCK ( hl_irqs_lock ) ;
static DEFINE_RWLOCK ( addr_space_lock ) ;
static struct hl_host_info * hl_get_hostinfo ( struct hpsb_highlevel * hl ,
2006-06-13 02:16:16 +04:00
struct hpsb_host * host )
2005-04-17 02:20:36 +04:00
{
struct hl_host_info * hi = NULL ;
if ( ! hl | | ! host )
return NULL ;
read_lock ( & hl - > host_info_lock ) ;
list_for_each_entry ( hi , & hl - > host_info_list , list ) {
if ( hi - > host = = host ) {
read_unlock ( & hl - > host_info_lock ) ;
return hi ;
}
}
read_unlock ( & hl - > host_info_lock ) ;
return NULL ;
}
2007-03-05 05:06:23 +03:00
/**
* hpsb_get_hostinfo - retrieve a hostinfo pointer bound to this driver / host
*
* Returns a per @ host and @ hl driver data structure that was previously stored
* by hpsb_create_hostinfo .
*/
2005-04-17 02:20:36 +04:00
void * hpsb_get_hostinfo ( struct hpsb_highlevel * hl , struct hpsb_host * host )
{
struct hl_host_info * hi = hl_get_hostinfo ( hl , host ) ;
2006-06-13 02:16:16 +04:00
return hi ? hi - > data : NULL ;
2005-04-17 02:20:36 +04:00
}
2007-03-05 05:06:23 +03:00
/**
* hpsb_create_hostinfo - allocate a hostinfo pointer bound to this driver / host
*
* Allocate a hostinfo pointer backed by memory with @ data_size and bind it to
* to this @ hl driver and @ host . If @ data_size is zero , then the return here is
* only valid for error checking .
*/
2005-04-17 02:20:36 +04:00
void * hpsb_create_hostinfo ( struct hpsb_highlevel * hl , struct hpsb_host * host ,
size_t data_size )
{
struct hl_host_info * hi ;
void * data ;
unsigned long flags ;
hi = hl_get_hostinfo ( hl , host ) ;
if ( hi ) {
2006-06-13 02:16:16 +04:00
HPSB_ERR ( " %s called hpsb_create_hostinfo when hostinfo already "
" exists " , hl - > name ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
2005-11-07 14:31:45 +03:00
hi = kzalloc ( sizeof ( * hi ) + data_size , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( ! hi )
return NULL ;
if ( data_size ) {
data = hi - > data = hi + 1 ;
hi - > size = data_size ;
} else
data = hi ;
hi - > host = host ;
write_lock_irqsave ( & hl - > host_info_lock , flags ) ;
list_add_tail ( & hi - > list , & hl - > host_info_list ) ;
write_unlock_irqrestore ( & hl - > host_info_lock , flags ) ;
return data ;
}
2007-03-05 05:06:23 +03:00
/**
* hpsb_set_hostinfo - set the hostinfo pointer to something useful
*
* Usually follows a call to hpsb_create_hostinfo , where the size is 0.
*/
2005-04-17 02:20:36 +04:00
int hpsb_set_hostinfo ( struct hpsb_highlevel * hl , struct hpsb_host * host ,
void * data )
{
struct hl_host_info * hi ;
hi = hl_get_hostinfo ( hl , host ) ;
if ( hi ) {
if ( ! hi - > size & & ! hi - > data ) {
hi - > data = data ;
return 0 ;
} else
2006-06-13 02:16:16 +04:00
HPSB_ERR ( " %s called hpsb_set_hostinfo when hostinfo "
" already has data " , hl - > name ) ;
2005-04-17 02:20:36 +04:00
} else
HPSB_ERR ( " %s called hpsb_set_hostinfo when no hostinfo exists " ,
hl - > name ) ;
return - EINVAL ;
}
2007-03-05 05:06:23 +03:00
/**
* hpsb_destroy_hostinfo - free and remove a hostinfo pointer
*
* Free and remove the hostinfo pointer bound to this @ hl driver and @ host .
*/
2005-04-17 02:20:36 +04:00
void hpsb_destroy_hostinfo ( struct hpsb_highlevel * hl , struct hpsb_host * host )
{
struct hl_host_info * hi ;
hi = hl_get_hostinfo ( hl , host ) ;
if ( hi ) {
unsigned long flags ;
write_lock_irqsave ( & hl - > host_info_lock , flags ) ;
list_del ( & hi - > list ) ;
write_unlock_irqrestore ( & hl - > host_info_lock , flags ) ;
kfree ( hi ) ;
}
return ;
}
2007-03-05 05:06:23 +03:00
/**
* hpsb_set_hostinfo_key - set an alternate lookup key for an hostinfo
*
* Sets an alternate lookup key for the hostinfo bound to this @ hl driver and
* @ host .
*/
2006-06-13 02:16:16 +04:00
void hpsb_set_hostinfo_key ( struct hpsb_highlevel * hl , struct hpsb_host * host ,
unsigned long key )
2005-04-17 02:20:36 +04:00
{
struct hl_host_info * hi ;
hi = hl_get_hostinfo ( hl , host ) ;
if ( hi )
hi - > key = key ;
return ;
}
2007-03-05 05:06:23 +03:00
/**
* hpsb_get_hostinfo_bykey - retrieve a hostinfo pointer by its alternate key
*/
2005-04-17 02:20:36 +04:00
void * hpsb_get_hostinfo_bykey ( struct hpsb_highlevel * hl , unsigned long key )
{
struct hl_host_info * hi ;
void * data = NULL ;
if ( ! hl )
return NULL ;
read_lock ( & hl - > host_info_lock ) ;
list_for_each_entry ( hi , & hl - > host_info_list , list ) {
if ( hi - > key = = key ) {
data = hi - > data ;
break ;
}
}
read_unlock ( & hl - > host_info_lock ) ;
return data ;
}
static int highlevel_for_each_host_reg ( struct hpsb_host * host , void * __data )
{
struct hpsb_highlevel * hl = __data ;
hl - > add_host ( host ) ;
2006-06-13 02:16:16 +04:00
if ( host - > update_config_rom & & hpsb_update_config_rom_image ( host ) < 0 )
HPSB_ERR ( " Failed to generate Configuration ROM image for host "
" %s-%d " , hl - > name , host - > id ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-03-05 05:06:23 +03:00
/**
* hpsb_register_highlevel - register highlevel driver
*
* The name pointer in @ hl has to stay valid at all times because the string is
* not copied .
*/
2005-04-17 02:20:36 +04:00
void hpsb_register_highlevel ( struct hpsb_highlevel * hl )
{
2006-06-13 02:16:01 +04:00
unsigned long flags ;
2008-05-20 00:07:28 +04:00
hpsb_init_highlevel ( hl ) ;
2006-06-13 02:16:16 +04:00
INIT_LIST_HEAD ( & hl - > addr_list ) ;
2005-04-17 02:20:36 +04:00
down_write ( & hl_drivers_sem ) ;
2006-06-13 02:16:16 +04:00
list_add_tail ( & hl - > hl_list , & hl_drivers ) ;
2005-04-17 02:20:36 +04:00
up_write ( & hl_drivers_sem ) ;
2006-06-13 02:16:01 +04:00
write_lock_irqsave ( & hl_irqs_lock , flags ) ;
2005-04-17 02:20:36 +04:00
list_add_tail ( & hl - > irq_list , & hl_irqs ) ;
2006-06-13 02:16:01 +04:00
write_unlock_irqrestore ( & hl_irqs_lock , flags ) ;
2005-04-17 02:20:36 +04:00
if ( hl - > add_host )
nodemgr_for_each_host ( hl , highlevel_for_each_host_reg ) ;
2006-06-13 02:16:16 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
static void __delete_addr ( struct hpsb_address_serve * as )
{
list_del ( & as - > host_list ) ;
list_del ( & as - > hl_list ) ;
kfree ( as ) ;
}
2006-06-13 02:16:16 +04:00
static void __unregister_host ( struct hpsb_highlevel * hl , struct hpsb_host * host ,
int update_cr )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
struct list_head * lh , * next ;
struct hpsb_address_serve * as ;
/* First, let the highlevel driver unreg */
if ( hl - > remove_host )
hl - > remove_host ( host ) ;
/* Remove any addresses that are matched for this highlevel driver
* and this particular host . */
write_lock_irqsave ( & addr_space_lock , flags ) ;
list_for_each_safe ( lh , next , & hl - > addr_list ) {
as = list_entry ( lh , struct hpsb_address_serve , hl_list ) ;
if ( as - > host = = host )
__delete_addr ( as ) ;
}
write_unlock_irqrestore ( & addr_space_lock , flags ) ;
/* Now update the config-rom to reflect anything removed by the
* highlevel driver . */
2006-06-13 02:16:16 +04:00
if ( update_cr & & host - > update_config_rom & &
hpsb_update_config_rom_image ( host ) < 0 )
HPSB_ERR ( " Failed to generate Configuration ROM image for host "
" %s-%d " , hl - > name , host - > id ) ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
/* Finally remove all the host info associated between these two. */
2005-04-17 02:20:36 +04:00
hpsb_destroy_hostinfo ( hl , host ) ;
}
static int highlevel_for_each_host_unreg ( struct hpsb_host * host , void * __data )
{
struct hpsb_highlevel * hl = __data ;
__unregister_host ( hl , host , 1 ) ;
return 0 ;
}
2007-03-05 05:06:23 +03:00
/**
* hpsb_unregister_highlevel - unregister highlevel driver
*/
2005-04-17 02:20:36 +04:00
void hpsb_unregister_highlevel ( struct hpsb_highlevel * hl )
{
2006-06-13 02:16:01 +04:00
unsigned long flags ;
write_lock_irqsave ( & hl_irqs_lock , flags ) ;
2005-04-17 02:20:36 +04:00
list_del ( & hl - > irq_list ) ;
2006-06-13 02:16:01 +04:00
write_unlock_irqrestore ( & hl_irqs_lock , flags ) ;
2005-04-17 02:20:36 +04:00
down_write ( & hl_drivers_sem ) ;
2006-06-13 02:16:16 +04:00
list_del ( & hl - > hl_list ) ;
2005-04-17 02:20:36 +04:00
up_write ( & hl_drivers_sem ) ;
nodemgr_for_each_host ( hl , highlevel_for_each_host_unreg ) ;
}
2007-03-05 05:06:23 +03:00
/**
* hpsb_allocate_and_register_addrspace - alloc ' and reg ' a host address space
*
* @ start and @ end are 48 bit pointers and have to be quadlet aligned .
* @ end points to the first address behind the handled addresses . This
* function can be called multiple times for a single hpsb_highlevel @ hl to
* implement sparse register sets . The requested region must not overlap any
* previously allocated region , otherwise registering will fail .
*
* It returns true for successful allocation . Address spaces can be
* unregistered with hpsb_unregister_addrspace . All remaining address spaces
* are automatically deallocated together with the hpsb_highlevel @ hl .
*/
2005-04-17 02:20:36 +04:00
u64 hpsb_allocate_and_register_addrspace ( struct hpsb_highlevel * hl ,
struct hpsb_host * host ,
2008-11-26 03:35:21 +03:00
const struct hpsb_address_ops * ops ,
2005-04-17 02:20:36 +04:00
u64 size , u64 alignment ,
u64 start , u64 end )
{
struct hpsb_address_serve * as , * a1 , * a2 ;
struct list_head * entry ;
2006-06-13 02:15:31 +04:00
u64 retval = CSR1212_INVALID_ADDR_SPACE ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
u64 align_mask = ~ ( alignment - 1 ) ;
if ( ( alignment & 3 ) | | ( alignment > 0x800000000000ULL ) | |
2006-03-26 13:39:56 +04:00
( hweight64 ( alignment ) ! = 1 ) ) {
2005-04-17 02:20:36 +04:00
HPSB_ERR ( " %s called with invalid alignment: 0x%048llx " ,
2008-03-06 05:24:54 +03:00
__func__ , ( unsigned long long ) alignment ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2006-06-13 02:13:21 +04:00
/* default range,
* avoids controller ' s posted write area ( see OHCI 1.1 clause 1.5 ) */
2006-06-13 02:15:31 +04:00
if ( start = = CSR1212_INVALID_ADDR_SPACE & &
end = = CSR1212_INVALID_ADDR_SPACE ) {
2006-06-13 02:13:21 +04:00
start = host - > middle_addr_space ;
2006-06-13 02:15:31 +04:00
end = CSR1212_ALL_SPACE_END ;
2005-04-17 02:20:36 +04:00
}
2006-06-13 02:16:16 +04:00
if ( ( ( start | end ) & ~ align_mask ) | | ( start > = end ) | |
( end > CSR1212_ALL_SPACE_END ) ) {
HPSB_ERR ( " %s called with invalid addresses "
2008-03-06 05:24:54 +03:00
" (start = %012Lx end = %012Lx) " , __func__ ,
2006-06-13 02:16:16 +04:00
( unsigned long long ) start , ( unsigned long long ) end ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2005-11-07 14:31:45 +03:00
as = kmalloc ( sizeof ( * as ) , GFP_KERNEL ) ;
if ( ! as )
2005-04-17 02:20:36 +04:00
return retval ;
INIT_LIST_HEAD ( & as - > host_list ) ;
INIT_LIST_HEAD ( & as - > hl_list ) ;
as - > op = ops ;
as - > host = host ;
write_lock_irqsave ( & addr_space_lock , flags ) ;
list_for_each ( entry , & host - > addr_space ) {
u64 a1sa , a1ea ;
u64 a2sa , a2ea ;
a1 = list_entry ( entry , struct hpsb_address_serve , host_list ) ;
2006-06-13 02:16:16 +04:00
a2 = list_entry ( entry - > next , struct hpsb_address_serve ,
host_list ) ;
2005-04-17 02:20:36 +04:00
a1sa = a1 - > start & align_mask ;
a1ea = ( a1 - > end + alignment - 1 ) & align_mask ;
a2sa = a2 - > start & align_mask ;
a2ea = ( a2 - > end + alignment - 1 ) & align_mask ;
2006-06-13 02:16:16 +04:00
if ( ( a2sa - a1ea > = size ) & & ( a2sa - start > = size ) & &
( a2sa > start ) ) {
2005-04-17 02:20:36 +04:00
as - > start = max ( start , a1ea ) ;
as - > end = as - > start + size ;
list_add ( & as - > host_list , entry ) ;
list_add_tail ( & as - > hl_list , & hl - > addr_list ) ;
retval = as - > start ;
break ;
}
}
write_unlock_irqrestore ( & addr_space_lock , flags ) ;
2006-06-13 02:16:16 +04:00
if ( retval = = CSR1212_INVALID_ADDR_SPACE )
2005-04-17 02:20:36 +04:00
kfree ( as ) ;
return retval ;
}
2007-03-05 05:06:23 +03:00
/**
* hpsb_register_addrspace - register a host address space
*
* @ start and @ end are 48 bit pointers and have to be quadlet aligned .
* @ end points to the first address behind the handled addresses . This
* function can be called multiple times for a single hpsb_highlevel @ hl to
* implement sparse register sets . The requested region must not overlap any
* previously allocated region , otherwise registering will fail .
*
* It returns true for successful allocation . Address spaces can be
* unregistered with hpsb_unregister_addrspace . All remaining address spaces
* are automatically deallocated together with the hpsb_highlevel @ hl .
*/
2005-04-17 02:20:36 +04:00
int hpsb_register_addrspace ( struct hpsb_highlevel * hl , struct hpsb_host * host ,
2008-11-26 03:35:21 +03:00
const struct hpsb_address_ops * ops ,
u64 start , u64 end )
2005-04-17 02:20:36 +04:00
{
2006-06-13 02:16:16 +04:00
struct hpsb_address_serve * as ;
2005-04-17 02:20:36 +04:00
struct list_head * lh ;
2006-06-13 02:16:16 +04:00
int retval = 0 ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
if ( ( ( start | end ) & 3 ) | | ( start > = end ) | |
( end > CSR1212_ALL_SPACE_END ) ) {
2008-03-06 05:24:54 +03:00
HPSB_ERR ( " %s called with invalid addresses " , __func__ ) ;
2006-06-13 02:16:16 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2008-11-26 03:34:55 +03:00
as = kmalloc ( sizeof ( * as ) , GFP_KERNEL ) ;
2005-11-07 14:31:45 +03:00
if ( ! as )
return 0 ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
INIT_LIST_HEAD ( & as - > host_list ) ;
INIT_LIST_HEAD ( & as - > hl_list ) ;
as - > op = ops ;
as - > start = start ;
as - > end = end ;
2005-04-17 02:20:36 +04:00
as - > host = host ;
write_lock_irqsave ( & addr_space_lock , flags ) ;
list_for_each ( lh , & host - > addr_space ) {
struct hpsb_address_serve * as_this =
list_entry ( lh , struct hpsb_address_serve , host_list ) ;
struct hpsb_address_serve * as_next =
2006-06-13 02:16:16 +04:00
list_entry ( lh - > next , struct hpsb_address_serve ,
host_list ) ;
2005-04-17 02:20:36 +04:00
if ( as_this - > end > as - > start )
break ;
if ( as_next - > start > = as - > end ) {
list_add ( & as - > host_list , lh ) ;
list_add_tail ( & as - > hl_list , & hl - > addr_list ) ;
retval = 1 ;
break ;
}
}
write_unlock_irqrestore ( & addr_space_lock , flags ) ;
if ( retval = = 0 )
kfree ( as ) ;
2006-06-13 02:16:16 +04:00
return retval ;
2005-04-17 02:20:36 +04:00
}
int hpsb_unregister_addrspace ( struct hpsb_highlevel * hl , struct hpsb_host * host ,
2006-06-13 02:16:16 +04:00
u64 start )
2005-04-17 02:20:36 +04:00
{
2006-06-13 02:16:16 +04:00
int retval = 0 ;
struct hpsb_address_serve * as ;
struct list_head * lh , * next ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
write_lock_irqsave ( & addr_space_lock , flags ) ;
2005-04-17 02:20:36 +04:00
list_for_each_safe ( lh , next , & hl - > addr_list ) {
2006-06-13 02:16:16 +04:00
as = list_entry ( lh , struct hpsb_address_serve , hl_list ) ;
if ( as - > start = = start & & as - > host = = host ) {
2005-04-17 02:20:36 +04:00
__delete_addr ( as ) ;
2006-06-13 02:16:16 +04:00
retval = 1 ;
break ;
}
}
write_unlock_irqrestore ( & addr_space_lock , flags ) ;
return retval ;
2005-04-17 02:20:36 +04:00
}
2008-11-26 03:35:21 +03:00
const static struct hpsb_address_ops dummy_ops ;
2008-11-26 03:34:25 +03:00
/* dummy address spaces as lower and upper bounds of the host's a.s. list */
2005-04-17 02:20:36 +04:00
static void init_hpsb_highlevel ( struct hpsb_host * host )
{
2008-11-26 03:34:25 +03:00
INIT_LIST_HEAD ( & host - > dummy_zero_addr . host_list ) ;
INIT_LIST_HEAD ( & host - > dummy_zero_addr . hl_list ) ;
INIT_LIST_HEAD ( & host - > dummy_max_addr . host_list ) ;
INIT_LIST_HEAD ( & host - > dummy_max_addr . hl_list ) ;
2005-04-17 02:20:36 +04:00
2008-11-26 03:34:25 +03:00
host - > dummy_zero_addr . op = host - > dummy_max_addr . op = & dummy_ops ;
2005-04-17 02:20:36 +04:00
2008-11-26 03:34:25 +03:00
host - > dummy_zero_addr . start = host - > dummy_zero_addr . end = 0 ;
host - > dummy_max_addr . start = host - > dummy_max_addr . end = ( ( u64 ) 1 ) < < 48 ;
2005-04-17 02:20:36 +04:00
2008-11-26 03:34:25 +03:00
list_add_tail ( & host - > dummy_zero_addr . host_list , & host - > addr_space ) ;
list_add_tail ( & host - > dummy_max_addr . host_list , & host - > addr_space ) ;
2005-04-17 02:20:36 +04:00
}
void highlevel_add_host ( struct hpsb_host * host )
{
2006-06-13 02:16:16 +04:00
struct hpsb_highlevel * hl ;
2005-04-17 02:20:36 +04:00
init_hpsb_highlevel ( host ) ;
down_read ( & hl_drivers_sem ) ;
2006-06-13 02:16:16 +04:00
list_for_each_entry ( hl , & hl_drivers , hl_list ) {
2005-04-17 02:20:36 +04:00
if ( hl - > add_host )
hl - > add_host ( host ) ;
}
2006-06-13 02:16:16 +04:00
up_read ( & hl_drivers_sem ) ;
if ( host - > update_config_rom & & hpsb_update_config_rom_image ( host ) < 0 )
HPSB_ERR ( " Failed to generate Configuration ROM image for host "
" %s-%d " , hl - > name , host - > id ) ;
2005-04-17 02:20:36 +04:00
}
void highlevel_remove_host ( struct hpsb_host * host )
{
2006-06-13 02:16:16 +04:00
struct hpsb_highlevel * hl ;
2005-04-17 02:20:36 +04:00
down_read ( & hl_drivers_sem ) ;
list_for_each_entry ( hl , & hl_drivers , hl_list )
__unregister_host ( hl , host , 0 ) ;
up_read ( & hl_drivers_sem ) ;
}
void highlevel_host_reset ( struct hpsb_host * host )
{
2006-06-13 02:16:01 +04:00
unsigned long flags ;
2006-06-13 02:16:16 +04:00
struct hpsb_highlevel * hl ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:01 +04:00
read_lock_irqsave ( & hl_irqs_lock , flags ) ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( hl , & hl_irqs , irq_list ) {
2006-06-13 02:16:16 +04:00
if ( hl - > host_reset )
hl - > host_reset ( host ) ;
}
2006-06-13 02:16:01 +04:00
read_unlock_irqrestore ( & hl_irqs_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
void highlevel_fcp_request ( struct hpsb_host * host , int nodeid , int direction ,
void * data , size_t length )
{
2006-06-13 02:16:01 +04:00
unsigned long flags ;
2006-06-13 02:16:16 +04:00
struct hpsb_highlevel * hl ;
int cts = ( ( quadlet_t * ) data ) [ 0 ] > > 4 ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
read_lock_irqsave ( & hl_irqs_lock , flags ) ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( hl , & hl_irqs , irq_list ) {
2006-06-13 02:16:16 +04:00
if ( hl - > fcp_request )
hl - > fcp_request ( host , nodeid , direction , cts , data ,
2005-04-17 02:20:36 +04:00
length ) ;
2006-06-13 02:16:16 +04:00
}
read_unlock_irqrestore ( & hl_irqs_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2007-03-05 05:06:23 +03:00
/*
* highlevel_read , highlevel_write , highlevel_lock , highlevel_lock64 :
*
* These functions are called to handle transactions . They are called when a
* packet arrives . The flags argument contains the second word of the first
* header quadlet of the incoming packet ( containing transaction label , retry
* code , transaction code and priority ) . These functions either return a
* response code or a negative number . In the first case a response will be
* generated . In the latter case , no response will be sent and the driver which
* handled the request will send the response itself .
*/
2006-06-13 02:16:16 +04:00
int highlevel_read ( struct hpsb_host * host , int nodeid , void * data , u64 addr ,
unsigned int length , u16 flags )
2005-04-17 02:20:36 +04:00
{
2006-06-13 02:16:16 +04:00
struct hpsb_address_serve * as ;
unsigned int partlength ;
int rcode = RCODE_ADDRESS_ERROR ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
read_lock ( & addr_space_lock ) ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( as , & host - > addr_space , host_list ) {
if ( as - > start > addr )
break ;
2006-06-13 02:16:16 +04:00
if ( as - > end > addr ) {
partlength = min ( as - > end - addr , ( u64 ) length ) ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
if ( as - > op - > read )
rcode = as - > op - > read ( host , nodeid , data ,
2005-04-17 02:20:36 +04:00
addr , partlength , flags ) ;
2006-06-13 02:16:16 +04:00
else
rcode = RCODE_TYPE_ERROR ;
2005-04-17 02:20:36 +04:00
data + = partlength ;
2006-06-13 02:16:16 +04:00
length - = partlength ;
addr + = partlength ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
if ( ( rcode ! = RCODE_COMPLETE ) | | ! length )
break ;
}
}
read_unlock ( & addr_space_lock ) ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
if ( length & & ( rcode = = RCODE_COMPLETE ) )
rcode = RCODE_ADDRESS_ERROR ;
return rcode ;
2005-04-17 02:20:36 +04:00
}
2006-06-13 02:16:16 +04:00
int highlevel_write ( struct hpsb_host * host , int nodeid , int destid , void * data ,
u64 addr , unsigned int length , u16 flags )
2005-04-17 02:20:36 +04:00
{
2006-06-13 02:16:16 +04:00
struct hpsb_address_serve * as ;
unsigned int partlength ;
int rcode = RCODE_ADDRESS_ERROR ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
read_lock ( & addr_space_lock ) ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( as , & host - > addr_space , host_list ) {
if ( as - > start > addr )
break ;
2006-06-13 02:16:16 +04:00
if ( as - > end > addr ) {
partlength = min ( as - > end - addr , ( u64 ) length ) ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
if ( as - > op - > write )
rcode = as - > op - > write ( host , nodeid , destid ,
data , addr , partlength ,
flags ) ;
else
rcode = RCODE_TYPE_ERROR ;
2005-04-17 02:20:36 +04:00
data + = partlength ;
2006-06-13 02:16:16 +04:00
length - = partlength ;
addr + = partlength ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
if ( ( rcode ! = RCODE_COMPLETE ) | | ! length )
break ;
}
}
read_unlock ( & addr_space_lock ) ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
if ( length & & ( rcode = = RCODE_COMPLETE ) )
rcode = RCODE_ADDRESS_ERROR ;
return rcode ;
2005-04-17 02:20:36 +04:00
}
int highlevel_lock ( struct hpsb_host * host , int nodeid , quadlet_t * store ,
2006-06-13 02:16:16 +04:00
u64 addr , quadlet_t data , quadlet_t arg , int ext_tcode ,
u16 flags )
2005-04-17 02:20:36 +04:00
{
2006-06-13 02:16:16 +04:00
struct hpsb_address_serve * as ;
int rcode = RCODE_ADDRESS_ERROR ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
read_lock ( & addr_space_lock ) ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( as , & host - > addr_space , host_list ) {
if ( as - > start > addr )
break ;
2006-06-13 02:16:16 +04:00
if ( as - > end > addr ) {
if ( as - > op - > lock )
rcode = as - > op - > lock ( host , nodeid , store , addr ,
data , arg , ext_tcode ,
flags ) ;
else
rcode = RCODE_TYPE_ERROR ;
break ;
}
}
read_unlock ( & addr_space_lock ) ;
return rcode ;
2005-04-17 02:20:36 +04:00
}
int highlevel_lock64 ( struct hpsb_host * host , int nodeid , octlet_t * store ,
2006-06-13 02:16:16 +04:00
u64 addr , octlet_t data , octlet_t arg , int ext_tcode ,
u16 flags )
2005-04-17 02:20:36 +04:00
{
2006-06-13 02:16:16 +04:00
struct hpsb_address_serve * as ;
int rcode = RCODE_ADDRESS_ERROR ;
2005-04-17 02:20:36 +04:00
2006-06-13 02:16:16 +04:00
read_lock ( & addr_space_lock ) ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( as , & host - > addr_space , host_list ) {
if ( as - > start > addr )
break ;
2006-06-13 02:16:16 +04:00
if ( as - > end > addr ) {
if ( as - > op - > lock64 )
rcode = as - > op - > lock64 ( host , nodeid , store ,
addr , data , arg ,
ext_tcode , flags ) ;
else
rcode = RCODE_TYPE_ERROR ;
break ;
}
}
read_unlock ( & addr_space_lock ) ;
return rcode ;
2005-04-17 02:20:36 +04:00
}