mirror of https://gitee.com/openkylin/linux.git
mac80211: 802.11p OCB mode support
This patch adds 802.11p OCB (Outside the Context of a BSS) mode support. When communicating in OCB mode a mandatory wildcard BSSID (48 '1' bits) is used. The EDCA parameters handling function was changed to support 802.11p specific values. The insertion of a newly discovered STAs is done in the similar way as in the IBSS mode -- through the deferred insertion. The OCB mode uses a periodic 'housekeeping task' for expiration of disconnected STAs (in the similar manner as in the MESH mode). New Kconfig option for verbose OCB debugging outputs is added. Signed-off-by: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
6e0bd6c35b
commit
239281f803
|
@ -263,6 +263,7 @@ struct ieee80211_vif_chanctx_switch {
|
||||||
* @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed,
|
* @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed,
|
||||||
* note that this is only called when it changes after the channel
|
* note that this is only called when it changes after the channel
|
||||||
* context had been assigned.
|
* context had been assigned.
|
||||||
|
* @BSS_CHANGED_OCB: OCB join status changed
|
||||||
*/
|
*/
|
||||||
enum ieee80211_bss_change {
|
enum ieee80211_bss_change {
|
||||||
BSS_CHANGED_ASSOC = 1<<0,
|
BSS_CHANGED_ASSOC = 1<<0,
|
||||||
|
@ -287,6 +288,7 @@ enum ieee80211_bss_change {
|
||||||
BSS_CHANGED_P2P_PS = 1<<19,
|
BSS_CHANGED_P2P_PS = 1<<19,
|
||||||
BSS_CHANGED_BEACON_INFO = 1<<20,
|
BSS_CHANGED_BEACON_INFO = 1<<20,
|
||||||
BSS_CHANGED_BANDWIDTH = 1<<21,
|
BSS_CHANGED_BANDWIDTH = 1<<21,
|
||||||
|
BSS_CHANGED_OCB = 1<<22,
|
||||||
|
|
||||||
/* when adding here, make sure to change ieee80211_reconfig */
|
/* when adding here, make sure to change ieee80211_reconfig */
|
||||||
};
|
};
|
||||||
|
|
|
@ -176,6 +176,17 @@ config MAC80211_HT_DEBUG
|
||||||
|
|
||||||
Do not select this option.
|
Do not select this option.
|
||||||
|
|
||||||
|
config MAC80211_OCB_DEBUG
|
||||||
|
bool "Verbose OCB debugging"
|
||||||
|
depends on MAC80211_DEBUG_MENU
|
||||||
|
---help---
|
||||||
|
Selecting this option causes mac80211 to print out
|
||||||
|
very verbose OCB debugging messages. It should not
|
||||||
|
be selected on production systems as those messages
|
||||||
|
are remotely triggerable.
|
||||||
|
|
||||||
|
Do not select this option.
|
||||||
|
|
||||||
config MAC80211_IBSS_DEBUG
|
config MAC80211_IBSS_DEBUG
|
||||||
bool "Verbose IBSS debugging"
|
bool "Verbose IBSS debugging"
|
||||||
depends on MAC80211_DEBUG_MENU
|
depends on MAC80211_DEBUG_MENU
|
||||||
|
|
|
@ -27,7 +27,8 @@ mac80211-y := \
|
||||||
event.o \
|
event.o \
|
||||||
chan.o \
|
chan.o \
|
||||||
trace.o mlme.o \
|
trace.o mlme.o \
|
||||||
tdls.o
|
tdls.o \
|
||||||
|
ocb.o
|
||||||
|
|
||||||
mac80211-$(CONFIG_MAC80211_LEDS) += led.o
|
mac80211-$(CONFIG_MAC80211_LEDS) += led.o
|
||||||
mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
|
mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
|
||||||
|
|
|
@ -2019,6 +2019,17 @@ static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
|
||||||
return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
|
return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ieee80211_join_ocb(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
struct ocb_setup *setup)
|
||||||
|
{
|
||||||
|
return ieee80211_ocb_join(IEEE80211_DEV_TO_SUB_IF(dev), setup);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ieee80211_leave_ocb(struct wiphy *wiphy, struct net_device *dev)
|
||||||
|
{
|
||||||
|
return ieee80211_ocb_leave(IEEE80211_DEV_TO_SUB_IF(dev));
|
||||||
|
}
|
||||||
|
|
||||||
static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
|
static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
|
||||||
int rate[IEEE80211_NUM_BANDS])
|
int rate[IEEE80211_NUM_BANDS])
|
||||||
{
|
{
|
||||||
|
@ -3693,6 +3704,8 @@ const struct cfg80211_ops mac80211_config_ops = {
|
||||||
.join_mesh = ieee80211_join_mesh,
|
.join_mesh = ieee80211_join_mesh,
|
||||||
.leave_mesh = ieee80211_leave_mesh,
|
.leave_mesh = ieee80211_leave_mesh,
|
||||||
#endif
|
#endif
|
||||||
|
.join_ocb = ieee80211_join_ocb,
|
||||||
|
.leave_ocb = ieee80211_leave_ocb,
|
||||||
.change_bss = ieee80211_change_bss,
|
.change_bss = ieee80211_change_bss,
|
||||||
.set_txq_params = ieee80211_set_txq_params,
|
.set_txq_params = ieee80211_set_txq_params,
|
||||||
.set_monitor_channel = ieee80211_set_monitor_channel,
|
.set_monitor_channel = ieee80211_set_monitor_channel,
|
||||||
|
|
|
@ -675,6 +675,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
case NL80211_IFTYPE_WDS:
|
case NL80211_IFTYPE_WDS:
|
||||||
case NL80211_IFTYPE_MESH_POINT:
|
case NL80211_IFTYPE_MESH_POINT:
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
#define __MAC80211_DEBUG_H
|
#define __MAC80211_DEBUG_H
|
||||||
#include <net/cfg80211.h>
|
#include <net/cfg80211.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_MAC80211_OCB_DEBUG
|
||||||
|
#define MAC80211_OCB_DEBUG 1
|
||||||
|
#else
|
||||||
|
#define MAC80211_OCB_DEBUG 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_MAC80211_IBSS_DEBUG
|
#ifdef CONFIG_MAC80211_IBSS_DEBUG
|
||||||
#define MAC80211_IBSS_DEBUG 1
|
#define MAC80211_IBSS_DEBUG 1
|
||||||
#else
|
#else
|
||||||
|
@ -131,6 +137,10 @@ do { \
|
||||||
_sdata_dbg(MAC80211_HT_DEBUG && net_ratelimit(), \
|
_sdata_dbg(MAC80211_HT_DEBUG && net_ratelimit(), \
|
||||||
sdata, fmt, ##__VA_ARGS__)
|
sdata, fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define ocb_dbg(sdata, fmt, ...) \
|
||||||
|
_sdata_dbg(MAC80211_OCB_DEBUG, \
|
||||||
|
sdata, fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
#define ibss_dbg(sdata, fmt, ...) \
|
#define ibss_dbg(sdata, fmt, ...) \
|
||||||
_sdata_dbg(MAC80211_IBSS_DEBUG, \
|
_sdata_dbg(MAC80211_IBSS_DEBUG, \
|
||||||
sdata, fmt, ##__VA_ARGS__)
|
sdata, fmt, ##__VA_ARGS__)
|
||||||
|
|
|
@ -214,7 +214,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
|
||||||
BSS_CHANGED_BEACON_ENABLED) &&
|
BSS_CHANGED_BEACON_ENABLED) &&
|
||||||
sdata->vif.type != NL80211_IFTYPE_AP &&
|
sdata->vif.type != NL80211_IFTYPE_AP &&
|
||||||
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||||
sdata->vif.type != NL80211_IFTYPE_MESH_POINT))
|
sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
|
||||||
|
sdata->vif.type != NL80211_IFTYPE_OCB))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
|
if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
|
||||||
|
|
|
@ -576,6 +576,25 @@ struct ieee80211_if_ibss {
|
||||||
} state;
|
} state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ieee80211_if_ocb - OCB mode state
|
||||||
|
*
|
||||||
|
* @housekeeping_timer: timer for periodic invocation of a housekeeping task
|
||||||
|
* @wrkq_flags: OCB deferred task action
|
||||||
|
* @incomplete_lock: delayed STA insertion lock
|
||||||
|
* @incomplete_stations: list of STAs waiting for delayed insertion
|
||||||
|
* @joined: indication if the interface is connected to an OCB network
|
||||||
|
*/
|
||||||
|
struct ieee80211_if_ocb {
|
||||||
|
struct timer_list housekeeping_timer;
|
||||||
|
unsigned long wrkq_flags;
|
||||||
|
|
||||||
|
spinlock_t incomplete_lock;
|
||||||
|
struct list_head incomplete_stations;
|
||||||
|
|
||||||
|
bool joined;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface
|
* struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface
|
||||||
*
|
*
|
||||||
|
@ -869,6 +888,7 @@ struct ieee80211_sub_if_data {
|
||||||
struct ieee80211_if_managed mgd;
|
struct ieee80211_if_managed mgd;
|
||||||
struct ieee80211_if_ibss ibss;
|
struct ieee80211_if_ibss ibss;
|
||||||
struct ieee80211_if_mesh mesh;
|
struct ieee80211_if_mesh mesh;
|
||||||
|
struct ieee80211_if_ocb ocb;
|
||||||
u32 mntr_flags;
|
u32 mntr_flags;
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
|
@ -1505,6 +1525,15 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
||||||
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
|
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
|
||||||
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
|
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
|
||||||
|
|
||||||
|
/* OCB code */
|
||||||
|
void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata);
|
||||||
|
void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
|
||||||
|
const u8 *bssid, const u8 *addr, u32 supp_rates);
|
||||||
|
void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata);
|
||||||
|
int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ocb_setup *setup);
|
||||||
|
int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata);
|
||||||
|
|
||||||
/* mesh code */
|
/* mesh code */
|
||||||
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
|
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
|
||||||
void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
|
@ -258,6 +258,15 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
|
||||||
/* we hold the RTNL here so can safely walk the list */
|
/* we hold the RTNL here so can safely walk the list */
|
||||||
list_for_each_entry(nsdata, &local->interfaces, list) {
|
list_for_each_entry(nsdata, &local->interfaces, list) {
|
||||||
if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {
|
if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {
|
||||||
|
/*
|
||||||
|
* Only OCB and monitor mode may coexist
|
||||||
|
*/
|
||||||
|
if ((sdata->vif.type == NL80211_IFTYPE_OCB &&
|
||||||
|
nsdata->vif.type != NL80211_IFTYPE_MONITOR) ||
|
||||||
|
(sdata->vif.type != NL80211_IFTYPE_MONITOR &&
|
||||||
|
nsdata->vif.type == NL80211_IFTYPE_OCB))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allow only a single IBSS interface to be up at any
|
* Allow only a single IBSS interface to be up at any
|
||||||
* time. This is restricted because beacon distribution
|
* time. This is restricted because beacon distribution
|
||||||
|
@ -1283,6 +1292,9 @@ static void ieee80211_iface_work(struct work_struct *work)
|
||||||
break;
|
break;
|
||||||
ieee80211_mesh_work(sdata);
|
ieee80211_mesh_work(sdata);
|
||||||
break;
|
break;
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
|
ieee80211_ocb_work(sdata);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1302,6 +1314,9 @@ static void ieee80211_recalc_smps_work(struct work_struct *work)
|
||||||
static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
|
static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
|
||||||
enum nl80211_iftype type)
|
enum nl80211_iftype type)
|
||||||
{
|
{
|
||||||
|
static const u8 bssid_wildcard[ETH_ALEN] = {0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff};
|
||||||
|
|
||||||
/* clear type-dependent union */
|
/* clear type-dependent union */
|
||||||
memset(&sdata->u, 0, sizeof(sdata->u));
|
memset(&sdata->u, 0, sizeof(sdata->u));
|
||||||
|
|
||||||
|
@ -1354,7 +1369,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
|
||||||
ieee80211_sta_setup_sdata(sdata);
|
ieee80211_sta_setup_sdata(sdata);
|
||||||
break;
|
break;
|
||||||
case NL80211_IFTYPE_OCB:
|
case NL80211_IFTYPE_OCB:
|
||||||
/* to be implemented in the future */
|
sdata->vif.bss_conf.bssid = bssid_wildcard;
|
||||||
|
ieee80211_ocb_setup_sdata(sdata);
|
||||||
break;
|
break;
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
|
sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
|
||||||
|
@ -1403,6 +1419,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
|
||||||
case NL80211_IFTYPE_AP:
|
case NL80211_IFTYPE_AP:
|
||||||
case NL80211_IFTYPE_STATION:
|
case NL80211_IFTYPE_STATION:
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
/*
|
/*
|
||||||
* Could maybe also all others here?
|
* Could maybe also all others here?
|
||||||
* Just not sure how that interacts
|
* Just not sure how that interacts
|
||||||
|
@ -1418,6 +1435,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
|
||||||
case NL80211_IFTYPE_AP:
|
case NL80211_IFTYPE_AP:
|
||||||
case NL80211_IFTYPE_STATION:
|
case NL80211_IFTYPE_STATION:
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
/*
|
/*
|
||||||
* Could probably support everything
|
* Could probably support everything
|
||||||
* but WDS here (WDS do_open can fail
|
* but WDS here (WDS do_open can fail
|
||||||
|
|
|
@ -0,0 +1,250 @@
|
||||||
|
/*
|
||||||
|
* OCB mode implementation
|
||||||
|
*
|
||||||
|
* Copyright: (c) 2014 Czech Technical University in Prague
|
||||||
|
* (c) 2014 Volkswagen Group Research
|
||||||
|
* Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz>
|
||||||
|
* Funded by: Volkswagen Group Research
|
||||||
|
*
|
||||||
|
* 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/delay.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/skbuff.h>
|
||||||
|
#include <linux/if_arp.h>
|
||||||
|
#include <linux/etherdevice.h>
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
|
#include <net/mac80211.h>
|
||||||
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
|
#include "ieee80211_i.h"
|
||||||
|
#include "driver-ops.h"
|
||||||
|
#include "rate.h"
|
||||||
|
|
||||||
|
#define IEEE80211_OCB_HOUSEKEEPING_INTERVAL (60 * HZ)
|
||||||
|
#define IEEE80211_OCB_PEER_INACTIVITY_LIMIT (240 * HZ)
|
||||||
|
#define IEEE80211_OCB_MAX_STA_ENTRIES 128
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum ocb_deferred_task_flags - mac80211 OCB deferred tasks
|
||||||
|
* @OCB_WORK_HOUSEKEEPING: run the periodic OCB housekeeping tasks
|
||||||
|
*
|
||||||
|
* These flags are used in @wrkq_flags field of &struct ieee80211_if_ocb
|
||||||
|
*/
|
||||||
|
enum ocb_deferred_task_flags {
|
||||||
|
OCB_WORK_HOUSEKEEPING,
|
||||||
|
};
|
||||||
|
|
||||||
|
void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
|
||||||
|
const u8 *bssid, const u8 *addr,
|
||||||
|
u32 supp_rates)
|
||||||
|
{
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||||
|
struct ieee80211_supported_band *sband;
|
||||||
|
enum nl80211_bss_scan_width scan_width;
|
||||||
|
struct sta_info *sta;
|
||||||
|
int band;
|
||||||
|
|
||||||
|
/* XXX: Consider removing the least recently used entry and
|
||||||
|
* allow new one to be added.
|
||||||
|
*/
|
||||||
|
if (local->num_sta >= IEEE80211_OCB_MAX_STA_ENTRIES) {
|
||||||
|
net_info_ratelimited("%s: No room for a new OCB STA entry %pM\n",
|
||||||
|
sdata->name, addr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ocb_dbg(sdata, "Adding new OCB station %pM\n", addr);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||||
|
if (WARN_ON_ONCE(!chanctx_conf)) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
band = chanctx_conf->def.chan->band;
|
||||||
|
scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
|
||||||
|
if (!sta)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sta->last_rx = jiffies;
|
||||||
|
|
||||||
|
/* Add only mandatory rates for now */
|
||||||
|
sband = local->hw.wiphy->bands[band];
|
||||||
|
sta->sta.supp_rates[band] =
|
||||||
|
ieee80211_mandatory_rates(sband, scan_width);
|
||||||
|
|
||||||
|
spin_lock(&ifocb->incomplete_lock);
|
||||||
|
list_add(&sta->list, &ifocb->incomplete_stations);
|
||||||
|
spin_unlock(&ifocb->incomplete_lock);
|
||||||
|
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta)
|
||||||
|
__acquires(RCU)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||||
|
u8 addr[ETH_ALEN];
|
||||||
|
|
||||||
|
memcpy(addr, sta->sta.addr, ETH_ALEN);
|
||||||
|
|
||||||
|
ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n",
|
||||||
|
addr, sdata->name);
|
||||||
|
|
||||||
|
sta_info_move_state(sta, IEEE80211_STA_AUTH);
|
||||||
|
sta_info_move_state(sta, IEEE80211_STA_ASSOC);
|
||||||
|
sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
|
||||||
|
|
||||||
|
rate_control_rate_init(sta);
|
||||||
|
|
||||||
|
/* If it fails, maybe we raced another insertion? */
|
||||||
|
if (sta_info_insert_rcu(sta))
|
||||||
|
return sta_info_get(sdata, addr);
|
||||||
|
return sta;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ieee80211_ocb_housekeeping(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
|
||||||
|
ocb_dbg(sdata, "Running ocb housekeeping\n");
|
||||||
|
|
||||||
|
ieee80211_sta_expire(sdata, IEEE80211_OCB_PEER_INACTIVITY_LIMIT);
|
||||||
|
|
||||||
|
mod_timer(&ifocb->housekeeping_timer,
|
||||||
|
round_jiffies(jiffies + IEEE80211_OCB_HOUSEKEEPING_INTERVAL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
|
if (ifocb->joined != true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sdata_lock(sdata);
|
||||||
|
|
||||||
|
spin_lock_bh(&ifocb->incomplete_lock);
|
||||||
|
while (!list_empty(&ifocb->incomplete_stations)) {
|
||||||
|
sta = list_first_entry(&ifocb->incomplete_stations,
|
||||||
|
struct sta_info, list);
|
||||||
|
list_del(&sta->list);
|
||||||
|
spin_unlock_bh(&ifocb->incomplete_lock);
|
||||||
|
|
||||||
|
ieee80211_ocb_finish_sta(sta);
|
||||||
|
rcu_read_unlock();
|
||||||
|
spin_lock_bh(&ifocb->incomplete_lock);
|
||||||
|
}
|
||||||
|
spin_unlock_bh(&ifocb->incomplete_lock);
|
||||||
|
|
||||||
|
if (test_and_clear_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags))
|
||||||
|
ieee80211_ocb_housekeeping(sdata);
|
||||||
|
|
||||||
|
sdata_unlock(sdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ieee80211_ocb_housekeeping_timer(unsigned long data)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata = (void *)data;
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
|
||||||
|
set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags);
|
||||||
|
|
||||||
|
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
|
||||||
|
setup_timer(&ifocb->housekeeping_timer,
|
||||||
|
ieee80211_ocb_housekeeping_timer,
|
||||||
|
(unsigned long)sdata);
|
||||||
|
INIT_LIST_HEAD(&ifocb->incomplete_stations);
|
||||||
|
spin_lock_init(&ifocb->incomplete_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ocb_setup *setup)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
u32 changed = BSS_CHANGED_OCB;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (ifocb->joined == true)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
|
||||||
|
sdata->smps_mode = IEEE80211_SMPS_OFF;
|
||||||
|
sdata->needed_rx_chains = sdata->local->rx_chains;
|
||||||
|
|
||||||
|
mutex_lock(&sdata->local->mtx);
|
||||||
|
err = ieee80211_vif_use_channel(sdata, &setup->chandef,
|
||||||
|
IEEE80211_CHANCTX_SHARED);
|
||||||
|
mutex_unlock(&sdata->local->mtx);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
ieee80211_bss_info_change_notify(sdata, changed);
|
||||||
|
|
||||||
|
ifocb->joined = true;
|
||||||
|
|
||||||
|
set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags);
|
||||||
|
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||||
|
|
||||||
|
netif_carrier_on(sdata->dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
|
ifocb->joined = false;
|
||||||
|
sta_info_flush(sdata);
|
||||||
|
|
||||||
|
spin_lock_bh(&ifocb->incomplete_lock);
|
||||||
|
while (!list_empty(&ifocb->incomplete_stations)) {
|
||||||
|
sta = list_first_entry(&ifocb->incomplete_stations,
|
||||||
|
struct sta_info, list);
|
||||||
|
list_del(&sta->list);
|
||||||
|
spin_unlock_bh(&ifocb->incomplete_lock);
|
||||||
|
|
||||||
|
sta_info_free(local, sta);
|
||||||
|
spin_lock_bh(&ifocb->incomplete_lock);
|
||||||
|
}
|
||||||
|
spin_unlock_bh(&ifocb->incomplete_lock);
|
||||||
|
|
||||||
|
netif_carrier_off(sdata->dev);
|
||||||
|
clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
|
||||||
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_OCB);
|
||||||
|
|
||||||
|
mutex_lock(&sdata->local->mtx);
|
||||||
|
ieee80211_vif_release_channel(sdata);
|
||||||
|
mutex_unlock(&sdata->local->mtx);
|
||||||
|
|
||||||
|
skb_queue_purge(&sdata->skb_queue);
|
||||||
|
|
||||||
|
del_timer_sync(&sdata->u.ocb.housekeeping_timer);
|
||||||
|
/* If the timer fired while we waited for it, it will have
|
||||||
|
* requeued the work. Now the work will be running again
|
||||||
|
* but will not rearm the timer again because it checks
|
||||||
|
* whether we are connected to the network or not -- at this
|
||||||
|
* point we shouldn't be anymore.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1032,6 +1032,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
|
||||||
ieee80211_is_pspoll(hdr->frame_control)) &&
|
ieee80211_is_pspoll(hdr->frame_control)) &&
|
||||||
rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||||
rx->sdata->vif.type != NL80211_IFTYPE_WDS &&
|
rx->sdata->vif.type != NL80211_IFTYPE_WDS &&
|
||||||
|
rx->sdata->vif.type != NL80211_IFTYPE_OCB &&
|
||||||
(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) {
|
(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) {
|
||||||
/*
|
/*
|
||||||
* accept port control frames from the AP even when it's not
|
* accept port control frames from the AP even when it's not
|
||||||
|
@ -1272,6 +1273,12 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
|
||||||
sta->last_rx_rate_vht_nss = status->vht_nss;
|
sta->last_rx_rate_vht_nss = status->vht_nss;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
|
||||||
|
u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
|
||||||
|
NL80211_IFTYPE_OCB);
|
||||||
|
/* OCB uses wild-card BSSID */
|
||||||
|
if (is_broadcast_ether_addr(bssid))
|
||||||
|
sta->last_rx = jiffies;
|
||||||
} else if (!is_multicast_ether_addr(hdr->addr1)) {
|
} else if (!is_multicast_ether_addr(hdr->addr1)) {
|
||||||
/*
|
/*
|
||||||
* Mesh beacons will update last_rx when if they are found to
|
* Mesh beacons will update last_rx when if they are found to
|
||||||
|
@ -2820,6 +2827,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
|
||||||
|
|
||||||
if (!ieee80211_vif_is_mesh(&sdata->vif) &&
|
if (!ieee80211_vif_is_mesh(&sdata->vif) &&
|
||||||
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||||
|
sdata->vif.type != NL80211_IFTYPE_OCB &&
|
||||||
sdata->vif.type != NL80211_IFTYPE_STATION)
|
sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||||
return RX_DROP_MONITOR;
|
return RX_DROP_MONITOR;
|
||||||
|
|
||||||
|
@ -3130,6 +3138,33 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
|
||||||
BIT(rate_idx));
|
BIT(rate_idx));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
|
if (!bssid)
|
||||||
|
return false;
|
||||||
|
if (ieee80211_is_beacon(hdr->frame_control)) {
|
||||||
|
return false;
|
||||||
|
} else if (!is_broadcast_ether_addr(bssid)) {
|
||||||
|
ocb_dbg(sdata, "BSSID mismatch in OCB mode!\n");
|
||||||
|
return false;
|
||||||
|
} else if (!multicast &&
|
||||||
|
!ether_addr_equal(sdata->dev->dev_addr,
|
||||||
|
hdr->addr1)) {
|
||||||
|
/* if we are in promisc mode we also accept
|
||||||
|
* packets not destined for us
|
||||||
|
*/
|
||||||
|
if (!(sdata->dev->flags & IFF_PROMISC))
|
||||||
|
return false;
|
||||||
|
rx->flags &= ~IEEE80211_RX_RA_MATCH;
|
||||||
|
} else if (!rx->sta) {
|
||||||
|
int rate_idx;
|
||||||
|
if (status->flag & RX_FLAG_HT)
|
||||||
|
rate_idx = 0; /* TODO: HT rates */
|
||||||
|
else
|
||||||
|
rate_idx = status->rate_idx;
|
||||||
|
ieee80211_ocb_rx_no_sta(sdata, bssid, hdr->addr2,
|
||||||
|
BIT(rate_idx));
|
||||||
|
}
|
||||||
|
break;
|
||||||
case NL80211_IFTYPE_MESH_POINT:
|
case NL80211_IFTYPE_MESH_POINT:
|
||||||
if (!multicast &&
|
if (!multicast &&
|
||||||
!ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
|
!ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
|
||||||
|
|
|
@ -296,6 +296,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
|
||||||
*/
|
*/
|
||||||
return TX_DROP;
|
return TX_DROP;
|
||||||
|
|
||||||
|
if (tx->sdata->vif.type == NL80211_IFTYPE_OCB)
|
||||||
|
return TX_CONTINUE;
|
||||||
|
|
||||||
if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
|
if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
|
||||||
return TX_CONTINUE;
|
return TX_CONTINUE;
|
||||||
|
|
||||||
|
@ -2013,6 +2016,17 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||||
goto fail_rcu;
|
goto fail_rcu;
|
||||||
band = chanctx_conf->def.chan->band;
|
band = chanctx_conf->def.chan->band;
|
||||||
break;
|
break;
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
|
/* DA SA BSSID */
|
||||||
|
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
||||||
|
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
|
||||||
|
eth_broadcast_addr(hdr.addr3);
|
||||||
|
hdrlen = 24;
|
||||||
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||||
|
if (!chanctx_conf)
|
||||||
|
goto fail_rcu;
|
||||||
|
band = chanctx_conf->def.chan->band;
|
||||||
|
break;
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
/* DA SA BSSID */
|
/* DA SA BSSID */
|
||||||
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
||||||
|
@ -2057,6 +2071,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||||
* EAPOL frames from the local station.
|
* EAPOL frames from the local station.
|
||||||
*/
|
*/
|
||||||
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
|
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
|
||||||
|
(sdata->vif.type != NL80211_IFTYPE_OCB) &&
|
||||||
!multicast && !authorized &&
|
!multicast && !authorized &&
|
||||||
(cpu_to_be16(ethertype) != sdata->control_port_protocol ||
|
(cpu_to_be16(ethertype) != sdata->control_port_protocol ||
|
||||||
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
|
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
|
||||||
|
|
|
@ -1101,6 +1101,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||||
int ac;
|
int ac;
|
||||||
bool use_11b, enable_qos;
|
bool use_11b, enable_qos;
|
||||||
|
bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */
|
||||||
int aCWmin, aCWmax;
|
int aCWmin, aCWmax;
|
||||||
|
|
||||||
if (!local->ops->conf_tx)
|
if (!local->ops->conf_tx)
|
||||||
|
@ -1125,6 +1126,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||||
*/
|
*/
|
||||||
enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
|
enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
|
||||||
|
|
||||||
|
is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB);
|
||||||
|
|
||||||
/* Set defaults according to 802.11-2007 Table 7-37 */
|
/* Set defaults according to 802.11-2007 Table 7-37 */
|
||||||
aCWmax = 1023;
|
aCWmax = 1023;
|
||||||
if (use_11b)
|
if (use_11b)
|
||||||
|
@ -1146,7 +1149,10 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||||
qparam.cw_max = aCWmax;
|
qparam.cw_max = aCWmax;
|
||||||
qparam.cw_min = aCWmin;
|
qparam.cw_min = aCWmin;
|
||||||
qparam.txop = 0;
|
qparam.txop = 0;
|
||||||
qparam.aifs = 7;
|
if (is_ocb)
|
||||||
|
qparam.aifs = 9;
|
||||||
|
else
|
||||||
|
qparam.aifs = 7;
|
||||||
break;
|
break;
|
||||||
/* never happens but let's not leave undefined */
|
/* never happens but let's not leave undefined */
|
||||||
default:
|
default:
|
||||||
|
@ -1154,21 +1160,32 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||||
qparam.cw_max = aCWmax;
|
qparam.cw_max = aCWmax;
|
||||||
qparam.cw_min = aCWmin;
|
qparam.cw_min = aCWmin;
|
||||||
qparam.txop = 0;
|
qparam.txop = 0;
|
||||||
qparam.aifs = 3;
|
if (is_ocb)
|
||||||
|
qparam.aifs = 6;
|
||||||
|
else
|
||||||
|
qparam.aifs = 3;
|
||||||
break;
|
break;
|
||||||
case IEEE80211_AC_VI:
|
case IEEE80211_AC_VI:
|
||||||
qparam.cw_max = aCWmin;
|
qparam.cw_max = aCWmin;
|
||||||
qparam.cw_min = (aCWmin + 1) / 2 - 1;
|
qparam.cw_min = (aCWmin + 1) / 2 - 1;
|
||||||
if (use_11b)
|
if (is_ocb)
|
||||||
|
qparam.txop = 0;
|
||||||
|
else if (use_11b)
|
||||||
qparam.txop = 6016/32;
|
qparam.txop = 6016/32;
|
||||||
else
|
else
|
||||||
qparam.txop = 3008/32;
|
qparam.txop = 3008/32;
|
||||||
qparam.aifs = 2;
|
|
||||||
|
if (is_ocb)
|
||||||
|
qparam.aifs = 3;
|
||||||
|
else
|
||||||
|
qparam.aifs = 2;
|
||||||
break;
|
break;
|
||||||
case IEEE80211_AC_VO:
|
case IEEE80211_AC_VO:
|
||||||
qparam.cw_max = (aCWmin + 1) / 2 - 1;
|
qparam.cw_max = (aCWmin + 1) / 2 - 1;
|
||||||
qparam.cw_min = (aCWmin + 1) / 4 - 1;
|
qparam.cw_min = (aCWmin + 1) / 4 - 1;
|
||||||
if (use_11b)
|
if (is_ocb)
|
||||||
|
qparam.txop = 0;
|
||||||
|
else if (use_11b)
|
||||||
qparam.txop = 3264/32;
|
qparam.txop = 3264/32;
|
||||||
else
|
else
|
||||||
qparam.txop = 1504/32;
|
qparam.txop = 1504/32;
|
||||||
|
@ -1842,7 +1859,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||||
sdata_unlock(sdata);
|
sdata_unlock(sdata);
|
||||||
break;
|
break;
|
||||||
case NL80211_IFTYPE_OCB:
|
case NL80211_IFTYPE_OCB:
|
||||||
/* to be implemented in the future */
|
changed |= BSS_CHANGED_OCB;
|
||||||
|
ieee80211_bss_info_change_notify(sdata, changed);
|
||||||
break;
|
break;
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
changed |= BSS_CHANGED_IBSS;
|
changed |= BSS_CHANGED_IBSS;
|
||||||
|
|
|
@ -148,6 +148,10 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
ra = skb->data;
|
ra = skb->data;
|
||||||
break;
|
break;
|
||||||
|
case NL80211_IFTYPE_OCB:
|
||||||
|
/* all stations are required to support WME */
|
||||||
|
qos = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue