2012-05-16 00:50:20 +04:00
/*
* Copyright ( C ) 2007 - 2012 Siemens AG
*
* Written by :
* Alexander Smirnov < alex . bluesman . smirnov @ gmail . com >
*
* Based on the code from ' linux - zigbee . sourceforge . net ' project .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/netdevice.h>
2012-05-16 00:50:28 +04:00
# include <net/netlink.h>
# include <linux/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"
2012-05-16 00:50:20 +04:00
2012-05-16 00:50:28 +04:00
static int
mac802154_netdev_register ( struct wpan_phy * phy , struct net_device * dev )
{
2014-10-25 19:16:40 +04:00
struct ieee802154_sub_if_data * sdata = IEEE802154_DEV_TO_SUB_IF ( dev ) ;
2014-10-25 19:16:35 +04:00
struct ieee802154_local * local ;
2012-05-16 00:50:28 +04:00
int err ;
2014-10-25 19:16:35 +04:00
local = wpan_phy_priv ( phy ) ;
2012-05-16 00:50:28 +04:00
2014-10-25 19:16:36 +04:00
sdata - > dev = dev ;
2014-10-25 19:16:37 +04:00
sdata - > local = local ;
2012-05-16 00:50:28 +04:00
2014-10-25 19:16:35 +04:00
dev - > needed_headroom = local - > hw . extra_tx_headroom ;
2012-05-16 00:50:28 +04:00
2014-10-25 19:16:35 +04:00
SET_NETDEV_DEV ( dev , & local - > phy - > dev ) ;
2012-05-16 00:50:28 +04:00
2014-10-25 19:16:38 +04:00
mutex_lock ( & local - > iflist_mtx ) ;
2014-10-25 19:16:35 +04:00
if ( ! local - > running ) {
2014-10-25 19:16:38 +04:00
mutex_unlock ( & local - > iflist_mtx ) ;
2012-05-16 00:50:28 +04:00
return - ENODEV ;
}
2014-10-25 19:16:38 +04:00
mutex_unlock ( & local - > iflist_mtx ) ;
2012-05-16 00:50:28 +04:00
err = register_netdev ( dev ) ;
if ( err < 0 )
return err ;
rtnl_lock ( ) ;
2014-10-25 19:16:38 +04:00
mutex_lock ( & local - > iflist_mtx ) ;
list_add_tail_rcu ( & sdata - > list , & local - > interfaces ) ;
mutex_unlock ( & local - > iflist_mtx ) ;
2012-05-16 00:50:28 +04:00
rtnl_unlock ( ) ;
return 0 ;
}
static void
mac802154_del_iface ( struct wpan_phy * phy , struct net_device * dev )
{
2014-10-25 19:16:40 +04:00
struct ieee802154_sub_if_data * sdata = IEEE802154_DEV_TO_SUB_IF ( dev ) ;
2014-07-02 07:31:09 +04:00
2012-05-16 00:50:28 +04:00
ASSERT_RTNL ( ) ;
2014-10-25 19:16:37 +04:00
BUG_ON ( sdata - > local - > phy ! = phy ) ;
2012-05-16 00:50:28 +04:00
2014-10-25 19:16:38 +04:00
mutex_lock ( & sdata - > local - > iflist_mtx ) ;
2012-05-16 00:50:28 +04:00
list_del_rcu ( & sdata - > list ) ;
2014-10-25 19:16:38 +04:00
mutex_unlock ( & sdata - > local - > iflist_mtx ) ;
2012-05-16 00:50:28 +04:00
synchronize_rcu ( ) ;
unregister_netdevice ( sdata - > dev ) ;
}
static struct net_device *
mac802154_add_iface ( struct wpan_phy * phy , const char * name , int type )
{
struct net_device * dev ;
int err = - ENOMEM ;
switch ( type ) {
2012-05-16 00:50:29 +04:00
case IEEE802154_DEV_MONITOR :
2014-10-25 19:16:36 +04:00
dev = alloc_netdev ( sizeof ( struct ieee802154_sub_if_data ) ,
net: set name_assign_type in alloc_netdev()
Extend alloc_netdev{,_mq{,s}}() to take name_assign_type as argument, and convert
all users to pass NET_NAME_UNKNOWN.
Coccinelle patch:
@@
expression sizeof_priv, name, setup, txqs, rxqs, count;
@@
(
-alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs)
+alloc_netdev_mqs(sizeof_priv, name, NET_NAME_UNKNOWN, setup, txqs, rxqs)
|
-alloc_netdev_mq(sizeof_priv, name, setup, count)
+alloc_netdev_mq(sizeof_priv, name, NET_NAME_UNKNOWN, setup, count)
|
-alloc_netdev(sizeof_priv, name, setup)
+alloc_netdev(sizeof_priv, name, NET_NAME_UNKNOWN, setup)
)
v9: move comments here from the wrong commit
Signed-off-by: Tom Gundersen <teg@jklm.no>
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-07-14 18:37:24 +04:00
name , NET_NAME_UNKNOWN ,
mac802154_monitor_setup ) ;
2012-05-16 00:50:29 +04:00
break ;
2012-06-26 03:24:48 +04:00
case IEEE802154_DEV_WPAN :
2014-10-25 19:16:36 +04:00
dev = alloc_netdev ( sizeof ( struct ieee802154_sub_if_data ) ,
net: set name_assign_type in alloc_netdev()
Extend alloc_netdev{,_mq{,s}}() to take name_assign_type as argument, and convert
all users to pass NET_NAME_UNKNOWN.
Coccinelle patch:
@@
expression sizeof_priv, name, setup, txqs, rxqs, count;
@@
(
-alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs)
+alloc_netdev_mqs(sizeof_priv, name, NET_NAME_UNKNOWN, setup, txqs, rxqs)
|
-alloc_netdev_mq(sizeof_priv, name, setup, count)
+alloc_netdev_mq(sizeof_priv, name, NET_NAME_UNKNOWN, setup, count)
|
-alloc_netdev(sizeof_priv, name, setup)
+alloc_netdev(sizeof_priv, name, NET_NAME_UNKNOWN, setup)
)
v9: move comments here from the wrong commit
Signed-off-by: Tom Gundersen <teg@jklm.no>
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-07-14 18:37:24 +04:00
name , NET_NAME_UNKNOWN ,
mac802154_wpan_setup ) ;
2012-06-26 03:24:48 +04:00
break ;
2012-05-16 00:50:28 +04:00
default :
dev = NULL ;
err = - EINVAL ;
break ;
}
if ( ! dev )
goto err ;
err = mac802154_netdev_register ( phy , dev ) ;
if ( err )
goto err_free ;
dev_hold ( dev ) ; /* we return an incremented device refcount */
return dev ;
err_free :
free_netdev ( dev ) ;
err :
return ERR_PTR ( err ) ;
}
2014-02-17 14:34:08 +04:00
static int mac802154_set_txpower ( struct wpan_phy * phy , int db )
{
2014-10-25 19:16:35 +04:00
struct ieee802154_local * local = wpan_phy_priv ( phy ) ;
2014-02-17 14:34:08 +04:00
2014-10-25 19:16:35 +04:00
return local - > ops - > set_txpower ( & local - > hw , db ) ;
2014-02-17 14:34:08 +04:00
}
2014-02-17 14:34:10 +04:00
static int mac802154_set_lbt ( struct wpan_phy * phy , bool on )
{
2014-10-25 19:16:35 +04:00
struct ieee802154_local * local = wpan_phy_priv ( phy ) ;
2014-02-17 14:34:10 +04:00
2014-10-25 19:16:35 +04:00
return local - > ops - > set_lbt ( & local - > hw , on ) ;
2014-02-17 14:34:10 +04:00
}
2014-02-17 14:34:11 +04:00
static int mac802154_set_cca_mode ( struct wpan_phy * phy , u8 mode )
{
2014-10-25 19:16:35 +04:00
struct ieee802154_local * local = wpan_phy_priv ( phy ) ;
2014-02-17 14:34:11 +04:00
2014-10-25 19:16:35 +04:00
return local - > ops - > set_cca_mode ( & local - > hw , mode ) ;
2014-02-17 14:34:11 +04:00
}
2014-02-17 14:34:12 +04:00
static int mac802154_set_cca_ed_level ( struct wpan_phy * phy , s32 level )
{
2014-10-25 19:16:35 +04:00
struct ieee802154_local * local = wpan_phy_priv ( phy ) ;
2014-02-17 14:34:12 +04:00
2014-10-25 19:16:35 +04:00
return local - > ops - > set_cca_ed_level ( & local - > hw , level ) ;
2014-02-17 14:34:12 +04:00
}
2014-02-17 14:34:14 +04:00
static int mac802154_set_csma_params ( struct wpan_phy * phy , u8 min_be ,
u8 max_be , u8 retries )
{
2014-10-25 19:16:35 +04:00
struct ieee802154_local * local = wpan_phy_priv ( phy ) ;
2014-02-17 14:34:14 +04:00
2014-10-25 19:16:35 +04:00
return local - > ops - > set_csma_params ( & local - > hw , min_be , max_be , retries ) ;
2014-02-17 14:34:14 +04:00
}
static int mac802154_set_frame_retries ( struct wpan_phy * phy , s8 retries )
{
2014-10-25 19:16:35 +04:00
struct ieee802154_local * local = wpan_phy_priv ( phy ) ;
2014-02-17 14:34:14 +04:00
2014-10-25 19:16:35 +04:00
return local - > ops - > set_frame_retries ( & local - > hw , retries ) ;
2014-02-17 14:34:14 +04:00
}
2014-10-27 19:13:30 +03:00
static void ieee802154_tasklet_handler ( unsigned long data )
{
struct ieee802154_local * local = ( struct ieee802154_local * ) data ;
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 ;
ieee802154_rx ( & local - > hw , skb ) ;
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 *
ieee802154_alloc_hw ( size_t priv_data_len , 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 ;
2014-10-26 11:37:08 +03:00
if ( ! ops | | ! ( ops - > xmit_async | | ops - > xmit_sync ) | | ! ops - > ed | |
! ops - > start | | ! ops - > stop | | ! ops - > set_channel ) {
2013-10-30 11:28:07 +04:00
pr_err ( " undefined IEEE802.15.4 device operations \n " ) ;
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
phy = wpan_phy_alloc ( priv_size ) ;
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-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
2014-10-27 19:13:30 +03:00
tasklet_init ( & local - > tasklet ,
ieee802154_tasklet_handler ,
( unsigned long ) local ) ;
skb_queue_head_init ( & local - > skb_queue ) ;
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
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-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-07-03 02:20:43 +04:00
int rc = - ENOSYS ;
2014-10-25 19:16:34 +04:00
if ( hw - > flags & IEEE802154_HW_TXPOWER ) {
2014-10-25 19:16:35 +04:00
if ( ! local - > ops - > set_txpower )
2014-07-03 02:20:43 +04:00
goto out ;
2014-10-25 19:16:35 +04:00
local - > phy - > set_txpower = mac802154_set_txpower ;
2014-07-03 02:20:43 +04:00
}
2014-10-25 19:16:34 +04:00
if ( hw - > flags & IEEE802154_HW_LBT ) {
2014-10-25 19:16:35 +04:00
if ( ! local - > ops - > set_lbt )
2014-07-03 02:20:43 +04:00
goto out ;
2014-10-25 19:16:35 +04:00
local - > phy - > set_lbt = mac802154_set_lbt ;
2014-07-03 02:20:43 +04:00
}
2014-10-25 19:16:34 +04:00
if ( hw - > flags & IEEE802154_HW_CCA_MODE ) {
2014-10-25 19:16:35 +04:00
if ( ! local - > ops - > set_cca_mode )
2014-07-03 02:20:43 +04:00
goto out ;
2014-10-25 19:16:35 +04:00
local - > phy - > set_cca_mode = mac802154_set_cca_mode ;
2014-07-03 02:20:43 +04:00
}
2014-10-25 19:16:34 +04:00
if ( hw - > flags & IEEE802154_HW_CCA_ED_LEVEL ) {
2014-10-25 19:16:35 +04:00
if ( ! local - > ops - > set_cca_ed_level )
2014-07-03 02:20:43 +04:00
goto out ;
2014-10-25 19:16:35 +04:00
local - > phy - > set_cca_ed_level = mac802154_set_cca_ed_level ;
2014-07-03 02:20:43 +04:00
}
2014-10-25 19:16:34 +04:00
if ( hw - > flags & IEEE802154_HW_CSMA_PARAMS ) {
2014-10-25 19:16:35 +04:00
if ( ! local - > ops - > set_csma_params )
2014-07-03 02:20:43 +04:00
goto out ;
2014-10-25 19:16:35 +04:00
local - > phy - > set_csma_params = mac802154_set_csma_params ;
2014-07-03 02:20:43 +04:00
}
2014-10-25 19:16:34 +04:00
if ( hw - > flags & IEEE802154_HW_FRAME_RETRIES ) {
2014-10-25 19:16:35 +04:00
if ( ! local - > ops - > set_frame_retries )
2014-07-03 02:20:43 +04:00
goto out ;
2014-10-25 19:16:35 +04:00
local - > phy - > set_frame_retries = mac802154_set_frame_retries ;
2014-07-03 02:20:43 +04:00
}
2012-05-16 00:50:20 +04:00
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-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-10-25 19:16:35 +04:00
local - > phy - > add_iface = mac802154_add_iface ;
local - > phy - > del_iface = mac802154_del_iface ;
2012-05-16 00:50:28 +04:00
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 ;
rtnl_lock ( ) ;
2014-10-25 19:16:38 +04:00
mutex_lock ( & local - > iflist_mtx ) ;
2014-10-25 19:16:35 +04:00
local - > running = MAC802154_DEVICE_RUN ;
2014-10-25 19:16:38 +04:00
mutex_unlock ( & local - > iflist_mtx ) ;
2012-05-16 00:50:20 +04:00
rtnl_unlock ( ) ;
return 0 ;
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 ) ;
2014-10-25 19:16:36 +04:00
struct ieee802154_sub_if_data * sdata , * next ;
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 ) ;
destroy_workqueue ( local - > workqueue ) ;
2012-05-16 00:50:20 +04:00
rtnl_lock ( ) ;
2014-10-25 19:16:38 +04:00
mutex_lock ( & local - > iflist_mtx ) ;
2014-10-25 19:16:35 +04:00
local - > running = MAC802154_DEVICE_STOPPED ;
2014-10-25 19:16:38 +04:00
mutex_unlock ( & local - > iflist_mtx ) ;
2012-05-16 00:50:20 +04:00
2014-10-25 19:16:38 +04:00
list_for_each_entry_safe ( sdata , next , & local - > interfaces , list ) {
mutex_lock ( & sdata - > local - > iflist_mtx ) ;
2012-05-16 00:50:28 +04:00
list_del ( & sdata - > list ) ;
2014-10-25 19:16:38 +04:00
mutex_unlock ( & sdata - > local - > iflist_mtx ) ;
2012-05-16 00:50:28 +04:00
unregister_netdevice ( sdata - > dev ) ;
}
2012-05-16 00:50:20 +04:00
rtnl_unlock ( ) ;
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
MODULE_DESCRIPTION ( " IEEE 802.15.4 implementation " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;