2007-05-08 04:33:32 +04:00
/*
* Core IEEE1394 transaction logic
2006-12-20 03:58:27 +03:00
*
* Copyright ( C ) 2004 - 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/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/pci.h>
# include <linux/delay.h>
# include <linux/poll.h>
# include <linux/list.h>
# include <linux/kthread.h>
# include <asm/uaccess.h>
# include <asm/semaphore.h>
# include "fw-transaction.h"
# include "fw-topology.h"
2006-12-20 03:58:31 +03:00
# include "fw-device.h"
2006-12-20 03:58:27 +03:00
2007-05-08 04:33:35 +04:00
# define HEADER_PRI(pri) ((pri) << 0)
# define HEADER_TCODE(tcode) ((tcode) << 4)
# define HEADER_RETRY(retry) ((retry) << 8)
# define HEADER_TLABEL(tlabel) ((tlabel) << 10)
# define HEADER_DESTINATION(destination) ((destination) << 16)
# define HEADER_SOURCE(source) ((source) << 16)
# define HEADER_RCODE(rcode) ((rcode) << 12)
# define HEADER_OFFSET_HIGH(offset_high) ((offset_high) << 0)
# define HEADER_DATA_LENGTH(length) ((length) << 16)
# define HEADER_EXTENDED_TCODE(tcode) ((tcode) << 0)
# define HEADER_GET_TCODE(q) (((q) >> 4) & 0x0f)
# define HEADER_GET_TLABEL(q) (((q) >> 10) & 0x3f)
# define HEADER_GET_RCODE(q) (((q) >> 12) & 0x0f)
# define HEADER_GET_DESTINATION(q) (((q) >> 16) & 0xffff)
# define HEADER_GET_SOURCE(q) (((q) >> 16) & 0xffff)
# define HEADER_GET_OFFSET_HIGH(q) (((q) >> 0) & 0xffff)
# define HEADER_GET_DATA_LENGTH(q) (((q) >> 16) & 0xffff)
# define HEADER_GET_EXTENDED_TCODE(q) (((q) >> 0) & 0xffff)
# define PHY_CONFIG_GAP_COUNT(gap_count) (((gap_count) << 16) | (1 << 22))
# define PHY_CONFIG_ROOT_ID(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23))
# define PHY_IDENTIFIER(id) ((id) << 30)
2006-12-20 03:58:27 +03:00
2007-02-06 22:49:32 +03:00
static int
close_transaction ( struct fw_transaction * transaction ,
struct fw_card * card , int rcode ,
u32 * payload , size_t length )
2006-12-20 03:58:27 +03:00
{
2007-02-06 22:49:32 +03:00
struct fw_transaction * t ;
2006-12-20 03:58:27 +03:00
unsigned long flags ;
spin_lock_irqsave ( & card - > lock , flags ) ;
2007-02-06 22:49:32 +03:00
list_for_each_entry ( t , & card - > transaction_list , link ) {
if ( t = = transaction ) {
list_del ( & t - > link ) ;
card - > tlabel_mask & = ~ ( 1 < < t - > tlabel ) ;
break ;
}
}
2006-12-20 03:58:27 +03:00
spin_unlock_irqrestore ( & card - > lock , flags ) ;
2007-02-06 22:49:32 +03:00
if ( & t - > link ! = & card - > transaction_list ) {
t - > callback ( card , rcode , payload , length , t - > callback_data ) ;
return 0 ;
}
return - ENOENT ;
2006-12-20 03:58:27 +03:00
}
2007-05-08 04:33:32 +04:00
/*
* Only valid for transactions that are potentially pending ( ie have
* been sent ) .
*/
2007-02-06 22:49:32 +03:00
int
fw_cancel_transaction ( struct fw_card * card ,
struct fw_transaction * transaction )
{
2007-05-08 04:33:32 +04:00
/*
* Cancel the packet transmission if it ' s still queued . That
2007-02-06 22:49:32 +03:00
* will call the packet transmission callback which cancels
2007-05-08 04:33:32 +04:00
* the transaction .
*/
2007-02-06 22:49:32 +03:00
if ( card - > driver - > cancel_packet ( card , & transaction - > packet ) = = 0 )
return 0 ;
2007-05-08 04:33:32 +04:00
/*
* If the request packet has already been sent , we need to see
* if the transaction is still pending and remove it in that case .
*/
2007-02-06 22:49:32 +03:00
return close_transaction ( transaction , card , RCODE_CANCELLED , NULL , 0 ) ;
}
EXPORT_SYMBOL ( fw_cancel_transaction ) ;
2006-12-20 03:58:27 +03:00
static void
transmit_complete_callback ( struct fw_packet * packet ,
struct fw_card * card , int status )
{
struct fw_transaction * t =
container_of ( packet , struct fw_transaction , packet ) ;
switch ( status ) {
case ACK_COMPLETE :
close_transaction ( t , card , RCODE_COMPLETE , NULL , 0 ) ;
break ;
case ACK_PENDING :
t - > timestamp = packet - > timestamp ;
break ;
case ACK_BUSY_X :
case ACK_BUSY_A :
case ACK_BUSY_B :
close_transaction ( t , card , RCODE_BUSY , NULL , 0 ) ;
break ;
case ACK_DATA_ERROR :
2007-01-26 08:38:34 +03:00
close_transaction ( t , card , RCODE_DATA_ERROR , NULL , 0 ) ;
break ;
2006-12-20 03:58:27 +03:00
case ACK_TYPE_ERROR :
2007-01-26 08:38:34 +03:00
close_transaction ( t , card , RCODE_TYPE_ERROR , NULL , 0 ) ;
2006-12-20 03:58:27 +03:00
break ;
default :
2007-05-08 04:33:32 +04:00
/*
* In this case the ack is really a juju specific
* rcode , so just forward that to the callback .
*/
2007-01-26 08:38:34 +03:00
close_transaction ( t , card , status , NULL , 0 ) ;
2006-12-20 03:58:27 +03:00
break ;
}
}
2007-01-22 21:17:37 +03:00
static void
2007-01-26 08:38:13 +03:00
fw_fill_request ( struct fw_packet * packet , int tcode , int tlabel ,
2007-03-07 20:12:55 +03:00
int node_id , int source_id , int generation , int speed ,
2007-01-26 08:38:13 +03:00
unsigned long long offset , void * payload , size_t length )
2006-12-20 03:58:27 +03:00
{
int ext_tcode ;
if ( tcode > 0x10 ) {
ext_tcode = tcode - 0x10 ;
tcode = TCODE_LOCK_REQUEST ;
} else
ext_tcode = 0 ;
packet - > header [ 0 ] =
2007-05-08 04:33:35 +04:00
HEADER_RETRY ( RETRY_X ) |
HEADER_TLABEL ( tlabel ) |
HEADER_TCODE ( tcode ) |
HEADER_DESTINATION ( node_id ) ;
2006-12-20 03:58:27 +03:00
packet - > header [ 1 ] =
2007-05-08 04:33:35 +04:00
HEADER_OFFSET_HIGH ( offset > > 32 ) | HEADER_SOURCE ( source_id ) ;
2006-12-20 03:58:27 +03:00
packet - > header [ 2 ] =
offset ;
switch ( tcode ) {
case TCODE_WRITE_QUADLET_REQUEST :
packet - > header [ 3 ] = * ( u32 * ) payload ;
packet - > header_length = 16 ;
packet - > payload_length = 0 ;
break ;
case TCODE_LOCK_REQUEST :
case TCODE_WRITE_BLOCK_REQUEST :
packet - > header [ 3 ] =
2007-05-08 04:33:35 +04:00
HEADER_DATA_LENGTH ( length ) |
HEADER_EXTENDED_TCODE ( ext_tcode ) ;
2006-12-20 03:58:27 +03:00
packet - > header_length = 16 ;
packet - > payload = payload ;
packet - > payload_length = length ;
break ;
case TCODE_READ_QUADLET_REQUEST :
packet - > header_length = 12 ;
packet - > payload_length = 0 ;
break ;
case TCODE_READ_BLOCK_REQUEST :
packet - > header [ 3 ] =
2007-05-08 04:33:35 +04:00
HEADER_DATA_LENGTH ( length ) |
HEADER_EXTENDED_TCODE ( ext_tcode ) ;
2006-12-20 03:58:27 +03:00
packet - > header_length = 16 ;
packet - > payload_length = 0 ;
break ;
}
packet - > speed = speed ;
packet - > generation = generation ;
2007-02-06 22:49:32 +03:00
packet - > ack = 0 ;
2006-12-20 03:58:27 +03:00
}
/**
* This function provides low - level access to the IEEE1394 transaction
* logic . Most C programs would use either fw_read ( ) , fw_write ( ) or
* fw_lock ( ) instead - those function are convenience wrappers for
* this function . The fw_send_request ( ) function is primarily
* provided as a flexible , one - stop entry point for languages bindings
* and protocol bindings .
*
* FIXME : Document this function further , in particular the possible
* values for rcode in the callback . In short , we map ACK_COMPLETE to
* RCODE_COMPLETE , internal errors set errno and set rcode to
* RCODE_SEND_ERROR ( which is out of range for standard ieee1394
* rcodes ) . All other rcodes are forwarded unchanged . For all
* errors , payload is NULL , length is 0.
*
* Can not expect the callback to be called before the function
* returns , though this does happen in some cases ( ACK_COMPLETE and
* errors ) .
*
* The payload is only used for write requests and must not be freed
* until the callback has been called .
*
* @ param card the card from which to send the request
* @ param tcode the tcode for this transaction . Do not use
2007-10-20 03:55:04 +04:00
* TCODE_LOCK_REQUEST directly , instead use TCODE_LOCK_MASK_SWAP
2006-12-20 03:58:27 +03:00
* etc . to specify tcode and ext_tcode .
2007-01-23 23:11:43 +03:00
* @ param node_id the destination node ID ( bus ID and PHY ID concatenated )
2006-12-20 03:58:27 +03:00
* @ param generation the generation for which node_id is valid
* @ param speed the speed to use for sending the request
* @ param offset the 48 bit offset on the destination node
* @ param payload the data payload for the request subaction
* @ param length the length in bytes of the data to read
* @ param callback function to be called when the transaction is completed
* @ param callback_data pointer to arbitrary data , which will be
* passed to the callback
*/
void
fw_send_request ( struct fw_card * card , struct fw_transaction * t ,
int tcode , int node_id , int generation , int speed ,
unsigned long long offset ,
void * payload , size_t length ,
fw_transaction_callback_t callback , void * callback_data )
{
unsigned long flags ;
2007-03-07 20:12:55 +03:00
int tlabel , source ;
2006-12-20 03:58:27 +03:00
2007-05-08 04:33:32 +04:00
/*
* Bump the flush timer up 100 ms first of all so we
* don ' t race with a flush timer callback .
*/
2006-12-20 03:58:27 +03:00
mod_timer ( & card - > flush_timer , jiffies + DIV_ROUND_UP ( HZ , 10 ) ) ;
2007-05-08 04:33:32 +04:00
/*
* Allocate tlabel from the bitmap and put the transaction on
* the list while holding the card spinlock .
*/
2006-12-20 03:58:27 +03:00
spin_lock_irqsave ( & card - > lock , flags ) ;
2007-03-07 20:12:55 +03:00
source = card - > node_id ;
2006-12-20 03:58:27 +03:00
tlabel = card - > current_tlabel ;
if ( card - > tlabel_mask & ( 1 < < tlabel ) ) {
spin_unlock_irqrestore ( & card - > lock , flags ) ;
callback ( card , RCODE_SEND_ERROR , NULL , 0 , callback_data ) ;
return ;
}
card - > current_tlabel = ( card - > current_tlabel + 1 ) & 0x1f ;
card - > tlabel_mask | = ( 1 < < tlabel ) ;
list_add_tail ( & t - > link , & card - > transaction_list ) ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
/* Initialize rest of transaction, fill out packet and send it. */
t - > node_id = node_id ;
t - > tlabel = tlabel ;
t - > callback = callback ;
t - > callback_data = callback_data ;
2007-01-26 08:38:13 +03:00
fw_fill_request ( & t - > packet , tcode , t - > tlabel ,
2007-03-07 20:12:55 +03:00
node_id , source , generation ,
speed , offset , payload , length ) ;
2006-12-20 03:58:27 +03:00
t - > packet . callback = transmit_complete_callback ;
card - > driver - > send_request ( card , & t - > packet ) ;
}
EXPORT_SYMBOL ( fw_send_request ) ;
static void
transmit_phy_packet_callback ( struct fw_packet * packet ,
struct fw_card * card , int status )
{
kfree ( packet ) ;
}
static void send_phy_packet ( struct fw_card * card , u32 data , int generation )
{
struct fw_packet * packet ;
2007-05-10 03:23:14 +04:00
packet = kzalloc ( sizeof ( * packet ) , GFP_ATOMIC ) ;
2006-12-20 03:58:27 +03:00
if ( packet = = NULL )
return ;
packet - > header [ 0 ] = data ;
packet - > header [ 1 ] = ~ data ;
packet - > header_length = 8 ;
packet - > payload_length = 0 ;
packet - > speed = SCODE_100 ;
packet - > generation = generation ;
packet - > callback = transmit_phy_packet_callback ;
card - > driver - > send_request ( card , packet ) ;
}
2007-01-26 08:37:50 +03:00
void fw_send_phy_config ( struct fw_card * card ,
int node_id , int generation , int gap_count )
2006-12-20 03:58:27 +03:00
{
u32 q ;
2007-05-08 04:33:35 +04:00
q = PHY_IDENTIFIER ( PHY_PACKET_CONFIG ) |
PHY_CONFIG_ROOT_ID ( node_id ) |
PHY_CONFIG_GAP_COUNT ( gap_count ) ;
2007-01-26 08:37:50 +03:00
2006-12-20 03:58:27 +03:00
send_phy_packet ( card , q , generation ) ;
}
void fw_flush_transactions ( struct fw_card * card )
{
struct fw_transaction * t , * next ;
struct list_head list ;
unsigned long flags ;
INIT_LIST_HEAD ( & list ) ;
spin_lock_irqsave ( & card - > lock , flags ) ;
list_splice_init ( & card - > transaction_list , & list ) ;
card - > tlabel_mask = 0 ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
2007-02-06 22:49:32 +03:00
list_for_each_entry_safe ( t , next , & list , link ) {
card - > driver - > cancel_packet ( card , & t - > packet ) ;
2007-05-08 04:33:32 +04:00
/*
* At this point cancel_packet will never call the
2007-02-06 22:49:32 +03:00
* transaction callback , since we just took all the
2007-05-08 04:33:32 +04:00
* transactions out of the list . So do it here .
*/
2006-12-20 03:58:27 +03:00
t - > callback ( card , RCODE_CANCELLED , NULL , 0 , t - > callback_data ) ;
2007-02-06 22:49:32 +03:00
}
2006-12-20 03:58:27 +03:00
}
static struct fw_address_handler *
lookup_overlapping_address_handler ( struct list_head * list ,
unsigned long long offset , size_t length )
{
struct fw_address_handler * handler ;
list_for_each_entry ( handler , list , link ) {
if ( handler - > offset < offset + length & &
offset < handler - > offset + handler - > length )
return handler ;
}
return NULL ;
}
static struct fw_address_handler *
lookup_enclosing_address_handler ( struct list_head * list ,
unsigned long long offset , size_t length )
{
struct fw_address_handler * handler ;
list_for_each_entry ( handler , list , link ) {
if ( handler - > offset < = offset & &
offset + length < = handler - > offset + handler - > length )
return handler ;
}
return NULL ;
}
static DEFINE_SPINLOCK ( address_handler_lock ) ;
static LIST_HEAD ( address_handler_list ) ;
2007-01-14 17:29:07 +03:00
const struct fw_address_region fw_low_memory_region =
2007-01-21 22:45:32 +03:00
{ . start = 0x000000000000ULL , . end = 0x000100000000ULL , } ;
2007-01-14 17:29:07 +03:00
const struct fw_address_region fw_high_memory_region =
2007-01-21 22:45:32 +03:00
{ . start = 0x000100000000ULL , . end = 0xffffe0000000ULL , } ;
2007-01-14 17:29:07 +03:00
const struct fw_address_region fw_private_region =
2007-01-21 22:45:32 +03:00
{ . start = 0xffffe0000000ULL , . end = 0xfffff0000000ULL , } ;
2007-01-14 17:29:07 +03:00
const struct fw_address_region fw_csr_region =
2007-01-21 22:45:32 +03:00
{ . start = 0xfffff0000000ULL , . end = 0xfffff0000800ULL , } ;
2007-01-14 17:29:07 +03:00
const struct fw_address_region fw_unit_space_region =
2007-01-21 22:45:32 +03:00
{ . start = 0xfffff0000900ULL , . end = 0x1000000000000ULL , } ;
2006-12-20 03:58:27 +03:00
EXPORT_SYMBOL ( fw_low_memory_region ) ;
EXPORT_SYMBOL ( fw_high_memory_region ) ;
EXPORT_SYMBOL ( fw_private_region ) ;
EXPORT_SYMBOL ( fw_csr_region ) ;
EXPORT_SYMBOL ( fw_unit_space_region ) ;
/**
* Allocate a range of addresses in the node space of the OHCI
* controller . When a request is received that falls within the
* specified address range , the specified callback is invoked . The
* parameters passed to the callback give the details of the
2007-07-17 04:10:16 +04:00
* particular request .
*
* Return value : 0 on success , non - zero otherwise .
* The start offset of the handler ' s address region is determined by
* fw_core_add_address_handler ( ) and is returned in handler - > offset .
* The offset is quadlet - aligned .
2006-12-20 03:58:27 +03:00
*/
int
fw_core_add_address_handler ( struct fw_address_handler * handler ,
2007-01-14 17:29:07 +03:00
const struct fw_address_region * region )
2006-12-20 03:58:27 +03:00
{
struct fw_address_handler * other ;
unsigned long flags ;
int ret = - EBUSY ;
spin_lock_irqsave ( & address_handler_lock , flags ) ;
2007-07-17 04:10:16 +04:00
handler - > offset = roundup ( region - > start , 4 ) ;
2006-12-20 03:58:27 +03:00
while ( handler - > offset + handler - > length < = region - > end ) {
other =
lookup_overlapping_address_handler ( & address_handler_list ,
handler - > offset ,
handler - > length ) ;
if ( other ! = NULL ) {
2007-07-17 04:10:16 +04:00
handler - > offset =
roundup ( other - > offset + other - > length , 4 ) ;
2006-12-20 03:58:27 +03:00
} else {
list_add_tail ( & handler - > link , & address_handler_list ) ;
ret = 0 ;
break ;
}
}
spin_unlock_irqrestore ( & address_handler_lock , flags ) ;
return ret ;
}
EXPORT_SYMBOL ( fw_core_add_address_handler ) ;
/**
* Deallocate a range of addresses allocated with fw_allocate . This
* will call the associated callback one last time with a the special
* tcode TCODE_DEALLOCATE , to let the client destroy the registered
* callback data . For convenience , the callback parameters offset and
* length are set to the start and the length respectively for the
* deallocated region , payload is set to NULL .
*/
void fw_core_remove_address_handler ( struct fw_address_handler * handler )
{
unsigned long flags ;
spin_lock_irqsave ( & address_handler_lock , flags ) ;
list_del ( & handler - > link ) ;
spin_unlock_irqrestore ( & address_handler_lock , flags ) ;
}
EXPORT_SYMBOL ( fw_core_remove_address_handler ) ;
struct fw_request {
struct fw_packet response ;
2007-01-26 08:38:13 +03:00
u32 request_header [ 4 ] ;
2006-12-20 03:58:27 +03:00
int ack ;
u32 length ;
u32 data [ 0 ] ;
} ;
static void
free_response_callback ( struct fw_packet * packet ,
struct fw_card * card , int status )
{
struct fw_request * request ;
request = container_of ( packet , struct fw_request , response ) ;
kfree ( request ) ;
}
2007-01-26 08:38:26 +03:00
void
2007-01-26 08:38:13 +03:00
fw_fill_response ( struct fw_packet * response , u32 * request_header ,
int rcode , void * payload , size_t length )
2006-12-20 03:58:27 +03:00
{
int tcode , tlabel , extended_tcode , source , destination ;
2007-05-08 04:33:35 +04:00
tcode = HEADER_GET_TCODE ( request_header [ 0 ] ) ;
tlabel = HEADER_GET_TLABEL ( request_header [ 0 ] ) ;
source = HEADER_GET_DESTINATION ( request_header [ 0 ] ) ;
destination = HEADER_GET_SOURCE ( request_header [ 1 ] ) ;
extended_tcode = HEADER_GET_EXTENDED_TCODE ( request_header [ 3 ] ) ;
2006-12-20 03:58:27 +03:00
response - > header [ 0 ] =
2007-05-08 04:33:35 +04:00
HEADER_RETRY ( RETRY_1 ) |
HEADER_TLABEL ( tlabel ) |
HEADER_DESTINATION ( destination ) ;
2007-01-26 08:38:13 +03:00
response - > header [ 1 ] =
2007-05-08 04:33:35 +04:00
HEADER_SOURCE ( source ) |
HEADER_RCODE ( rcode ) ;
2006-12-20 03:58:27 +03:00
response - > header [ 2 ] = 0 ;
switch ( tcode ) {
case TCODE_WRITE_QUADLET_REQUEST :
case TCODE_WRITE_BLOCK_REQUEST :
2007-05-08 04:33:35 +04:00
response - > header [ 0 ] | = HEADER_TCODE ( TCODE_WRITE_RESPONSE ) ;
2006-12-20 03:58:27 +03:00
response - > header_length = 12 ;
response - > payload_length = 0 ;
break ;
case TCODE_READ_QUADLET_REQUEST :
response - > header [ 0 ] | =
2007-05-08 04:33:35 +04:00
HEADER_TCODE ( TCODE_READ_QUADLET_RESPONSE ) ;
2007-01-26 08:38:26 +03:00
if ( payload ! = NULL )
response - > header [ 3 ] = * ( u32 * ) payload ;
else
response - > header [ 3 ] = 0 ;
2006-12-20 03:58:27 +03:00
response - > header_length = 16 ;
response - > payload_length = 0 ;
break ;
case TCODE_READ_BLOCK_REQUEST :
case TCODE_LOCK_REQUEST :
2007-05-08 04:33:35 +04:00
response - > header [ 0 ] | = HEADER_TCODE ( tcode + 2 ) ;
2006-12-20 03:58:27 +03:00
response - > header [ 3 ] =
2007-05-08 04:33:35 +04:00
HEADER_DATA_LENGTH ( length ) |
HEADER_EXTENDED_TCODE ( extended_tcode ) ;
2006-12-20 03:58:27 +03:00
response - > header_length = 16 ;
2007-01-26 08:38:13 +03:00
response - > payload = payload ;
response - > payload_length = length ;
2006-12-20 03:58:27 +03:00
break ;
default :
BUG ( ) ;
return ;
}
}
2007-01-26 08:38:26 +03:00
EXPORT_SYMBOL ( fw_fill_response ) ;
2006-12-20 03:58:27 +03:00
static struct fw_request *
2007-01-26 08:37:57 +03:00
allocate_request ( struct fw_packet * p )
2006-12-20 03:58:27 +03:00
{
struct fw_request * request ;
u32 * data , length ;
2007-01-26 08:37:57 +03:00
int request_tcode , t ;
2006-12-20 03:58:27 +03:00
2007-05-08 04:33:35 +04:00
request_tcode = HEADER_GET_TCODE ( p - > header [ 0 ] ) ;
2006-12-20 03:58:27 +03:00
switch ( request_tcode ) {
case TCODE_WRITE_QUADLET_REQUEST :
2007-01-26 08:37:57 +03:00
data = & p - > header [ 3 ] ;
2006-12-20 03:58:27 +03:00
length = 4 ;
break ;
case TCODE_WRITE_BLOCK_REQUEST :
case TCODE_LOCK_REQUEST :
2007-01-26 08:37:57 +03:00
data = p - > payload ;
2007-05-08 04:33:35 +04:00
length = HEADER_GET_DATA_LENGTH ( p - > header [ 3 ] ) ;
2006-12-20 03:58:27 +03:00
break ;
case TCODE_READ_QUADLET_REQUEST :
data = NULL ;
length = 4 ;
break ;
case TCODE_READ_BLOCK_REQUEST :
data = NULL ;
2007-05-08 04:33:35 +04:00
length = HEADER_GET_DATA_LENGTH ( p - > header [ 3 ] ) ;
2006-12-20 03:58:27 +03:00
break ;
default :
BUG ( ) ;
return NULL ;
}
2007-05-10 03:23:14 +04:00
request = kmalloc ( sizeof ( * request ) + length , GFP_ATOMIC ) ;
2006-12-20 03:58:27 +03:00
if ( request = = NULL )
return NULL ;
2007-01-26 08:37:57 +03:00
t = ( p - > timestamp & 0x1fff ) + 4000 ;
if ( t > = 8000 )
t = ( p - > timestamp & ~ 0x1fff ) + 0x2000 + t - 8000 ;
else
t = ( p - > timestamp & ~ 0x1fff ) + t ;
request - > response . speed = p - > speed ;
request - > response . timestamp = t ;
request - > response . generation = p - > generation ;
2007-02-06 22:49:32 +03:00
request - > response . ack = 0 ;
2006-12-20 03:58:27 +03:00
request - > response . callback = free_response_callback ;
2007-01-26 08:37:57 +03:00
request - > ack = p - > ack ;
2007-01-26 08:38:26 +03:00
request - > length = length ;
2006-12-20 03:58:27 +03:00
if ( data )
2007-02-17 01:34:37 +03:00
memcpy ( request - > data , data , length ) ;
2006-12-20 03:58:27 +03:00
2007-05-10 03:23:14 +04:00
memcpy ( request - > request_header , p - > header , sizeof ( p - > header ) ) ;
2006-12-20 03:58:27 +03:00
return request ;
}
void
fw_send_response ( struct fw_card * card , struct fw_request * request , int rcode )
{
2007-05-08 04:33:32 +04:00
/*
* Broadcast packets are reported as ACK_COMPLETE , so this
2006-12-20 03:58:27 +03:00
* check is sufficient to ensure we don ' t send response to
2007-05-08 04:33:32 +04:00
* broadcast packets or posted writes .
*/
2007-07-17 04:15:36 +04:00
if ( request - > ack ! = ACK_PENDING ) {
kfree ( request ) ;
2006-12-20 03:58:27 +03:00
return ;
2007-07-17 04:15:36 +04:00
}
2006-12-20 03:58:27 +03:00
2007-01-26 08:38:13 +03:00
if ( rcode = = RCODE_COMPLETE )
fw_fill_response ( & request - > response , request - > request_header ,
rcode , request - > data , request - > length ) ;
else
fw_fill_response ( & request - > response , request - > request_header ,
rcode , NULL , 0 ) ;
2006-12-20 03:58:27 +03:00
card - > driver - > send_response ( card , & request - > response ) ;
}
EXPORT_SYMBOL ( fw_send_response ) ;
void
2007-01-26 08:37:57 +03:00
fw_core_handle_request ( struct fw_card * card , struct fw_packet * p )
2006-12-20 03:58:27 +03:00
{
struct fw_address_handler * handler ;
struct fw_request * request ;
unsigned long long offset ;
unsigned long flags ;
2007-01-26 08:37:57 +03:00
int tcode , destination , source ;
2006-12-20 03:58:27 +03:00
2007-01-26 08:37:57 +03:00
if ( p - > ack ! = ACK_PENDING & & p - > ack ! = ACK_COMPLETE )
2006-12-20 03:58:27 +03:00
return ;
2007-01-26 08:37:57 +03:00
request = allocate_request ( p ) ;
2006-12-20 03:58:27 +03:00
if ( request = = NULL ) {
/* FIXME: send statically allocated busy packet. */
return ;
}
offset =
( ( unsigned long long )
2007-05-08 04:33:35 +04:00
HEADER_GET_OFFSET_HIGH ( p - > header [ 1 ] ) < < 32 ) | p - > header [ 2 ] ;
tcode = HEADER_GET_TCODE ( p - > header [ 0 ] ) ;
destination = HEADER_GET_DESTINATION ( p - > header [ 0 ] ) ;
source = HEADER_GET_SOURCE ( p - > header [ 0 ] ) ;
2006-12-20 03:58:27 +03:00
spin_lock_irqsave ( & address_handler_lock , flags ) ;
handler = lookup_enclosing_address_handler ( & address_handler_list ,
offset , request - > length ) ;
spin_unlock_irqrestore ( & address_handler_lock , flags ) ;
2007-05-08 04:33:32 +04:00
/*
* FIXME : lookup the fw_node corresponding to the sender of
2006-12-20 03:58:27 +03:00
* this request and pass that to the address handler instead
* of the node ID . We may also want to move the address
* allocations to fw_node so we only do this callback if the
2007-05-08 04:33:32 +04:00
* upper layers registered it for this node .
*/
2006-12-20 03:58:27 +03:00
if ( handler = = NULL )
fw_send_response ( card , request , RCODE_ADDRESS_ERROR ) ;
else
handler - > address_callback ( card , request ,
tcode , destination , source ,
2007-01-26 08:37:57 +03:00
p - > generation , p - > speed , offset ,
2006-12-20 03:58:27 +03:00
request - > data , request - > length ,
handler - > callback_data ) ;
}
EXPORT_SYMBOL ( fw_core_handle_request ) ;
void
2007-01-26 08:37:57 +03:00
fw_core_handle_response ( struct fw_card * card , struct fw_packet * p )
2006-12-20 03:58:27 +03:00
{
struct fw_transaction * t ;
unsigned long flags ;
u32 * data ;
size_t data_length ;
int tcode , tlabel , destination , source , rcode ;
2007-05-08 04:33:35 +04:00
tcode = HEADER_GET_TCODE ( p - > header [ 0 ] ) ;
tlabel = HEADER_GET_TLABEL ( p - > header [ 0 ] ) ;
destination = HEADER_GET_DESTINATION ( p - > header [ 0 ] ) ;
source = HEADER_GET_SOURCE ( p - > header [ 1 ] ) ;
rcode = HEADER_GET_RCODE ( p - > header [ 1 ] ) ;
2006-12-20 03:58:27 +03:00
spin_lock_irqsave ( & card - > lock , flags ) ;
list_for_each_entry ( t , & card - > transaction_list , link ) {
if ( t - > node_id = = source & & t - > tlabel = = tlabel ) {
list_del ( & t - > link ) ;
card - > tlabel_mask & = ~ ( 1 < < t - > tlabel ) ;
break ;
}
}
spin_unlock_irqrestore ( & card - > lock , flags ) ;
if ( & t - > link = = & card - > transaction_list ) {
2007-02-06 22:49:30 +03:00
fw_notify ( " Unsolicited response (source %x, tlabel %x) \n " ,
source , tlabel ) ;
2006-12-20 03:58:27 +03:00
return ;
}
2007-05-08 04:33:32 +04:00
/*
* FIXME : sanity check packet , is length correct , does tcodes
* and addresses match .
*/
2006-12-20 03:58:27 +03:00
switch ( tcode ) {
case TCODE_READ_QUADLET_RESPONSE :
2007-01-26 08:37:57 +03:00
data = ( u32 * ) & p - > header [ 3 ] ;
2006-12-20 03:58:27 +03:00
data_length = 4 ;
break ;
case TCODE_WRITE_RESPONSE :
data = NULL ;
data_length = 0 ;
break ;
case TCODE_READ_BLOCK_RESPONSE :
case TCODE_LOCK_RESPONSE :
2007-01-26 08:38:26 +03:00
data = p - > payload ;
2007-05-08 04:33:35 +04:00
data_length = HEADER_GET_DATA_LENGTH ( p - > header [ 3 ] ) ;
2006-12-20 03:58:27 +03:00
break ;
default :
/* Should never happen, this is just to shut up gcc. */
data = NULL ;
data_length = 0 ;
break ;
}
t - > callback ( card , rcode , data , data_length , t - > callback_data ) ;
}
EXPORT_SYMBOL ( fw_core_handle_response ) ;
2007-08-02 22:34:17 +04:00
static const struct fw_address_region topology_map_region =
2007-03-07 20:12:55 +03:00
{ . start = 0xfffff0001000ull , . end = 0xfffff0001400ull , } ;
static void
handle_topology_map ( struct fw_card * card , struct fw_request * request ,
int tcode , int destination , int source ,
int generation , int speed ,
unsigned long long offset ,
void * payload , size_t length , void * callback_data )
{
int i , start , end ;
u32 * map ;
if ( ! TCODE_IS_READ_REQUEST ( tcode ) ) {
fw_send_response ( card , request , RCODE_TYPE_ERROR ) ;
return ;
}
if ( ( offset & 3 ) > 0 | | ( length & 3 ) > 0 ) {
fw_send_response ( card , request , RCODE_ADDRESS_ERROR ) ;
return ;
}
start = ( offset - topology_map_region . start ) / 4 ;
end = start + length / 4 ;
map = payload ;
for ( i = 0 ; i < length / 4 ; i + + )
map [ i ] = cpu_to_be32 ( card - > topology_map [ start + i ] ) ;
fw_send_response ( card , request , RCODE_COMPLETE ) ;
}
static struct fw_address_handler topology_map = {
2007-03-07 20:12:56 +03:00
. length = 0x200 ,
2007-03-07 20:12:55 +03:00
. address_callback = handle_topology_map ,
} ;
2007-08-02 22:34:17 +04:00
static const struct fw_address_region registers_region =
2007-03-07 20:12:56 +03:00
{ . start = 0xfffff0000000ull , . end = 0xfffff0000400ull , } ;
static void
handle_registers ( struct fw_card * card , struct fw_request * request ,
int tcode , int destination , int source ,
int generation , int speed ,
unsigned long long offset ,
void * payload , size_t length , void * callback_data )
{
int reg = offset - CSR_REGISTER_BASE ;
unsigned long long bus_time ;
__be32 * data = payload ;
switch ( reg ) {
case CSR_CYCLE_TIME :
case CSR_BUS_TIME :
if ( ! TCODE_IS_READ_REQUEST ( tcode ) | | length ! = 4 ) {
fw_send_response ( card , request , RCODE_TYPE_ERROR ) ;
break ;
}
bus_time = card - > driver - > get_bus_time ( card ) ;
if ( reg = = CSR_CYCLE_TIME )
* data = cpu_to_be32 ( bus_time ) ;
else
* data = cpu_to_be32 ( bus_time > > 25 ) ;
fw_send_response ( card , request , RCODE_COMPLETE ) ;
break ;
case CSR_BUS_MANAGER_ID :
case CSR_BANDWIDTH_AVAILABLE :
case CSR_CHANNELS_AVAILABLE_HI :
case CSR_CHANNELS_AVAILABLE_LO :
2007-05-08 04:33:32 +04:00
/*
* FIXME : these are handled by the OHCI hardware and
2007-03-07 20:12:56 +03:00
* the stack never sees these request . If we add
* support for a new type of controller that doesn ' t
* handle this in hardware we need to deal with these
2007-05-08 04:33:32 +04:00
* transactions .
*/
2007-03-07 20:12:56 +03:00
BUG ( ) ;
break ;
case CSR_BUSY_TIMEOUT :
/* FIXME: Implement this. */
default :
fw_send_response ( card , request , RCODE_ADDRESS_ERROR ) ;
break ;
}
}
static struct fw_address_handler registers = {
. length = 0x400 ,
. address_callback = handle_registers ,
} ;
2006-12-20 03:58:27 +03:00
MODULE_AUTHOR ( " Kristian Hoegsberg <krh@bitplanet.net> " ) ;
MODULE_DESCRIPTION ( " Core IEEE1394 transaction logic " ) ;
MODULE_LICENSE ( " GPL " ) ;
2007-03-07 20:12:36 +03:00
static const u32 vendor_textual_descriptor [ ] = {
2006-12-20 03:58:27 +03:00
/* textual descriptor leaf () */
2007-03-07 20:12:36 +03:00
0x00060000 ,
2006-12-20 03:58:27 +03:00
0x00000000 ,
0x00000000 ,
0x4c696e75 , /* L i n u */
0x78204669 , /* x F i */
0x72657769 , /* r e w i */
2007-03-07 20:12:36 +03:00
0x72650000 , /* r e */
2006-12-20 03:58:27 +03:00
} ;
2007-03-07 20:12:36 +03:00
static const u32 model_textual_descriptor [ ] = {
/* model descriptor leaf () */
0x00030000 ,
0x00000000 ,
0x00000000 ,
0x4a756a75 , /* J u j u */
} ;
static struct fw_descriptor vendor_id_descriptor = {
. length = ARRAY_SIZE ( vendor_textual_descriptor ) ,
. immediate = 0x03d00d1e ,
2006-12-20 03:58:27 +03:00
. key = 0x81000000 ,
2007-03-07 20:12:36 +03:00
. data = vendor_textual_descriptor ,
} ;
static struct fw_descriptor model_id_descriptor = {
. length = ARRAY_SIZE ( model_textual_descriptor ) ,
. immediate = 0x17000001 ,
. key = 0x81000000 ,
. data = model_textual_descriptor ,
2006-12-20 03:58:27 +03:00
} ;
static int __init fw_core_init ( void )
{
int retval ;
retval = bus_register ( & fw_bus_type ) ;
if ( retval < 0 )
return retval ;
2007-03-07 20:12:44 +03:00
fw_cdev_major = register_chrdev ( 0 , " firewire " , & fw_device_ops ) ;
if ( fw_cdev_major < 0 ) {
bus_unregister ( & fw_bus_type ) ;
return fw_cdev_major ;
}
2007-03-07 20:12:55 +03:00
retval = fw_core_add_address_handler ( & topology_map ,
& topology_map_region ) ;
BUG_ON ( retval < 0 ) ;
2007-03-07 20:12:56 +03:00
retval = fw_core_add_address_handler ( & registers ,
& registers_region ) ;
BUG_ON ( retval < 0 ) ;
2006-12-20 03:58:27 +03:00
/* Add the vendor textual descriptor. */
2007-03-07 20:12:36 +03:00
retval = fw_core_add_descriptor ( & vendor_id_descriptor ) ;
BUG_ON ( retval < 0 ) ;
retval = fw_core_add_descriptor ( & model_id_descriptor ) ;
2006-12-20 03:58:27 +03:00
BUG_ON ( retval < 0 ) ;
return 0 ;
}
static void __exit fw_core_cleanup ( void )
{
2007-03-07 20:12:44 +03:00
unregister_chrdev ( fw_cdev_major , " firewire " ) ;
2006-12-20 03:58:27 +03:00
bus_unregister ( & fw_bus_type ) ;
}
module_init ( fw_core_init ) ;
module_exit ( fw_core_cleanup ) ;