2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-05-16 00:50:20 +04:00
/*
* Copyright ( C ) 2007 - 2012 Siemens AG
*
* Written by :
* Alexander Smirnov < alex . bluesman . smirnov @ gmail . com >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/netdevice.h>
2012-05-16 00:50:28 +04:00
# include <net/netlink.h>
2014-11-17 10:20:49 +03:00
# include <net/nl802154.h>
2012-05-16 00:50:20 +04:00
# include <net/mac802154.h>
2014-03-15 00:23:59 +04:00
# include <net/ieee802154_netdev.h>
2012-05-16 00:50:20 +04:00
# include <net/route.h>
2014-10-25 11:41:02 +04:00
# include <net/cfg802154.h>
2012-05-16 00:50:20 +04:00
2014-10-25 11:41:00 +04:00
# include "ieee802154_i.h"
2014-11-02 06:18:36 +03:00
# include "cfg.h"
2012-05-16 00:50:20 +04:00
2020-11-03 12:18:19 +03:00
static void ieee802154_tasklet_handler ( struct tasklet_struct * t )
2014-10-27 19:13:30 +03:00
{
2020-11-03 12:18:19 +03:00
struct ieee802154_local * local = from_tasklet ( local , t , tasklet ) ;
2014-10-27 19:13:30 +03:00
struct sk_buff * skb ;
while ( ( skb = skb_dequeue ( & local - > skb_queue ) ) ) {
switch ( skb - > pkt_type ) {
case IEEE802154_RX_MSG :
/* Clear skb->pkt_type in order to not confuse kernel
* netstack .
*/
skb - > pkt_type = 0 ;
2015-07-07 08:20:43 +03:00
ieee802154_rx ( local , skb ) ;
2014-10-27 19:13:30 +03:00
break ;
default :
WARN ( 1 , " mac802154: Packet is of unknown type %d \n " ,
skb - > pkt_type ) ;
kfree_skb ( skb ) ;
break ;
}
}
}
2014-10-25 19:16:34 +04:00
struct ieee802154_hw *
2014-10-28 20:21:18 +03:00
ieee802154_alloc_hw ( size_t priv_data_len , const struct ieee802154_ops * ops )
2012-05-16 00:50:20 +04:00
{
struct wpan_phy * phy ;
2014-10-25 19:16:35 +04:00
struct ieee802154_local * local ;
2012-05-16 00:50:20 +04:00
size_t priv_size ;
2015-06-23 09:11:03 +03:00
if ( WARN_ON ( ! ops | | ! ( ops - > xmit_async | | ops - > xmit_sync ) | | ! ops - > ed | |
! ops - > start | | ! ops - > stop | | ! ops - > set_channel ) )
2012-05-16 00:50:20 +04:00
return NULL ;
/* Ensure 32-byte alignment of our private data and hw private data.
2014-10-25 19:16:35 +04:00
* We use the wpan_phy priv data for both our ieee802154_local and for
2012-05-16 00:50:20 +04:00
* the driver ' s private data
*
* in memory it ' ll be like this :
*
2014-10-25 19:16:35 +04:00
* + - - - - - - - - - - - - - - - - - - - - - - - - - +
* | struct wpan_phy |
* + - - - - - - - - - - - - - - - - - - - - - - - - - +
* | struct ieee802154_local |
* + - - - - - - - - - - - - - - - - - - - - - - - - - +
* | driver ' s private data |
* + - - - - - - - - - - - - - - - - - - - - - - - - - +
2012-05-16 00:50:20 +04:00
*
* Due to ieee802154 layer isn ' t aware of driver and MAC structures ,
2014-10-25 07:25:08 +04:00
* so lets align them here .
2012-05-16 00:50:20 +04:00
*/
2014-10-25 19:16:35 +04:00
priv_size = ALIGN ( sizeof ( * local ) , NETDEV_ALIGN ) + priv_data_len ;
2012-05-16 00:50:20 +04:00
2014-11-09 10:36:47 +03:00
phy = wpan_phy_new ( & mac802154_config_ops , priv_size ) ;
2012-05-16 00:50:20 +04:00
if ( ! phy ) {
2013-10-30 11:28:07 +04:00
pr_err ( " failure to allocate master IEEE802.15.4 device \n " ) ;
2012-05-16 00:50:20 +04:00
return NULL ;
}
2014-11-12 05:36:51 +03:00
phy - > privid = mac802154_wpan_phy_privid ;
2014-10-25 19:16:35 +04:00
local = wpan_phy_priv ( phy ) ;
local - > phy = phy ;
local - > hw . phy = local - > phy ;
local - > hw . priv = ( char * ) local + ALIGN ( sizeof ( * local ) , NETDEV_ALIGN ) ;
local - > ops = ops ;
2012-05-16 00:50:20 +04:00
2014-10-25 19:16:38 +04:00
INIT_LIST_HEAD ( & local - > interfaces ) ;
mutex_init ( & local - > iflist_mtx ) ;
2012-05-16 00:50:20 +04:00
2020-11-03 12:18:19 +03:00
tasklet_setup ( & local - > tasklet , ieee802154_tasklet_handler ) ;
2014-10-27 19:13:30 +03:00
skb_queue_head_init ( & local - > skb_queue ) ;
2022-05-19 18:05:07 +03:00
INIT_WORK ( & local - > sync_tx_work , ieee802154_xmit_sync_worker ) ;
2015-07-21 17:44:47 +03:00
2015-05-17 22:44:43 +03:00
/* init supported flags with 802.15.4 default ranges */
phy - > supported . max_minbe = 8 ;
phy - > supported . min_maxbe = 3 ;
phy - > supported . max_maxbe = 8 ;
2015-08-10 22:15:56 +03:00
phy - > supported . min_frame_retries = 0 ;
2015-05-17 22:44:43 +03:00
phy - > supported . max_frame_retries = 7 ;
phy - > supported . max_csma_backoffs = 5 ;
phy - > supported . lbt = NL802154_SUPPORTED_BOOL_FALSE ;
2015-05-17 22:44:47 +03:00
/* always supported */
phy - > supported . iftypes = BIT ( NL802154_IFTYPE_NODE ) ;
2014-10-25 19:16:35 +04:00
return & local - > hw ;
2012-05-16 00:50:20 +04:00
}
2014-10-25 19:16:34 +04:00
EXPORT_SYMBOL ( ieee802154_alloc_hw ) ;
2012-05-16 00:50:20 +04:00
2022-02-01 21:06:28 +03:00
void ieee802154_configure_durations ( struct wpan_phy * phy )
{
u32 duration = 0 ;
switch ( phy - > current_page ) {
case 0 :
2022-04-28 19:41:40 +03:00
if ( BIT ( phy - > current_channel ) & 0x1 )
2022-02-01 21:06:28 +03:00
/* 868 MHz BPSK 802.15.4-2003: 20 ksym/s */
duration = 50 * NSEC_PER_USEC ;
2022-04-28 19:41:40 +03:00
else if ( BIT ( phy - > current_channel ) & 0x7FE )
2022-02-01 21:06:28 +03:00
/* 915 MHz BPSK 802.15.4-2003: 40 ksym/s */
duration = 25 * NSEC_PER_USEC ;
2022-04-28 19:41:40 +03:00
else if ( BIT ( phy - > current_channel ) & 0x7FFF800 )
2022-02-01 21:06:28 +03:00
/* 2400 MHz O-QPSK 802.15.4-2006: 62.5 ksym/s */
duration = 16 * NSEC_PER_USEC ;
break ;
case 2 :
2022-04-28 19:41:40 +03:00
if ( BIT ( phy - > current_channel ) & 0x1 )
2022-02-01 21:06:28 +03:00
/* 868 MHz O-QPSK 802.15.4-2006: 25 ksym/s */
duration = 40 * NSEC_PER_USEC ;
2022-04-28 19:41:40 +03:00
else if ( BIT ( phy - > current_channel ) & 0x7FE )
2022-02-01 21:06:28 +03:00
/* 915 MHz O-QPSK 802.15.4-2006: 62.5 ksym/s */
duration = 16 * NSEC_PER_USEC ;
break ;
case 3 :
2022-04-28 19:41:40 +03:00
if ( BIT ( phy - > current_channel ) & 0x3FFF )
2022-02-01 21:06:28 +03:00
/* 2.4 GHz CSS 802.15.4a-2007: 1/6 Msym/s */
duration = 6 * NSEC_PER_USEC ;
break ;
default :
break ;
}
if ( ! duration ) {
pr_debug ( " Unknown PHY symbol duration \n " ) ;
return ;
}
phy - > symbol_duration = duration ;
phy - > lifs_period = ( IEEE802154_LIFS_PERIOD * phy - > symbol_duration ) / NSEC_PER_SEC ;
phy - > sifs_period = ( IEEE802154_SIFS_PERIOD * phy - > symbol_duration ) / NSEC_PER_SEC ;
}
EXPORT_SYMBOL ( ieee802154_configure_durations ) ;
2014-10-25 19:16:34 +04:00
void ieee802154_free_hw ( struct ieee802154_hw * hw )
2012-05-16 00:50:20 +04:00
{
2014-10-25 19:16:39 +04:00
struct ieee802154_local * local = hw_to_local ( hw ) ;
2012-05-16 00:50:20 +04:00
2014-10-25 19:16:38 +04:00
BUG_ON ( ! list_empty ( & local - > interfaces ) ) ;
2012-05-16 00:50:28 +04:00
2014-10-25 19:16:38 +04:00
mutex_destroy ( & local - > iflist_mtx ) ;
2012-12-14 05:03:03 +04:00
2014-10-25 19:16:35 +04:00
wpan_phy_free ( local - > phy ) ;
2012-05-16 00:50:20 +04:00
}
2014-10-25 19:16:34 +04:00
EXPORT_SYMBOL ( ieee802154_free_hw ) ;
2012-05-16 00:50:20 +04:00
2014-11-12 21:51:56 +03:00
static void ieee802154_setup_wpan_phy_pib ( struct wpan_phy * wpan_phy )
{
/* TODO warn on empty symbol_duration
* Should be done when all drivers sets this value .
*/
2022-02-01 21:06:27 +03:00
wpan_phy - > lifs_period =
( IEEE802154_LIFS_PERIOD * wpan_phy - > symbol_duration ) / 1000 ;
wpan_phy - > sifs_period =
( IEEE802154_SIFS_PERIOD * wpan_phy - > symbol_duration ) / 1000 ;
2014-11-12 21:51:56 +03:00
}
2014-10-25 19:16:34 +04:00
int ieee802154_register_hw ( struct ieee802154_hw * hw )
2012-05-16 00:50:20 +04:00
{
2014-10-25 19:16:39 +04:00
struct ieee802154_local * local = hw_to_local ( hw ) ;
2014-11-05 22:51:19 +03:00
struct net_device * dev ;
2014-07-03 02:20:43 +04:00
int rc = - ENOSYS ;
2014-10-25 19:16:41 +04:00
local - > workqueue =
2014-10-25 19:16:35 +04:00
create_singlethread_workqueue ( wpan_phy_name ( local - > phy ) ) ;
2014-10-25 19:16:41 +04:00
if ( ! local - > workqueue ) {
2014-07-03 02:20:43 +04:00
rc = - ENOMEM ;
2012-05-16 00:50:20 +04:00
goto out ;
2014-07-03 02:20:43 +04:00
}
2012-05-16 00:50:20 +04:00
2014-11-12 21:51:56 +03:00
hrtimer_init ( & local - > ifs_timer , CLOCK_MONOTONIC , HRTIMER_MODE_REL ) ;
local - > ifs_timer . function = ieee802154_xmit_ifs_timer ;
2014-10-25 19:16:35 +04:00
wpan_phy_set_dev ( local - > phy , local - > hw . parent ) ;
2012-05-16 00:50:20 +04:00
2014-11-12 21:51:56 +03:00
ieee802154_setup_wpan_phy_pib ( local - > phy ) ;
2022-02-01 21:06:28 +03:00
ieee802154_configure_durations ( local - > phy ) ;
2015-05-17 22:44:43 +03:00
if ( ! ( hw - > flags & IEEE802154_HW_CSMA_PARAMS ) ) {
local - > phy - > supported . min_csma_backoffs = 4 ;
local - > phy - > supported . max_csma_backoffs = 4 ;
local - > phy - > supported . min_maxbe = 5 ;
local - > phy - > supported . max_maxbe = 5 ;
local - > phy - > supported . min_minbe = 3 ;
local - > phy - > supported . max_minbe = 3 ;
}
if ( ! ( hw - > flags & IEEE802154_HW_FRAME_RETRIES ) ) {
2015-08-10 22:15:56 +03:00
local - > phy - > supported . min_frame_retries = 3 ;
local - > phy - > supported . max_frame_retries = 3 ;
2015-05-17 22:44:43 +03:00
}
2015-05-17 22:44:47 +03:00
if ( hw - > flags & IEEE802154_HW_PROMISCUOUS )
local - > phy - > supported . iftypes | = BIT ( NL802154_IFTYPE_MONITOR ) ;
2014-10-25 19:16:35 +04:00
rc = wpan_phy_register ( local - > phy ) ;
2012-05-16 00:50:20 +04:00
if ( rc < 0 )
goto out_wq ;
2014-11-05 22:51:19 +03:00
rtnl_lock ( ) ;
2015-04-30 18:44:57 +03:00
dev = ieee802154_if_add ( local , " wpan%d " , NET_NAME_ENUM ,
NL802154_IFTYPE_NODE ,
2014-11-17 10:20:52 +03:00
cpu_to_le64 ( 0x0000000000000000ULL ) ) ;
2014-11-05 22:51:19 +03:00
if ( IS_ERR ( dev ) ) {
rtnl_unlock ( ) ;
rc = PTR_ERR ( dev ) ;
2015-04-30 18:44:53 +03:00
goto out_phy ;
2014-11-05 22:51:19 +03:00
}
rtnl_unlock ( ) ;
2012-05-16 00:50:20 +04:00
return 0 ;
2015-04-30 18:44:53 +03:00
out_phy :
wpan_phy_unregister ( local - > phy ) ;
2012-05-16 00:50:20 +04:00
out_wq :
2014-10-25 19:16:41 +04:00
destroy_workqueue ( local - > workqueue ) ;
2012-05-16 00:50:20 +04:00
out :
return rc ;
}
2014-10-25 19:16:34 +04:00
EXPORT_SYMBOL ( ieee802154_register_hw ) ;
2012-05-16 00:50:20 +04:00
2014-10-25 19:16:34 +04:00
void ieee802154_unregister_hw ( struct ieee802154_hw * hw )
2012-05-16 00:50:20 +04:00
{
2014-10-25 19:16:39 +04:00
struct ieee802154_local * local = hw_to_local ( hw ) ;
2012-05-16 00:50:20 +04:00
2014-10-27 19:13:30 +03:00
tasklet_kill ( & local - > tasklet ) ;
2014-10-25 19:16:41 +04:00
flush_workqueue ( local - > workqueue ) ;
2012-05-16 00:50:20 +04:00
rtnl_lock ( ) ;
2014-11-12 05:36:48 +03:00
ieee802154_remove_interfaces ( local ) ;
2012-05-16 00:50:28 +04:00
2012-05-16 00:50:20 +04:00
rtnl_unlock ( ) ;
2016-02-10 13:49:38 +03:00
destroy_workqueue ( local - > workqueue ) ;
2014-10-25 19:16:35 +04:00
wpan_phy_unregister ( local - > phy ) ;
2012-05-16 00:50:20 +04:00
}
2014-10-25 19:16:34 +04:00
EXPORT_SYMBOL ( ieee802154_unregister_hw ) ;
2012-05-16 00:50:20 +04:00
2014-11-12 05:36:53 +03:00
static int __init ieee802154_init ( void )
{
return ieee802154_iface_init ( ) ;
}
static void __exit ieee802154_exit ( void )
{
ieee802154_iface_exit ( ) ;
rcu_barrier ( ) ;
}
subsys_initcall ( ieee802154_init ) ;
module_exit ( ieee802154_exit ) ;
2014-11-12 05:36:52 +03:00
MODULE_DESCRIPTION ( " IEEE 802.15.4 subsystem " ) ;
2012-05-16 00:50:20 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;