Commit | Line | Data |
---|---|---|
70ebe4a4 | 1 | /* Copyright 2011-2014 Autronica Fire and Security AS |
f421436a AB |
2 | * |
3 | * This program is free software; you can redistribute it and/or modify it | |
4 | * under the terms of the GNU General Public License as published by the Free | |
5 | * Software Foundation; either version 2 of the License, or (at your option) | |
6 | * any later version. | |
7 | * | |
8 | * Author(s): | |
70ebe4a4 | 9 | * 2011-2014 Arvid Brodin, arvid.brodin@alten.se |
f421436a AB |
10 | */ |
11 | ||
12 | #include <linux/netdevice.h> | |
13 | #include <linux/rculist.h> | |
14 | #include <linux/timer.h> | |
15 | #include <linux/etherdevice.h> | |
16 | #include "hsr_main.h" | |
17 | #include "hsr_device.h" | |
18 | #include "hsr_netlink.h" | |
19 | #include "hsr_framereg.h" | |
51f3c605 | 20 | #include "hsr_slave.h" |
f421436a AB |
21 | |
22 | ||
23 | /* List of all registered virtual HSR devices */ | |
24 | static LIST_HEAD(hsr_list); | |
25 | ||
70ebe4a4 | 26 | void register_hsr_master(struct hsr_priv *hsr) |
f421436a | 27 | { |
70ebe4a4 | 28 | list_add_tail_rcu(&hsr->hsr_list, &hsr_list); |
f421436a AB |
29 | } |
30 | ||
70ebe4a4 | 31 | void unregister_hsr_master(struct hsr_priv *hsr) |
f421436a | 32 | { |
70ebe4a4 | 33 | struct hsr_priv *hsr_it; |
f421436a | 34 | |
70ebe4a4 AB |
35 | list_for_each_entry(hsr_it, &hsr_list, hsr_list) |
36 | if (hsr_it == hsr) { | |
37 | list_del_rcu(&hsr_it->hsr_list); | |
f421436a AB |
38 | return; |
39 | } | |
40 | } | |
41 | ||
f421436a AB |
42 | |
43 | static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, | |
44 | void *ptr) | |
45 | { | |
c5a75911 AB |
46 | struct net_device *dev; |
47 | struct hsr_port *port, *master; | |
70ebe4a4 | 48 | struct hsr_priv *hsr; |
f421436a AB |
49 | int mtu_max; |
50 | int res; | |
f421436a AB |
51 | |
52 | dev = netdev_notifier_info_to_dev(ptr); | |
c5a75911 AB |
53 | port = hsr_port_get_rtnl(dev); |
54 | if (port == NULL) { | |
f421436a | 55 | if (!is_hsr_master(dev)) |
c5a75911 | 56 | return NOTIFY_DONE; /* Not an HSR device */ |
70ebe4a4 | 57 | hsr = netdev_priv(dev); |
c5a75911 AB |
58 | port = hsr_port_get_hsr(hsr, HSR_PT_MASTER); |
59 | } else { | |
60 | hsr = port->hsr; | |
f421436a AB |
61 | } |
62 | ||
63 | switch (event) { | |
64 | case NETDEV_UP: /* Administrative state DOWN */ | |
65 | case NETDEV_DOWN: /* Administrative state UP */ | |
66 | case NETDEV_CHANGE: /* Link (carrier) state changes */ | |
e9aae56e | 67 | hsr_check_carrier_and_operstate(hsr); |
f421436a AB |
68 | break; |
69 | case NETDEV_CHANGEADDR: | |
c5a75911 AB |
70 | if (port->type == HSR_PT_MASTER) { |
71 | /* This should not happen since there's no | |
72 | * ndo_set_mac_address() for HSR devices - i.e. not | |
73 | * supported. | |
74 | */ | |
f421436a | 75 | break; |
c5a75911 | 76 | } |
f421436a | 77 | |
c5a75911 AB |
78 | master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); |
79 | ||
80 | if (port->type == HSR_PT_SLAVE_A) { | |
81 | ether_addr_copy(master->dev->dev_addr, dev->dev_addr); | |
82 | call_netdevice_notifiers(NETDEV_CHANGEADDR, master->dev); | |
51f3c605 | 83 | } |
f421436a AB |
84 | |
85 | /* Make sure we recognize frames from ourselves in hsr_rcv() */ | |
c5a75911 | 86 | port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); |
70ebe4a4 | 87 | res = hsr_create_self_node(&hsr->self_node_db, |
c5a75911 AB |
88 | master->dev->dev_addr, |
89 | port ? | |
90 | port->dev->dev_addr : | |
91 | master->dev->dev_addr); | |
f421436a | 92 | if (res) |
c5a75911 | 93 | netdev_warn(master->dev, |
f421436a | 94 | "Could not update HSR node address.\n"); |
f421436a AB |
95 | break; |
96 | case NETDEV_CHANGEMTU: | |
c5a75911 | 97 | if (port->type == HSR_PT_MASTER) |
f421436a | 98 | break; /* Handled in ndo_change_mtu() */ |
c5a75911 AB |
99 | mtu_max = hsr_get_max_mtu(port->hsr); |
100 | master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER); | |
101 | master->dev->mtu = mtu_max; | |
f421436a AB |
102 | break; |
103 | case NETDEV_UNREGISTER: | |
c5a75911 | 104 | hsr_del_port(port); |
f421436a AB |
105 | break; |
106 | case NETDEV_PRE_TYPE_CHANGE: | |
107 | /* HSR works only on Ethernet devices. Refuse slave to change | |
108 | * its type. | |
109 | */ | |
110 | return NOTIFY_BAD; | |
111 | } | |
112 | ||
113 | return NOTIFY_DONE; | |
114 | } | |
115 | ||
116 | ||
c5a75911 AB |
117 | struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt) |
118 | { | |
119 | struct hsr_port *port; | |
120 | ||
121 | hsr_for_each_port(hsr, port) | |
122 | if (port->type == pt) | |
123 | return port; | |
124 | return NULL; | |
125 | } | |
126 | ||
f421436a AB |
127 | static struct notifier_block hsr_nb = { |
128 | .notifier_call = hsr_netdev_notify, /* Slave event notifications */ | |
129 | }; | |
130 | ||
131 | ||
132 | static int __init hsr_init(void) | |
133 | { | |
134 | int res; | |
135 | ||
70ebe4a4 | 136 | BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN); |
f421436a | 137 | |
f421436a | 138 | register_netdevice_notifier(&hsr_nb); |
f421436a AB |
139 | res = hsr_netlink_init(); |
140 | ||
141 | return res; | |
142 | } | |
143 | ||
144 | static void __exit hsr_exit(void) | |
145 | { | |
146 | unregister_netdevice_notifier(&hsr_nb); | |
f421436a | 147 | hsr_netlink_exit(); |
f421436a AB |
148 | } |
149 | ||
150 | module_init(hsr_init); | |
151 | module_exit(hsr_exit); | |
152 | MODULE_LICENSE("GPL"); |