forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
3.9 KiB
170 lines
3.9 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* Copyright 2011-2014 Autronica Fire and Security AS |
|
* |
|
* Author(s): |
|
* 2011-2014 Arvid Brodin, [email protected] |
|
* |
|
* Event handling for HSR and PRP devices. |
|
*/ |
|
|
|
#include <linux/netdevice.h> |
|
#include <net/rtnetlink.h> |
|
#include <linux/rculist.h> |
|
#include <linux/timer.h> |
|
#include <linux/etherdevice.h> |
|
#include "hsr_main.h" |
|
#include "hsr_device.h" |
|
#include "hsr_netlink.h" |
|
#include "hsr_framereg.h" |
|
#include "hsr_slave.h" |
|
|
|
static bool hsr_slave_empty(struct hsr_priv *hsr) |
|
{ |
|
struct hsr_port *port; |
|
|
|
hsr_for_each_port(hsr, port) |
|
if (port->type != HSR_PT_MASTER) |
|
return false; |
|
return true; |
|
} |
|
|
|
static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, |
|
void *ptr) |
|
{ |
|
struct hsr_port *port, *master; |
|
struct net_device *dev; |
|
struct hsr_priv *hsr; |
|
LIST_HEAD(list_kill); |
|
int mtu_max; |
|
int res; |
|
|
|
dev = netdev_notifier_info_to_dev(ptr); |
|
port = hsr_port_get_rtnl(dev); |
|
if (!port) { |
|
if (!is_hsr_master(dev)) |
|
return NOTIFY_DONE; /* Not an HSR device */ |
|
hsr = netdev_priv(dev); |
|
port = hsr_port_get_hsr(hsr, HSR_PT_MASTER); |
|
if (!port) { |
|
/* Resend of notification concerning removed device? */ |
|
return NOTIFY_DONE; |
|
} |
|
} else { |
|
hsr = port->hsr; |
|
} |
|
|
|
switch (event) { |
|
case NETDEV_UP: /* Administrative state DOWN */ |
|
case NETDEV_DOWN: /* Administrative state UP */ |
|
case NETDEV_CHANGE: /* Link (carrier) state changes */ |
|
hsr_check_carrier_and_operstate(hsr); |
|
break; |
|
case NETDEV_CHANGENAME: |
|
if (is_hsr_master(dev)) |
|
hsr_debugfs_rename(dev); |
|
break; |
|
case NETDEV_CHANGEADDR: |
|
if (port->type == HSR_PT_MASTER) { |
|
/* This should not happen since there's no |
|
* ndo_set_mac_address() for HSR devices - i.e. not |
|
* supported. |
|
*/ |
|
break; |
|
} |
|
|
|
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); |
|
|
|
if (port->type == HSR_PT_SLAVE_A) { |
|
ether_addr_copy(master->dev->dev_addr, dev->dev_addr); |
|
call_netdevice_notifiers(NETDEV_CHANGEADDR, |
|
master->dev); |
|
} |
|
|
|
/* Make sure we recognize frames from ourselves in hsr_rcv() */ |
|
port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); |
|
res = hsr_create_self_node(hsr, |
|
master->dev->dev_addr, |
|
port ? |
|
port->dev->dev_addr : |
|
master->dev->dev_addr); |
|
if (res) |
|
netdev_warn(master->dev, |
|
"Could not update HSR node address.\n"); |
|
break; |
|
case NETDEV_CHANGEMTU: |
|
if (port->type == HSR_PT_MASTER) |
|
break; /* Handled in ndo_change_mtu() */ |
|
mtu_max = hsr_get_max_mtu(port->hsr); |
|
master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER); |
|
master->dev->mtu = mtu_max; |
|
break; |
|
case NETDEV_UNREGISTER: |
|
if (!is_hsr_master(dev)) { |
|
master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER); |
|
hsr_del_port(port); |
|
if (hsr_slave_empty(master->hsr)) { |
|
const struct rtnl_link_ops *ops; |
|
|
|
ops = master->dev->rtnl_link_ops; |
|
ops->dellink(master->dev, &list_kill); |
|
unregister_netdevice_many(&list_kill); |
|
} |
|
} |
|
break; |
|
case NETDEV_PRE_TYPE_CHANGE: |
|
/* HSR works only on Ethernet devices. Refuse slave to change |
|
* its type. |
|
*/ |
|
return NOTIFY_BAD; |
|
} |
|
|
|
return NOTIFY_DONE; |
|
} |
|
|
|
struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt) |
|
{ |
|
struct hsr_port *port; |
|
|
|
hsr_for_each_port(hsr, port) |
|
if (port->type == pt) |
|
return port; |
|
return NULL; |
|
} |
|
|
|
int hsr_get_version(struct net_device *dev, enum hsr_version *ver) |
|
{ |
|
struct hsr_priv *hsr; |
|
|
|
hsr = netdev_priv(dev); |
|
*ver = hsr->prot_version; |
|
|
|
return 0; |
|
} |
|
EXPORT_SYMBOL(hsr_get_version); |
|
|
|
static struct notifier_block hsr_nb = { |
|
.notifier_call = hsr_netdev_notify, /* Slave event notifications */ |
|
}; |
|
|
|
static int __init hsr_init(void) |
|
{ |
|
int res; |
|
|
|
BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN); |
|
|
|
register_netdevice_notifier(&hsr_nb); |
|
res = hsr_netlink_init(); |
|
|
|
return res; |
|
} |
|
|
|
static void __exit hsr_exit(void) |
|
{ |
|
hsr_netlink_exit(); |
|
hsr_debugfs_remove_root(); |
|
unregister_netdevice_notifier(&hsr_nb); |
|
} |
|
|
|
module_init(hsr_init); |
|
module_exit(hsr_exit); |
|
MODULE_LICENSE("GPL");
|
|
|