mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
mac802154: Handle disassociations
Devices may decide to disassociate from their coordinator for different reasons (device turning off, coordinator signal strength too low, etc), the MAC layer just has to send a disassociation notification. If the ack of the disassociation notification is not received, the device may consider itself disassociated anyway. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Acked-by: Stefan Schmidt <stefan@datenfreihafen.org> Acked-by: Alexander Aring <aahringo@redhat.com> Link: https://lore.kernel.org/linux-wpan/20230927181214.129346-7-miquel.raynal@bootlin.com
This commit is contained in:
parent
7b18313e84
commit
9860d9be89
@ -46,6 +46,7 @@ bool cfg802154_device_is_parent(struct wpan_dev *wpan_dev,
|
||||
|
||||
return cfg802154_pan_device_is_matching(wpan_dev->parent, target);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cfg802154_device_is_parent);
|
||||
|
||||
struct ieee802154_pan_device *
|
||||
cfg802154_device_is_child(struct wpan_dev *wpan_dev,
|
||||
@ -61,3 +62,4 @@ cfg802154_device_is_child(struct wpan_dev *wpan_dev,
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cfg802154_device_is_child);
|
||||
|
@ -383,6 +383,105 @@ free_parent:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mac802154_disassociate_from_parent(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev)
|
||||
{
|
||||
struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
|
||||
struct ieee802154_pan_device *child, *tmp;
|
||||
struct ieee802154_sub_if_data *sdata;
|
||||
u64 eaddr;
|
||||
int ret;
|
||||
|
||||
sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev);
|
||||
|
||||
/* Start by disassociating all the children and preventing new ones to
|
||||
* attempt associations.
|
||||
*/
|
||||
list_for_each_entry_safe(child, tmp, &wpan_dev->children, node) {
|
||||
ret = mac802154_send_disassociation_notif(sdata, child,
|
||||
IEEE802154_COORD_WISHES_DEVICE_TO_LEAVE);
|
||||
if (ret) {
|
||||
eaddr = swab64((__force u64)child->extended_addr);
|
||||
dev_err(&sdata->dev->dev,
|
||||
"Disassociation with %8phC may have failed (%d)\n",
|
||||
&eaddr, ret);
|
||||
}
|
||||
|
||||
list_del(&child->node);
|
||||
}
|
||||
|
||||
ret = mac802154_send_disassociation_notif(sdata, wpan_dev->parent,
|
||||
IEEE802154_DEVICE_WISHES_TO_LEAVE);
|
||||
if (ret) {
|
||||
eaddr = swab64((__force u64)wpan_dev->parent->extended_addr);
|
||||
dev_err(&sdata->dev->dev,
|
||||
"Disassociation from %8phC may have failed (%d)\n",
|
||||
&eaddr, ret);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
kfree(wpan_dev->parent);
|
||||
wpan_dev->parent = NULL;
|
||||
wpan_dev->pan_id = cpu_to_le16(IEEE802154_PAN_ID_BROADCAST);
|
||||
wpan_dev->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST);
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_AFILT) {
|
||||
ret = drv_set_pan_id(local, wpan_dev->pan_id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = drv_set_short_addr(local, wpan_dev->short_addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mac802154_disassociate_child(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev,
|
||||
struct ieee802154_pan_device *child)
|
||||
{
|
||||
struct ieee802154_sub_if_data *sdata;
|
||||
int ret;
|
||||
|
||||
sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev);
|
||||
|
||||
ret = mac802154_send_disassociation_notif(sdata, child,
|
||||
IEEE802154_COORD_WISHES_DEVICE_TO_LEAVE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_del(&child->node);
|
||||
kfree(child);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mac802154_disassociate(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev,
|
||||
struct ieee802154_addr *target)
|
||||
{
|
||||
u64 teaddr = swab64((__force u64)target->extended_addr);
|
||||
struct ieee802154_pan_device *pan_device;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (cfg802154_device_is_parent(wpan_dev, target))
|
||||
return mac802154_disassociate_from_parent(wpan_phy, wpan_dev);
|
||||
|
||||
pan_device = cfg802154_device_is_child(wpan_dev, target);
|
||||
if (pan_device)
|
||||
return mac802154_disassociate_child(wpan_phy, wpan_dev,
|
||||
pan_device);
|
||||
|
||||
dev_err(&wpan_dev->netdev->dev,
|
||||
"Device %8phC is not associated with us\n", &teaddr);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
|
||||
static void
|
||||
ieee802154_get_llsec_table(struct wpan_phy *wpan_phy,
|
||||
@ -595,6 +694,7 @@ const struct cfg802154_ops mac802154_config_ops = {
|
||||
.send_beacons = mac802154_send_beacons,
|
||||
.stop_beacons = mac802154_stop_beacons,
|
||||
.associate = mac802154_associate,
|
||||
.disassociate = mac802154_disassociate,
|
||||
#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
|
||||
.get_llsec_table = ieee802154_get_llsec_table,
|
||||
.lock_llsec_table = ieee802154_lock_llsec_table,
|
||||
|
@ -315,6 +315,10 @@ static inline bool mac802154_is_associating(struct ieee802154_local *local)
|
||||
return test_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
|
||||
}
|
||||
|
||||
int mac802154_send_disassociation_notif(struct ieee802154_sub_if_data *sdata,
|
||||
struct ieee802154_pan_device *target,
|
||||
u8 reason);
|
||||
|
||||
/* interface handling */
|
||||
int ieee802154_iface_init(void);
|
||||
void ieee802154_iface_exit(void);
|
||||
|
@ -637,3 +637,63 @@ int mac802154_process_association_resp(struct ieee802154_sub_if_data *sdata,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac802154_send_disassociation_notif(struct ieee802154_sub_if_data *sdata,
|
||||
struct ieee802154_pan_device *target,
|
||||
u8 reason)
|
||||
{
|
||||
struct ieee802154_disassociation_notif_frame frame = {};
|
||||
u64 teaddr = swab64((__force u64)target->extended_addr);
|
||||
struct ieee802154_local *local = sdata->local;
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
struct sk_buff *skb;
|
||||
int ret;
|
||||
|
||||
frame.mhr.fc.type = IEEE802154_FC_TYPE_MAC_CMD;
|
||||
frame.mhr.fc.security_enabled = 0;
|
||||
frame.mhr.fc.frame_pending = 0;
|
||||
frame.mhr.fc.ack_request = 1;
|
||||
frame.mhr.fc.intra_pan = 1;
|
||||
frame.mhr.fc.dest_addr_mode = (target->mode == IEEE802154_ADDR_LONG) ?
|
||||
IEEE802154_EXTENDED_ADDRESSING : IEEE802154_SHORT_ADDRESSING;
|
||||
frame.mhr.fc.version = IEEE802154_2003_STD;
|
||||
frame.mhr.fc.source_addr_mode = IEEE802154_EXTENDED_ADDRESSING;
|
||||
frame.mhr.source.mode = IEEE802154_ADDR_LONG;
|
||||
frame.mhr.source.pan_id = wpan_dev->pan_id;
|
||||
frame.mhr.source.extended_addr = wpan_dev->extended_addr;
|
||||
frame.mhr.dest.mode = target->mode;
|
||||
frame.mhr.dest.pan_id = wpan_dev->pan_id;
|
||||
if (target->mode == IEEE802154_ADDR_LONG)
|
||||
frame.mhr.dest.extended_addr = target->extended_addr;
|
||||
else
|
||||
frame.mhr.dest.short_addr = target->short_addr;
|
||||
frame.mhr.seq = atomic_inc_return(&wpan_dev->dsn) & 0xFF;
|
||||
frame.mac_pl.cmd_id = IEEE802154_CMD_DISASSOCIATION_NOTIFY;
|
||||
frame.disassoc_pl = reason;
|
||||
|
||||
skb = alloc_skb(IEEE802154_MAC_CMD_SKB_SZ + sizeof(frame.disassoc_pl),
|
||||
GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOBUFS;
|
||||
|
||||
skb->dev = sdata->dev;
|
||||
|
||||
ret = ieee802154_mac_cmd_push(skb, &frame, &frame.disassoc_pl,
|
||||
sizeof(frame.disassoc_pl));
|
||||
if (ret) {
|
||||
kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ieee802154_mlme_tx_one_locked(local, sdata, skb);
|
||||
if (ret) {
|
||||
dev_warn(&sdata->dev->dev,
|
||||
"No DISASSOC ACK received from %8phC\n", &teaddr);
|
||||
if (ret > 0)
|
||||
ret = (ret == IEEE802154_NO_ACK) ? -EREMOTEIO : -EIO;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&sdata->dev->dev, "DISASSOC ACK received from %8phC\n", &teaddr);
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user