10e7623bdSMurali Karicheri // SPDX-License-Identifier: GPL-2.0
270ebe4a4SArvid Brodin /* Copyright 2011-2014 Autronica Fire and Security AS
3f421436aSArvid Brodin *
4f421436aSArvid Brodin * Author(s):
570ebe4a4SArvid Brodin * 2011-2014 Arvid Brodin, arvid.brodin@alten.se
68f4c0e01SMurali Karicheri *
78f4c0e01SMurali Karicheri * Event handling for HSR and PRP devices.
8f421436aSArvid Brodin */
9f421436aSArvid Brodin
10f421436aSArvid Brodin #include <linux/netdevice.h>
11de0083c7STaehee Yoo #include <net/rtnetlink.h>
12f421436aSArvid Brodin #include <linux/rculist.h>
13f421436aSArvid Brodin #include <linux/timer.h>
14f421436aSArvid Brodin #include <linux/etherdevice.h>
15f421436aSArvid Brodin #include "hsr_main.h"
16f421436aSArvid Brodin #include "hsr_device.h"
17f421436aSArvid Brodin #include "hsr_netlink.h"
18f421436aSArvid Brodin #include "hsr_framereg.h"
1951f3c605SArvid Brodin #include "hsr_slave.h"
20f421436aSArvid Brodin
hsr_slave_empty(struct hsr_priv * hsr)2134a9c361STaehee Yoo static bool hsr_slave_empty(struct hsr_priv *hsr)
2234a9c361STaehee Yoo {
2334a9c361STaehee Yoo struct hsr_port *port;
2434a9c361STaehee Yoo
2534a9c361STaehee Yoo hsr_for_each_port(hsr, port)
2634a9c361STaehee Yoo if (port->type != HSR_PT_MASTER)
2734a9c361STaehee Yoo return false;
2834a9c361STaehee Yoo return true;
2934a9c361STaehee Yoo }
3034a9c361STaehee Yoo
hsr_netdev_notify(struct notifier_block * nb,unsigned long event,void * ptr)31f421436aSArvid Brodin static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,
32f421436aSArvid Brodin void *ptr)
33f421436aSArvid Brodin {
34c5a75911SArvid Brodin struct hsr_port *port, *master;
3534a9c361STaehee Yoo struct net_device *dev;
3670ebe4a4SArvid Brodin struct hsr_priv *hsr;
3734a9c361STaehee Yoo LIST_HEAD(list_kill);
38f421436aSArvid Brodin int mtu_max;
39f421436aSArvid Brodin int res;
40f421436aSArvid Brodin
41f421436aSArvid Brodin dev = netdev_notifier_info_to_dev(ptr);
42c5a75911SArvid Brodin port = hsr_port_get_rtnl(dev);
4305ca6e64SMurali Karicheri if (!port) {
44f421436aSArvid Brodin if (!is_hsr_master(dev))
45c5a75911SArvid Brodin return NOTIFY_DONE; /* Not an HSR device */
4670ebe4a4SArvid Brodin hsr = netdev_priv(dev);
47c5a75911SArvid Brodin port = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
4805ca6e64SMurali Karicheri if (!port) {
4956b08fdcSArvid Brodin /* Resend of notification concerning removed device? */
5056b08fdcSArvid Brodin return NOTIFY_DONE;
5156b08fdcSArvid Brodin }
52c5a75911SArvid Brodin } else {
53c5a75911SArvid Brodin hsr = port->hsr;
54f421436aSArvid Brodin }
55f421436aSArvid Brodin
56f421436aSArvid Brodin switch (event) {
57f421436aSArvid Brodin case NETDEV_UP: /* Administrative state DOWN */
58f421436aSArvid Brodin case NETDEV_DOWN: /* Administrative state UP */
59f421436aSArvid Brodin case NETDEV_CHANGE: /* Link (carrier) state changes */
60e9aae56eSArvid Brodin hsr_check_carrier_and_operstate(hsr);
61f421436aSArvid Brodin break;
624c2d5e33STaehee Yoo case NETDEV_CHANGENAME:
6304b69426STaehee Yoo if (is_hsr_master(dev))
644c2d5e33STaehee Yoo hsr_debugfs_rename(dev);
654c2d5e33STaehee Yoo break;
66f421436aSArvid Brodin case NETDEV_CHANGEADDR:
67c5a75911SArvid Brodin if (port->type == HSR_PT_MASTER) {
68c5a75911SArvid Brodin /* This should not happen since there's no
69c5a75911SArvid Brodin * ndo_set_mac_address() for HSR devices - i.e. not
70c5a75911SArvid Brodin * supported.
71f421436aSArvid Brodin */
72f421436aSArvid Brodin break;
73c5a75911SArvid Brodin }
74f421436aSArvid Brodin
75c5a75911SArvid Brodin master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
76c5a75911SArvid Brodin
77c5a75911SArvid Brodin if (port->type == HSR_PT_SLAVE_A) {
78e35b8d7dSJakub Kicinski eth_hw_addr_set(master->dev, dev->dev_addr);
79d595b85aSMurali Karicheri call_netdevice_notifiers(NETDEV_CHANGEADDR,
80d595b85aSMurali Karicheri master->dev);
8151f3c605SArvid Brodin }
82f421436aSArvid Brodin
83f421436aSArvid Brodin /* Make sure we recognize frames from ourselves in hsr_rcv() */
84c5a75911SArvid Brodin port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
8592a35678STaehee Yoo res = hsr_create_self_node(hsr,
86c5a75911SArvid Brodin master->dev->dev_addr,
87c5a75911SArvid Brodin port ?
88c5a75911SArvid Brodin port->dev->dev_addr :
89c5a75911SArvid Brodin master->dev->dev_addr);
90f421436aSArvid Brodin if (res)
91c5a75911SArvid Brodin netdev_warn(master->dev,
92f421436aSArvid Brodin "Could not update HSR node address.\n");
93f421436aSArvid Brodin break;
94f421436aSArvid Brodin case NETDEV_CHANGEMTU:
95c5a75911SArvid Brodin if (port->type == HSR_PT_MASTER)
96f421436aSArvid Brodin break; /* Handled in ndo_change_mtu() */
97c5a75911SArvid Brodin mtu_max = hsr_get_max_mtu(port->hsr);
98c5a75911SArvid Brodin master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER);
99c5a75911SArvid Brodin master->dev->mtu = mtu_max;
100f421436aSArvid Brodin break;
101f421436aSArvid Brodin case NETDEV_UNREGISTER:
10234a9c361STaehee Yoo if (!is_hsr_master(dev)) {
10334a9c361STaehee Yoo master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER);
104c5a75911SArvid Brodin hsr_del_port(port);
10534a9c361STaehee Yoo if (hsr_slave_empty(master->hsr)) {
106de0083c7STaehee Yoo const struct rtnl_link_ops *ops;
107de0083c7STaehee Yoo
108de0083c7STaehee Yoo ops = master->dev->rtnl_link_ops;
109de0083c7STaehee Yoo ops->dellink(master->dev, &list_kill);
11034a9c361STaehee Yoo unregister_netdevice_many(&list_kill);
11134a9c361STaehee Yoo }
11234a9c361STaehee Yoo }
113f421436aSArvid Brodin break;
114f421436aSArvid Brodin case NETDEV_PRE_TYPE_CHANGE:
115f421436aSArvid Brodin /* HSR works only on Ethernet devices. Refuse slave to change
116f421436aSArvid Brodin * its type.
117f421436aSArvid Brodin */
118f421436aSArvid Brodin return NOTIFY_BAD;
119f421436aSArvid Brodin }
120f421436aSArvid Brodin
121f421436aSArvid Brodin return NOTIFY_DONE;
122f421436aSArvid Brodin }
123f421436aSArvid Brodin
hsr_port_get_hsr(struct hsr_priv * hsr,enum hsr_port_type pt)124c5a75911SArvid Brodin struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt)
125c5a75911SArvid Brodin {
126c5a75911SArvid Brodin struct hsr_port *port;
127c5a75911SArvid Brodin
128c5a75911SArvid Brodin hsr_for_each_port(hsr, port)
129c5a75911SArvid Brodin if (port->type == pt)
130c5a75911SArvid Brodin return port;
131c5a75911SArvid Brodin return NULL;
132c5a75911SArvid Brodin }
133c5a75911SArvid Brodin
hsr_get_version(struct net_device * dev,enum hsr_version * ver)134dcf0cd1cSGeorge McCollister int hsr_get_version(struct net_device *dev, enum hsr_version *ver)
135dcf0cd1cSGeorge McCollister {
136dcf0cd1cSGeorge McCollister struct hsr_priv *hsr;
137dcf0cd1cSGeorge McCollister
138dcf0cd1cSGeorge McCollister hsr = netdev_priv(dev);
139dcf0cd1cSGeorge McCollister *ver = hsr->prot_version;
140dcf0cd1cSGeorge McCollister
141dcf0cd1cSGeorge McCollister return 0;
142dcf0cd1cSGeorge McCollister }
143dcf0cd1cSGeorge McCollister EXPORT_SYMBOL(hsr_get_version);
144dcf0cd1cSGeorge McCollister
145f421436aSArvid Brodin static struct notifier_block hsr_nb = {
146f421436aSArvid Brodin .notifier_call = hsr_netdev_notify, /* Slave event notifications */
147f421436aSArvid Brodin };
148f421436aSArvid Brodin
hsr_init(void)149f421436aSArvid Brodin static int __init hsr_init(void)
150f421436aSArvid Brodin {
151*0f25725dSFelix Maurer int err;
152f421436aSArvid Brodin
15370ebe4a4SArvid Brodin BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN);
154f421436aSArvid Brodin
155*0f25725dSFelix Maurer err = register_netdevice_notifier(&hsr_nb);
156*0f25725dSFelix Maurer if (err)
157*0f25725dSFelix Maurer return err;
158f421436aSArvid Brodin
159*0f25725dSFelix Maurer err = hsr_netlink_init();
160*0f25725dSFelix Maurer if (err) {
161*0f25725dSFelix Maurer unregister_netdevice_notifier(&hsr_nb);
162*0f25725dSFelix Maurer return err;
163*0f25725dSFelix Maurer }
164*0f25725dSFelix Maurer
165*0f25725dSFelix Maurer return 0;
166f421436aSArvid Brodin }
167f421436aSArvid Brodin
hsr_exit(void)168f421436aSArvid Brodin static void __exit hsr_exit(void)
169f421436aSArvid Brodin {
170f421436aSArvid Brodin hsr_netlink_exit();
171c6c4ccd7STaehee Yoo hsr_debugfs_remove_root();
172de0083c7STaehee Yoo unregister_netdevice_notifier(&hsr_nb);
173f421436aSArvid Brodin }
174f421436aSArvid Brodin
175f421436aSArvid Brodin module_init(hsr_init);
176f421436aSArvid Brodin module_exit(hsr_exit);
177f421436aSArvid Brodin MODULE_LICENSE("GPL");
178