|  | /* 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. | 
|  | * | 
|  | * Authors: | 
|  | * Alexander Aring <aar@pengutronix.de> | 
|  | * | 
|  | * Based on: net/mac80211/cfg.c | 
|  | */ | 
|  |  | 
|  | #include <net/rtnetlink.h> | 
|  | #include <net/cfg802154.h> | 
|  |  | 
|  | #include "ieee802154_i.h" | 
|  | #include "driver-ops.h" | 
|  | #include "cfg.h" | 
|  |  | 
|  | static struct net_device * | 
|  | ieee802154_add_iface_deprecated(struct wpan_phy *wpan_phy, | 
|  | const char *name, | 
|  | unsigned char name_assign_type, int type) | 
|  | { | 
|  | struct ieee802154_local *local = wpan_phy_priv(wpan_phy); | 
|  | struct net_device *dev; | 
|  |  | 
|  | rtnl_lock(); | 
|  | dev = ieee802154_if_add(local, name, name_assign_type, type, | 
|  | cpu_to_le64(0x0000000000000000ULL)); | 
|  | rtnl_unlock(); | 
|  |  | 
|  | return dev; | 
|  | } | 
|  |  | 
|  | static void ieee802154_del_iface_deprecated(struct wpan_phy *wpan_phy, | 
|  | struct net_device *dev) | 
|  | { | 
|  | struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); | 
|  |  | 
|  | ieee802154_if_remove(sdata); | 
|  | } | 
|  |  | 
|  | static int | 
|  | ieee802154_add_iface(struct wpan_phy *phy, const char *name, | 
|  | unsigned char name_assign_type, | 
|  | enum nl802154_iftype type, __le64 extended_addr) | 
|  | { | 
|  | struct ieee802154_local *local = wpan_phy_priv(phy); | 
|  | struct net_device *err; | 
|  |  | 
|  | err = ieee802154_if_add(local, name, name_assign_type, type, | 
|  | extended_addr); | 
|  | return PTR_ERR_OR_ZERO(err); | 
|  | } | 
|  |  | 
|  | static int | 
|  | ieee802154_del_iface(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev) | 
|  | { | 
|  | ieee802154_if_remove(IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev)); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | ieee802154_set_channel(struct wpan_phy *wpan_phy, u8 page, u8 channel) | 
|  | { | 
|  | struct ieee802154_local *local = wpan_phy_priv(wpan_phy); | 
|  | int ret; | 
|  |  | 
|  | ASSERT_RTNL(); | 
|  |  | 
|  | /* check if phy support this setting */ | 
|  | if (!(wpan_phy->channels_supported[page] & BIT(channel))) | 
|  | return -EINVAL; | 
|  |  | 
|  | ret = drv_set_channel(local, page, channel); | 
|  | if (!ret) { | 
|  | wpan_phy->current_page = page; | 
|  | wpan_phy->current_channel = channel; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int | 
|  | ieee802154_set_cca_mode(struct wpan_phy *wpan_phy, | 
|  | const struct wpan_phy_cca *cca) | 
|  | { | 
|  | struct ieee802154_local *local = wpan_phy_priv(wpan_phy); | 
|  | int ret; | 
|  |  | 
|  | ASSERT_RTNL(); | 
|  |  | 
|  | /* check if phy support this setting */ | 
|  | if (!(local->hw.flags & IEEE802154_HW_CCA_MODE)) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | ret = drv_set_cca_mode(local, cca); | 
|  | if (!ret) | 
|  | wpan_phy->cca = *cca; | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int | 
|  | ieee802154_set_pan_id(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, | 
|  | __le16 pan_id) | 
|  | { | 
|  | ASSERT_RTNL(); | 
|  |  | 
|  | /* TODO | 
|  | * I am not sure about to check here on broadcast pan_id. | 
|  | * Broadcast is a valid setting, comment from 802.15.4: | 
|  | * If this value is 0xffff, the device is not associated. | 
|  | * | 
|  | * This could useful to simple deassociate an device. | 
|  | */ | 
|  | if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST)) | 
|  | return -EINVAL; | 
|  |  | 
|  | wpan_dev->pan_id = pan_id; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | ieee802154_set_backoff_exponent(struct wpan_phy *wpan_phy, | 
|  | struct wpan_dev *wpan_dev, | 
|  | u8 min_be, u8 max_be) | 
|  | { | 
|  | struct ieee802154_local *local = wpan_phy_priv(wpan_phy); | 
|  |  | 
|  | ASSERT_RTNL(); | 
|  |  | 
|  | if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS)) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | wpan_dev->min_be = min_be; | 
|  | wpan_dev->max_be = max_be; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | ieee802154_set_short_addr(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, | 
|  | __le16 short_addr) | 
|  | { | 
|  | ASSERT_RTNL(); | 
|  |  | 
|  | /* TODO | 
|  | * I am not sure about to check here on broadcast short_addr. | 
|  | * Broadcast is a valid setting, comment from 802.15.4: | 
|  | * A value of 0xfffe indicates that the device has | 
|  | * associated but has not been allocated an address. A | 
|  | * value of 0xffff indicates that the device does not | 
|  | * have a short address. | 
|  | * | 
|  | * I think we should allow to set these settings but | 
|  | * don't allow to allow socket communication with it. | 
|  | */ | 
|  | if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) || | 
|  | short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST)) | 
|  | return -EINVAL; | 
|  |  | 
|  | wpan_dev->short_addr = short_addr; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | ieee802154_set_max_csma_backoffs(struct wpan_phy *wpan_phy, | 
|  | struct wpan_dev *wpan_dev, | 
|  | u8 max_csma_backoffs) | 
|  | { | 
|  | struct ieee802154_local *local = wpan_phy_priv(wpan_phy); | 
|  |  | 
|  | ASSERT_RTNL(); | 
|  |  | 
|  | if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS)) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | wpan_dev->csma_retries = max_csma_backoffs; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | ieee802154_set_max_frame_retries(struct wpan_phy *wpan_phy, | 
|  | struct wpan_dev *wpan_dev, | 
|  | s8 max_frame_retries) | 
|  | { | 
|  | struct ieee802154_local *local = wpan_phy_priv(wpan_phy); | 
|  |  | 
|  | ASSERT_RTNL(); | 
|  |  | 
|  | if (!(local->hw.flags & IEEE802154_HW_FRAME_RETRIES)) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | wpan_dev->frame_retries = max_frame_retries; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | ieee802154_set_lbt_mode(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, | 
|  | bool mode) | 
|  | { | 
|  | struct ieee802154_local *local = wpan_phy_priv(wpan_phy); | 
|  |  | 
|  | ASSERT_RTNL(); | 
|  |  | 
|  | if (!(local->hw.flags & IEEE802154_HW_LBT)) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | wpan_dev->lbt = mode; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | const struct cfg802154_ops mac802154_config_ops = { | 
|  | .add_virtual_intf_deprecated = ieee802154_add_iface_deprecated, | 
|  | .del_virtual_intf_deprecated = ieee802154_del_iface_deprecated, | 
|  | .add_virtual_intf = ieee802154_add_iface, | 
|  | .del_virtual_intf = ieee802154_del_iface, | 
|  | .set_channel = ieee802154_set_channel, | 
|  | .set_cca_mode = ieee802154_set_cca_mode, | 
|  | .set_pan_id = ieee802154_set_pan_id, | 
|  | .set_short_addr = ieee802154_set_short_addr, | 
|  | .set_backoff_exponent = ieee802154_set_backoff_exponent, | 
|  | .set_max_csma_backoffs = ieee802154_set_max_csma_backoffs, | 
|  | .set_max_frame_retries = ieee802154_set_max_frame_retries, | 
|  | .set_lbt_mode = ieee802154_set_lbt_mode, | 
|  | }; |