1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright 2011-2014 Autronica Fire and Security AS |
3 | * |
4 | * Author(s): |
5 | * 2011-2014 Arvid Brodin, arvid.brodin@alten.se |
6 | * |
7 | * Event handling for HSR and PRP devices. |
8 | */ |
9 | |
10 | #include <linux/netdevice.h> |
11 | #include <net/rtnetlink.h> |
12 | #include <linux/rculist.h> |
13 | #include <linux/timer.h> |
14 | #include <linux/etherdevice.h> |
15 | #include "hsr_main.h" |
16 | #include "hsr_device.h" |
17 | #include "hsr_netlink.h" |
18 | #include "hsr_framereg.h" |
19 | #include "hsr_slave.h" |
20 | |
21 | static bool hsr_slave_empty(struct hsr_priv *hsr) |
22 | { |
23 | struct hsr_port *port; |
24 | |
25 | hsr_for_each_port(hsr, port) |
26 | if (port->type != HSR_PT_MASTER) |
27 | return false; |
28 | return true; |
29 | } |
30 | |
31 | static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, |
32 | void *ptr) |
33 | { |
34 | struct hsr_port *port, *master; |
35 | struct net_device *dev; |
36 | struct hsr_priv *hsr; |
37 | LIST_HEAD(list_kill); |
38 | int mtu_max; |
39 | int res; |
40 | |
41 | dev = netdev_notifier_info_to_dev(info: ptr); |
42 | port = hsr_port_get_rtnl(dev); |
43 | if (!port) { |
44 | if (!is_hsr_master(dev)) |
45 | return NOTIFY_DONE; /* Not an HSR device */ |
46 | hsr = netdev_priv(dev); |
47 | port = hsr_port_get_hsr(hsr, pt: HSR_PT_MASTER); |
48 | if (!port) { |
49 | /* Resend of notification concerning removed device? */ |
50 | return NOTIFY_DONE; |
51 | } |
52 | } else { |
53 | hsr = port->hsr; |
54 | } |
55 | |
56 | switch (event) { |
57 | case NETDEV_UP: /* Administrative state DOWN */ |
58 | case NETDEV_DOWN: /* Administrative state UP */ |
59 | case NETDEV_CHANGE: /* Link (carrier) state changes */ |
60 | hsr_check_carrier_and_operstate(hsr); |
61 | break; |
62 | case NETDEV_CHANGENAME: |
63 | if (is_hsr_master(dev)) |
64 | hsr_debugfs_rename(dev); |
65 | break; |
66 | case NETDEV_CHANGEADDR: |
67 | if (port->type == HSR_PT_MASTER) { |
68 | /* This should not happen since there's no |
69 | * ndo_set_mac_address() for HSR devices - i.e. not |
70 | * supported. |
71 | */ |
72 | break; |
73 | } |
74 | |
75 | master = hsr_port_get_hsr(hsr, pt: HSR_PT_MASTER); |
76 | |
77 | if (port->type == HSR_PT_SLAVE_A) { |
78 | eth_hw_addr_set(dev: master->dev, addr: dev->dev_addr); |
79 | call_netdevice_notifiers(val: NETDEV_CHANGEADDR, |
80 | dev: master->dev); |
81 | } |
82 | |
83 | /* Make sure we recognize frames from ourselves in hsr_rcv() */ |
84 | port = hsr_port_get_hsr(hsr, pt: HSR_PT_SLAVE_B); |
85 | res = hsr_create_self_node(hsr, |
86 | addr_a: master->dev->dev_addr, |
87 | addr_b: port ? |
88 | port->dev->dev_addr : |
89 | master->dev->dev_addr); |
90 | if (res) |
91 | netdev_warn(dev: master->dev, |
92 | format: "Could not update HSR node address.\n" ); |
93 | break; |
94 | case NETDEV_CHANGEMTU: |
95 | if (port->type == HSR_PT_MASTER) |
96 | break; /* Handled in ndo_change_mtu() */ |
97 | mtu_max = hsr_get_max_mtu(hsr: port->hsr); |
98 | master = hsr_port_get_hsr(hsr: port->hsr, pt: HSR_PT_MASTER); |
99 | master->dev->mtu = mtu_max; |
100 | break; |
101 | case NETDEV_UNREGISTER: |
102 | if (!is_hsr_master(dev)) { |
103 | master = hsr_port_get_hsr(hsr: port->hsr, pt: HSR_PT_MASTER); |
104 | hsr_del_port(port); |
105 | if (hsr_slave_empty(hsr: master->hsr)) { |
106 | const struct rtnl_link_ops *ops; |
107 | |
108 | ops = master->dev->rtnl_link_ops; |
109 | ops->dellink(master->dev, &list_kill); |
110 | unregister_netdevice_many(head: &list_kill); |
111 | } |
112 | } |
113 | break; |
114 | case NETDEV_PRE_TYPE_CHANGE: |
115 | /* HSR works only on Ethernet devices. Refuse slave to change |
116 | * its type. |
117 | */ |
118 | return NOTIFY_BAD; |
119 | } |
120 | |
121 | return NOTIFY_DONE; |
122 | } |
123 | |
124 | struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt) |
125 | { |
126 | struct hsr_port *port; |
127 | |
128 | hsr_for_each_port(hsr, port) |
129 | if (port->type == pt) |
130 | return port; |
131 | return NULL; |
132 | } |
133 | |
134 | int hsr_get_version(struct net_device *dev, enum hsr_version *ver) |
135 | { |
136 | struct hsr_priv *hsr; |
137 | |
138 | hsr = netdev_priv(dev); |
139 | *ver = hsr->prot_version; |
140 | |
141 | return 0; |
142 | } |
143 | EXPORT_SYMBOL(hsr_get_version); |
144 | |
145 | static struct notifier_block hsr_nb = { |
146 | .notifier_call = hsr_netdev_notify, /* Slave event notifications */ |
147 | }; |
148 | |
149 | static int __init hsr_init(void) |
150 | { |
151 | int res; |
152 | |
153 | BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN); |
154 | |
155 | register_netdevice_notifier(nb: &hsr_nb); |
156 | res = hsr_netlink_init(); |
157 | |
158 | return res; |
159 | } |
160 | |
161 | static void __exit hsr_exit(void) |
162 | { |
163 | hsr_netlink_exit(); |
164 | hsr_debugfs_remove_root(); |
165 | unregister_netdevice_notifier(nb: &hsr_nb); |
166 | } |
167 | |
168 | module_init(hsr_init); |
169 | module_exit(hsr_exit); |
170 | MODULE_LICENSE("GPL" ); |
171 | |