2006-12-19 19:58:27 -05:00
/* -*- c-basic-offset: 8 -*-
*
* fw - transaction . c - core IEEE1394 transaction logic
*
* 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-19 19:58:31 -05:00
# include "fw-device.h"
2006-12-19 19:58:27 -05: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)
2007-01-26 00:38:38 -05:00
# define header_get_rcode(q) (((q) >> 12) & 0x0f)
2006-12-19 19:58:27 -05:00
# 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))
2007-01-23 21:11:43 +01:00
# define phy_config_root_id(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23))
2006-12-19 19:58:27 -05:00
# define phy_identifier(id) ((id) << 30)
static void
close_transaction ( struct fw_transaction * t , struct fw_card * card , int rcode ,
u32 * payload , size_t length )
{
unsigned long flags ;
spin_lock_irqsave ( & card - > lock , flags ) ;
card - > tlabel_mask & = ~ ( 1 < < t - > tlabel ) ;
list_del ( & t - > link ) ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
t - > callback ( card , rcode , payload , length , t - > callback_data ) ;
}
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 00:38:34 -05:00
close_transaction ( t , card , RCODE_DATA_ERROR , NULL , 0 ) ;
break ;
2006-12-19 19:58:27 -05:00
case ACK_TYPE_ERROR :
2007-01-26 00:38:34 -05:00
close_transaction ( t , card , RCODE_TYPE_ERROR , NULL , 0 ) ;
2006-12-19 19:58:27 -05:00
break ;
default :
2007-01-26 00:38:34 -05:00
/* In this case the ack is really a juju specific
* rcode , so just forward that to the callback . */
close_transaction ( t , card , status , NULL , 0 ) ;
2006-12-19 19:58:27 -05:00
break ;
}
}
2007-01-22 19:17:37 +01:00
static void
2007-01-26 00:38:13 -05:00
fw_fill_request ( struct fw_packet * packet , int tcode , int tlabel ,
int node_id , int generation , int speed ,
unsigned long long offset , void * payload , size_t length )
2006-12-19 19:58:27 -05:00
{
int ext_tcode ;
if ( tcode > 0x10 ) {
ext_tcode = tcode - 0x10 ;
tcode = TCODE_LOCK_REQUEST ;
} else
ext_tcode = 0 ;
packet - > header [ 0 ] =
header_retry ( RETRY_X ) |
header_tlabel ( tlabel ) |
header_tcode ( tcode ) |
2007-01-23 21:11:43 +01:00
header_destination ( node_id ) ;
2006-12-19 19:58:27 -05:00
packet - > header [ 1 ] =
header_offset_high ( offset > > 32 ) | header_source ( 0 ) ;
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 ] =
header_data_length ( length ) |
header_extended_tcode ( ext_tcode ) ;
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 ] =
header_data_length ( length ) |
header_extended_tcode ( ext_tcode ) ;
packet - > header_length = 16 ;
packet - > payload_length = 0 ;
break ;
}
packet - > speed = speed ;
packet - > generation = generation ;
}
/**
* 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
* TCODE_LOCK_REQUEST directly , insted use TCODE_LOCK_MASK_SWAP
* etc . to specify tcode and ext_tcode .
2007-01-23 21:11:43 +01:00
* @ param node_id the destination node ID ( bus ID and PHY ID concatenated )
2006-12-19 19:58:27 -05: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 ;
int tlabel ;
/* Bump the flush timer up 100ms first of all so we
* don ' t race with a flush timer callback . */
mod_timer ( & card - > flush_timer , jiffies + DIV_ROUND_UP ( HZ , 10 ) ) ;
/* Allocate tlabel from the bitmap and put the transaction on
* the list while holding the card spinlock . */
spin_lock_irqsave ( & card - > lock , flags ) ;
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 00:38:13 -05:00
fw_fill_request ( & t - > packet , tcode , t - > tlabel ,
node_id , generation , speed , offset , payload , length ) ;
2006-12-19 19:58:27 -05: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 ;
packet = kzalloc ( sizeof * packet , GFP_ATOMIC ) ;
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 00:37:50 -05:00
void fw_send_phy_config ( struct fw_card * card ,
int node_id , int generation , int gap_count )
2006-12-19 19:58:27 -05:00
{
u32 q ;
2007-01-26 00:37:50 -05:00
q = phy_identifier ( PHY_PACKET_CONFIG ) |
phy_config_root_id ( node_id ) |
phy_config_gap_count ( gap_count ) ;
2006-12-19 19:58:27 -05: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 ) ;
list_for_each_entry_safe ( t , next , & list , link )
t - > callback ( card , RCODE_CANCELLED , NULL , 0 , t - > callback_data ) ;
}
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 15:29:07 +01:00
const struct fw_address_region fw_low_memory_region =
2007-01-21 20:45:32 +01:00
{ . start = 0x000000000000ULL , . end = 0x000100000000ULL , } ;
2007-01-14 15:29:07 +01:00
const struct fw_address_region fw_high_memory_region =
2007-01-21 20:45:32 +01:00
{ . start = 0x000100000000ULL , . end = 0xffffe0000000ULL , } ;
2007-01-14 15:29:07 +01:00
const struct fw_address_region fw_private_region =
2007-01-21 20:45:32 +01:00
{ . start = 0xffffe0000000ULL , . end = 0xfffff0000000ULL , } ;
2007-01-14 15:29:07 +01:00
const struct fw_address_region fw_csr_region =
2007-01-21 20:45:32 +01:00
{ . start = 0xfffff0000000ULL , . end = 0xfffff0000800ULL , } ;
2007-01-14 15:29:07 +01:00
const struct fw_address_region fw_unit_space_region =
2007-01-21 20:45:32 +01:00
{ . start = 0xfffff0000900ULL , . end = 0x1000000000000ULL , } ;
2006-12-19 19:58:27 -05: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
* particular request
*/
int
fw_core_add_address_handler ( struct fw_address_handler * handler ,
2007-01-14 15:29:07 +01:00
const struct fw_address_region * region )
2006-12-19 19:58:27 -05:00
{
struct fw_address_handler * other ;
unsigned long flags ;
int ret = - EBUSY ;
spin_lock_irqsave ( & address_handler_lock , flags ) ;
handler - > offset = region - > start ;
while ( handler - > offset + handler - > length < = region - > end ) {
other =
lookup_overlapping_address_handler ( & address_handler_list ,
handler - > offset ,
handler - > length ) ;
if ( other ! = NULL ) {
handler - > offset + = other - > length ;
} 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 00:38:13 -05:00
u32 request_header [ 4 ] ;
2006-12-19 19:58:27 -05: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 00:38:26 -05:00
void
2007-01-26 00:38:13 -05:00
fw_fill_response ( struct fw_packet * response , u32 * request_header ,
int rcode , void * payload , size_t length )
2006-12-19 19:58:27 -05:00
{
int tcode , tlabel , extended_tcode , source , destination ;
2007-01-26 00:38:13 -05: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-19 19:58:27 -05:00
response - > header [ 0 ] =
header_retry ( RETRY_1 ) |
header_tlabel ( tlabel ) |
header_destination ( destination ) ;
2007-01-26 00:38:13 -05:00
response - > header [ 1 ] =
header_source ( source ) |
header_rcode ( rcode ) ;
2006-12-19 19:58:27 -05:00
response - > header [ 2 ] = 0 ;
switch ( tcode ) {
case TCODE_WRITE_QUADLET_REQUEST :
case TCODE_WRITE_BLOCK_REQUEST :
response - > header [ 0 ] | = header_tcode ( TCODE_WRITE_RESPONSE ) ;
response - > header_length = 12 ;
response - > payload_length = 0 ;
break ;
case TCODE_READ_QUADLET_REQUEST :
response - > header [ 0 ] | =
header_tcode ( TCODE_READ_QUADLET_RESPONSE ) ;
2007-01-26 00:38:26 -05:00
if ( payload ! = NULL )
response - > header [ 3 ] = * ( u32 * ) payload ;
else
response - > header [ 3 ] = 0 ;
2006-12-19 19:58:27 -05:00
response - > header_length = 16 ;
response - > payload_length = 0 ;
break ;
case TCODE_READ_BLOCK_REQUEST :
case TCODE_LOCK_REQUEST :
response - > header [ 0 ] | = header_tcode ( tcode + 2 ) ;
response - > header [ 3 ] =
2007-01-26 00:38:13 -05:00
header_data_length ( length ) |
2006-12-19 19:58:27 -05:00
header_extended_tcode ( extended_tcode ) ;
response - > header_length = 16 ;
2007-01-26 00:38:13 -05:00
response - > payload = payload ;
response - > payload_length = length ;
2006-12-19 19:58:27 -05:00
break ;
default :
BUG ( ) ;
return ;
}
}
2007-01-26 00:38:26 -05:00
EXPORT_SYMBOL ( fw_fill_response ) ;
2006-12-19 19:58:27 -05:00
static struct fw_request *
2007-01-26 00:37:57 -05:00
allocate_request ( struct fw_packet * p )
2006-12-19 19:58:27 -05:00
{
struct fw_request * request ;
u32 * data , length ;
2007-01-26 00:37:57 -05:00
int request_tcode , t ;
2006-12-19 19:58:27 -05:00
2007-01-26 00:37:57 -05:00
request_tcode = header_get_tcode ( p - > header [ 0 ] ) ;
2006-12-19 19:58:27 -05:00
switch ( request_tcode ) {
case TCODE_WRITE_QUADLET_REQUEST :
2007-01-26 00:37:57 -05:00
data = & p - > header [ 3 ] ;
2006-12-19 19:58:27 -05:00
length = 4 ;
break ;
case TCODE_WRITE_BLOCK_REQUEST :
case TCODE_LOCK_REQUEST :
2007-01-26 00:37:57 -05:00
data = p - > payload ;
length = header_get_data_length ( p - > header [ 3 ] ) ;
2006-12-19 19:58:27 -05:00
break ;
case TCODE_READ_QUADLET_REQUEST :
data = NULL ;
length = 4 ;
break ;
case TCODE_READ_BLOCK_REQUEST :
data = NULL ;
2007-01-26 00:37:57 -05:00
length = header_get_data_length ( p - > header [ 3 ] ) ;
2006-12-19 19:58:27 -05:00
break ;
default :
BUG ( ) ;
return NULL ;
}
request = kmalloc ( sizeof * request + length , GFP_ATOMIC ) ;
if ( request = = NULL )
return NULL ;
2007-01-26 00:37:57 -05: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 ;
2006-12-19 19:58:27 -05:00
request - > response . callback = free_response_callback ;
2007-01-26 00:37:57 -05:00
request - > ack = p - > ack ;
2007-01-26 00:38:26 -05:00
request - > length = length ;
2006-12-19 19:58:27 -05:00
if ( data )
2007-01-26 00:38:26 -05:00
memcpy ( request - > data , p - > payload , length ) ;
2006-12-19 19:58:27 -05:00
2007-01-26 00:38:13 -05:00
memcpy ( request - > request_header , p - > header , sizeof p - > header ) ;
2006-12-19 19:58:27 -05:00
return request ;
}
void
fw_send_response ( struct fw_card * card , struct fw_request * request , int rcode )
{
/* Broadcast packets are reported as ACK_COMPLETE, so this
* check is sufficient to ensure we don ' t send response to
* broadcast packets or posted writes . */
if ( request - > ack ! = ACK_PENDING )
return ;
2007-01-26 00:38:13 -05: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-19 19:58:27 -05:00
card - > driver - > send_response ( card , & request - > response ) ;
}
EXPORT_SYMBOL ( fw_send_response ) ;
void
2007-01-26 00:37:57 -05:00
fw_core_handle_request ( struct fw_card * card , struct fw_packet * p )
2006-12-19 19:58:27 -05:00
{
struct fw_address_handler * handler ;
struct fw_request * request ;
unsigned long long offset ;
unsigned long flags ;
2007-01-26 00:37:57 -05:00
int tcode , destination , source ;
2006-12-19 19:58:27 -05:00
2007-01-26 00:37:57 -05:00
if ( p - > payload_length > 2048 ) {
2006-12-19 19:58:27 -05:00
/* FIXME: send error response. */
return ;
}
2007-01-26 00:37:57 -05:00
if ( p - > ack ! = ACK_PENDING & & p - > ack ! = ACK_COMPLETE )
2006-12-19 19:58:27 -05:00
return ;
2007-01-26 00:37:57 -05:00
request = allocate_request ( p ) ;
2006-12-19 19:58:27 -05:00
if ( request = = NULL ) {
/* FIXME: send statically allocated busy packet. */
return ;
}
offset =
( ( unsigned long long )
2007-01-26 00:37:57 -05: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-19 19:58:27 -05: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 ) ;
/* FIXME: lookup the fw_node corresponding to the sender of
* 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
* upper layers registered it for this node . */
if ( handler = = NULL )
fw_send_response ( card , request , RCODE_ADDRESS_ERROR ) ;
else
handler - > address_callback ( card , request ,
tcode , destination , source ,
2007-01-26 00:37:57 -05:00
p - > generation , p - > speed , offset ,
2006-12-19 19:58:27 -05:00
request - > data , request - > length ,
handler - > callback_data ) ;
}
EXPORT_SYMBOL ( fw_core_handle_request ) ;
void
2007-01-26 00:37:57 -05:00
fw_core_handle_response ( struct fw_card * card , struct fw_packet * p )
2006-12-19 19:58:27 -05:00
{
struct fw_transaction * t ;
unsigned long flags ;
u32 * data ;
size_t data_length ;
int tcode , tlabel , destination , source , rcode ;
2007-01-26 00:37:57 -05: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-19 19:58:27 -05: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 ) {
fw_notify ( " Unsolicited response \n " ) ;
return ;
}
/* FIXME: sanity check packet, is length correct, does tcodes
* and addresses match . */
switch ( tcode ) {
case TCODE_READ_QUADLET_RESPONSE :
2007-01-26 00:37:57 -05:00
data = ( u32 * ) & p - > header [ 3 ] ;
2006-12-19 19:58:27 -05: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 00:38:26 -05:00
data = p - > payload ;
2007-01-26 00:37:57 -05:00
data_length = header_get_data_length ( p - > header [ 3 ] ) ;
2006-12-19 19:58:27 -05: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 ) ;
MODULE_AUTHOR ( " Kristian Hoegsberg <krh@bitplanet.net> " ) ;
MODULE_DESCRIPTION ( " Core IEEE1394 transaction logic " ) ;
MODULE_LICENSE ( " GPL " ) ;
2007-01-14 15:29:07 +01:00
static const u32 vendor_textual_descriptor_data [ ] = {
2006-12-19 19:58:27 -05:00
/* textual descriptor leaf () */
0x00080000 ,
0x00000000 ,
0x00000000 ,
0x4c696e75 , /* L i n u */
0x78204669 , /* x F i */
0x72657769 , /* r e w i */
0x72652028 , /* r e ( */
0x4a554a55 , /* J U J U */
0x29000000 , /* ) */
} ;
static struct fw_descriptor vendor_textual_descriptor = {
. length = ARRAY_SIZE ( vendor_textual_descriptor_data ) ,
. key = 0x81000000 ,
2007-01-21 20:45:32 +01:00
. data = vendor_textual_descriptor_data ,
2006-12-19 19:58:27 -05:00
} ;
static int __init fw_core_init ( void )
{
int retval ;
retval = bus_register ( & fw_bus_type ) ;
if ( retval < 0 )
return retval ;
/* Add the vendor textual descriptor. */
retval = fw_core_add_descriptor ( & vendor_textual_descriptor ) ;
BUG_ON ( retval < 0 ) ;
return 0 ;
}
static void __exit fw_core_cleanup ( void )
{
bus_unregister ( & fw_bus_type ) ;
}
module_init ( fw_core_init ) ;
module_exit ( fw_core_cleanup ) ;