2009-05-18 13:08:06 -04:00
/*
* IPv4 over IEEE 1394 , per RFC 2734
*
* Copyright ( C ) 2009 Jay Fenlason < fenlason @ redhat . com >
*
* based on eth1394 by Ben Collins et al
*/
2009-06-07 22:57:53 +02:00
# include <linux/bug.h>
2009-05-18 13:08:06 -04:00
# include <linux/device.h>
# include <linux/ethtool.h>
# include <linux/firewire.h>
# include <linux/firewire-constants.h>
# include <linux/highmem.h>
# include <linux/in.h>
# include <linux/ip.h>
2009-06-07 22:57:53 +02:00
# include <linux/jiffies.h>
2009-05-18 13:08:06 -04:00
# include <linux/mod_devicetable.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
2009-06-14 11:45:27 +02:00
# include <linux/mutex.h>
2009-05-18 13:08:06 -04:00
# include <linux/netdevice.h>
# include <linux/skbuff.h>
2009-06-14 11:45:27 +02:00
# include <linux/spinlock.h>
2009-05-18 13:08:06 -04:00
# include <asm/unaligned.h>
# include <net/arp.h>
2009-06-07 22:57:53 +02:00
# define FWNET_MAX_FRAGMENTS 25 /* arbitrary limit */
# define FWNET_ISO_PAGE_COUNT (PAGE_SIZE < 16 * 1024 ? 4 : 2)
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
# define IEEE1394_BROADCAST_CHANNEL 31
# define IEEE1394_ALL_NODES (0xffc0 | 0x003f)
# define IEEE1394_MAX_PAYLOAD_S100 512
# define FWNET_NO_FIFO_ADDR (~0ULL)
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
# define IANA_SPECIFIER_ID 0x00005eU
# define RFC2734_SW_VERSION 0x000001U
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
# define IEEE1394_GASP_HDR_SIZE 8
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
# define RFC2374_UNFRAG_HDR_SIZE 4
# define RFC2374_FRAG_HDR_SIZE 8
# define RFC2374_FRAG_OVERHEAD 4
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
# define RFC2374_HDR_UNFRAG 0 /* unfragmented */
# define RFC2374_HDR_FIRSTFRAG 1 /* first fragment */
# define RFC2374_HDR_LASTFRAG 2 /* last fragment */
# define RFC2374_HDR_INTFRAG 3 /* interior fragment */
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
# define RFC2734_HW_ADDR_LEN 16
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
struct rfc2734_arp {
__be16 hw_type ; /* 0x0018 */
__be16 proto_type ; /* 0x0806 */
u8 hw_addr_len ; /* 16 */
u8 ip_addr_len ; /* 4 */
__be16 opcode ; /* ARP Opcode */
/* Above is exactly the same format as struct arphdr */
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
__be64 s_uniq_id ; /* Sender's 64bit EUI */
u8 max_rec ; /* Sender's max packet size */
u8 sspd ; /* Sender's max speed */
__be16 fifo_hi ; /* hi 16bits of sender's FIFO addr */
__be32 fifo_lo ; /* lo 32bits of sender's FIFO addr */
__be32 sip ; /* Sender's IP Address */
__be32 tip ; /* IP Address of requested hw addr */
} __attribute__ ( ( packed ) ) ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
/* This header format is specific to this driver implementation. */
# define FWNET_ALEN 8
# define FWNET_HLEN 10
struct fwnet_header {
u8 h_dest [ FWNET_ALEN ] ; /* destination address */
__be16 h_proto ; /* packet type ID field */
} __attribute__ ( ( packed ) ) ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
/* IPv4 and IPv6 encapsulation header */
struct rfc2734_header {
2009-05-18 13:08:06 -04:00
u32 w0 ;
u32 w1 ;
} ;
2009-06-07 22:57:53 +02:00
# define fwnet_get_hdr_lf(h) (((h)->w0 & 0xc0000000) >> 30)
# define fwnet_get_hdr_ether_type(h) (((h)->w0 & 0x0000ffff))
# define fwnet_get_hdr_dg_size(h) (((h)->w0 & 0x0fff0000) >> 16)
# define fwnet_get_hdr_fg_off(h) (((h)->w0 & 0x00000fff))
# define fwnet_get_hdr_dgl(h) (((h)->w1 & 0xffff0000) >> 16)
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
# define fwnet_set_hdr_lf(lf) ((lf) << 30)
# define fwnet_set_hdr_ether_type(et) (et)
# define fwnet_set_hdr_dg_size(dgs) ((dgs) << 16)
# define fwnet_set_hdr_fg_off(fgo) (fgo)
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
# define fwnet_set_hdr_dgl(dgl) ((dgl) << 16)
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
static inline void fwnet_make_uf_hdr ( struct rfc2734_header * hdr ,
unsigned ether_type )
{
hdr - > w0 = fwnet_set_hdr_lf ( RFC2374_HDR_UNFRAG )
| fwnet_set_hdr_ether_type ( ether_type ) ;
}
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
static inline void fwnet_make_ff_hdr ( struct rfc2734_header * hdr ,
unsigned ether_type , unsigned dg_size , unsigned dgl )
{
hdr - > w0 = fwnet_set_hdr_lf ( RFC2374_HDR_FIRSTFRAG )
| fwnet_set_hdr_dg_size ( dg_size )
| fwnet_set_hdr_ether_type ( ether_type ) ;
hdr - > w1 = fwnet_set_hdr_dgl ( dgl ) ;
}
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
static inline void fwnet_make_sf_hdr ( struct rfc2734_header * hdr ,
unsigned lf , unsigned dg_size , unsigned fg_off , unsigned dgl )
{
hdr - > w0 = fwnet_set_hdr_lf ( lf )
| fwnet_set_hdr_dg_size ( dg_size )
| fwnet_set_hdr_fg_off ( fg_off ) ;
hdr - > w1 = fwnet_set_hdr_dgl ( dgl ) ;
}
2009-05-18 13:08:06 -04:00
/* This list keeps track of what parts of the datagram have been filled in */
2009-06-07 22:57:53 +02:00
struct fwnet_fragment_info {
struct list_head fi_link ;
2009-05-18 13:08:06 -04:00
u16 offset ;
u16 len ;
} ;
2009-06-07 22:57:53 +02:00
struct fwnet_partial_datagram {
struct list_head pd_link ;
struct list_head fi_list ;
2009-05-18 13:08:06 -04:00
struct sk_buff * skb ;
/* FIXME Why not use skb->data? */
char * pbuf ;
u16 datagram_label ;
u16 ether_type ;
u16 datagram_size ;
} ;
2009-06-14 11:45:27 +02:00
static DEFINE_MUTEX ( fwnet_device_mutex ) ;
static LIST_HEAD ( fwnet_device_list ) ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
struct fwnet_device {
2009-06-14 11:45:27 +02:00
struct list_head dev_link ;
2009-05-18 13:08:06 -04:00
spinlock_t lock ;
2009-06-07 22:57:53 +02:00
enum {
FWNET_BROADCAST_ERROR ,
FWNET_BROADCAST_RUNNING ,
FWNET_BROADCAST_STOPPED ,
} broadcast_state ;
2009-05-18 13:08:06 -04:00
struct fw_iso_context * broadcast_rcv_context ;
struct fw_iso_buffer broadcast_rcv_buffer ;
void * * broadcast_rcv_buffer_ptrs ;
unsigned broadcast_rcv_next_ptr ;
unsigned num_broadcast_rcv_ptrs ;
unsigned rcv_buffer_size ;
/*
* This value is the maximum unfragmented datagram size that can be
* sent by the hardware . It already has the GASP overhead and the
* unfragmented datagram header overhead calculated into it .
*/
unsigned broadcast_xmt_max_payload ;
u16 broadcast_xmt_datagramlabel ;
/*
2009-06-07 22:57:53 +02:00
* The CSR address that remote nodes must send datagrams to for us to
2009-05-18 13:08:06 -04:00
* receive them .
*/
struct fw_address_handler handler ;
u64 local_fifo ;
/* List of packets to be sent */
struct list_head packet_list ;
/*
* List of packets that were broadcasted . When we get an ISO interrupt
* one of them has been sent
*/
struct list_head broadcasted_list ;
/* List of packets that have been sent but not yet acked */
struct list_head sent_list ;
2009-06-14 11:45:27 +02:00
struct list_head peer_list ;
2009-05-18 13:08:06 -04:00
struct fw_card * card ;
2009-06-14 11:45:27 +02:00
struct net_device * netdev ;
} ;
struct fwnet_peer {
struct list_head peer_link ;
struct fwnet_device * dev ;
u64 guid ;
u64 fifo ;
/* guarded by dev->lock */
struct list_head pd_list ; /* received partial datagrams */
unsigned pdg_size ; /* pd_list size */
u16 datagram_label ; /* outgoing datagram label */
unsigned max_payload ; /* includes RFC2374_FRAG_HDR_SIZE overhead */
int node_id ;
int generation ;
unsigned speed ;
2009-05-18 13:08:06 -04:00
} ;
/* This is our task struct. It's used for the packet complete callback. */
2009-06-07 22:57:53 +02:00
struct fwnet_packet_task {
2009-05-18 13:08:06 -04:00
/*
2009-06-07 22:57:53 +02:00
* ptask can actually be on dev - > packet_list , dev - > broadcasted_list ,
* or dev - > sent_list depending on its current state .
2009-05-18 13:08:06 -04:00
*/
2009-06-07 22:57:53 +02:00
struct list_head pt_link ;
2009-05-18 13:08:06 -04:00
struct fw_transaction transaction ;
2009-06-07 22:57:53 +02:00
struct rfc2734_header hdr ;
2009-05-18 13:08:06 -04:00
struct sk_buff * skb ;
2009-06-07 22:57:53 +02:00
struct fwnet_device * dev ;
2009-05-18 13:08:06 -04:00
int outstanding_pkts ;
unsigned max_payload ;
u64 fifo_addr ;
u16 dest_node ;
u8 generation ;
u8 speed ;
} ;
2009-06-07 22:57:53 +02:00
/*
* saddr = = NULL means use device source address .
* daddr = = NULL means leave destination address ( eg unresolved arp ) .
*/
static int fwnet_header_create ( struct sk_buff * skb , struct net_device * net ,
unsigned short type , const void * daddr ,
const void * saddr , unsigned len )
{
struct fwnet_header * h ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
h = ( struct fwnet_header * ) skb_push ( skb , sizeof ( * h ) ) ;
put_unaligned_be16 ( type , & h - > h_proto ) ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
if ( net - > flags & ( IFF_LOOPBACK | IFF_NOARP ) ) {
memset ( h - > h_dest , 0 , net - > addr_len ) ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
return net - > hard_header_len ;
2009-05-18 13:08:06 -04:00
}
if ( daddr ) {
2009-06-07 22:57:53 +02:00
memcpy ( h - > h_dest , daddr , net - > addr_len ) ;
return net - > hard_header_len ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
return - net - > hard_header_len ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
static int fwnet_header_rebuild ( struct sk_buff * skb )
2009-05-18 13:08:06 -04:00
{
2009-06-07 22:57:53 +02:00
struct fwnet_header * h = ( struct fwnet_header * ) skb - > data ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
if ( get_unaligned_be16 ( & h - > h_proto ) = = ETH_P_IP )
return arp_find ( ( unsigned char * ) & h - > h_dest , skb ) ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
fw_notify ( " %s: unable to resolve type %04x addresses \n " ,
skb - > dev - > name , be16_to_cpu ( h - > h_proto ) ) ;
2009-05-18 13:08:06 -04:00
return 0 ;
}
2009-06-07 22:57:53 +02:00
static int fwnet_header_cache ( const struct neighbour * neigh ,
struct hh_cache * hh )
{
struct net_device * net ;
struct fwnet_header * h ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
if ( hh - > hh_type = = cpu_to_be16 ( ETH_P_802_3 ) )
2009-05-18 13:08:06 -04:00
return - 1 ;
2009-06-07 22:57:53 +02:00
net = neigh - > dev ;
h = ( struct fwnet_header * ) ( ( u8 * ) hh - > hh_data + 16 - sizeof ( * h ) ) ;
h - > h_proto = hh - > hh_type ;
memcpy ( h - > h_dest , neigh - > ha , net - > addr_len ) ;
hh - > hh_len = FWNET_HLEN ;
2009-05-18 13:08:06 -04:00
return 0 ;
}
/* Called by Address Resolution module to notify changes in address. */
2009-06-07 22:57:53 +02:00
static void fwnet_header_cache_update ( struct hh_cache * hh ,
const struct net_device * net , const unsigned char * haddr )
{
memcpy ( ( u8 * ) hh - > hh_data + 16 - FWNET_HLEN , haddr , net - > addr_len ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
static int fwnet_header_parse ( const struct sk_buff * skb , unsigned char * haddr )
{
memcpy ( haddr , skb - > dev - > dev_addr , FWNET_ALEN ) ;
return FWNET_ALEN ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
static const struct header_ops fwnet_header_ops = {
. create = fwnet_header_create ,
. rebuild = fwnet_header_rebuild ,
. cache = fwnet_header_cache ,
. cache_update = fwnet_header_cache_update ,
. parse = fwnet_header_parse ,
2009-05-18 13:08:06 -04:00
} ;
/* FIXME: is this correct for all cases? */
2009-06-07 22:57:53 +02:00
static bool fwnet_frag_overlap ( struct fwnet_partial_datagram * pd ,
unsigned offset , unsigned len )
2009-05-18 13:08:06 -04:00
{
2009-06-07 22:57:53 +02:00
struct fwnet_fragment_info * fi ;
2009-05-18 13:08:06 -04:00
unsigned end = offset + len ;
2009-06-07 22:57:53 +02:00
list_for_each_entry ( fi , & pd - > fi_list , fi_link )
if ( offset < fi - > offset + fi - > len & & end > fi - > offset )
2009-05-18 13:08:06 -04:00
return true ;
2009-06-07 22:57:53 +02:00
2009-05-18 13:08:06 -04:00
return false ;
}
/* Assumes that new fragment does not overlap any existing fragments */
2009-06-07 22:57:53 +02:00
static struct fwnet_fragment_info * fwnet_frag_new (
struct fwnet_partial_datagram * pd , unsigned offset , unsigned len )
{
struct fwnet_fragment_info * fi , * fi2 , * new ;
2009-05-18 13:08:06 -04:00
struct list_head * list ;
2009-06-07 22:57:53 +02:00
list = & pd - > fi_list ;
list_for_each_entry ( fi , & pd - > fi_list , fi_link ) {
2009-05-18 13:08:06 -04:00
if ( fi - > offset + fi - > len = = offset ) {
/* The new fragment can be tacked on to the end */
/* Did the new fragment plug a hole? */
2009-06-07 22:57:53 +02:00
fi2 = list_entry ( fi - > fi_link . next ,
struct fwnet_fragment_info , fi_link ) ;
2009-05-18 13:08:06 -04:00
if ( fi - > offset + fi - > len = = fi2 - > offset ) {
/* glue fragments together */
fi - > len + = len + fi2 - > len ;
2009-06-07 22:57:53 +02:00
list_del ( & fi2 - > fi_link ) ;
2009-05-18 13:08:06 -04:00
kfree ( fi2 ) ;
} else {
fi - > len + = len ;
}
2009-06-07 22:57:53 +02:00
2009-05-18 13:08:06 -04:00
return fi ;
}
if ( offset + len = = fi - > offset ) {
/* The new fragment can be tacked on to the beginning */
/* Did the new fragment plug a hole? */
2009-06-07 22:57:53 +02:00
fi2 = list_entry ( fi - > fi_link . prev ,
struct fwnet_fragment_info , fi_link ) ;
2009-05-18 13:08:06 -04:00
if ( fi2 - > offset + fi2 - > len = = fi - > offset ) {
/* glue fragments together */
fi2 - > len + = fi - > len + len ;
2009-06-07 22:57:53 +02:00
list_del ( & fi - > fi_link ) ;
2009-05-18 13:08:06 -04:00
kfree ( fi ) ;
2009-06-07 22:57:53 +02:00
2009-05-18 13:08:06 -04:00
return fi2 ;
}
fi - > offset = offset ;
fi - > len + = len ;
2009-06-07 22:57:53 +02:00
2009-05-18 13:08:06 -04:00
return fi ;
}
if ( offset > fi - > offset + fi - > len ) {
2009-06-07 22:57:53 +02:00
list = & fi - > fi_link ;
2009-05-18 13:08:06 -04:00
break ;
}
if ( offset + len < fi - > offset ) {
2009-06-07 22:57:53 +02:00
list = fi - > fi_link . prev ;
2009-05-18 13:08:06 -04:00
break ;
}
}
new = kmalloc ( sizeof ( * new ) , GFP_ATOMIC ) ;
if ( ! new ) {
2009-06-07 22:57:53 +02:00
fw_error ( " out of memory \n " ) ;
2009-05-18 13:08:06 -04:00
return NULL ;
}
new - > offset = offset ;
new - > len = len ;
2009-06-07 22:57:53 +02:00
list_add ( & new - > fi_link , list ) ;
2009-05-18 13:08:06 -04:00
return new ;
}
2009-06-07 22:57:53 +02:00
static struct fwnet_partial_datagram * fwnet_pd_new ( struct net_device * net ,
struct fwnet_peer * peer , u16 datagram_label , unsigned dg_size ,
void * frag_buf , unsigned frag_off , unsigned frag_len )
{
struct fwnet_partial_datagram * new ;
struct fwnet_fragment_info * fi ;
2009-05-18 13:08:06 -04:00
new = kmalloc ( sizeof ( * new ) , GFP_ATOMIC ) ;
if ( ! new )
goto fail ;
2009-06-07 22:57:53 +02:00
INIT_LIST_HEAD ( & new - > fi_list ) ;
fi = fwnet_frag_new ( new , frag_off , frag_len ) ;
if ( fi = = NULL )
2009-05-18 13:08:06 -04:00
goto fail_w_new ;
2009-06-07 22:57:53 +02:00
2009-05-18 13:08:06 -04:00
new - > datagram_label = datagram_label ;
new - > datagram_size = dg_size ;
2009-06-07 22:57:53 +02:00
new - > skb = dev_alloc_skb ( dg_size + net - > hard_header_len + 15 ) ;
if ( new - > skb = = NULL )
2009-05-18 13:08:06 -04:00
goto fail_w_fi ;
2009-06-07 22:57:53 +02:00
skb_reserve ( new - > skb , ( net - > hard_header_len + 15 ) & ~ 15 ) ;
2009-05-18 13:08:06 -04:00
new - > pbuf = skb_put ( new - > skb , dg_size ) ;
memcpy ( new - > pbuf + frag_off , frag_buf , frag_len ) ;
2009-06-07 22:57:53 +02:00
list_add_tail ( & new - > pd_link , & peer - > pd_list ) ;
2009-05-18 13:08:06 -04:00
return new ;
fail_w_fi :
kfree ( fi ) ;
fail_w_new :
kfree ( new ) ;
fail :
2009-06-07 22:57:53 +02:00
fw_error ( " out of memory \n " ) ;
2009-05-18 13:08:06 -04:00
return NULL ;
}
2009-06-07 22:57:53 +02:00
static struct fwnet_partial_datagram * fwnet_pd_find ( struct fwnet_peer * peer ,
u16 datagram_label )
{
struct fwnet_partial_datagram * pd ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
list_for_each_entry ( pd , & peer - > pd_list , pd_link )
if ( pd - > datagram_label = = datagram_label )
2009-05-18 13:08:06 -04:00
return pd ;
2009-06-07 22:57:53 +02:00
2009-05-18 13:08:06 -04:00
return NULL ;
}
2009-06-07 22:57:53 +02:00
static void fwnet_pd_delete ( struct fwnet_partial_datagram * old )
{
struct fwnet_fragment_info * fi , * n ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
list_for_each_entry_safe ( fi , n , & old - > fi_list , fi_link )
2009-05-18 13:08:06 -04:00
kfree ( fi ) ;
2009-06-07 22:57:53 +02:00
list_del ( & old - > pd_link ) ;
2009-05-18 13:08:06 -04:00
dev_kfree_skb_any ( old - > skb ) ;
kfree ( old ) ;
}
2009-06-07 22:57:53 +02:00
static bool fwnet_pd_update ( struct fwnet_peer * peer ,
struct fwnet_partial_datagram * pd , void * frag_buf ,
unsigned frag_off , unsigned frag_len )
{
if ( fwnet_frag_new ( pd , frag_off , frag_len ) = = NULL )
2009-05-18 13:08:06 -04:00
return false ;
2009-06-07 22:57:53 +02:00
2009-05-18 13:08:06 -04:00
memcpy ( pd - > pbuf + frag_off , frag_buf , frag_len ) ;
/*
* Move list entry to beginnig of list so that oldest partial
* datagrams percolate to the end of the list
*/
2009-06-07 22:57:53 +02:00
list_move_tail ( & pd - > pd_link , & peer - > pd_list ) ;
2009-05-18 13:08:06 -04:00
return true ;
}
2009-06-07 22:57:53 +02:00
static bool fwnet_pd_is_complete ( struct fwnet_partial_datagram * pd )
{
struct fwnet_fragment_info * fi ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
fi = list_entry ( pd - > fi_list . next , struct fwnet_fragment_info , fi_link ) ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
return fi - > len = = pd - > datagram_size ;
2009-05-18 13:08:06 -04:00
}
2009-06-14 11:45:27 +02:00
/* caller must hold dev->lock */
2009-06-07 22:57:53 +02:00
static struct fwnet_peer * fwnet_peer_find_by_guid ( struct fwnet_device * dev ,
u64 guid )
{
2009-06-14 11:45:27 +02:00
struct fwnet_peer * peer ;
2009-05-18 13:08:06 -04:00
2009-06-14 11:45:27 +02:00
list_for_each_entry ( peer , & dev - > peer_list , peer_link )
if ( peer - > guid = = guid )
return peer ;
2009-05-18 13:08:06 -04:00
2009-06-14 11:45:27 +02:00
return NULL ;
2009-05-18 13:08:06 -04:00
}
2009-06-14 11:45:27 +02:00
/* caller must hold dev->lock */
2009-06-07 22:57:53 +02:00
static struct fwnet_peer * fwnet_peer_find_by_node_id ( struct fwnet_device * dev ,
2009-06-14 11:45:27 +02:00
int node_id , int generation )
2009-06-07 22:57:53 +02:00
{
2009-06-14 11:45:27 +02:00
struct fwnet_peer * peer ;
2009-05-18 13:08:06 -04:00
2009-06-14 11:45:27 +02:00
list_for_each_entry ( peer , & dev - > peer_list , peer_link )
if ( peer - > node_id = = node_id & &
peer - > generation = = generation )
return peer ;
2009-06-07 22:57:53 +02:00
2009-06-14 11:45:27 +02:00
return NULL ;
2009-05-18 13:08:06 -04:00
}
2009-06-14 11:45:27 +02:00
/* See IEEE 1394-2008 table 6-4, table 8-8, table 16-18. */
static unsigned fwnet_max_payload ( unsigned max_rec , unsigned speed )
2009-06-07 22:57:53 +02:00
{
2009-06-14 11:45:27 +02:00
max_rec = min ( max_rec , speed + 8 ) ;
max_rec = min ( max_rec , 0xbU ) ; /* <= 4096 */
if ( max_rec < 8 ) {
fw_notify ( " max_rec %x out of range \n " , max_rec ) ;
max_rec = 8 ;
2009-05-18 13:08:06 -04:00
}
2009-06-14 11:45:27 +02:00
return ( 1 < < ( max_rec + 1 ) ) - RFC2374_FRAG_HDR_SIZE ;
2009-05-18 13:08:06 -04:00
}
2009-06-14 11:45:27 +02:00
2009-06-07 22:57:53 +02:00
static int fwnet_finish_incoming_packet ( struct net_device * net ,
struct sk_buff * skb , u16 source_node_id ,
bool is_broadcast , u16 ether_type )
{
struct fwnet_device * dev ;
static const __be64 broadcast_hw = cpu_to_be64 ( ~ 0ULL ) ;
2009-05-18 13:08:06 -04:00
int status ;
2009-06-07 22:57:53 +02:00
__be64 guid ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
dev = netdev_priv ( net ) ;
2009-05-18 13:08:06 -04:00
/* Write metadata, and then pass to the receive level */
2009-06-07 22:57:53 +02:00
skb - > dev = net ;
2009-05-18 13:08:06 -04:00
skb - > ip_summed = CHECKSUM_UNNECESSARY ; /* don't check it */
/*
* Parse the encapsulation header . This actually does the job of
* converting to an ethernet frame header , as well as arp
* conversion if needed . ARP conversion is easier in this
* direction , since we are using ethernet as our backend .
*/
/*
* If this is an ARP packet , convert it . First , we want to make
* use of some of the fields , since they tell us a little bit
* about the sending machine .
*/
if ( ether_type = = ETH_P_ARP ) {
2009-06-07 22:57:53 +02:00
struct rfc2734_arp * arp1394 ;
2009-05-18 13:08:06 -04:00
struct arphdr * arp ;
unsigned char * arp_ptr ;
u64 fifo_addr ;
2009-06-07 22:57:53 +02:00
u64 peer_guid ;
2009-06-14 11:45:27 +02:00
unsigned sspd ;
2009-05-18 13:08:06 -04:00
u16 max_payload ;
2009-06-07 22:57:53 +02:00
struct fwnet_peer * peer ;
2009-06-14 11:45:27 +02:00
unsigned long flags ;
arp1394 = ( struct rfc2734_arp * ) skb - > data ;
arp = ( struct arphdr * ) skb - > data ;
arp_ptr = ( unsigned char * ) ( arp + 1 ) ;
peer_guid = get_unaligned_be64 ( & arp1394 - > s_uniq_id ) ;
fifo_addr = ( u64 ) get_unaligned_be16 ( & arp1394 - > fifo_hi ) < < 32
| get_unaligned_be32 ( & arp1394 - > fifo_lo ) ;
2009-05-18 13:08:06 -04:00
sspd = arp1394 - > sspd ;
2009-06-07 22:57:53 +02:00
/* Sanity check. OS X 10.3 PPC reportedly sends 131. */
if ( sspd > SCODE_3200 ) {
fw_notify ( " sspd %x out of range \n " , sspd ) ;
2009-06-14 11:45:27 +02:00
sspd = SCODE_3200 ;
2009-05-18 13:08:06 -04:00
}
2009-06-14 11:45:27 +02:00
max_payload = fwnet_max_payload ( arp1394 - > max_rec , sspd ) ;
2009-05-18 13:08:06 -04:00
2009-06-14 11:45:27 +02:00
spin_lock_irqsave ( & dev - > lock , flags ) ;
2009-06-07 22:57:53 +02:00
peer = fwnet_peer_find_by_guid ( dev , peer_guid ) ;
2009-06-14 11:45:27 +02:00
if ( peer ) {
peer - > fifo = fifo_addr ;
if ( peer - > speed > sspd )
peer - > speed = sspd ;
if ( peer - > max_payload > max_payload )
peer - > max_payload = max_payload ;
}
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
2009-06-07 22:57:53 +02:00
if ( ! peer ) {
fw_notify ( " No peer for ARP packet from %016llx \n " ,
( unsigned long long ) peer_guid ) ;
2009-05-18 13:08:06 -04:00
goto failed_proto ;
}
2009-06-07 22:57:53 +02:00
2009-05-18 13:08:06 -04:00
/*
* Now that we ' re done with the 1394 specific stuff , we ' ll
* need to alter some of the data . Believe it or not , all
* that needs to be done is sender_IP_address needs to be
* moved , the destination hardware address get stuffed
* in and the hardware address length set to 8.
*
* IMPORTANT : The code below overwrites 1394 specific data
* needed above so keep the munging of the data for the
* higher level IP stack last .
*/
arp - > ar_hln = 8 ;
2009-06-07 22:57:53 +02:00
/* skip over sender unique id */
arp_ptr + = arp - > ar_hln ;
/* move sender IP addr */
put_unaligned ( arp1394 - > sip , ( u32 * ) arp_ptr ) ;
/* skip over sender IP addr */
arp_ptr + = arp - > ar_pln ;
2009-05-18 13:08:06 -04:00
if ( arp - > ar_op = = htons ( ARPOP_REQUEST ) )
memset ( arp_ptr , 0 , sizeof ( u64 ) ) ;
else
2009-06-07 22:57:53 +02:00
memcpy ( arp_ptr , net - > dev_addr , sizeof ( u64 ) ) ;
2009-05-18 13:08:06 -04:00
}
/* Now add the ethernet header. */
2009-06-07 22:57:53 +02:00
guid = cpu_to_be64 ( dev - > card - > guid ) ;
if ( dev_hard_header ( skb , net , ether_type ,
is_broadcast ? & broadcast_hw : & guid ,
NULL , skb - > len ) > = 0 ) {
struct fwnet_header * eth ;
2009-05-18 13:08:06 -04:00
u16 * rawp ;
__be16 protocol ;
skb_reset_mac_header ( skb ) ;
skb_pull ( skb , sizeof ( * eth ) ) ;
2009-06-07 22:57:53 +02:00
eth = ( struct fwnet_header * ) skb_mac_header ( skb ) ;
2009-05-18 13:08:06 -04:00
if ( * eth - > h_dest & 1 ) {
2009-06-07 22:57:53 +02:00
if ( memcmp ( eth - > h_dest , net - > broadcast ,
net - > addr_len ) = = 0 )
2009-05-18 13:08:06 -04:00
skb - > pkt_type = PACKET_BROADCAST ;
#if 0
else
skb - > pkt_type = PACKET_MULTICAST ;
# endif
} else {
2009-06-14 11:46:57 +02:00
if ( memcmp ( eth - > h_dest , net - > dev_addr , net - > addr_len ) )
2009-05-18 13:08:06 -04:00
skb - > pkt_type = PACKET_OTHERHOST ;
}
if ( ntohs ( eth - > h_proto ) > = 1536 ) {
protocol = eth - > h_proto ;
} else {
rawp = ( u16 * ) skb - > data ;
2009-06-07 22:57:53 +02:00
if ( * rawp = = 0xffff )
2009-05-18 13:08:06 -04:00
protocol = htons ( ETH_P_802_3 ) ;
2009-06-07 22:57:53 +02:00
else
2009-05-18 13:08:06 -04:00
protocol = htons ( ETH_P_802_2 ) ;
}
skb - > protocol = protocol ;
}
status = netif_rx ( skb ) ;
2009-06-07 22:57:53 +02:00
if ( status = = NET_RX_DROP ) {
net - > stats . rx_errors + + ;
net - > stats . rx_dropped + + ;
2009-05-18 13:08:06 -04:00
} else {
2009-06-07 22:57:53 +02:00
net - > stats . rx_packets + + ;
net - > stats . rx_bytes + = skb - > len ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
if ( netif_queue_stopped ( net ) )
netif_wake_queue ( net ) ;
2009-05-18 13:08:06 -04:00
return 0 ;
failed_proto :
2009-06-07 22:57:53 +02:00
net - > stats . rx_errors + + ;
net - > stats . rx_dropped + + ;
2009-05-18 13:08:06 -04:00
dev_kfree_skb_any ( skb ) ;
2009-06-07 22:57:53 +02:00
if ( netif_queue_stopped ( net ) )
netif_wake_queue ( net ) ;
2009-05-18 13:08:06 -04:00
return 0 ;
}
2009-06-07 22:57:53 +02:00
static int fwnet_incoming_packet ( struct fwnet_device * dev , __be32 * buf , int len ,
2009-06-14 11:45:27 +02:00
int source_node_id , int generation ,
bool is_broadcast )
2009-06-07 22:57:53 +02:00
{
2009-05-18 13:08:06 -04:00
struct sk_buff * skb ;
2009-06-14 11:45:27 +02:00
struct net_device * net = dev - > netdev ;
2009-06-07 22:57:53 +02:00
struct rfc2734_header hdr ;
2009-05-18 13:08:06 -04:00
unsigned lf ;
unsigned long flags ;
2009-06-07 22:57:53 +02:00
struct fwnet_peer * peer ;
struct fwnet_partial_datagram * pd ;
2009-05-18 13:08:06 -04:00
int fg_off ;
int dg_size ;
u16 datagram_label ;
int retval ;
u16 ether_type ;
2009-06-07 22:57:53 +02:00
hdr . w0 = be32_to_cpu ( buf [ 0 ] ) ;
lf = fwnet_get_hdr_lf ( & hdr ) ;
if ( lf = = RFC2374_HDR_UNFRAG ) {
2009-05-18 13:08:06 -04:00
/*
* An unfragmented datagram has been received by the ieee1394
* bus . Build an skbuff around it so we can pass it to the
* high level network layer .
*/
2009-06-07 22:57:53 +02:00
ether_type = fwnet_get_hdr_ether_type ( & hdr ) ;
2009-05-18 13:08:06 -04:00
buf + + ;
2009-06-07 22:57:53 +02:00
len - = RFC2374_UNFRAG_HDR_SIZE ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
skb = dev_alloc_skb ( len + net - > hard_header_len + 15 ) ;
2009-05-18 13:08:06 -04:00
if ( unlikely ( ! skb ) ) {
2009-06-07 22:57:53 +02:00
fw_error ( " out of memory \n " ) ;
net - > stats . rx_dropped + + ;
2009-05-18 13:08:06 -04:00
return - 1 ;
}
2009-06-07 22:57:53 +02:00
skb_reserve ( skb , ( net - > hard_header_len + 15 ) & ~ 15 ) ;
memcpy ( skb_put ( skb , len ) , buf , len ) ;
return fwnet_finish_incoming_packet ( net , skb , source_node_id ,
is_broadcast , ether_type ) ;
2009-05-18 13:08:06 -04:00
}
/* A datagram fragment has been received, now the fun begins. */
hdr . w1 = ntohl ( buf [ 1 ] ) ;
2009-06-07 22:57:53 +02:00
buf + = 2 ;
len - = RFC2374_FRAG_HDR_SIZE ;
if ( lf = = RFC2374_HDR_FIRSTFRAG ) {
ether_type = fwnet_get_hdr_ether_type ( & hdr ) ;
2009-05-18 13:08:06 -04:00
fg_off = 0 ;
} else {
2009-06-07 22:57:53 +02:00
ether_type = 0 ;
fg_off = fwnet_get_hdr_fg_off ( & hdr ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
datagram_label = fwnet_get_hdr_dgl ( & hdr ) ;
dg_size = fwnet_get_hdr_dg_size ( & hdr ) ; /* ??? + 1 */
2009-06-14 11:45:27 +02:00
spin_lock_irqsave ( & dev - > lock , flags ) ;
peer = fwnet_peer_find_by_node_id ( dev , source_node_id , generation ) ;
if ( ! peer )
goto bad_proto ;
2009-06-07 22:57:53 +02:00
pd = fwnet_pd_find ( peer , datagram_label ) ;
2009-05-18 13:08:06 -04:00
if ( pd = = NULL ) {
2009-06-07 22:57:53 +02:00
while ( peer - > pdg_size > = FWNET_MAX_FRAGMENTS ) {
2009-05-18 13:08:06 -04:00
/* remove the oldest */
2009-06-07 22:57:53 +02:00
fwnet_pd_delete ( list_first_entry ( & peer - > pd_list ,
struct fwnet_partial_datagram , pd_link ) ) ;
peer - > pdg_size - - ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
pd = fwnet_pd_new ( net , peer , datagram_label ,
dg_size , buf , fg_off , len ) ;
if ( pd = = NULL ) {
2009-05-18 13:08:06 -04:00
retval = - ENOMEM ;
goto bad_proto ;
}
2009-06-07 22:57:53 +02:00
peer - > pdg_size + + ;
2009-05-18 13:08:06 -04:00
} else {
2009-06-07 22:57:53 +02:00
if ( fwnet_frag_overlap ( pd , fg_off , len ) | |
pd - > datagram_size ! = dg_size ) {
2009-05-18 13:08:06 -04:00
/*
* Differing datagram sizes or overlapping fragments ,
2009-06-07 22:57:53 +02:00
* discard old datagram and start a new one .
2009-05-18 13:08:06 -04:00
*/
2009-06-07 22:57:53 +02:00
fwnet_pd_delete ( pd ) ;
pd = fwnet_pd_new ( net , peer , datagram_label ,
dg_size , buf , fg_off , len ) ;
if ( pd = = NULL ) {
2009-05-18 13:08:06 -04:00
retval = - ENOMEM ;
2009-06-07 22:57:53 +02:00
peer - > pdg_size - - ;
2009-05-18 13:08:06 -04:00
goto bad_proto ;
}
} else {
2009-06-07 22:57:53 +02:00
if ( ! fwnet_pd_update ( peer , pd , buf , fg_off , len ) ) {
2009-05-18 13:08:06 -04:00
/*
* Couldn ' t save off fragment anyway
* so might as well obliterate the
* datagram now .
*/
2009-06-07 22:57:53 +02:00
fwnet_pd_delete ( pd ) ;
peer - > pdg_size - - ;
2009-05-18 13:08:06 -04:00
goto bad_proto ;
}
}
} /* new datagram or add to existing one */
2009-06-07 22:57:53 +02:00
if ( lf = = RFC2374_HDR_FIRSTFRAG )
2009-05-18 13:08:06 -04:00
pd - > ether_type = ether_type ;
2009-06-07 22:57:53 +02:00
if ( fwnet_pd_is_complete ( pd ) ) {
2009-05-18 13:08:06 -04:00
ether_type = pd - > ether_type ;
2009-06-07 22:57:53 +02:00
peer - > pdg_size - - ;
2009-05-18 13:08:06 -04:00
skb = skb_get ( pd - > skb ) ;
2009-06-07 22:57:53 +02:00
fwnet_pd_delete ( pd ) ;
2009-06-14 11:45:27 +02:00
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
2009-06-07 22:57:53 +02:00
return fwnet_finish_incoming_packet ( net , skb , source_node_id ,
false , ether_type ) ;
2009-05-18 13:08:06 -04:00
}
/*
* Datagram is not complete , we ' re done for the
* moment .
*/
2009-06-14 11:45:27 +02:00
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
2009-06-07 22:57:53 +02:00
2009-05-18 13:08:06 -04:00
return 0 ;
bad_proto :
2009-06-14 11:45:27 +02:00
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
2009-06-07 22:57:53 +02:00
if ( netif_queue_stopped ( net ) )
netif_wake_queue ( net ) ;
2009-05-18 13:08:06 -04:00
return 0 ;
}
2009-06-07 22:57:53 +02:00
static void fwnet_receive_packet ( struct fw_card * card , struct fw_request * r ,
int tcode , int destination , int source , int generation ,
int speed , unsigned long long offset , void * payload ,
size_t length , void * callback_data )
{
2009-06-16 22:35:32 +02:00
struct fwnet_device * dev = callback_data ;
int rcode ;
2009-05-18 13:08:06 -04:00
2009-06-16 22:35:32 +02:00
if ( destination = = IEEE1394_ALL_NODES ) {
kfree ( r ) ;
2009-06-07 22:57:53 +02:00
2009-05-18 13:08:06 -04:00
return ;
}
2009-06-07 22:57:53 +02:00
2009-06-16 22:35:32 +02:00
if ( offset ! = dev - > handler . offset )
rcode = RCODE_ADDRESS_ERROR ;
else if ( tcode ! = TCODE_WRITE_BLOCK_REQUEST )
rcode = RCODE_TYPE_ERROR ;
else if ( fwnet_incoming_packet ( dev , payload , length ,
source , generation , false ) ! = 0 ) {
2009-06-07 22:57:53 +02:00
fw_error ( " Incoming packet failure \n " ) ;
2009-06-16 22:35:32 +02:00
rcode = RCODE_CONFLICT_ERROR ;
} else
rcode = RCODE_COMPLETE ;
2009-06-07 22:57:53 +02:00
2009-06-16 22:35:32 +02:00
fw_send_response ( card , r , rcode ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
static void fwnet_receive_broadcast ( struct fw_iso_context * context ,
u32 cycle , size_t header_length , void * header , void * data )
{
struct fwnet_device * dev ;
2009-05-18 13:08:06 -04:00
struct fw_iso_packet packet ;
struct fw_card * card ;
2009-06-07 22:57:53 +02:00
__be16 * hdr_ptr ;
__be32 * buf_ptr ;
2009-05-18 13:08:06 -04:00
int retval ;
u32 length ;
u16 source_node_id ;
u32 specifier_id ;
u32 ver ;
unsigned long offset ;
unsigned long flags ;
2009-06-07 22:57:53 +02:00
dev = data ;
card = dev - > card ;
2009-05-18 13:08:06 -04:00
hdr_ptr = header ;
2009-06-07 22:57:53 +02:00
length = be16_to_cpup ( hdr_ptr ) ;
spin_lock_irqsave ( & dev - > lock , flags ) ;
offset = dev - > rcv_buffer_size * dev - > broadcast_rcv_next_ptr ;
buf_ptr = dev - > broadcast_rcv_buffer_ptrs [ dev - > broadcast_rcv_next_ptr + + ] ;
if ( dev - > broadcast_rcv_next_ptr = = dev - > num_broadcast_rcv_ptrs )
dev - > broadcast_rcv_next_ptr = 0 ;
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
2009-05-18 13:08:06 -04:00
specifier_id = ( be32_to_cpu ( buf_ptr [ 0 ] ) & 0xffff ) < < 8
| ( be32_to_cpu ( buf_ptr [ 1 ] ) & 0xff000000 ) > > 24 ;
2009-06-07 22:57:53 +02:00
ver = be32_to_cpu ( buf_ptr [ 1 ] ) & 0xffffff ;
2009-05-18 13:08:06 -04:00
source_node_id = be32_to_cpu ( buf_ptr [ 0 ] ) > > 16 ;
2009-06-07 22:57:53 +02:00
if ( specifier_id = = IANA_SPECIFIER_ID & & ver = = RFC2734_SW_VERSION ) {
2009-05-18 13:08:06 -04:00
buf_ptr + = 2 ;
2009-06-07 22:57:53 +02:00
length - = IEEE1394_GASP_HDR_SIZE ;
fwnet_incoming_packet ( dev , buf_ptr , length ,
2009-06-14 11:45:27 +02:00
source_node_id , - 1 , true ) ;
2009-06-07 22:57:53 +02:00
}
packet . payload_length = dev - > rcv_buffer_size ;
2009-05-18 13:08:06 -04:00
packet . interrupt = 1 ;
packet . skip = 0 ;
packet . tag = 3 ;
packet . sy = 0 ;
2009-06-07 22:57:53 +02:00
packet . header_length = IEEE1394_GASP_HDR_SIZE ;
spin_lock_irqsave ( & dev - > lock , flags ) ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
retval = fw_iso_context_queue ( dev - > broadcast_rcv_context , & packet ,
& dev - > broadcast_rcv_buffer , offset ) ;
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
if ( retval < 0 )
fw_error ( " requeue failed \n " ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
static struct kmem_cache * fwnet_packet_task_cache ;
2010-01-18 22:36:49 +01:00
static void fwnet_free_ptask ( struct fwnet_packet_task * ptask )
{
dev_kfree_skb_any ( ptask - > skb ) ;
kmem_cache_free ( fwnet_packet_task_cache , ptask ) ;
}
2009-06-07 22:57:53 +02:00
static int fwnet_send_packet ( struct fwnet_packet_task * ptask ) ;
static void fwnet_transmit_packet_done ( struct fwnet_packet_task * ptask )
{
2010-01-18 22:36:49 +01:00
struct fwnet_device * dev = ptask - > dev ;
2009-05-18 13:08:06 -04:00
unsigned long flags ;
2010-01-18 22:36:49 +01:00
bool free ;
2009-06-07 22:57:53 +02:00
spin_lock_irqsave ( & dev - > lock , flags ) ;
2010-01-18 22:36:49 +01:00
ptask - > outstanding_pkts - - ;
/* Check whether we or the networking TX soft-IRQ is last user. */
free = ( ptask - > outstanding_pkts = = 0 & & ! list_empty ( & ptask - > pt_link ) ) ;
if ( ptask - > outstanding_pkts = = 0 )
list_del ( & ptask - > pt_link ) ;
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
2009-06-07 22:57:53 +02:00
if ( ptask - > outstanding_pkts > 0 ) {
2009-05-18 13:08:06 -04:00
u16 dg_size ;
u16 fg_off ;
u16 datagram_label ;
u16 lf ;
struct sk_buff * skb ;
/* Update the ptask to point to the next fragment and send it */
2009-06-07 22:57:53 +02:00
lf = fwnet_get_hdr_lf ( & ptask - > hdr ) ;
2009-05-18 13:08:06 -04:00
switch ( lf ) {
2009-06-07 22:57:53 +02:00
case RFC2374_HDR_LASTFRAG :
case RFC2374_HDR_UNFRAG :
2009-05-18 13:08:06 -04:00
default :
2009-06-07 22:57:53 +02:00
fw_error ( " Outstanding packet %x lf %x, header %x,%x \n " ,
ptask - > outstanding_pkts , lf , ptask - > hdr . w0 ,
ptask - > hdr . w1 ) ;
2009-05-18 13:08:06 -04:00
BUG ( ) ;
2009-06-07 22:57:53 +02:00
case RFC2374_HDR_FIRSTFRAG :
2009-05-18 13:08:06 -04:00
/* Set frag type here for future interior fragments */
2009-06-07 22:57:53 +02:00
dg_size = fwnet_get_hdr_dg_size ( & ptask - > hdr ) ;
fg_off = ptask - > max_payload - RFC2374_FRAG_HDR_SIZE ;
datagram_label = fwnet_get_hdr_dgl ( & ptask - > hdr ) ;
2009-05-18 13:08:06 -04:00
break ;
2009-06-07 22:57:53 +02:00
case RFC2374_HDR_INTFRAG :
dg_size = fwnet_get_hdr_dg_size ( & ptask - > hdr ) ;
fg_off = fwnet_get_hdr_fg_off ( & ptask - > hdr )
+ ptask - > max_payload - RFC2374_FRAG_HDR_SIZE ;
datagram_label = fwnet_get_hdr_dgl ( & ptask - > hdr ) ;
2009-05-18 13:08:06 -04:00
break ;
}
skb = ptask - > skb ;
2009-06-07 22:57:53 +02:00
skb_pull ( skb , ptask - > max_payload ) ;
if ( ptask - > outstanding_pkts > 1 ) {
fwnet_make_sf_hdr ( & ptask - > hdr , RFC2374_HDR_INTFRAG ,
dg_size , fg_off , datagram_label ) ;
2009-05-18 13:08:06 -04:00
} else {
2009-06-07 22:57:53 +02:00
fwnet_make_sf_hdr ( & ptask - > hdr , RFC2374_HDR_LASTFRAG ,
dg_size , fg_off , datagram_label ) ;
ptask - > max_payload = skb - > len + RFC2374_FRAG_HDR_SIZE ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
fwnet_send_packet ( ptask ) ;
2009-05-18 13:08:06 -04:00
}
2010-01-18 22:36:49 +01:00
if ( free )
fwnet_free_ptask ( ptask ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
static void fwnet_write_complete ( struct fw_card * card , int rcode ,
void * payload , size_t length , void * data )
{
struct fwnet_packet_task * ptask ;
2009-05-18 13:08:06 -04:00
ptask = data ;
2009-06-07 22:57:53 +02:00
if ( rcode = = RCODE_COMPLETE )
fwnet_transmit_packet_done ( ptask ) ;
else
fw_error ( " fwnet_write_complete: failed: %x \n " , rcode ) ;
2009-05-18 13:08:06 -04:00
/* ??? error recovery */
}
2009-06-07 22:57:53 +02:00
static int fwnet_send_packet ( struct fwnet_packet_task * ptask )
{
struct fwnet_device * dev ;
2009-05-18 13:08:06 -04:00
unsigned tx_len ;
2009-06-07 22:57:53 +02:00
struct rfc2734_header * bufhdr ;
2009-05-18 13:08:06 -04:00
unsigned long flags ;
2010-01-18 22:36:49 +01:00
bool free ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
dev = ptask - > dev ;
2009-05-18 13:08:06 -04:00
tx_len = ptask - > max_payload ;
2009-06-07 22:57:53 +02:00
switch ( fwnet_get_hdr_lf ( & ptask - > hdr ) ) {
case RFC2374_HDR_UNFRAG :
bufhdr = ( struct rfc2734_header * )
skb_push ( ptask - > skb , RFC2374_UNFRAG_HDR_SIZE ) ;
put_unaligned_be32 ( ptask - > hdr . w0 , & bufhdr - > w0 ) ;
2009-05-18 13:08:06 -04:00
break ;
2009-06-07 22:57:53 +02:00
case RFC2374_HDR_FIRSTFRAG :
case RFC2374_HDR_INTFRAG :
case RFC2374_HDR_LASTFRAG :
bufhdr = ( struct rfc2734_header * )
skb_push ( ptask - > skb , RFC2374_FRAG_HDR_SIZE ) ;
put_unaligned_be32 ( ptask - > hdr . w0 , & bufhdr - > w0 ) ;
put_unaligned_be32 ( ptask - > hdr . w1 , & bufhdr - > w1 ) ;
2009-05-18 13:08:06 -04:00
break ;
default :
BUG ( ) ;
}
2009-06-07 22:57:53 +02:00
if ( ptask - > dest_node = = IEEE1394_ALL_NODES ) {
u8 * p ;
2009-05-18 13:08:06 -04:00
int generation ;
2009-06-07 22:57:53 +02:00
int node_id ;
2009-05-18 13:08:06 -04:00
/* ptask->generation may not have been set yet */
2009-06-07 22:57:53 +02:00
generation = dev - > card - > generation ;
2009-05-18 13:08:06 -04:00
smp_rmb ( ) ;
2009-06-07 22:57:53 +02:00
node_id = dev - > card - > node_id ;
p = skb_push ( ptask - > skb , 8 ) ;
put_unaligned_be32 ( node_id < < 16 | IANA_SPECIFIER_ID > > 8 , p ) ;
put_unaligned_be32 ( ( IANA_SPECIFIER_ID & 0xff ) < < 24
| RFC2734_SW_VERSION , & p [ 4 ] ) ;
/* We should not transmit if broadcast_channel.valid == 0. */
fw_send_request ( dev - > card , & ptask - > transaction ,
TCODE_STREAM_DATA ,
fw_stream_packet_destination_id ( 3 ,
IEEE1394_BROADCAST_CHANNEL , 0 ) ,
generation , SCODE_100 , 0ULL , ptask - > skb - > data ,
tx_len + 8 , fwnet_write_complete , ptask ) ;
spin_lock_irqsave ( & dev - > lock , flags ) ;
2010-01-18 22:36:49 +01:00
/* If the AT tasklet already ran, we may be last user. */
free = ( ptask - > outstanding_pkts = = 0 & & list_empty ( & ptask - > pt_link ) ) ;
if ( ! free )
list_add_tail ( & ptask - > pt_link , & dev - > broadcasted_list ) ;
2009-06-07 22:57:53 +02:00
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
2010-01-18 22:36:49 +01:00
goto out ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
fw_send_request ( dev - > card , & ptask - > transaction ,
TCODE_WRITE_BLOCK_REQUEST , ptask - > dest_node ,
ptask - > generation , ptask - > speed , ptask - > fifo_addr ,
ptask - > skb - > data , tx_len , fwnet_write_complete , ptask ) ;
spin_lock_irqsave ( & dev - > lock , flags ) ;
2010-01-18 22:36:49 +01:00
/* If the AT tasklet already ran, we may be last user. */
free = ( ptask - > outstanding_pkts = = 0 & & list_empty ( & ptask - > pt_link ) ) ;
if ( ! free )
list_add_tail ( & ptask - > pt_link , & dev - > sent_list ) ;
2009-06-07 22:57:53 +02:00
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
2009-06-14 11:45:27 +02:00
dev - > netdev - > trans_start = jiffies ;
2010-01-18 22:36:49 +01:00
out :
if ( free )
fwnet_free_ptask ( ptask ) ;
2009-06-07 22:57:53 +02:00
2009-05-18 13:08:06 -04:00
return 0 ;
}
2009-06-07 22:57:53 +02:00
static int fwnet_broadcast_start ( struct fwnet_device * dev )
{
2009-05-18 13:08:06 -04:00
struct fw_iso_context * context ;
int retval ;
unsigned num_packets ;
unsigned max_receive ;
struct fw_iso_packet packet ;
unsigned long offset ;
unsigned u ;
2009-06-07 22:57:53 +02:00
if ( dev - > local_fifo = = FWNET_NO_FIFO_ADDR ) {
/* outside OHCI posted write area? */
static const struct fw_address_region region = {
. start = 0xffff00000000ULL ,
. end = CSR_REGISTER_BASE ,
} ;
dev - > handler . length = 4096 ;
dev - > handler . address_callback = fwnet_receive_packet ;
dev - > handler . callback_data = dev ;
retval = fw_core_add_address_handler ( & dev - > handler , & region ) ;
if ( retval < 0 )
2009-05-18 13:08:06 -04:00
goto failed_initial ;
2009-06-07 22:57:53 +02:00
dev - > local_fifo = dev - > handler . offset ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
max_receive = 1U < < ( dev - > card - > max_receive + 1 ) ;
num_packets = ( FWNET_ISO_PAGE_COUNT * PAGE_SIZE ) / max_receive ;
if ( ! dev - > broadcast_rcv_context ) {
2009-05-18 13:08:06 -04:00
void * * ptrptr ;
2009-06-07 22:57:53 +02:00
context = fw_iso_context_create ( dev - > card ,
FW_ISO_CONTEXT_RECEIVE , IEEE1394_BROADCAST_CHANNEL ,
dev - > card - > link_speed , 8 , fwnet_receive_broadcast , dev ) ;
2009-05-18 13:08:06 -04:00
if ( IS_ERR ( context ) ) {
retval = PTR_ERR ( context ) ;
goto failed_context_create ;
}
2009-06-07 22:57:53 +02:00
retval = fw_iso_buffer_init ( & dev - > broadcast_rcv_buffer ,
dev - > card , FWNET_ISO_PAGE_COUNT , DMA_FROM_DEVICE ) ;
if ( retval < 0 )
2009-05-18 13:08:06 -04:00
goto failed_buffer_init ;
2009-06-07 22:57:53 +02:00
ptrptr = kmalloc ( sizeof ( void * ) * num_packets , GFP_KERNEL ) ;
if ( ! ptrptr ) {
2009-05-18 13:08:06 -04:00
retval = - ENOMEM ;
goto failed_ptrs_alloc ;
}
2009-06-07 22:57:53 +02:00
dev - > broadcast_rcv_buffer_ptrs = ptrptr ;
for ( u = 0 ; u < FWNET_ISO_PAGE_COUNT ; u + + ) {
2009-05-18 13:08:06 -04:00
void * ptr ;
unsigned v ;
2009-06-07 22:57:53 +02:00
ptr = kmap ( dev - > broadcast_rcv_buffer . pages [ u ] ) ;
for ( v = 0 ; v < num_packets / FWNET_ISO_PAGE_COUNT ; v + + )
* ptrptr + + = ( void * )
( ( char * ) ptr + v * max_receive ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
dev - > broadcast_rcv_context = context ;
} else {
context = dev - > broadcast_rcv_context ;
}
2009-05-18 13:08:06 -04:00
packet . payload_length = max_receive ;
packet . interrupt = 1 ;
packet . skip = 0 ;
packet . tag = 3 ;
packet . sy = 0 ;
2009-06-07 22:57:53 +02:00
packet . header_length = IEEE1394_GASP_HDR_SIZE ;
2009-05-18 13:08:06 -04:00
offset = 0 ;
2009-06-07 22:57:53 +02:00
for ( u = 0 ; u < num_packets ; u + + ) {
retval = fw_iso_context_queue ( context , & packet ,
& dev - > broadcast_rcv_buffer , offset ) ;
if ( retval < 0 )
2009-05-18 13:08:06 -04:00
goto failed_rcv_queue ;
2009-06-07 22:57:53 +02:00
2009-05-18 13:08:06 -04:00
offset + = max_receive ;
}
2009-06-07 22:57:53 +02:00
dev - > num_broadcast_rcv_ptrs = num_packets ;
dev - > rcv_buffer_size = max_receive ;
dev - > broadcast_rcv_next_ptr = 0U ;
retval = fw_iso_context_start ( context , - 1 , 0 ,
FW_ISO_CONTEXT_MATCH_ALL_TAGS ) ; /* ??? sync */
if ( retval < 0 )
2009-05-18 13:08:06 -04:00
goto failed_rcv_queue ;
2009-06-07 22:57:53 +02:00
/* FIXME: adjust it according to the min. speed of all known peers? */
dev - > broadcast_xmt_max_payload = IEEE1394_MAX_PAYLOAD_S100
- IEEE1394_GASP_HDR_SIZE - RFC2374_UNFRAG_HDR_SIZE ;
dev - > broadcast_state = FWNET_BROADCAST_RUNNING ;
2009-05-18 13:08:06 -04:00
return 0 ;
failed_rcv_queue :
2009-06-07 22:57:53 +02:00
kfree ( dev - > broadcast_rcv_buffer_ptrs ) ;
dev - > broadcast_rcv_buffer_ptrs = NULL ;
2009-05-18 13:08:06 -04:00
failed_ptrs_alloc :
2009-06-07 22:57:53 +02:00
fw_iso_buffer_destroy ( & dev - > broadcast_rcv_buffer , dev - > card ) ;
2009-05-18 13:08:06 -04:00
failed_buffer_init :
2009-06-07 22:57:53 +02:00
fw_iso_context_destroy ( context ) ;
dev - > broadcast_rcv_context = NULL ;
2009-05-18 13:08:06 -04:00
failed_context_create :
2009-06-07 22:57:53 +02:00
fw_core_remove_address_handler ( & dev - > handler ) ;
2009-05-18 13:08:06 -04:00
failed_initial :
2009-06-07 22:57:53 +02:00
dev - > local_fifo = FWNET_NO_FIFO_ADDR ;
2009-05-18 13:08:06 -04:00
return retval ;
}
2009-06-07 22:57:53 +02:00
/* ifup */
static int fwnet_open ( struct net_device * net )
{
struct fwnet_device * dev = netdev_priv ( net ) ;
2009-05-18 13:08:06 -04:00
int ret ;
2009-06-07 22:57:53 +02:00
if ( dev - > broadcast_state = = FWNET_BROADCAST_ERROR ) {
ret = fwnet_broadcast_start ( dev ) ;
2009-05-18 13:08:06 -04:00
if ( ret )
return ret ;
}
2009-06-07 22:57:53 +02:00
netif_start_queue ( net ) ;
2009-05-18 13:08:06 -04:00
return 0 ;
}
2009-06-07 22:57:53 +02:00
/* ifdown */
static int fwnet_stop ( struct net_device * net )
2009-05-18 13:08:06 -04:00
{
2009-06-07 22:57:53 +02:00
netif_stop_queue ( net ) ;
/* Deallocate iso context for use by other applications? */
2009-05-18 13:08:06 -04:00
return 0 ;
}
2009-08-31 19:50:51 +00:00
static netdev_tx_t fwnet_tx ( struct sk_buff * skb , struct net_device * net )
2009-05-18 13:08:06 -04:00
{
2009-06-07 22:57:53 +02:00
struct fwnet_header hdr_buf ;
struct fwnet_device * dev = netdev_priv ( net ) ;
2009-05-18 13:08:06 -04:00
__be16 proto ;
u16 dest_node ;
unsigned max_payload ;
u16 dg_size ;
u16 * datagram_label_ptr ;
2009-06-07 22:57:53 +02:00
struct fwnet_packet_task * ptask ;
2009-06-14 11:45:27 +02:00
struct fwnet_peer * peer ;
unsigned long flags ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
ptask = kmem_cache_alloc ( fwnet_packet_task_cache , GFP_ATOMIC ) ;
2009-05-18 13:08:06 -04:00
if ( ptask = = NULL )
goto fail ;
skb = skb_share_check ( skb , GFP_ATOMIC ) ;
if ( ! skb )
goto fail ;
/*
2009-06-07 22:57:53 +02:00
* Make a copy of the driver - specific header .
2009-05-18 13:08:06 -04:00
* We might need to rebuild the header on tx failure .
*/
memcpy ( & hdr_buf , skb - > data , sizeof ( hdr_buf ) ) ;
skb_pull ( skb , sizeof ( hdr_buf ) ) ;
proto = hdr_buf . h_proto ;
dg_size = skb - > len ;
2009-06-14 11:45:27 +02:00
/* serialize access to peer, including peer->datagram_label */
spin_lock_irqsave ( & dev - > lock , flags ) ;
2009-05-18 13:08:06 -04:00
/*
* Set the transmission type for the packet . ARP packets and IP
* broadcast packets are sent via GASP .
*/
2009-06-07 22:57:53 +02:00
if ( memcmp ( hdr_buf . h_dest , net - > broadcast , FWNET_ALEN ) = = 0
2009-05-18 13:08:06 -04:00
| | proto = = htons ( ETH_P_ARP )
2009-06-07 22:57:53 +02:00
| | ( proto = = htons ( ETH_P_IP )
& & IN_MULTICAST ( ntohl ( ip_hdr ( skb ) - > daddr ) ) ) ) {
2009-06-14 11:45:27 +02:00
max_payload = dev - > broadcast_xmt_max_payload ;
2009-06-07 22:57:53 +02:00
datagram_label_ptr = & dev - > broadcast_xmt_datagramlabel ;
2009-06-14 11:45:27 +02:00
ptask - > fifo_addr = FWNET_NO_FIFO_ADDR ;
ptask - > generation = 0 ;
ptask - > dest_node = IEEE1394_ALL_NODES ;
ptask - > speed = SCODE_100 ;
2009-05-18 13:08:06 -04:00
} else {
2009-06-07 22:57:53 +02:00
__be64 guid = get_unaligned ( ( __be64 * ) hdr_buf . h_dest ) ;
2009-05-18 13:08:06 -04:00
u8 generation ;
2009-06-07 22:57:53 +02:00
peer = fwnet_peer_find_by_guid ( dev , be64_to_cpu ( guid ) ) ;
2009-06-14 11:45:27 +02:00
if ( ! peer | | peer - > fifo = = FWNET_NO_FIFO_ADDR )
goto fail_unlock ;
2009-05-18 13:08:06 -04:00
2009-06-14 11:45:27 +02:00
generation = peer - > generation ;
dest_node = peer - > node_id ;
max_payload = peer - > max_payload ;
2009-06-07 22:57:53 +02:00
datagram_label_ptr = & peer - > datagram_label ;
2009-05-18 13:08:06 -04:00
2009-06-14 11:45:27 +02:00
ptask - > fifo_addr = peer - > fifo ;
ptask - > generation = generation ;
ptask - > dest_node = dest_node ;
ptask - > speed = peer - > speed ;
2009-05-18 13:08:06 -04:00
}
/* If this is an ARP packet, convert it */
if ( proto = = htons ( ETH_P_ARP ) ) {
struct arphdr * arp = ( struct arphdr * ) skb - > data ;
unsigned char * arp_ptr = ( unsigned char * ) ( arp + 1 ) ;
2009-06-07 22:57:53 +02:00
struct rfc2734_arp * arp1394 = ( struct rfc2734_arp * ) skb - > data ;
__be32 ipaddr ;
ipaddr = get_unaligned ( ( __be32 * ) ( arp_ptr + FWNET_ALEN ) ) ;
arp1394 - > hw_addr_len = RFC2734_HW_ADDR_LEN ;
arp1394 - > max_rec = dev - > card - > max_receive ;
arp1394 - > sspd = dev - > card - > link_speed ;
put_unaligned_be16 ( dev - > local_fifo > > 32 ,
& arp1394 - > fifo_hi ) ;
put_unaligned_be32 ( dev - > local_fifo & 0xffffffff ,
& arp1394 - > fifo_lo ) ;
put_unaligned ( ipaddr , & arp1394 - > sip ) ;
2009-05-18 13:08:06 -04:00
}
ptask - > hdr . w0 = 0 ;
ptask - > hdr . w1 = 0 ;
ptask - > skb = skb ;
2009-06-07 22:57:53 +02:00
ptask - > dev = dev ;
2009-05-18 13:08:06 -04:00
/* Does it all fit in one packet? */
2009-06-07 22:57:53 +02:00
if ( dg_size < = max_payload ) {
fwnet_make_uf_hdr ( & ptask - > hdr , ntohs ( proto ) ) ;
2009-05-18 13:08:06 -04:00
ptask - > outstanding_pkts = 1 ;
2009-06-07 22:57:53 +02:00
max_payload = dg_size + RFC2374_UNFRAG_HDR_SIZE ;
2009-05-18 13:08:06 -04:00
} else {
u16 datagram_label ;
2009-06-07 22:57:53 +02:00
max_payload - = RFC2374_FRAG_OVERHEAD ;
2009-05-18 13:08:06 -04:00
datagram_label = ( * datagram_label_ptr ) + + ;
2009-06-07 22:57:53 +02:00
fwnet_make_ff_hdr ( & ptask - > hdr , ntohs ( proto ) , dg_size ,
datagram_label ) ;
2009-05-18 13:08:06 -04:00
ptask - > outstanding_pkts = DIV_ROUND_UP ( dg_size , max_payload ) ;
2009-06-07 22:57:53 +02:00
max_payload + = RFC2374_FRAG_HDR_SIZE ;
2009-05-18 13:08:06 -04:00
}
2009-06-14 11:45:27 +02:00
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
2009-05-18 13:08:06 -04:00
ptask - > max_payload = max_payload ;
2010-01-18 22:36:49 +01:00
INIT_LIST_HEAD ( & ptask - > pt_link ) ;
2009-06-07 22:57:53 +02:00
fwnet_send_packet ( ptask ) ;
2009-05-18 13:08:06 -04:00
return NETDEV_TX_OK ;
2009-06-14 11:45:27 +02:00
fail_unlock :
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
2009-05-18 13:08:06 -04:00
fail :
if ( ptask )
2009-06-07 22:57:53 +02:00
kmem_cache_free ( fwnet_packet_task_cache , ptask ) ;
2009-05-18 13:08:06 -04:00
if ( skb ! = NULL )
dev_kfree_skb ( skb ) ;
2009-06-07 22:57:53 +02:00
net - > stats . tx_dropped + + ;
net - > stats . tx_errors + + ;
2009-05-18 13:08:06 -04:00
/*
* FIXME : According to a patch from 2003 - 02 - 26 , " returning non-zero
* causes serious problems " here, allegedly. Before that patch,
* - ERRNO was returned which is not appropriate under Linux 2.6 .
* Perhaps more needs to be done ? Stop the queue in serious
* conditions and restart it elsewhere ?
*/
return NETDEV_TX_OK ;
}
2009-06-07 22:57:53 +02:00
static int fwnet_change_mtu ( struct net_device * net , int new_mtu )
{
2009-05-18 13:08:06 -04:00
if ( new_mtu < 68 )
return - EINVAL ;
2009-06-07 22:57:53 +02:00
net - > mtu = new_mtu ;
2009-05-18 13:08:06 -04:00
return 0 ;
}
2009-06-07 22:57:53 +02:00
static void fwnet_get_drvinfo ( struct net_device * net ,
struct ethtool_drvinfo * info )
{
strcpy ( info - > driver , KBUILD_MODNAME ) ;
strcpy ( info - > bus_info , " ieee1394 " ) ;
2009-05-18 13:08:06 -04:00
}
2009-09-02 01:03:33 -07:00
static const struct ethtool_ops fwnet_ethtool_ops = {
2009-06-07 22:57:53 +02:00
. get_drvinfo = fwnet_get_drvinfo ,
2009-05-18 13:08:06 -04:00
} ;
2009-06-07 22:57:53 +02:00
static const struct net_device_ops fwnet_netdev_ops = {
. ndo_open = fwnet_open ,
. ndo_stop = fwnet_stop ,
. ndo_start_xmit = fwnet_tx ,
. ndo_change_mtu = fwnet_change_mtu ,
2009-05-18 13:08:06 -04:00
} ;
2009-06-07 22:57:53 +02:00
static void fwnet_init_dev ( struct net_device * net )
{
net - > header_ops = & fwnet_header_ops ;
net - > netdev_ops = & fwnet_netdev_ops ;
2009-06-14 11:47:44 +02:00
net - > watchdog_timeo = 2 * HZ ;
2009-06-07 22:57:53 +02:00
net - > flags = IFF_BROADCAST | IFF_MULTICAST ;
net - > features = NETIF_F_HIGHDMA ;
net - > addr_len = FWNET_ALEN ;
net - > hard_header_len = FWNET_HLEN ;
net - > type = ARPHRD_IEEE1394 ;
2009-06-14 11:47:44 +02:00
net - > tx_queue_len = 10 ;
2009-06-07 22:57:53 +02:00
SET_ETHTOOL_OPS ( net , & fwnet_ethtool_ops ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-14 11:45:27 +02:00
/* caller must hold fwnet_device_mutex */
static struct fwnet_device * fwnet_dev_find ( struct fw_card * card )
{
struct fwnet_device * dev ;
list_for_each_entry ( dev , & fwnet_device_list , dev_link )
if ( dev - > card = = card )
return dev ;
return NULL ;
}
static int fwnet_add_peer ( struct fwnet_device * dev ,
struct fw_unit * unit , struct fw_device * device )
{
struct fwnet_peer * peer ;
peer = kmalloc ( sizeof ( * peer ) , GFP_KERNEL ) ;
if ( ! peer )
return - ENOMEM ;
2009-06-16 20:43:55 +02:00
dev_set_drvdata ( & unit - > device , peer ) ;
2009-06-14 11:45:27 +02:00
peer - > dev = dev ;
peer - > guid = ( u64 ) device - > config_rom [ 3 ] < < 32 | device - > config_rom [ 4 ] ;
peer - > fifo = FWNET_NO_FIFO_ADDR ;
INIT_LIST_HEAD ( & peer - > pd_list ) ;
peer - > pdg_size = 0 ;
peer - > datagram_label = 0 ;
peer - > speed = device - > max_speed ;
peer - > max_payload = fwnet_max_payload ( device - > max_rec , peer - > speed ) ;
peer - > generation = device - > generation ;
smp_rmb ( ) ;
peer - > node_id = device - > node_id ;
spin_lock_irq ( & dev - > lock ) ;
list_add_tail ( & peer - > peer_link , & dev - > peer_list ) ;
spin_unlock_irq ( & dev - > lock ) ;
return 0 ;
}
2009-06-07 22:57:53 +02:00
static int fwnet_probe ( struct device * _dev )
{
struct fw_unit * unit = fw_unit ( _dev ) ;
struct fw_device * device = fw_parent_device ( unit ) ;
struct fw_card * card = device - > card ;
struct net_device * net ;
2009-06-16 20:43:55 +02:00
bool allocated_netdev = false ;
2009-06-07 22:57:53 +02:00
struct fwnet_device * dev ;
2009-05-18 13:08:06 -04:00
unsigned max_mtu ;
2009-06-14 11:45:27 +02:00
int ret ;
2009-05-18 13:08:06 -04:00
2009-06-14 11:45:27 +02:00
mutex_lock ( & fwnet_device_mutex ) ;
2009-05-18 13:08:06 -04:00
2009-06-14 11:45:27 +02:00
dev = fwnet_dev_find ( card ) ;
if ( dev ) {
net = dev - > netdev ;
goto have_dev ;
2009-05-18 13:08:06 -04:00
}
2009-06-14 11:45:27 +02:00
2009-06-07 22:57:53 +02:00
net = alloc_netdev ( sizeof ( * dev ) , " firewire%d " , fwnet_init_dev ) ;
if ( net = = NULL ) {
2009-06-14 11:45:27 +02:00
ret = - ENOMEM ;
2009-05-18 13:08:06 -04:00
goto out ;
}
2009-06-16 20:43:55 +02:00
allocated_netdev = true ;
2009-06-07 22:57:53 +02:00
SET_NETDEV_DEV ( net , card - > device ) ;
dev = netdev_priv ( net ) ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
spin_lock_init ( & dev - > lock ) ;
dev - > broadcast_state = FWNET_BROADCAST_ERROR ;
dev - > broadcast_rcv_context = NULL ;
dev - > broadcast_xmt_max_payload = 0 ;
dev - > broadcast_xmt_datagramlabel = 0 ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
dev - > local_fifo = FWNET_NO_FIFO_ADDR ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
INIT_LIST_HEAD ( & dev - > packet_list ) ;
INIT_LIST_HEAD ( & dev - > broadcasted_list ) ;
INIT_LIST_HEAD ( & dev - > sent_list ) ;
2009-06-14 11:45:27 +02:00
INIT_LIST_HEAD ( & dev - > peer_list ) ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
dev - > card = card ;
2009-06-14 11:45:27 +02:00
dev - > netdev = net ;
2009-05-18 13:08:06 -04:00
/*
* Use the RFC 2734 default 1500 octets or the maximum payload
* as initial MTU
*/
max_mtu = ( 1 < < ( card - > max_receive + 1 ) )
2009-06-07 22:57:53 +02:00
- sizeof ( struct rfc2734_header ) - IEEE1394_GASP_HDR_SIZE ;
net - > mtu = min ( 1500U , max_mtu ) ;
2009-05-18 13:08:06 -04:00
/* Set our hardware address while we're at it */
2009-06-07 22:57:53 +02:00
put_unaligned_be64 ( card - > guid , net - > dev_addr ) ;
put_unaligned_be64 ( ~ 0ULL , net - > broadcast ) ;
2009-06-14 11:45:27 +02:00
ret = register_netdev ( net ) ;
if ( ret ) {
2009-06-07 22:57:53 +02:00
fw_error ( " Cannot register the driver \n " ) ;
2009-05-18 13:08:06 -04:00
goto out ;
}
2009-06-14 11:45:27 +02:00
list_add_tail ( & dev - > dev_link , & fwnet_device_list ) ;
2009-06-07 22:57:53 +02:00
fw_notify ( " %s: IPv4 over FireWire on device %016llx \n " ,
net - > name , ( unsigned long long ) card - > guid ) ;
2009-06-14 11:45:27 +02:00
have_dev :
ret = fwnet_add_peer ( dev , unit , device ) ;
2009-06-16 20:43:55 +02:00
if ( ret & & allocated_netdev ) {
2009-06-14 11:45:27 +02:00
unregister_netdev ( net ) ;
list_del ( & dev - > dev_link ) ;
}
2009-05-18 13:08:06 -04:00
out :
2009-06-16 20:43:55 +02:00
if ( ret & & allocated_netdev )
2009-06-07 22:57:53 +02:00
free_netdev ( net ) ;
2009-06-14 11:45:27 +02:00
mutex_unlock ( & fwnet_device_mutex ) ;
return ret ;
}
static void fwnet_remove_peer ( struct fwnet_peer * peer )
{
struct fwnet_partial_datagram * pd , * pd_next ;
spin_lock_irq ( & peer - > dev - > lock ) ;
list_del ( & peer - > peer_link ) ;
spin_unlock_irq ( & peer - > dev - > lock ) ;
list_for_each_entry_safe ( pd , pd_next , & peer - > pd_list , pd_link )
fwnet_pd_delete ( pd ) ;
kfree ( peer ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
static int fwnet_remove ( struct device * _dev )
{
2009-06-16 20:43:55 +02:00
struct fwnet_peer * peer = dev_get_drvdata ( _dev ) ;
2009-06-14 11:45:27 +02:00
struct fwnet_device * dev = peer - > dev ;
2009-06-07 22:57:53 +02:00
struct net_device * net ;
struct fwnet_packet_task * ptask , * pt_next ;
2009-06-14 11:45:27 +02:00
mutex_lock ( & fwnet_device_mutex ) ;
2009-05-18 13:08:06 -04:00
2009-06-14 11:45:27 +02:00
fwnet_remove_peer ( peer ) ;
2009-06-07 22:57:53 +02:00
2009-06-14 11:45:27 +02:00
if ( list_empty ( & dev - > peer_list ) ) {
net = dev - > netdev ;
2009-06-07 22:57:53 +02:00
unregister_netdev ( net ) ;
if ( dev - > local_fifo ! = FWNET_NO_FIFO_ADDR )
fw_core_remove_address_handler ( & dev - > handler ) ;
if ( dev - > broadcast_rcv_context ) {
fw_iso_context_stop ( dev - > broadcast_rcv_context ) ;
fw_iso_buffer_destroy ( & dev - > broadcast_rcv_buffer ,
dev - > card ) ;
fw_iso_context_destroy ( dev - > broadcast_rcv_context ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
list_for_each_entry_safe ( ptask , pt_next ,
& dev - > packet_list , pt_link ) {
dev_kfree_skb_any ( ptask - > skb ) ;
kmem_cache_free ( fwnet_packet_task_cache , ptask ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
list_for_each_entry_safe ( ptask , pt_next ,
& dev - > broadcasted_list , pt_link ) {
dev_kfree_skb_any ( ptask - > skb ) ;
kmem_cache_free ( fwnet_packet_task_cache , ptask ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
list_for_each_entry_safe ( ptask , pt_next ,
& dev - > sent_list , pt_link ) {
dev_kfree_skb_any ( ptask - > skb ) ;
kmem_cache_free ( fwnet_packet_task_cache , ptask ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-16 20:43:55 +02:00
list_del ( & dev - > dev_link ) ;
2009-06-07 22:57:53 +02:00
free_netdev ( net ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
2009-06-14 11:45:27 +02:00
mutex_unlock ( & fwnet_device_mutex ) ;
2009-05-18 13:08:06 -04:00
return 0 ;
}
2009-06-07 22:57:53 +02:00
/*
* FIXME abort partially sent fragmented datagrams ,
* discard partially received fragmented datagrams
*/
static void fwnet_update ( struct fw_unit * unit )
{
struct fw_device * device = fw_parent_device ( unit ) ;
2009-06-16 20:43:55 +02:00
struct fwnet_peer * peer = dev_get_drvdata ( & unit - > device ) ;
2009-06-14 11:45:27 +02:00
int generation ;
2009-05-18 13:08:06 -04:00
2009-06-14 11:45:27 +02:00
generation = device - > generation ;
spin_lock_irq ( & peer - > dev - > lock ) ;
peer - > node_id = device - > node_id ;
peer - > generation = generation ;
spin_unlock_irq ( & peer - > dev - > lock ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
static const struct ieee1394_device_id fwnet_id_table [ ] = {
{
. match_flags = IEEE1394_MATCH_SPECIFIER_ID |
IEEE1394_MATCH_VERSION ,
. specifier_id = IANA_SPECIFIER_ID ,
. version = RFC2734_SW_VERSION ,
} ,
{ }
} ;
static struct fw_driver fwnet_driver = {
2009-05-18 13:08:06 -04:00
. driver = {
2009-06-07 22:57:53 +02:00
. owner = THIS_MODULE ,
. name = " net " ,
. bus = & fw_bus_type ,
. probe = fwnet_probe ,
. remove = fwnet_remove ,
2009-05-18 13:08:06 -04:00
} ,
2009-06-07 22:57:53 +02:00
. update = fwnet_update ,
. id_table = fwnet_id_table ,
} ;
static const u32 rfc2374_unit_directory_data [ ] = {
0x00040000 , /* directory_length */
0x1200005e , /* unit_specifier_id: IANA */
0x81000003 , /* textual descriptor offset */
0x13000001 , /* unit_sw_version: RFC 2734 */
0x81000005 , /* textual descriptor offset */
0x00030000 , /* descriptor_length */
0x00000000 , /* text */
0x00000000 , /* minimal ASCII, en */
0x49414e41 , /* I A N A */
0x00030000 , /* descriptor_length */
0x00000000 , /* text */
0x00000000 , /* minimal ASCII, en */
0x49507634 , /* I P v 4 */
} ;
static struct fw_descriptor rfc2374_unit_directory = {
. length = ARRAY_SIZE ( rfc2374_unit_directory_data ) ,
. key = ( CSR_DIRECTORY | CSR_UNIT ) < < 24 ,
. data = rfc2374_unit_directory_data
2009-05-18 13:08:06 -04:00
} ;
2009-06-07 22:57:53 +02:00
static int __init fwnet_init ( void )
{
int err ;
err = fw_core_add_descriptor ( & rfc2374_unit_directory ) ;
if ( err )
return err ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
fwnet_packet_task_cache = kmem_cache_create ( " packet_task " ,
sizeof ( struct fwnet_packet_task ) , 0 , 0 , NULL ) ;
if ( ! fwnet_packet_task_cache ) {
err = - ENOMEM ;
goto out ;
}
err = driver_register ( & fwnet_driver . driver ) ;
if ( ! err )
return 0 ;
kmem_cache_destroy ( fwnet_packet_task_cache ) ;
out :
fw_core_remove_descriptor ( & rfc2374_unit_directory ) ;
return err ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
module_init ( fwnet_init ) ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
static void __exit fwnet_cleanup ( void )
{
driver_unregister ( & fwnet_driver . driver ) ;
kmem_cache_destroy ( fwnet_packet_task_cache ) ;
fw_core_remove_descriptor ( & rfc2374_unit_directory ) ;
2009-05-18 13:08:06 -04:00
}
2009-06-07 22:57:53 +02:00
module_exit ( fwnet_cleanup ) ;
2009-05-18 13:08:06 -04:00
2009-06-07 22:57:53 +02:00
MODULE_AUTHOR ( " Jay Fenlason <fenlason@redhat.com> " ) ;
MODULE_DESCRIPTION ( " IPv4 over IEEE1394 as per RFC 2734 " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DEVICE_TABLE ( ieee1394 , fwnet_id_table ) ;