1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2007-2012 Siemens AG |
4 | * |
5 | * Written by: |
6 | * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> |
7 | */ |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/module.h> |
11 | #include <linux/netdevice.h> |
12 | |
13 | #include <net/netlink.h> |
14 | #include <net/nl802154.h> |
15 | #include <net/mac802154.h> |
16 | #include <net/ieee802154_netdev.h> |
17 | #include <net/route.h> |
18 | #include <net/cfg802154.h> |
19 | |
20 | #include "ieee802154_i.h" |
21 | #include "cfg.h" |
22 | |
23 | static void ieee802154_tasklet_handler(struct tasklet_struct *t) |
24 | { |
25 | struct ieee802154_local *local = from_tasklet(local, t, tasklet); |
26 | struct sk_buff *skb; |
27 | |
28 | while ((skb = skb_dequeue(list: &local->skb_queue))) { |
29 | switch (skb->pkt_type) { |
30 | case IEEE802154_RX_MSG: |
31 | /* Clear skb->pkt_type in order to not confuse kernel |
32 | * netstack. |
33 | */ |
34 | skb->pkt_type = 0; |
35 | ieee802154_rx(local, skb); |
36 | break; |
37 | default: |
38 | WARN(1, "mac802154: Packet is of unknown type %d\n" , |
39 | skb->pkt_type); |
40 | kfree_skb(skb); |
41 | break; |
42 | } |
43 | } |
44 | } |
45 | |
46 | struct ieee802154_hw * |
47 | ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops) |
48 | { |
49 | struct wpan_phy *phy; |
50 | struct ieee802154_local *local; |
51 | size_t priv_size; |
52 | |
53 | if (WARN_ON(!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed || |
54 | !ops->start || !ops->stop || !ops->set_channel)) |
55 | return NULL; |
56 | |
57 | /* Ensure 32-byte alignment of our private data and hw private data. |
58 | * We use the wpan_phy priv data for both our ieee802154_local and for |
59 | * the driver's private data |
60 | * |
61 | * in memory it'll be like this: |
62 | * |
63 | * +-------------------------+ |
64 | * | struct wpan_phy | |
65 | * +-------------------------+ |
66 | * | struct ieee802154_local | |
67 | * +-------------------------+ |
68 | * | driver's private data | |
69 | * +-------------------------+ |
70 | * |
71 | * Due to ieee802154 layer isn't aware of driver and MAC structures, |
72 | * so lets align them here. |
73 | */ |
74 | |
75 | priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len; |
76 | |
77 | phy = wpan_phy_new(ops: &mac802154_config_ops, priv_size); |
78 | if (!phy) { |
79 | pr_err("failure to allocate master IEEE802.15.4 device\n" ); |
80 | return NULL; |
81 | } |
82 | |
83 | phy->privid = mac802154_wpan_phy_privid; |
84 | |
85 | local = wpan_phy_priv(phy); |
86 | local->phy = phy; |
87 | local->hw.phy = local->phy; |
88 | local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); |
89 | local->ops = ops; |
90 | |
91 | INIT_LIST_HEAD(list: &local->interfaces); |
92 | INIT_LIST_HEAD(list: &local->rx_beacon_list); |
93 | INIT_LIST_HEAD(list: &local->rx_mac_cmd_list); |
94 | mutex_init(&local->iflist_mtx); |
95 | |
96 | tasklet_setup(t: &local->tasklet, callback: ieee802154_tasklet_handler); |
97 | |
98 | skb_queue_head_init(list: &local->skb_queue); |
99 | |
100 | INIT_WORK(&local->sync_tx_work, ieee802154_xmit_sync_worker); |
101 | INIT_DELAYED_WORK(&local->scan_work, mac802154_scan_worker); |
102 | INIT_WORK(&local->rx_beacon_work, mac802154_rx_beacon_worker); |
103 | INIT_DELAYED_WORK(&local->beacon_work, mac802154_beacon_worker); |
104 | INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker); |
105 | |
106 | /* init supported flags with 802.15.4 default ranges */ |
107 | phy->supported.max_minbe = 8; |
108 | phy->supported.min_maxbe = 3; |
109 | phy->supported.max_maxbe = 8; |
110 | phy->supported.min_frame_retries = 0; |
111 | phy->supported.max_frame_retries = 7; |
112 | phy->supported.max_csma_backoffs = 5; |
113 | phy->supported.lbt = NL802154_SUPPORTED_BOOL_FALSE; |
114 | |
115 | /* always supported */ |
116 | phy->supported.iftypes = BIT(NL802154_IFTYPE_NODE) | BIT(NL802154_IFTYPE_COORD); |
117 | |
118 | return &local->hw; |
119 | } |
120 | EXPORT_SYMBOL(ieee802154_alloc_hw); |
121 | |
122 | void ieee802154_configure_durations(struct wpan_phy *phy, |
123 | unsigned int page, unsigned int channel) |
124 | { |
125 | u32 duration = 0; |
126 | |
127 | switch (page) { |
128 | case 0: |
129 | if (BIT(channel) & 0x1) |
130 | /* 868 MHz BPSK 802.15.4-2003: 20 ksym/s */ |
131 | duration = 50 * NSEC_PER_USEC; |
132 | else if (BIT(channel) & 0x7FE) |
133 | /* 915 MHz BPSK 802.15.4-2003: 40 ksym/s */ |
134 | duration = 25 * NSEC_PER_USEC; |
135 | else if (BIT(channel) & 0x7FFF800) |
136 | /* 2400 MHz O-QPSK 802.15.4-2006: 62.5 ksym/s */ |
137 | duration = 16 * NSEC_PER_USEC; |
138 | break; |
139 | case 2: |
140 | if (BIT(channel) & 0x1) |
141 | /* 868 MHz O-QPSK 802.15.4-2006: 25 ksym/s */ |
142 | duration = 40 * NSEC_PER_USEC; |
143 | else if (BIT(channel) & 0x7FE) |
144 | /* 915 MHz O-QPSK 802.15.4-2006: 62.5 ksym/s */ |
145 | duration = 16 * NSEC_PER_USEC; |
146 | break; |
147 | case 3: |
148 | if (BIT(channel) & 0x3FFF) |
149 | /* 2.4 GHz CSS 802.15.4a-2007: 1/6 Msym/s */ |
150 | duration = 6 * NSEC_PER_USEC; |
151 | break; |
152 | default: |
153 | break; |
154 | } |
155 | |
156 | if (!duration) { |
157 | pr_debug("Unknown PHY symbol duration\n" ); |
158 | return; |
159 | } |
160 | |
161 | phy->symbol_duration = duration; |
162 | phy->lifs_period = (IEEE802154_LIFS_PERIOD * phy->symbol_duration) / NSEC_PER_SEC; |
163 | phy->sifs_period = (IEEE802154_SIFS_PERIOD * phy->symbol_duration) / NSEC_PER_SEC; |
164 | } |
165 | EXPORT_SYMBOL(ieee802154_configure_durations); |
166 | |
167 | void ieee802154_free_hw(struct ieee802154_hw *hw) |
168 | { |
169 | struct ieee802154_local *local = hw_to_local(hw); |
170 | |
171 | BUG_ON(!list_empty(&local->interfaces)); |
172 | |
173 | mutex_destroy(lock: &local->iflist_mtx); |
174 | |
175 | wpan_phy_free(phy: local->phy); |
176 | } |
177 | EXPORT_SYMBOL(ieee802154_free_hw); |
178 | |
179 | static void ieee802154_setup_wpan_phy_pib(struct wpan_phy *wpan_phy) |
180 | { |
181 | /* TODO warn on empty symbol_duration |
182 | * Should be done when all drivers sets this value. |
183 | */ |
184 | |
185 | wpan_phy->lifs_period = |
186 | (IEEE802154_LIFS_PERIOD * wpan_phy->symbol_duration) / 1000; |
187 | wpan_phy->sifs_period = |
188 | (IEEE802154_SIFS_PERIOD * wpan_phy->symbol_duration) / 1000; |
189 | } |
190 | |
191 | int ieee802154_register_hw(struct ieee802154_hw *hw) |
192 | { |
193 | struct ieee802154_local *local = hw_to_local(hw); |
194 | char mac_wq_name[IFNAMSIZ + 10] = {}; |
195 | struct net_device *dev; |
196 | int rc = -ENOSYS; |
197 | |
198 | local->workqueue = |
199 | create_singlethread_workqueue(wpan_phy_name(local->phy)); |
200 | if (!local->workqueue) { |
201 | rc = -ENOMEM; |
202 | goto out; |
203 | } |
204 | |
205 | snprintf(buf: mac_wq_name, IFNAMSIZ + 10, fmt: "%s-mac-cmds" , wpan_phy_name(phy: local->phy)); |
206 | local->mac_wq = create_singlethread_workqueue(mac_wq_name); |
207 | if (!local->mac_wq) { |
208 | rc = -ENOMEM; |
209 | goto out_wq; |
210 | } |
211 | |
212 | hrtimer_init(timer: &local->ifs_timer, CLOCK_MONOTONIC, mode: HRTIMER_MODE_REL); |
213 | local->ifs_timer.function = ieee802154_xmit_ifs_timer; |
214 | |
215 | wpan_phy_set_dev(phy: local->phy, dev: local->hw.parent); |
216 | |
217 | ieee802154_setup_wpan_phy_pib(wpan_phy: local->phy); |
218 | |
219 | ieee802154_configure_durations(local->phy, local->phy->current_page, |
220 | local->phy->current_channel); |
221 | |
222 | if (!(hw->flags & IEEE802154_HW_CSMA_PARAMS)) { |
223 | local->phy->supported.min_csma_backoffs = 4; |
224 | local->phy->supported.max_csma_backoffs = 4; |
225 | local->phy->supported.min_maxbe = 5; |
226 | local->phy->supported.max_maxbe = 5; |
227 | local->phy->supported.min_minbe = 3; |
228 | local->phy->supported.max_minbe = 3; |
229 | } |
230 | |
231 | if (!(hw->flags & IEEE802154_HW_FRAME_RETRIES)) { |
232 | local->phy->supported.min_frame_retries = 3; |
233 | local->phy->supported.max_frame_retries = 3; |
234 | } |
235 | |
236 | if (hw->flags & IEEE802154_HW_PROMISCUOUS) |
237 | local->phy->supported.iftypes |= BIT(NL802154_IFTYPE_MONITOR); |
238 | |
239 | rc = wpan_phy_register(phy: local->phy); |
240 | if (rc < 0) |
241 | goto out_mac_wq; |
242 | |
243 | rtnl_lock(); |
244 | |
245 | dev = ieee802154_if_add(local, name: "wpan%d" , NET_NAME_ENUM, |
246 | type: NL802154_IFTYPE_NODE, |
247 | cpu_to_le64(0x0000000000000000ULL)); |
248 | if (IS_ERR(ptr: dev)) { |
249 | rtnl_unlock(); |
250 | rc = PTR_ERR(ptr: dev); |
251 | goto out_phy; |
252 | } |
253 | |
254 | rtnl_unlock(); |
255 | |
256 | return 0; |
257 | |
258 | out_phy: |
259 | wpan_phy_unregister(phy: local->phy); |
260 | out_mac_wq: |
261 | destroy_workqueue(wq: local->mac_wq); |
262 | out_wq: |
263 | destroy_workqueue(wq: local->workqueue); |
264 | out: |
265 | return rc; |
266 | } |
267 | EXPORT_SYMBOL(ieee802154_register_hw); |
268 | |
269 | void ieee802154_unregister_hw(struct ieee802154_hw *hw) |
270 | { |
271 | struct ieee802154_local *local = hw_to_local(hw); |
272 | |
273 | tasklet_kill(t: &local->tasklet); |
274 | flush_workqueue(local->workqueue); |
275 | |
276 | rtnl_lock(); |
277 | |
278 | ieee802154_remove_interfaces(local); |
279 | |
280 | rtnl_unlock(); |
281 | |
282 | destroy_workqueue(wq: local->mac_wq); |
283 | destroy_workqueue(wq: local->workqueue); |
284 | wpan_phy_unregister(phy: local->phy); |
285 | } |
286 | EXPORT_SYMBOL(ieee802154_unregister_hw); |
287 | |
288 | static int __init ieee802154_init(void) |
289 | { |
290 | return ieee802154_iface_init(); |
291 | } |
292 | |
293 | static void __exit ieee802154_exit(void) |
294 | { |
295 | ieee802154_iface_exit(); |
296 | |
297 | rcu_barrier(); |
298 | } |
299 | |
300 | subsys_initcall(ieee802154_init); |
301 | module_exit(ieee802154_exit); |
302 | |
303 | MODULE_DESCRIPTION("IEEE 802.15.4 subsystem" ); |
304 | MODULE_LICENSE("GPL v2" ); |
305 | |