2014-07-05 01:34:38 +04:00
/* Copyright 2011-2014 Autronica Fire and Security AS
2013-10-31 00:10:47 +04:00
*
* 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 .
*
* Author ( s ) :
2014-07-05 01:34:38 +04:00
* 2011 - 2014 Arvid Brodin , arvid . brodin @ alten . se
2013-10-31 00:10:47 +04:00
*
* This file contains device methods for creating , using and destroying
* virtual HSR devices .
*/
# include <linux/netdevice.h>
# include <linux/skbuff.h>
# include <linux/etherdevice.h>
# include <linux/rtnetlink.h>
# include <linux/pkt_sched.h>
# include "hsr_device.h"
2014-07-05 01:35:24 +04:00
# include "hsr_slave.h"
2013-10-31 00:10:47 +04:00
# include "hsr_framereg.h"
# include "hsr_main.h"
static bool is_admin_up ( struct net_device * dev )
{
return dev & & ( dev - > flags & IFF_UP ) ;
}
static bool is_slave_up ( struct net_device * dev )
{
return dev & & is_admin_up ( dev ) & & netif_oper_up ( dev ) ;
}
static void __hsr_set_operstate ( struct net_device * dev , int transition )
{
write_lock_bh ( & dev_base_lock ) ;
if ( dev - > operstate ! = transition ) {
dev - > operstate = transition ;
write_unlock_bh ( & dev_base_lock ) ;
netdev_state_change ( dev ) ;
} else {
write_unlock_bh ( & dev_base_lock ) ;
}
}
2014-07-05 01:36:40 +04:00
static void hsr_set_operstate ( struct net_device * hsr_dev , bool has_carrier )
2013-10-31 00:10:47 +04:00
{
if ( ! is_admin_up ( hsr_dev ) ) {
__hsr_set_operstate ( hsr_dev , IF_OPER_DOWN ) ;
return ;
}
2014-07-05 01:36:40 +04:00
if ( has_carrier )
2013-10-31 00:10:47 +04:00
__hsr_set_operstate ( hsr_dev , IF_OPER_UP ) ;
else
__hsr_set_operstate ( hsr_dev , IF_OPER_LOWERLAYERDOWN ) ;
}
2014-07-05 01:36:40 +04:00
static bool hsr_check_carrier ( struct hsr_priv * hsr )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:36:40 +04:00
bool has_carrier ;
has_carrier = ( is_slave_up ( hsr - > slave [ 0 ] ) | | is_slave_up ( hsr - > slave [ 1 ] ) ) ;
if ( has_carrier )
netif_carrier_on ( hsr - > dev ) ;
2013-10-31 00:10:47 +04:00
else
2014-07-05 01:36:40 +04:00
netif_carrier_off ( hsr - > dev ) ;
return has_carrier ;
2013-10-31 00:10:47 +04:00
}
2014-07-05 01:36:40 +04:00
static void hsr_check_announce ( struct net_device * hsr_dev ,
unsigned char old_operstate )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
hsr = netdev_priv ( hsr_dev ) ;
2013-10-31 00:10:47 +04:00
if ( ( hsr_dev - > operstate = = IF_OPER_UP ) & & ( old_operstate ! = IF_OPER_UP ) ) {
/* Went up */
2014-07-05 01:34:38 +04:00
hsr - > announce_count = 0 ;
hsr - > announce_timer . expires = jiffies +
2013-10-31 00:10:47 +04:00
msecs_to_jiffies ( HSR_ANNOUNCE_INTERVAL ) ;
2014-07-05 01:34:38 +04:00
add_timer ( & hsr - > announce_timer ) ;
2013-10-31 00:10:47 +04:00
}
if ( ( hsr_dev - > operstate ! = IF_OPER_UP ) & & ( old_operstate = = IF_OPER_UP ) )
/* Went down */
2014-07-05 01:34:38 +04:00
del_timer ( & hsr - > announce_timer ) ;
2013-10-31 00:10:47 +04:00
}
2014-07-05 01:36:40 +04:00
void hsr_check_carrier_and_operstate ( struct hsr_priv * hsr )
{
unsigned char old_operstate ;
bool has_carrier ;
/* netif_stacked_transfer_operstate() cannot be used here since
* it doesn ' t set IF_OPER_LOWERLAYERDOWN ( ? )
*/
old_operstate = hsr - > dev - > operstate ;
has_carrier = hsr_check_carrier ( hsr ) ;
hsr_set_operstate ( hsr - > dev , has_carrier ) ;
hsr_check_announce ( hsr - > dev , old_operstate ) ;
}
2014-07-05 01:34:38 +04:00
int hsr_get_max_mtu ( struct hsr_priv * hsr )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:37:27 +04:00
unsigned int mtu_max ;
struct net_device * slave ;
mtu_max = ETH_DATA_LEN ;
rcu_read_lock ( ) ;
slave = hsr - > slave [ 0 ] ;
if ( slave )
mtu_max = min ( slave - > mtu , mtu_max ) ;
slave = hsr - > slave [ 1 ] ;
if ( slave )
mtu_max = min ( slave - > mtu , mtu_max ) ;
rcu_read_unlock ( ) ;
if ( mtu_max < HSR_HLEN )
return 0 ;
2014-07-05 01:34:38 +04:00
return mtu_max - HSR_HLEN ;
2013-10-31 00:10:47 +04:00
}
2014-07-05 01:37:27 +04:00
2013-10-31 00:10:47 +04:00
static int hsr_dev_change_mtu ( struct net_device * dev , int new_mtu )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
hsr = netdev_priv ( dev ) ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
if ( new_mtu > hsr_get_max_mtu ( hsr ) ) {
netdev_info ( hsr - > dev , " A HSR master's MTU cannot be greater than the smallest MTU of its slaves minus the HSR Tag length (%d octets). \n " ,
HSR_HLEN ) ;
2013-10-31 00:10:47 +04:00
return - EINVAL ;
}
dev - > mtu = new_mtu ;
return 0 ;
}
static int hsr_dev_open ( struct net_device * dev )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2014-07-05 01:37:27 +04:00
struct net_device * slave ;
2013-10-31 00:10:47 +04:00
int i ;
char * slave_name ;
2014-07-05 01:34:38 +04:00
hsr = netdev_priv ( dev ) ;
2013-10-31 00:10:47 +04:00
for ( i = 0 ; i < HSR_MAX_SLAVE ; i + + ) {
2014-07-05 01:37:27 +04:00
slave = hsr - > slave [ i ] ;
if ( slave )
slave_name = slave - > name ;
2013-10-31 00:10:47 +04:00
else
slave_name = " null " ;
2014-07-05 01:37:27 +04:00
if ( ! is_slave_up ( slave ) )
2013-10-31 00:10:47 +04:00
netdev_warn ( dev , " Slave %c (%s) is not up; please bring it up to get a working HSR network \n " ,
' A ' + i , slave_name ) ;
}
return 0 ;
}
static int hsr_dev_close ( struct net_device * dev )
{
/* Nothing to do here. We could try to restore the state of the slaves
* to what they were before being changed by the hsr master dev ' s state ,
* but they might have been changed manually in the mean time too , so
* taking them up or down here might be confusing and is probably not a
* good idea .
*/
return 0 ;
}
2014-07-05 01:34:38 +04:00
static void hsr_fill_tag ( struct hsr_ethhdr * hsr_ethhdr , struct hsr_priv * hsr )
2013-10-31 00:10:47 +04:00
{
unsigned long irqflags ;
/* IEC 62439-1:2010, p 48, says the 4-bit "path" field can take values
* between 0001 - 1001 ( " ring identifier " , for regular HSR frames ) ,
* or 1111 ( " HSR management " , supervision frames ) . Unfortunately , the
* spec writers forgot to explain what a " ring identifier " is , or
* how it is used . So we just set this to 0001 for regular frames ,
* and 1111 for supervision frames .
*/
set_hsr_tag_path ( & hsr_ethhdr - > hsr_tag , 0x1 ) ;
/* IEC 62439-1:2010, p 12: "The link service data unit in an Ethernet
* frame is the content of the frame located between the Length / Type
* field and the Frame Check Sequence . "
*
* IEC 62439 - 3 , p 48 , specifies the " original LPDU " to include the
* original " LT " field ( what " LT " means is not explained anywhere as
* far as I can see - perhaps " Length/Type " ? ) . So LSDU_size might
* equal original length + 2.
* Also , the fact that this field is not used anywhere ( might be used
* by a RedBox connecting HSR and PRP nets ? ) means I cannot test its
* correctness . Instead of guessing , I set this to 0 here , to make any
* problems immediately apparent . Anyone using this driver with PRP / HSR
* RedBoxes might need to fix this . . .
*/
set_hsr_tag_LSDU_size ( & hsr_ethhdr - > hsr_tag , 0 ) ;
2014-07-05 01:34:38 +04:00
spin_lock_irqsave ( & hsr - > seqnr_lock , irqflags ) ;
hsr_ethhdr - > hsr_tag . sequence_nr = htons ( hsr - > sequence_nr ) ;
hsr - > sequence_nr + + ;
spin_unlock_irqrestore ( & hsr - > seqnr_lock , irqflags ) ;
2013-10-31 00:10:47 +04:00
hsr_ethhdr - > hsr_tag . encap_proto = hsr_ethhdr - > ethhdr . h_proto ;
hsr_ethhdr - > ethhdr . h_proto = htons ( ETH_P_PRP ) ;
}
2014-07-05 01:34:38 +04:00
static int slave_xmit ( struct sk_buff * skb , struct hsr_priv * hsr ,
2013-10-31 00:10:47 +04:00
enum hsr_dev_idx dev_idx )
{
struct hsr_ethhdr * hsr_ethhdr ;
hsr_ethhdr = ( struct hsr_ethhdr * ) skb - > data ;
2014-07-05 01:34:38 +04:00
skb - > dev = hsr - > slave [ dev_idx ] ;
2014-07-05 01:37:27 +04:00
if ( unlikely ( ! skb - > dev ) )
return NET_XMIT_DROP ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
hsr_addr_subst_dest ( hsr , & hsr_ethhdr - > ethhdr , dev_idx ) ;
2013-10-31 00:10:47 +04:00
/* Address substitution (IEC62439-3 pp 26, 50): replace mac
* address of outgoing frame with that of the outgoing slave ' s .
*/
2014-02-18 22:37:20 +04:00
ether_addr_copy ( hsr_ethhdr - > ethhdr . h_source , skb - > dev - > dev_addr ) ;
2013-10-31 00:10:47 +04:00
return dev_queue_xmit ( skb ) ;
}
static int hsr_dev_xmit ( struct sk_buff * skb , struct net_device * dev )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2013-10-31 00:10:47 +04:00
struct hsr_ethhdr * hsr_ethhdr ;
struct sk_buff * skb2 ;
int res1 , res2 ;
2014-07-05 01:34:38 +04:00
hsr = netdev_priv ( dev ) ;
2013-10-31 00:10:47 +04:00
hsr_ethhdr = ( struct hsr_ethhdr * ) skb - > data ;
if ( ( skb - > protocol ! = htons ( ETH_P_PRP ) ) | |
( hsr_ethhdr - > ethhdr . h_proto ! = htons ( ETH_P_PRP ) ) ) {
2014-07-05 01:34:38 +04:00
hsr_fill_tag ( hsr_ethhdr , hsr ) ;
2013-10-31 00:10:47 +04:00
skb - > protocol = htons ( ETH_P_PRP ) ;
}
skb2 = pskb_copy ( skb , GFP_ATOMIC ) ;
2014-07-05 01:37:27 +04:00
res1 = slave_xmit ( skb , hsr , HSR_DEV_SLAVE_A ) ;
res2 = slave_xmit ( skb2 , hsr , HSR_DEV_SLAVE_B ) ;
2013-10-31 00:10:47 +04:00
if ( likely ( res1 = = NET_XMIT_SUCCESS | | res1 = = NET_XMIT_CN | |
res2 = = NET_XMIT_SUCCESS | | res2 = = NET_XMIT_CN ) ) {
2014-07-05 01:34:38 +04:00
hsr - > dev - > stats . tx_packets + + ;
hsr - > dev - > stats . tx_bytes + = skb - > len ;
2013-10-31 00:10:47 +04:00
} else {
2014-07-05 01:34:38 +04:00
hsr - > dev - > stats . tx_dropped + + ;
2013-10-31 00:10:47 +04:00
}
return NETDEV_TX_OK ;
}
static int hsr_header_create ( struct sk_buff * skb , struct net_device * dev ,
unsigned short type , const void * daddr ,
const void * saddr , unsigned int len )
{
int res ;
/* Make room for the HSR tag now. We will fill it in later (in
* hsr_dev_xmit )
*/
2014-07-05 01:34:38 +04:00
if ( skb_headroom ( skb ) < HSR_HLEN + ETH_HLEN )
2013-10-31 00:10:47 +04:00
return - ENOBUFS ;
2014-07-05 01:34:38 +04:00
skb_push ( skb , HSR_HLEN ) ;
2013-10-31 00:10:47 +04:00
/* To allow VLAN/HSR combos we should probably use
2014-07-05 01:34:38 +04:00
* res = dev_hard_header ( skb , dev , type , daddr , saddr , len + HSR_HLEN ) ;
2013-10-31 00:10:47 +04:00
* here instead . It would require other changes too , though - e . g .
* separate headers for each slave etc . . .
*/
2014-07-05 01:34:38 +04:00
res = eth_header ( skb , dev , type , daddr , saddr , len + HSR_HLEN ) ;
2013-10-31 00:10:47 +04:00
if ( res < = 0 )
return res ;
skb_reset_mac_header ( skb ) ;
2014-07-05 01:34:38 +04:00
return res + HSR_HLEN ;
2013-10-31 00:10:47 +04:00
}
static const struct header_ops hsr_header_ops = {
. create = hsr_header_create ,
. parse = eth_header_parse ,
} ;
/* HSR:2010 supervision frames should be padded so that the whole frame,
* including headers and FCS , is 64 bytes ( without VLAN ) .
*/
static int hsr_pad ( int size )
{
2014-07-05 01:34:38 +04:00
const int min_size = ETH_ZLEN - HSR_HLEN - ETH_HLEN ;
2013-10-31 00:10:47 +04:00
if ( size > = min_size )
return size ;
return min_size ;
}
static void send_hsr_supervision_frame ( struct net_device * hsr_dev , u8 type )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2013-10-31 00:10:47 +04:00
struct sk_buff * skb ;
int hlen , tlen ;
struct hsr_sup_tag * hsr_stag ;
struct hsr_sup_payload * hsr_sp ;
unsigned long irqflags ;
hlen = LL_RESERVED_SPACE ( hsr_dev ) ;
tlen = hsr_dev - > needed_tailroom ;
skb = alloc_skb ( hsr_pad ( sizeof ( struct hsr_sup_payload ) ) + hlen + tlen ,
GFP_ATOMIC ) ;
if ( skb = = NULL )
return ;
2014-07-05 01:34:38 +04:00
hsr = netdev_priv ( hsr_dev ) ;
2013-10-31 00:10:47 +04:00
skb_reserve ( skb , hlen ) ;
skb - > dev = hsr_dev ;
skb - > protocol = htons ( ETH_P_PRP ) ;
skb - > priority = TC_PRIO_CONTROL ;
if ( dev_hard_header ( skb , skb - > dev , ETH_P_PRP ,
2014-07-05 01:34:38 +04:00
hsr - > sup_multicast_addr ,
2013-10-31 00:10:47 +04:00
skb - > dev - > dev_addr , skb - > len ) < 0 )
goto out ;
skb_pull ( skb , sizeof ( struct ethhdr ) ) ;
hsr_stag = ( typeof ( hsr_stag ) ) skb - > data ;
set_hsr_stag_path ( hsr_stag , 0xf ) ;
set_hsr_stag_HSR_Ver ( hsr_stag , 0 ) ;
2014-07-05 01:34:38 +04:00
spin_lock_irqsave ( & hsr - > seqnr_lock , irqflags ) ;
hsr_stag - > sequence_nr = htons ( hsr - > sequence_nr ) ;
hsr - > sequence_nr + + ;
spin_unlock_irqrestore ( & hsr - > seqnr_lock , irqflags ) ;
2013-10-31 00:10:47 +04:00
hsr_stag - > HSR_TLV_Type = type ;
hsr_stag - > HSR_TLV_Length = 12 ;
skb_push ( skb , sizeof ( struct ethhdr ) ) ;
/* Payload: MacAddressA */
hsr_sp = ( typeof ( hsr_sp ) ) skb_put ( skb , sizeof ( * hsr_sp ) ) ;
2014-02-18 22:37:20 +04:00
ether_addr_copy ( hsr_sp - > MacAddressA , hsr_dev - > dev_addr ) ;
2013-10-31 00:10:47 +04:00
dev_queue_xmit ( skb ) ;
return ;
out :
kfree_skb ( skb ) ;
}
/* Announce (supervision frame) timer function
*/
static void hsr_announce ( unsigned long data )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
hsr = ( struct hsr_priv * ) data ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
if ( hsr - > announce_count < 3 ) {
send_hsr_supervision_frame ( hsr - > dev , HSR_TLV_ANNOUNCE ) ;
hsr - > announce_count + + ;
2013-10-31 00:10:47 +04:00
} else {
2014-07-05 01:34:38 +04:00
send_hsr_supervision_frame ( hsr - > dev , HSR_TLV_LIFE_CHECK ) ;
2013-10-31 00:10:47 +04:00
}
2014-07-05 01:34:38 +04:00
if ( hsr - > announce_count < 3 )
hsr - > announce_timer . expires = jiffies +
2013-10-31 00:10:47 +04:00
msecs_to_jiffies ( HSR_ANNOUNCE_INTERVAL ) ;
else
2014-07-05 01:34:38 +04:00
hsr - > announce_timer . expires = jiffies +
2013-10-31 00:10:47 +04:00
msecs_to_jiffies ( HSR_LIFE_CHECK_INTERVAL ) ;
2014-07-05 01:34:38 +04:00
if ( is_admin_up ( hsr - > dev ) )
add_timer ( & hsr - > announce_timer ) ;
2013-10-31 00:10:47 +04:00
}
static void restore_slaves ( struct net_device * hsr_dev )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
hsr = netdev_priv ( hsr_dev ) ;
2014-07-05 01:37:27 +04:00
hsr_del_slave ( hsr , 1 ) ;
hsr_del_slave ( hsr , 0 ) ;
2013-10-31 00:10:47 +04:00
}
static void reclaim_hsr_dev ( struct rcu_head * rh )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
hsr = container_of ( rh , struct hsr_priv , rcu_head ) ;
free_netdev ( hsr - > dev ) ;
2013-10-31 00:10:47 +04:00
}
/* According to comments in the declaration of struct net_device, this function
* is " Called from unregister, can be used to call free_netdev " . Ok then . . .
*/
static void hsr_dev_destroy ( struct net_device * hsr_dev )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
hsr = netdev_priv ( hsr_dev ) ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:35:47 +04:00
del_timer_sync ( & hsr - > prune_timer ) ;
del_timer_sync ( & hsr - > announce_timer ) ;
2014-07-05 01:34:38 +04:00
unregister_hsr_master ( hsr ) ; /* calls list_del_rcu on hsr */
2013-10-31 00:10:47 +04:00
restore_slaves ( hsr_dev ) ;
2014-07-05 01:34:38 +04:00
call_rcu ( & hsr - > rcu_head , reclaim_hsr_dev ) ; /* reclaim hsr */
2013-10-31 00:10:47 +04:00
}
static const struct net_device_ops hsr_device_ops = {
. ndo_change_mtu = hsr_dev_change_mtu ,
. ndo_open = hsr_dev_open ,
. ndo_stop = hsr_dev_close ,
. ndo_start_xmit = hsr_dev_xmit ,
} ;
void hsr_dev_setup ( struct net_device * dev )
{
random_ether_addr ( dev - > dev_addr ) ;
ether_setup ( dev ) ;
dev - > header_ops = & hsr_header_ops ;
dev - > netdev_ops = & hsr_device_ops ;
dev - > tx_queue_len = 0 ;
dev - > destructor = hsr_dev_destroy ;
}
/* Return true if dev is a HSR master; return false otherwise.
*/
bool is_hsr_master ( struct net_device * dev )
{
return ( dev - > netdev_ops - > ndo_start_xmit = = hsr_dev_xmit ) ;
}
/* Default multicast address for HSR Supervision frames */
2014-02-18 22:37:20 +04:00
static const unsigned char def_multicast_addr [ ETH_ALEN ] __aligned ( 2 ) = {
2013-10-31 00:10:47 +04:00
0x01 , 0x15 , 0x4e , 0x00 , 0x01 , 0x00
} ;
int hsr_dev_finalize ( struct net_device * hsr_dev , struct net_device * slave [ 2 ] ,
unsigned char multicast_spec )
{
2014-07-05 01:34:38 +04:00
struct hsr_priv * hsr ;
2013-10-31 00:10:47 +04:00
int res ;
2014-07-05 01:34:38 +04:00
hsr = netdev_priv ( hsr_dev ) ;
hsr - > dev = hsr_dev ;
2014-07-05 01:37:27 +04:00
hsr - > slave [ 0 ] = NULL ;
hsr - > slave [ 1 ] = NULL ;
2014-07-05 01:34:38 +04:00
INIT_LIST_HEAD ( & hsr - > node_db ) ;
INIT_LIST_HEAD ( & hsr - > self_node_db ) ;
2014-07-05 01:37:27 +04:00
ether_addr_copy ( hsr_dev - > dev_addr , slave [ 0 ] - > dev_addr ) ;
/* Make sure we recognize frames from ourselves in hsr_rcv() */
res = hsr_create_self_node ( & hsr - > self_node_db , hsr_dev - > dev_addr ,
slave [ 1 ] - > dev_addr ) ;
if ( res < 0 )
return res ;
hsr_dev - > features = slave [ 0 ] - > features & slave [ 1 ] - > features ;
/* Prevent recursive tx locking */
hsr_dev - > features | = NETIF_F_LLTX ;
/* VLAN on top of HSR needs testing and probably some work on
* hsr_header_create ( ) etc .
*/
hsr_dev - > features | = NETIF_F_VLAN_CHALLENGED ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
spin_lock_init ( & hsr - > seqnr_lock ) ;
2013-10-31 00:10:47 +04:00
/* Overflow soon to find bugs easier: */
2014-07-05 01:34:38 +04:00
hsr - > sequence_nr = USHRT_MAX - 1024 ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:34:38 +04:00
init_timer ( & hsr - > announce_timer ) ;
hsr - > announce_timer . function = hsr_announce ;
hsr - > announce_timer . data = ( unsigned long ) hsr ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:35:47 +04:00
init_timer ( & hsr - > prune_timer ) ;
hsr - > prune_timer . function = hsr_prune_nodes ;
hsr - > prune_timer . data = ( unsigned long ) hsr ;
2014-07-05 01:34:38 +04:00
ether_addr_copy ( hsr - > sup_multicast_addr , def_multicast_addr ) ;
hsr - > sup_multicast_addr [ ETH_ALEN - 1 ] = multicast_spec ;
2013-10-31 00:10:47 +04:00
/* FIXME: should I modify the value of these?
*
* - hsr_dev - > flags - i . e .
* IFF_MASTER / SLAVE ?
* - hsr_dev - > priv_flags - i . e .
* IFF_EBRIDGE ?
* IFF_TX_SKB_SHARING ?
* IFF_HSR_MASTER / SLAVE ?
*/
/* Make sure the 1st call to netif_carrier_on() gets through */
netif_carrier_off ( hsr_dev ) ;
res = register_netdevice ( hsr_dev ) ;
if ( res )
2014-07-05 01:37:27 +04:00
return res ;
res = hsr_add_slave ( hsr , slave [ 0 ] , 0 ) ;
if ( res )
return res ;
res = hsr_add_slave ( hsr , slave [ 1 ] , 1 ) ;
if ( res ) {
hsr_del_slave ( hsr , 0 ) ;
return res ;
}
2013-10-31 00:10:47 +04:00
2014-07-05 01:35:47 +04:00
hsr - > prune_timer . expires = jiffies + msecs_to_jiffies ( PRUNE_PERIOD ) ;
add_timer ( & hsr - > prune_timer ) ;
2014-07-05 01:34:38 +04:00
register_hsr_master ( hsr ) ;
2013-10-31 00:10:47 +04:00
return 0 ;
}