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>
# include <net/wpan-phy.h>
# include "mac802154.h"
2012-05-16 00:50:28 +04:00
int mac802154_slave_open ( struct net_device * dev )
{
struct mac802154_sub_if_data * priv = netdev_priv ( dev ) ;
2014-03-31 23:37:45 +04:00
struct mac802154_sub_if_data * subif ;
2012-05-16 00:50:28 +04:00
struct mac802154_priv * ipriv = priv - > hw ;
int res = 0 ;
2014-03-31 23:37:45 +04:00
ASSERT_RTNL ( ) ;
if ( priv - > type = = IEEE802154_DEV_WPAN ) {
mutex_lock ( & priv - > hw - > slaves_mtx ) ;
list_for_each_entry ( subif , & priv - > hw - > slaves , list ) {
if ( subif ! = priv & & subif - > type = = priv - > type & &
subif - > running ) {
mutex_unlock ( & priv - > hw - > slaves_mtx ) ;
return - EBUSY ;
}
}
mutex_unlock ( & priv - > hw - > slaves_mtx ) ;
}
mutex_lock ( & priv - > hw - > slaves_mtx ) ;
priv - > running = true ;
mutex_unlock ( & priv - > hw - > slaves_mtx ) ;
2012-05-16 00:50:28 +04:00
if ( ipriv - > open_count + + = = 0 ) {
res = ipriv - > ops - > start ( & ipriv - > hw ) ;
WARN_ON ( res ) ;
if ( res )
goto err ;
}
if ( ipriv - > ops - > ieee_addr ) {
2014-03-15 00:23:59 +04:00
__le64 addr = ieee802154_devaddr_from_raw ( dev - > dev_addr ) ;
res = ipriv - > ops - > ieee_addr ( & ipriv - > hw , addr ) ;
2012-05-16 00:50:28 +04:00
WARN_ON ( res ) ;
if ( res )
goto err ;
mac802154_dev_set_ieee_addr ( dev ) ;
}
netif_start_queue ( dev ) ;
return 0 ;
err :
priv - > hw - > open_count - - ;
return res ;
}
int mac802154_slave_close ( struct net_device * dev )
{
struct mac802154_sub_if_data * priv = netdev_priv ( dev ) ;
struct mac802154_priv * ipriv = priv - > hw ;
2014-03-31 23:37:45 +04:00
ASSERT_RTNL ( ) ;
2012-05-16 00:50:28 +04:00
netif_stop_queue ( dev ) ;
2014-03-31 23:37:45 +04:00
mutex_lock ( & priv - > hw - > slaves_mtx ) ;
priv - > running = false ;
mutex_unlock ( & priv - > hw - > slaves_mtx ) ;
2012-05-16 00:50:28 +04:00
if ( ! - - ipriv - > open_count )
ipriv - > ops - > stop ( & ipriv - > hw ) ;
return 0 ;
}
static int
mac802154_netdev_register ( struct wpan_phy * phy , struct net_device * dev )
{
struct mac802154_sub_if_data * priv ;
struct mac802154_priv * ipriv ;
int err ;
ipriv = wpan_phy_priv ( phy ) ;
priv = netdev_priv ( dev ) ;
priv - > dev = dev ;
priv - > hw = ipriv ;
dev - > needed_headroom = ipriv - > hw . extra_tx_headroom ;
SET_NETDEV_DEV ( dev , & ipriv - > phy - > dev ) ;
mutex_lock ( & ipriv - > slaves_mtx ) ;
if ( ! ipriv - > running ) {
mutex_unlock ( & ipriv - > slaves_mtx ) ;
return - ENODEV ;
}
mutex_unlock ( & ipriv - > slaves_mtx ) ;
err = register_netdev ( dev ) ;
if ( err < 0 )
return err ;
rtnl_lock ( ) ;
mutex_lock ( & ipriv - > slaves_mtx ) ;
list_add_tail_rcu ( & priv - > list , & ipriv - > slaves ) ;
mutex_unlock ( & ipriv - > slaves_mtx ) ;
rtnl_unlock ( ) ;
return 0 ;
}
static void
mac802154_del_iface ( struct wpan_phy * phy , struct net_device * dev )
{
struct mac802154_sub_if_data * sdata ;
2014-07-02 07:31:09 +04:00
2012-05-16 00:50:28 +04:00
ASSERT_RTNL ( ) ;
sdata = netdev_priv ( dev ) ;
BUG_ON ( sdata - > hw - > phy ! = phy ) ;
mutex_lock ( & sdata - > hw - > slaves_mtx ) ;
list_del_rcu ( & sdata - > list ) ;
mutex_unlock ( & sdata - > hw - > slaves_mtx ) ;
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 :
dev = alloc_netdev ( sizeof ( struct mac802154_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 :
dev = alloc_netdev ( sizeof ( struct mac802154_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 )
{
struct mac802154_priv * priv = wpan_phy_priv ( phy ) ;
return priv - > ops - > set_txpower ( & priv - > hw , db ) ;
}
2014-02-17 14:34:10 +04:00
static int mac802154_set_lbt ( struct wpan_phy * phy , bool on )
{
struct mac802154_priv * priv = wpan_phy_priv ( phy ) ;
return priv - > ops - > set_lbt ( & priv - > hw , on ) ;
}
2014-02-17 14:34:11 +04:00
static int mac802154_set_cca_mode ( struct wpan_phy * phy , u8 mode )
{
struct mac802154_priv * priv = wpan_phy_priv ( phy ) ;
return priv - > ops - > set_cca_mode ( & priv - > hw , mode ) ;
}
2014-02-17 14:34:12 +04:00
static int mac802154_set_cca_ed_level ( struct wpan_phy * phy , s32 level )
{
struct mac802154_priv * priv = wpan_phy_priv ( phy ) ;
return priv - > ops - > set_cca_ed_level ( & priv - > hw , level ) ;
}
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 )
{
struct mac802154_priv * priv = wpan_phy_priv ( phy ) ;
return priv - > ops - > set_csma_params ( & priv - > hw , min_be , max_be , retries ) ;
}
static int mac802154_set_frame_retries ( struct wpan_phy * phy , s8 retries )
{
struct mac802154_priv * priv = wpan_phy_priv ( phy ) ;
return priv - > ops - > set_frame_retries ( & priv - > hw , retries ) ;
}
2012-05-16 00:50:20 +04:00
struct ieee802154_dev *
ieee802154_alloc_device ( size_t priv_data_len , struct ieee802154_ops * ops )
{
struct wpan_phy * phy ;
struct mac802154_priv * priv ;
size_t priv_size ;
if ( ! ops | | ! ops - > xmit | | ! 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.
* We use the wpan_phy priv data for both our mac802154_priv and for
* the driver ' s private data
*
* in memory it ' ll be like this :
*
* + - - - - - - - - - - - - - - - - - - - - - - - +
* | struct wpan_phy |
* + - - - - - - - - - - - - - - - - - - - - - - - +
* | struct mac802154_priv |
* + - - - - - - - - - - - - - - - - - - - - - - - +
* | driver ' s private data |
* + - - - - - - - - - - - - - - - - - - - - - - - +
*
* 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
*/
priv_size = ALIGN ( sizeof ( * priv ) , NETDEV_ALIGN ) + priv_data_len ;
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 ;
}
priv = wpan_phy_priv ( phy ) ;
2014-07-02 07:31:09 +04:00
priv - > phy = phy ;
priv - > hw . phy = priv - > phy ;
2012-05-16 00:50:20 +04:00
priv - > hw . priv = ( char * ) priv + ALIGN ( sizeof ( * priv ) , NETDEV_ALIGN ) ;
priv - > ops = ops ;
INIT_LIST_HEAD ( & priv - > slaves ) ;
mutex_init ( & priv - > slaves_mtx ) ;
return & priv - > hw ;
}
EXPORT_SYMBOL ( ieee802154_alloc_device ) ;
void ieee802154_free_device ( struct ieee802154_dev * hw )
{
struct mac802154_priv * priv = mac802154_to_priv ( hw ) ;
2012-05-16 00:50:28 +04:00
BUG_ON ( ! list_empty ( & priv - > slaves ) ) ;
2012-05-16 00:50:20 +04:00
mutex_destroy ( & priv - > slaves_mtx ) ;
2012-12-14 05:03:03 +04:00
wpan_phy_free ( priv - > phy ) ;
2012-05-16 00:50:20 +04:00
}
EXPORT_SYMBOL ( ieee802154_free_device ) ;
int ieee802154_register_device ( struct ieee802154_dev * dev )
{
struct mac802154_priv * priv = mac802154_to_priv ( dev ) ;
2014-07-03 02:20:43 +04:00
int rc = - ENOSYS ;
if ( dev - > flags & IEEE802154_HW_TXPOWER ) {
if ( ! priv - > ops - > set_txpower )
goto out ;
priv - > phy - > set_txpower = mac802154_set_txpower ;
}
if ( dev - > flags & IEEE802154_HW_LBT ) {
if ( ! priv - > ops - > set_lbt )
goto out ;
priv - > phy - > set_lbt = mac802154_set_lbt ;
}
if ( dev - > flags & IEEE802154_HW_CCA_MODE ) {
if ( ! priv - > ops - > set_cca_mode )
goto out ;
priv - > phy - > set_cca_mode = mac802154_set_cca_mode ;
}
if ( dev - > flags & IEEE802154_HW_CCA_ED_LEVEL ) {
if ( ! priv - > ops - > set_cca_ed_level )
goto out ;
priv - > phy - > set_cca_ed_level = mac802154_set_cca_ed_level ;
}
if ( dev - > flags & IEEE802154_HW_CSMA_PARAMS ) {
if ( ! priv - > ops - > set_csma_params )
goto out ;
priv - > phy - > set_csma_params = mac802154_set_csma_params ;
}
if ( dev - > flags & IEEE802154_HW_FRAME_RETRIES ) {
if ( ! priv - > ops - > set_frame_retries )
goto out ;
priv - > phy - > set_frame_retries = mac802154_set_frame_retries ;
}
2012-05-16 00:50:20 +04:00
priv - > dev_workqueue =
create_singlethread_workqueue ( wpan_phy_name ( priv - > phy ) ) ;
2014-07-03 02:20:43 +04:00
if ( ! priv - > dev_workqueue ) {
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
wpan_phy_set_dev ( priv - > phy , priv - > hw . parent ) ;
2012-05-16 00:50:28 +04:00
priv - > phy - > add_iface = mac802154_add_iface ;
priv - > phy - > del_iface = mac802154_del_iface ;
2012-05-16 00:50:20 +04:00
rc = wpan_phy_register ( priv - > phy ) ;
if ( rc < 0 )
goto out_wq ;
rtnl_lock ( ) ;
mutex_lock ( & priv - > slaves_mtx ) ;
priv - > running = MAC802154_DEVICE_RUN ;
mutex_unlock ( & priv - > slaves_mtx ) ;
rtnl_unlock ( ) ;
return 0 ;
out_wq :
destroy_workqueue ( priv - > dev_workqueue ) ;
out :
return rc ;
}
EXPORT_SYMBOL ( ieee802154_register_device ) ;
void ieee802154_unregister_device ( struct ieee802154_dev * dev )
{
struct mac802154_priv * priv = mac802154_to_priv ( dev ) ;
2012-05-16 00:50:28 +04:00
struct mac802154_sub_if_data * sdata , * next ;
2012-05-16 00:50:20 +04:00
flush_workqueue ( priv - > dev_workqueue ) ;
destroy_workqueue ( priv - > dev_workqueue ) ;
rtnl_lock ( ) ;
mutex_lock ( & priv - > slaves_mtx ) ;
priv - > running = MAC802154_DEVICE_STOPPED ;
mutex_unlock ( & priv - > slaves_mtx ) ;
2012-05-16 00:50:28 +04:00
list_for_each_entry_safe ( sdata , next , & priv - > slaves , list ) {
mutex_lock ( & sdata - > hw - > slaves_mtx ) ;
list_del ( & sdata - > list ) ;
mutex_unlock ( & sdata - > hw - > slaves_mtx ) ;
unregister_netdevice ( sdata - > dev ) ;
}
2012-05-16 00:50:20 +04:00
rtnl_unlock ( ) ;
wpan_phy_unregister ( priv - > phy ) ;
}
EXPORT_SYMBOL ( ieee802154_unregister_device ) ;
MODULE_DESCRIPTION ( " IEEE 802.15.4 implementation " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;