mirror of
https://github.com/torvalds/linux.git
synced 2024-11-30 16:11:38 +00:00
8f4c0e0178
Parallel Redundancy Protocol (PRP) is another redundancy protocol introduced by IEC 63439 standard. It is similar to HSR in many aspects:- - Use a pair of Ethernet interfaces to created the PRP device - Use a 6 byte redundancy protocol part (RCT, Redundancy Check Trailer) similar to HSR Tag. - Has Link Redundancy Entity (LRE) that works with RCT to implement redundancy. Key difference is that the protocol unit is a trailer instead of a prefix as in HSR. That makes it inter-operable with tradition network components such as bridges/switches which treat it as pad bytes, whereas HSR nodes requires some kind of translators (Called redbox) to talk to regular network devices. This features allows regular linux box to be converted to a DAN-P box. DAN-P stands for Dual Attached Node - PRP similar to DAN-H (Dual Attached Node - HSR). Add a comment at the header/source code to explicitly state that the driver files also handles PRP protocol as well. Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> Signed-off-by: David S. Miller <davem@davemloft.net>
160 lines
3.7 KiB
C
160 lines
3.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright 2011-2014 Autronica Fire and Security AS
|
|
*
|
|
* Author(s):
|
|
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
|
|
*
|
|
* 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;
|
|
}
|
|
|
|
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");
|