18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2007-2012 Siemens AG
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Written by:
68c2ecf20Sopenharmony_ci * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <net/netlink.h>
148c2ecf20Sopenharmony_ci#include <net/nl802154.h>
158c2ecf20Sopenharmony_ci#include <net/mac802154.h>
168c2ecf20Sopenharmony_ci#include <net/ieee802154_netdev.h>
178c2ecf20Sopenharmony_ci#include <net/route.h>
188c2ecf20Sopenharmony_ci#include <net/cfg802154.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "ieee802154_i.h"
218c2ecf20Sopenharmony_ci#include "cfg.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic void ieee802154_tasklet_handler(unsigned long data)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	struct ieee802154_local *local = (struct ieee802154_local *)data;
268c2ecf20Sopenharmony_ci	struct sk_buff *skb;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&local->skb_queue))) {
298c2ecf20Sopenharmony_ci		switch (skb->pkt_type) {
308c2ecf20Sopenharmony_ci		case IEEE802154_RX_MSG:
318c2ecf20Sopenharmony_ci			/* Clear skb->pkt_type in order to not confuse kernel
328c2ecf20Sopenharmony_ci			 * netstack.
338c2ecf20Sopenharmony_ci			 */
348c2ecf20Sopenharmony_ci			skb->pkt_type = 0;
358c2ecf20Sopenharmony_ci			ieee802154_rx(local, skb);
368c2ecf20Sopenharmony_ci			break;
378c2ecf20Sopenharmony_ci		default:
388c2ecf20Sopenharmony_ci			WARN(1, "mac802154: Packet is of unknown type %d\n",
398c2ecf20Sopenharmony_ci			     skb->pkt_type);
408c2ecf20Sopenharmony_ci			kfree_skb(skb);
418c2ecf20Sopenharmony_ci			break;
428c2ecf20Sopenharmony_ci		}
438c2ecf20Sopenharmony_ci	}
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistruct ieee802154_hw *
478c2ecf20Sopenharmony_ciieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	struct wpan_phy *phy;
508c2ecf20Sopenharmony_ci	struct ieee802154_local *local;
518c2ecf20Sopenharmony_ci	size_t priv_size;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	if (WARN_ON(!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed ||
548c2ecf20Sopenharmony_ci		    !ops->start || !ops->stop || !ops->set_channel))
558c2ecf20Sopenharmony_ci		return NULL;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* Ensure 32-byte alignment of our private data and hw private data.
588c2ecf20Sopenharmony_ci	 * We use the wpan_phy priv data for both our ieee802154_local and for
598c2ecf20Sopenharmony_ci	 * the driver's private data
608c2ecf20Sopenharmony_ci	 *
618c2ecf20Sopenharmony_ci	 * in memory it'll be like this:
628c2ecf20Sopenharmony_ci	 *
638c2ecf20Sopenharmony_ci	 * +-------------------------+
648c2ecf20Sopenharmony_ci	 * | struct wpan_phy         |
658c2ecf20Sopenharmony_ci	 * +-------------------------+
668c2ecf20Sopenharmony_ci	 * | struct ieee802154_local |
678c2ecf20Sopenharmony_ci	 * +-------------------------+
688c2ecf20Sopenharmony_ci	 * | driver's private data   |
698c2ecf20Sopenharmony_ci	 * +-------------------------+
708c2ecf20Sopenharmony_ci	 *
718c2ecf20Sopenharmony_ci	 * Due to ieee802154 layer isn't aware of driver and MAC structures,
728c2ecf20Sopenharmony_ci	 * so lets align them here.
738c2ecf20Sopenharmony_ci	 */
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	phy = wpan_phy_new(&mac802154_config_ops, priv_size);
788c2ecf20Sopenharmony_ci	if (!phy) {
798c2ecf20Sopenharmony_ci		pr_err("failure to allocate master IEEE802.15.4 device\n");
808c2ecf20Sopenharmony_ci		return NULL;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	phy->privid = mac802154_wpan_phy_privid;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	local = wpan_phy_priv(phy);
868c2ecf20Sopenharmony_ci	local->phy = phy;
878c2ecf20Sopenharmony_ci	local->hw.phy = local->phy;
888c2ecf20Sopenharmony_ci	local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
898c2ecf20Sopenharmony_ci	local->ops = ops;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&local->interfaces);
928c2ecf20Sopenharmony_ci	mutex_init(&local->iflist_mtx);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	tasklet_init(&local->tasklet,
958c2ecf20Sopenharmony_ci		     ieee802154_tasklet_handler,
968c2ecf20Sopenharmony_ci		     (unsigned long)local);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	skb_queue_head_init(&local->skb_queue);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	INIT_WORK(&local->tx_work, ieee802154_xmit_worker);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* init supported flags with 802.15.4 default ranges */
1038c2ecf20Sopenharmony_ci	phy->supported.max_minbe = 8;
1048c2ecf20Sopenharmony_ci	phy->supported.min_maxbe = 3;
1058c2ecf20Sopenharmony_ci	phy->supported.max_maxbe = 8;
1068c2ecf20Sopenharmony_ci	phy->supported.min_frame_retries = 0;
1078c2ecf20Sopenharmony_ci	phy->supported.max_frame_retries = 7;
1088c2ecf20Sopenharmony_ci	phy->supported.max_csma_backoffs = 5;
1098c2ecf20Sopenharmony_ci	phy->supported.lbt = NL802154_SUPPORTED_BOOL_FALSE;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* always supported */
1128c2ecf20Sopenharmony_ci	phy->supported.iftypes = BIT(NL802154_IFTYPE_NODE);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return &local->hw;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee802154_alloc_hw);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_civoid ieee802154_free_hw(struct ieee802154_hw *hw)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct ieee802154_local *local = hw_to_local(hw);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	BUG_ON(!list_empty(&local->interfaces));
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	mutex_destroy(&local->iflist_mtx);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	wpan_phy_free(local->phy);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee802154_free_hw);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic void ieee802154_setup_wpan_phy_pib(struct wpan_phy *wpan_phy)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	/* TODO warn on empty symbol_duration
1338c2ecf20Sopenharmony_ci	 * Should be done when all drivers sets this value.
1348c2ecf20Sopenharmony_ci	 */
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	wpan_phy->lifs_period = IEEE802154_LIFS_PERIOD *
1378c2ecf20Sopenharmony_ci				wpan_phy->symbol_duration;
1388c2ecf20Sopenharmony_ci	wpan_phy->sifs_period = IEEE802154_SIFS_PERIOD *
1398c2ecf20Sopenharmony_ci				wpan_phy->symbol_duration;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ciint ieee802154_register_hw(struct ieee802154_hw *hw)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct ieee802154_local *local = hw_to_local(hw);
1458c2ecf20Sopenharmony_ci	struct net_device *dev;
1468c2ecf20Sopenharmony_ci	int rc = -ENOSYS;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	local->workqueue =
1498c2ecf20Sopenharmony_ci		create_singlethread_workqueue(wpan_phy_name(local->phy));
1508c2ecf20Sopenharmony_ci	if (!local->workqueue) {
1518c2ecf20Sopenharmony_ci		rc = -ENOMEM;
1528c2ecf20Sopenharmony_ci		goto out;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	hrtimer_init(&local->ifs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
1568c2ecf20Sopenharmony_ci	local->ifs_timer.function = ieee802154_xmit_ifs_timer;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	wpan_phy_set_dev(local->phy, local->hw.parent);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	ieee802154_setup_wpan_phy_pib(local->phy);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (!(hw->flags & IEEE802154_HW_CSMA_PARAMS)) {
1638c2ecf20Sopenharmony_ci		local->phy->supported.min_csma_backoffs = 4;
1648c2ecf20Sopenharmony_ci		local->phy->supported.max_csma_backoffs = 4;
1658c2ecf20Sopenharmony_ci		local->phy->supported.min_maxbe = 5;
1668c2ecf20Sopenharmony_ci		local->phy->supported.max_maxbe = 5;
1678c2ecf20Sopenharmony_ci		local->phy->supported.min_minbe = 3;
1688c2ecf20Sopenharmony_ci		local->phy->supported.max_minbe = 3;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (!(hw->flags & IEEE802154_HW_FRAME_RETRIES)) {
1728c2ecf20Sopenharmony_ci		local->phy->supported.min_frame_retries = 3;
1738c2ecf20Sopenharmony_ci		local->phy->supported.max_frame_retries = 3;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (hw->flags & IEEE802154_HW_PROMISCUOUS)
1778c2ecf20Sopenharmony_ci		local->phy->supported.iftypes |= BIT(NL802154_IFTYPE_MONITOR);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	rc = wpan_phy_register(local->phy);
1808c2ecf20Sopenharmony_ci	if (rc < 0)
1818c2ecf20Sopenharmony_ci		goto out_wq;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	rtnl_lock();
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	dev = ieee802154_if_add(local, "wpan%d", NET_NAME_ENUM,
1868c2ecf20Sopenharmony_ci				NL802154_IFTYPE_NODE,
1878c2ecf20Sopenharmony_ci				cpu_to_le64(0x0000000000000000ULL));
1888c2ecf20Sopenharmony_ci	if (IS_ERR(dev)) {
1898c2ecf20Sopenharmony_ci		rtnl_unlock();
1908c2ecf20Sopenharmony_ci		rc = PTR_ERR(dev);
1918c2ecf20Sopenharmony_ci		goto out_phy;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	rtnl_unlock();
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return 0;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ciout_phy:
1998c2ecf20Sopenharmony_ci	wpan_phy_unregister(local->phy);
2008c2ecf20Sopenharmony_ciout_wq:
2018c2ecf20Sopenharmony_ci	destroy_workqueue(local->workqueue);
2028c2ecf20Sopenharmony_ciout:
2038c2ecf20Sopenharmony_ci	return rc;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee802154_register_hw);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_civoid ieee802154_unregister_hw(struct ieee802154_hw *hw)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct ieee802154_local *local = hw_to_local(hw);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	tasklet_kill(&local->tasklet);
2128c2ecf20Sopenharmony_ci	flush_workqueue(local->workqueue);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	rtnl_lock();
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	ieee802154_remove_interfaces(local);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	rtnl_unlock();
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	destroy_workqueue(local->workqueue);
2218c2ecf20Sopenharmony_ci	wpan_phy_unregister(local->phy);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee802154_unregister_hw);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int __init ieee802154_init(void)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	return ieee802154_iface_init();
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic void __exit ieee802154_exit(void)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	ieee802154_iface_exit();
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	rcu_barrier();
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cisubsys_initcall(ieee802154_init);
2388c2ecf20Sopenharmony_cimodule_exit(ieee802154_exit);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IEEE 802.15.4 subsystem");
2418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
242