/*
* Copyright (c) 2008, 2009 open80211s Ltd.
* Author: Luis Carlos Cobo <luisca@cozybit.com>
*
* 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.
*/
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/random.h>
#include "ieee80211_i.h"
#include "rate.h"
#include "mesh.h"
#define PLINK_GET_LLID(p) (p + 2)
#define PLINK_GET_PLID(p) (p + 4)
#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
jiffies + HZ * t / 1000))
/* We only need a valid sta if user configured a minimum rssi_threshold. */
#define rssi_threshold_check(sta, sdata) \
(sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\
(sta && (s8) -ewma_read(&sta->avg_signal) > \
sdata->u.mesh.mshcfg.rssi_threshold))
enum plink_event {
PLINK_UNDEFINED,
OPN_ACPT,
OPN_RJCT,
OPN_IGNR,
CNF_ACPT,
CNF_RJCT,
CNF_IGNR,
CLS_ACPT,
CLS_IGNR
};
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
enum ieee80211_self_protected_actioncode action,
u8 *da, __le16 llid, __le16 plid, __le16 reason);
/**
* mesh_plink_fsm_restart - restart a mesh peer link finite state machine
*
* @sta: mesh peer link to restart
*
* Locking: this function must be called holding sta->lock
*/
static inline void mesh_plink_fsm_restart(struct sta_info *sta)
{
sta->plink_state = NL80211_PLINK_LISTEN;
sta->llid = sta->plid = sta->reason = 0;
sta->plink_retries = 0;
}
/*
* mesh_set_short_slot_time - enable / disable ERP short slot time.
*
* The standard indirectly mandates mesh STAs to turn off short slot time by
* disallowing advertising this (802.11-2012 8.4.1.4), but that doesn't mean we
* can't be sneaky about it. Enable short slot time if all mesh STAs in the
* MBSS support ERP rates.
*
* Returns BSS_CHANGED_ERP_SLOT or 0 for no change.
*/
static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
struct sta_info *sta;
u32 erp_rates = 0, changed = 0;
int i;
bool short_slot = false;
if (band == IEEE80211_BAND_5GHZ) {
/* (IEEE 802.11-2012 19.4.5) */
short_slot = true;
goto out;
} else if (band != IEEE80211_BAND_2GHZ ||
(band == IEEE80211_BAND_2GHZ &&
local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
goto out;
for (i = 0; i < sband->n_bitrates; i++)
if (sband->bitrates[i].flags & IEEE80211_RATE_ERP_G)
erp_rates |= BIT(i);
if (!erp_rates)
goto out;
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sdata != sta->sdata ||
sta->plink_state != NL80211_PLINK_ESTAB)
continue;
short_slot = false;
if (erp_rates & sta->sta.supp_rates[band])
short_slot = true;
else
break;
}
rcu_read_unlock();
out:
if (sdata->vif.bss_conf.use_short_slot != short_slot) {
sdata->vif.bss_conf.use_short_slot = short_slot;
changed = BSS_CHANGED_ERP_SLOT;
mpl_dbg(sdata, "mesh_plink %pM: ERP short slot time %d\n",
sdata->vif.addr, short_slot);
}
return changed;
}
/**
* mesh_set_ht_prot_mode - set correct HT protection mode
*
* Section 9.23.3.5 of IEEE 80211-2012 describes the protection rules for HT
* mesh STA in a MBSS. Three HT protection modes are supported for now, non-HT
* mixed mode, 20MHz-protection and no-protection mode. non-HT mixed mode is
* selected if any non-HT peers are present in our MBSS. 20MHz-protection mode
* is selected if all peers in our 20/40MHz MBSS support HT and atleast one
* HT20 peer is present. Otherwise no-protection mode is selected.
*/
static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
u32 changed = 0;
u16 ht_opmode;
bool non_ht_sta = false, ht20_sta = false;
if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
return 0;
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list,