/*
* 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 const char * const mplstates[] = {
[NL80211_PLINK_LISTEN] = "LISTEN",
[NL80211_PLINK_OPN_SNT] = "OPN-SNT",
[NL80211_PLINK_OPN_RCVD] = "OPN-RCVD",
[NL80211_PLINK_CNF_RCVD] = "CNF_RCVD",
[NL80211_PLINK_ESTAB] = "ESTAB",
[NL80211_PLINK_HOLDING] = "HOLDING",
[NL80211_PLINK_BLOCKED] = "BLOCKED"
};
static const char * const mplevents[] = {
[PLINK_UNDEFINED] = "NONE",
[OPN_ACPT] = "OPN_ACPT",
[OPN_RJCT] = "OPN_RJCT",
[OPN_IGNR] = "OPN_IGNR",
[CNF_ACPT] = "CNF_ACPT",
[CNF_RJCT] = "CNF_RJCT",
[CNF_IGNR] = "CNF_IGNR",
[CLS_ACPT] = "CLS_ACPT",
[CLS_IGNR] = "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