Commit 707be0ae authored by John W. Linville's avatar John W. Linville
parents 040a7831 88bc40e8
...@@ -1540,11 +1540,6 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, ...@@ -1540,11 +1540,6 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
/* now send back TX status */ /* now send back TX status */
txi = IEEE80211_SKB_CB(skb); txi = IEEE80211_SKB_CB(skb);
if (txi->control.vif)
hwsim_check_magic(txi->control.vif);
if (txi->control.sta)
hwsim_check_sta_magic(txi->control.sta);
ieee80211_tx_info_clear_status(txi); ieee80211_tx_info_clear_status(txi);
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
......
...@@ -1245,6 +1245,12 @@ enum nl80211_commands { ...@@ -1245,6 +1245,12 @@ enum nl80211_commands {
* @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
* or 0 to disable background scan. * or 0 to disable background scan.
* *
* @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from
* userspace. If unset it is assumed the hint comes directly from
* a user. If set code could specify exactly what type of source
* was used to provide the hint. For the different types of
* allowed user regulatory hints see nl80211_user_reg_hint_type.
*
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
*/ */
...@@ -1498,6 +1504,8 @@ enum nl80211_attrs { ...@@ -1498,6 +1504,8 @@ enum nl80211_attrs {
NL80211_ATTR_WDEV, NL80211_ATTR_WDEV,
NL80211_ATTR_USER_REG_HINT_TYPE,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
...@@ -1550,6 +1558,8 @@ enum nl80211_attrs { ...@@ -1550,6 +1558,8 @@ enum nl80211_attrs {
/* default RSSI threshold for scan results if none specified. */ /* default RSSI threshold for scan results if none specified. */
#define NL80211_SCAN_RSSI_THOLD_OFF -300 #define NL80211_SCAN_RSSI_THOLD_OFF -300
#define NL80211_CQM_TXE_MAX_INTVL 1800
/** /**
* enum nl80211_iftype - (virtual) interface types * enum nl80211_iftype - (virtual) interface types
* *
...@@ -2058,6 +2068,26 @@ enum nl80211_dfs_regions { ...@@ -2058,6 +2068,26 @@ enum nl80211_dfs_regions {
NL80211_DFS_JP = 3, NL80211_DFS_JP = 3,
}; };
/**
* enum nl80211_user_reg_hint_type - type of user regulatory hint
*
* @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always
* assumed if the attribute is not set.
* @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular
* base station. Device drivers that have been tested to work
* properly to support this type of hint can enable these hints
* by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature
* capability on the struct wiphy. The wireless core will
* ignore all cell base station hints until at least one device
* present has been registered with the wireless core that
* has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a
* supported feature.
*/
enum nl80211_user_reg_hint_type {
NL80211_USER_REG_HINT_USER = 0,
NL80211_USER_REG_HINT_CELL_BASE = 1,
};
/** /**
* enum nl80211_survey_info - survey information * enum nl80211_survey_info - survey information
* *
...@@ -2589,6 +2619,17 @@ enum nl80211_ps_state { ...@@ -2589,6 +2619,17 @@ enum nl80211_ps_state {
* @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
* @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many
* consecutive packets were not acknowledged by the peer * consecutive packets were not acknowledged by the peer
* @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures
* during the given %NL80211_ATTR_CQM_TXE_INTVL before an
* %NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and
* %NL80211_ATTR_CQM_TXE_PKTS is generated.
* @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given
* %NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is
* checked.
* @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic
* interval in which %NL80211_ATTR_CQM_TXE_PKTS and
* %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an
* %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
* @__NL80211_ATTR_CQM_AFTER_LAST: internal * @__NL80211_ATTR_CQM_AFTER_LAST: internal
* @NL80211_ATTR_CQM_MAX: highest key attribute * @NL80211_ATTR_CQM_MAX: highest key attribute
*/ */
...@@ -2598,6 +2639,9 @@ enum nl80211_attr_cqm { ...@@ -2598,6 +2639,9 @@ enum nl80211_attr_cqm {
NL80211_ATTR_CQM_RSSI_HYST, NL80211_ATTR_CQM_RSSI_HYST,
NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
NL80211_ATTR_CQM_PKT_LOSS_EVENT, NL80211_ATTR_CQM_PKT_LOSS_EVENT,
NL80211_ATTR_CQM_TXE_RATE,
NL80211_ATTR_CQM_TXE_PKTS,
NL80211_ATTR_CQM_TXE_INTVL,
/* keep last */ /* keep last */
__NL80211_ATTR_CQM_AFTER_LAST, __NL80211_ATTR_CQM_AFTER_LAST,
...@@ -2947,11 +2991,15 @@ enum nl80211_ap_sme_features { ...@@ -2947,11 +2991,15 @@ enum nl80211_ap_sme_features {
* @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates. * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates.
* @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
* the connected inactive stations in AP mode. * the connected inactive stations in AP mode.
* @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
* to work properly to suppport receiving regulatory hints from
* cellular base stations.
*/ */
enum nl80211_feature_flags { enum nl80211_feature_flags {
NL80211_FEATURE_SK_TX_STATUS = 1 << 0, NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
NL80211_FEATURE_HT_IBSS = 1 << 1, NL80211_FEATURE_HT_IBSS = 1 << 1,
NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2,
NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3,
}; };
/** /**
......
...@@ -1504,8 +1504,6 @@ struct cfg80211_gtk_rekey_data { ...@@ -1504,8 +1504,6 @@ struct cfg80211_gtk_rekey_data {
* interfaces are active this callback should reject the configuration. * interfaces are active this callback should reject the configuration.
* If no interfaces are active or the device is down, the channel should * If no interfaces are active or the device is down, the channel should
* be stored for when a monitor interface becomes active. * be stored for when a monitor interface becomes active.
* @set_monitor_enabled: Notify driver that there are only monitor
* interfaces running.
* *
* @scan: Request to do a scan. If returning zero, the scan request is given * @scan: Request to do a scan. If returning zero, the scan request is given
* the driver, and will be valid until passed to cfg80211_scan_done(). * the driver, and will be valid until passed to cfg80211_scan_done().
...@@ -1575,6 +1573,8 @@ struct cfg80211_gtk_rekey_data { ...@@ -1575,6 +1573,8 @@ struct cfg80211_gtk_rekey_data {
* @set_power_mgmt: Configure WLAN power management. A timeout value of -1 * @set_power_mgmt: Configure WLAN power management. A timeout value of -1
* allows the driver to adjust the dynamic ps timeout value. * allows the driver to adjust the dynamic ps timeout value.
* @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold. * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
* @set_cqm_txe_config: Configure connection quality monitor TX error
* thresholds.
* @sched_scan_start: Tell the driver to start a scheduled scan. * @sched_scan_start: Tell the driver to start a scheduled scan.
* @sched_scan_stop: Tell the driver to stop an ongoing scheduled * @sched_scan_stop: Tell the driver to stop an ongoing scheduled
* scan. The driver_initiated flag specifies whether the driver * scan. The driver_initiated flag specifies whether the driver
...@@ -1612,6 +1612,10 @@ struct cfg80211_gtk_rekey_data { ...@@ -1612,6 +1612,10 @@ struct cfg80211_gtk_rekey_data {
* @get_et_strings: Ethtool API to get a set of strings to describe stats * @get_et_strings: Ethtool API to get a set of strings to describe stats
* and perhaps other supported types of ethtool data-sets. * and perhaps other supported types of ethtool data-sets.
* See @ethtool_ops.get_strings * See @ethtool_ops.get_strings
*
* @get_channel: Get the current operating channel for the virtual interface.
* For monitor interfaces, it should return %NULL unless there's a single
* current monitoring channel.
*/ */
struct cfg80211_ops { struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
...@@ -1781,6 +1785,10 @@ struct cfg80211_ops { ...@@ -1781,6 +1785,10 @@ struct cfg80211_ops {
struct net_device *dev, struct net_device *dev,
s32 rssi_thold, u32 rssi_hyst); s32 rssi_thold, u32 rssi_hyst);
int (*set_cqm_txe_config)(struct wiphy *wiphy,
struct net_device *dev,
u32 rate, u32 pkts, u32 intvl);
void (*mgmt_frame_register)(struct wiphy *wiphy, void (*mgmt_frame_register)(struct wiphy *wiphy,
struct wireless_dev *wdev, struct wireless_dev *wdev,
u16 frame_type, bool reg); u16 frame_type, bool reg);
...@@ -1820,7 +1828,10 @@ struct cfg80211_ops { ...@@ -1820,7 +1828,10 @@ struct cfg80211_ops {
void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev, void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev,
u32 sset, u8 *data); u32 sset, u8 *data);
void (*set_monitor_enabled)(struct wiphy *wiphy, bool enabled); struct ieee80211_channel *
(*get_channel)(struct wiphy *wiphy,
struct wireless_dev *wdev,
enum nl80211_channel_type *type);
}; };
/* /*
...@@ -3390,6 +3401,21 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, ...@@ -3390,6 +3401,21 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
void cfg80211_cqm_pktloss_notify(struct net_device *dev, void cfg80211_cqm_pktloss_notify(struct net_device *dev,
const u8 *peer, u32 num_packets, gfp_t gfp); const u8 *peer, u32 num_packets, gfp_t gfp);
/**
* cfg80211_cqm_txe_notify - TX error rate event
* @dev: network device
* @peer: peer's MAC address
* @num_packets: how many packets were lost
* @rate: % of packets which failed transmission
* @intvl: interval (in s) over which the TX failure threshold was breached.
* @gfp: context flags
*
* Notify userspace when configured % TX failures over number of packets in a
* given interval is exceeded.
*/
void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer,
u32 num_packets, u32 rate, u32 intvl, gfp_t gfp);
/** /**
* cfg80211_gtk_rekey_notify - notify userspace about driver rekeying * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
* @dev: network device * @dev: network device
......
...@@ -52,6 +52,10 @@ enum environment_cap { ...@@ -52,6 +52,10 @@ enum environment_cap {
* DFS master operation on a known DFS region (NL80211_DFS_*), * DFS master operation on a known DFS region (NL80211_DFS_*),
* dfs_region represents that region. Drivers can use this and the * dfs_region represents that region. Drivers can use this and the
* @alpha2 to adjust their device's DFS parameters as required. * @alpha2 to adjust their device's DFS parameters as required.
* @user_reg_hint_type: if the @initiator was of type
* %NL80211_REGDOM_SET_BY_USER, this classifies the type
* of hint passed. This could be any of the %NL80211_USER_REG_HINT_*
* types.
* @intersect: indicates whether the wireless core should intersect * @intersect: indicates whether the wireless core should intersect
* the requested regulatory domain with the presently set regulatory * the requested regulatory domain with the presently set regulatory
* domain. * domain.
...@@ -70,6 +74,7 @@ enum environment_cap { ...@@ -70,6 +74,7 @@ enum environment_cap {
struct regulatory_request { struct regulatory_request {
int wiphy_idx; int wiphy_idx;
enum nl80211_reg_initiator initiator; enum nl80211_reg_initiator initiator;
enum nl80211_user_reg_hint_type user_reg_hint_type;
char alpha2[2]; char alpha2[2];
u8 dfs_region; u8 dfs_region;
bool intersect; bool intersect;
......
...@@ -2493,6 +2493,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, ...@@ -2493,6 +2493,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
skb->dev = sdata->dev; skb->dev = sdata->dev;
if (!need_offchan) { if (!need_offchan) {
*cookie = (unsigned long) skb;
ieee80211_tx_skb(sdata, skb); ieee80211_tx_skb(sdata, skb);
ret = 0; ret = 0;
goto out_unlock; goto out_unlock;
...@@ -2982,14 +2983,14 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, ...@@ -2982,14 +2983,14 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
return 0; return 0;
} }
static void ieee80211_set_monitor_enabled(struct wiphy *wiphy, bool enabled) static struct ieee80211_channel *
ieee80211_cfg_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
enum nl80211_channel_type *type)
{ {
struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_local *local = wiphy_priv(wiphy);
if (enabled) *type = local->_oper_channel_type;
WARN_ON(ieee80211_add_virtual_monitor(local)); return local->oper_channel;
else
ieee80211_del_virtual_monitor(local);
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
...@@ -3066,11 +3067,11 @@ struct cfg80211_ops mac80211_config_ops = { ...@@ -3066,11 +3067,11 @@ struct cfg80211_ops mac80211_config_ops = {
.tdls_mgmt = ieee80211_tdls_mgmt, .tdls_mgmt = ieee80211_tdls_mgmt,
.probe_client = ieee80211_probe_client, .probe_client = ieee80211_probe_client,
.set_noack_map = ieee80211_set_noack_map, .set_noack_map = ieee80211_set_noack_map,
.set_monitor_enabled = ieee80211_set_monitor_enabled,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.set_wakeup = ieee80211_set_wakeup, .set_wakeup = ieee80211_set_wakeup,
#endif #endif
.get_et_sset_count = ieee80211_get_et_sset_count, .get_et_sset_count = ieee80211_get_et_sset_count,
.get_et_stats = ieee80211_get_et_stats, .get_et_stats = ieee80211_get_et_stats,
.get_et_strings = ieee80211_get_et_strings, .get_et_strings = ieee80211_get_et_strings,
.get_channel = ieee80211_cfg_get_channel,
}; };
...@@ -1491,10 +1491,6 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, ...@@ -1491,10 +1491,6 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, bool need_basic); struct sk_buff *skb, bool need_basic);
/* virtual monitor */
int ieee80211_add_virtual_monitor(struct ieee80211_local *local);
void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
/* channel management */ /* channel management */
enum ieee80211_chan_mode { enum ieee80211_chan_mode {
CHAN_MODE_UNDEFINED, CHAN_MODE_UNDEFINED,
......
...@@ -331,7 +331,7 @@ static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata) ...@@ -331,7 +331,7 @@ static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata)
sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE; sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
} }
int ieee80211_add_virtual_monitor(struct ieee80211_local *local) static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
int ret = 0; int ret = 0;
...@@ -377,7 +377,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local) ...@@ -377,7 +377,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
return ret; return ret;
} }
void ieee80211_del_virtual_monitor(struct ieee80211_local *local) static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
...@@ -497,6 +497,12 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) ...@@ -497,6 +497,12 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
break; break;
} }
if (local->monitors == 0 && local->open_count == 0) {
res = ieee80211_add_virtual_monitor(local);
if (res)
goto err_stop;
}
/* must be before the call to ieee80211_configure_filter */ /* must be before the call to ieee80211_configure_filter */
local->monitors++; local->monitors++;
if (local->monitors == 1) { if (local->monitors == 1) {
...@@ -511,6 +517,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) ...@@ -511,6 +517,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
break; break;
default: default:
if (coming_up) { if (coming_up) {
ieee80211_del_virtual_monitor(local);
res = drv_add_interface(local, sdata); res = drv_add_interface(local, sdata);
if (res) if (res)
goto err_stop; goto err_stop;
...@@ -745,6 +753,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ...@@ -745,6 +753,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
if (local->monitors == 0) { if (local->monitors == 0) {
local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR; local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR;
hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
ieee80211_del_virtual_monitor(local);
} }
ieee80211_adjust_monitor_flags(sdata, -1); ieee80211_adjust_monitor_flags(sdata, -1);
...@@ -818,6 +827,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ...@@ -818,6 +827,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
} }
} }
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
if (local->monitors == local->open_count && local->monitors > 0)
ieee80211_add_virtual_monitor(local);
} }
static int ieee80211_stop(struct net_device *dev) static int ieee80211_stop(struct net_device *dev)
......
...@@ -1360,6 +1360,17 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ...@@ -1360,6 +1360,17 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
} }
mutex_unlock(&local->sta_mtx); mutex_unlock(&local->sta_mtx);
/*
* if we want to get out of ps before disassoc (why?) we have
* to do it before sending disassoc, as otherwise the null-packet
* won't be valid.
*/
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
local->ps_sdata = NULL;
/* flush out any pending frame (e.g. DELBA) before deauth/disassoc */ /* flush out any pending frame (e.g. DELBA) before deauth/disassoc */
if (tx) if (tx)
drv_flush(local, false); drv_flush(local, false);
...@@ -1395,12 +1406,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ...@@ -1395,12 +1406,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
del_timer_sync(&local->dynamic_ps_timer); del_timer_sync(&local->dynamic_ps_timer);
cancel_work_sync(&local->dynamic_ps_enable_work); cancel_work_sync(&local->dynamic_ps_enable_work);
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
local->ps_sdata = NULL;
/* Disable ARP filtering */ /* Disable ARP filtering */
if (sdata->vif.bss_conf.arp_filter_enabled) { if (sdata->vif.bss_conf.arp_filter_enabled) {
sdata->vif.bss_conf.arp_filter_enabled = false; sdata->vif.bss_conf.arp_filter_enabled = false;
......
...@@ -324,6 +324,7 @@ void ieee80211_sw_roc_work(struct work_struct *work) ...@@ -324,6 +324,7 @@ void ieee80211_sw_roc_work(struct work_struct *work)
container_of(work, struct ieee80211_roc_work, work.work); container_of(work, struct ieee80211_roc_work, work.work);
struct ieee80211_sub_if_data *sdata = roc->sdata; struct ieee80211_sub_if_data *sdata = roc->sdata;
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
bool started;
mutex_lock(&local->mtx); mutex_lock(&local->mtx);
...@@ -366,9 +367,10 @@ void ieee80211_sw_roc_work(struct work_struct *work) ...@@ -366,9 +367,10 @@ void ieee80211_sw_roc_work(struct work_struct *work)
/* finish this ROC */ /* finish this ROC */
finish: finish:
list_del(&roc->list); list_del(&roc->list);
started = roc->started;
ieee80211_roc_notify_destroy(roc); ieee80211_roc_notify_destroy(roc);
if (roc->started) { if (started) {
drv_flush(local, false); drv_flush(local, false);
local->tmp_channel = NULL; local->tmp_channel = NULL;
...@@ -379,7 +381,7 @@ void ieee80211_sw_roc_work(struct work_struct *work) ...@@ -379,7 +381,7 @@ void ieee80211_sw_roc_work(struct work_struct *work)
ieee80211_recalc_idle(local); ieee80211_recalc_idle(local);
if (roc->started) if (started)
ieee80211_start_next_roc(local); ieee80211_start_next_roc(local);
} }
......
...@@ -74,6 +74,27 @@ config CFG80211_REG_DEBUG ...@@ -74,6 +74,27 @@ config CFG80211_REG_DEBUG
If unsure, say N. If unsure, say N.
config CFG80211_CERTIFICATION_ONUS
bool "cfg80211 certification onus"
depends on CFG80211 && EXPERT
default n
---help---
You should disable this option unless you are both capable
and willing to ensure your system will remain regulatory
compliant with the features available under this option.
Some options may still be under heavy development and
for whatever reason regulatory compliance has not or
cannot yet be verified. Regulatory verification may at
times only be possible until you have the final system
in place.
This option should only be enabled by system integrators
or distributions that have done work necessary to ensure
regulatory certification on the system with the enabled
features. Alternatively you can enable this option if
you are a wireless researcher and are working in a controlled
and approved environment by your local regulatory agency.
config CFG80211_DEFAULT_PS config CFG80211_DEFAULT_PS
bool "enable powersave by default" bool "enable powersave by default"
depends on CFG80211 depends on CFG80211
......
...@@ -82,7 +82,6 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, ...@@ -82,7 +82,6 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
int freq, enum nl80211_channel_type chantype) int freq, enum nl80211_channel_type chantype)
{ {
struct ieee80211_channel *chan; struct ieee80211_channel *chan;
int err;
if (!rdev->ops->set_monitor_channel) if (!rdev->ops->set_monitor_channel)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -93,13 +92,7 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, ...@@ -93,13 +92,7 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
if (!chan) if (!chan)
return -EINVAL; return -EINVAL;
err = rdev->ops->set_monitor_channel(&rdev->wiphy, chan, chantype); return rdev->ops->set_monitor_channel(&rdev->wiphy, chan, chantype);
if (!err) {
rdev->monitor_channel = chan;
rdev->monitor_channel_type = chantype;
}
return err;
} }
void void
...@@ -134,9 +127,16 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, ...@@ -134,9 +127,16 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
break; break;
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_GO:
if (wdev->beacon_interval) {
*chan = wdev->channel;
*chanmode = CHAN_MODE_SHARED;
}
return;
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
if (wdev->mesh_id_len) {
*chan = wdev->channel; *chan = wdev->channel;
*chanmode = CHAN_MODE_SHARED; *chanmode = CHAN_MODE_SHARED;
}
return; return;
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP_VLAN:
......
...@@ -542,7 +542,7 @@ int wiphy_register(struct wiphy *wiphy) ...@@ -542,7 +542,7 @@ int wiphy_register(struct wiphy *wiphy)
} }
/* set up regulatory info */ /* set up regulatory info */
regulatory_update(wiphy, NL80211_REGDOM_SET_BY_CORE); wiphy_regulatory_register(wiphy);
list_add_rcu(&rdev->list, &cfg80211_rdev_list); list_add_rcu(&rdev->list, &cfg80211_rdev_list);
cfg80211_rdev_list_generation++; cfg80211_rdev_list_generation++;
...@@ -652,9 +652,11 @@ void wiphy_unregister(struct wiphy *wiphy) ...@@ -652,9 +652,11 @@ void wiphy_unregister(struct wiphy *wiphy)
/* nothing */ /* nothing */
cfg80211_unlock_rdev(rdev); cfg80211_unlock_rdev(rdev);
/* If this device got a regulatory hint tell core its /*
* free to listen now to a new shiny device regulatory hint */ * If this device got a regulatory hint tell core its
reg_device_remove(wiphy); * free to listen now to a new shiny device regulatory hint
*/
wiphy_regulatory_deregister(wiphy);
cfg80211_rdev_list_generation++; cfg80211_rdev_list_generation++;
device_del(&rdev->wiphy.dev); device_del(&rdev->wiphy.dev);
...@@ -736,60 +738,14 @@ static struct device_type wiphy_type = { ...@@ -736,60 +738,14 @@ static struct device_type wiphy_type = {
.name = "wlan", .name = "wlan",
}; };
static struct ieee80211_channel *
cfg80211_get_any_chan(struct cfg80211_registered_device *rdev)
{
struct ieee80211_supported_band *sband;
int i;
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
sband = rdev->wiphy.bands[i];
if (sband && sband->n_channels > 0)
return &sband->channels[0];
}
return NULL;
}
static void cfg80211_init_mon_chan(struct cfg80211_registered_device *rdev)
{
struct ieee80211_channel *chan;
chan = cfg80211_get_any_chan(rdev);
if (WARN_ON(!chan))
return;
mutex_lock(&rdev->devlist_mtx);
WARN_ON(cfg80211_set_monitor_channel(rdev, chan->center_freq,
NL80211_CHAN_NO_HT));
mutex_unlock(&rdev->devlist_mtx);
}
void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
enum nl80211_iftype iftype, int num) enum nl80211_iftype iftype, int num)
{ {
bool has_monitors_only_old = cfg80211_has_monitors_only(rdev);
bool has_monitors_only_new;
ASSERT_RTNL(); ASSERT_RTNL();
rdev->num_running_ifaces += num; rdev->num_running_ifaces += num;
if (iftype == NL80211_IFTYPE_MONITOR) if (iftype == NL80211_IFTYPE_MONITOR)
rdev->num_running_monitor_ifaces += num; rdev->num_running_monitor_ifaces += num;
has_monitors_only_new = cfg80211_has_monitors_only(rdev);
if (has_monitors_only_new != has_monitors_only_old) {
if (rdev->ops->set_monitor_enabled)
rdev->ops->set_monitor_enabled(&rdev->wiphy,
has_monitors_only_new);
if (!has_monitors_only_new) {
rdev->monitor_channel = NULL;
rdev->monitor_channel_type = NL80211_CHAN_NO_HT;
} else {
cfg80211_init_mon_chan(rdev);
}
}
} }
static int cfg80211_netdev_notifier_call(struct notifier_block *nb, static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
...@@ -912,6 +868,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, ...@@ -912,6 +868,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
mutex_unlock(&rdev->devlist_mtx); mutex_unlock(&rdev->devlist_mtx);
dev_put(dev); dev_put(dev);
} }
cfg80211_update_iface_num(rdev, wdev->iftype, 1);
cfg80211_lock_rdev(rdev); cfg80211_lock_rdev(rdev);
mutex_lock(&rdev->devlist_mtx); mutex_lock(&rdev->devlist_mtx);
wdev_lock(wdev); wdev_lock(wdev);
...@@ -1006,7 +963,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, ...@@ -1006,7 +963,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
mutex_unlock(&rdev->devlist_mtx); mutex_unlock(&rdev->devlist_mtx);
if (ret) if (ret)
return notifier_from_errno(ret); return notifier_from_errno(ret);
cfg80211_update_iface_num(rdev, wdev->iftype, 1);
break; break;
} }
......
...@@ -61,9 +61,6 @@ struct cfg80211_registered_device { ...@@ -61,9 +61,6 @@ struct cfg80211_registered_device {
int num_running_ifaces; int num_running_ifaces;
int num_running_monitor_ifaces; int num_running_monitor_ifaces;
struct ieee80211_channel *monitor_channel;
enum nl80211_channel_type monitor_channel_type;
/* BSSes/scanning */ /* BSSes/scanning */
spinlock_t bss_lock; spinlock_t bss_lock;
struct list_head bss_list; struct list_head bss_list;
......
...@@ -919,6 +919,19 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev, ...@@ -919,6 +919,19 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,
} }
EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
void cfg80211_cqm_txe_notify(struct net_device *dev,
const u8 *peer, u32 num_packets,
u32 rate, u32 intvl, gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
nl80211_send_cqm_txe_notify(rdev, dev, peer, num_packets,
rate, intvl, gfp);
}
EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
const u8 *replay_ctr, gfp_t gfp) const u8 *replay_ctr, gfp_t gfp)
{ {
......
...@@ -354,6 +354,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { ...@@ -354,6 +354,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
[NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
[NL80211_ATTR_WDEV] = { .type = NLA_U64 }, [NL80211_ATTR_WDEV] = { .type = NLA_U64 },
[NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 },
}; };
/* policy for the key attributes */ /* policy for the key attributes */
...@@ -1759,11 +1760,17 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, ...@@ -1759,11 +1760,17 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
(cfg80211_rdev_list_generation << 2))) (cfg80211_rdev_list_generation << 2)))
goto nla_put_failure; goto nla_put_failure;
if (rdev->monitor_channel) { if (rdev->ops->get_channel) {
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, struct ieee80211_channel *chan;
rdev->monitor_channel->center_freq) || enum nl80211_channel_type channel_type;
chan = rdev->ops->get_channel(&rdev->wiphy, wdev,
&channel_type);
if (chan &&
(nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
chan->center_freq) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
rdev->monitor_channel_type)) channel_type)))
goto nla_put_failure; goto nla_put_failure;
} }
...@@ -3576,6 +3583,7 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) ...@@ -3576,6 +3583,7 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
{ {
int r; int r;
char *data = NULL; char *data = NULL;
enum nl80211_user_reg_hint_type user_reg_hint_type;
/* /*
* You should only get this when cfg80211 hasn't yet initialized * You should only get this when cfg80211 hasn't yet initialized
...@@ -3595,7 +3603,21 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) ...@@ -3595,7 +3603,21 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
r = regulatory_hint_user(data); if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE])
user_reg_hint_type =
nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]);
else
user_reg_hint_type = NL80211_USER_REG_HINT_USER;
switch (user_reg_hint_type) {
case NL80211_USER_REG_HINT_USER:
case NL80211_USER_REG_HINT_CELL_BASE:
break;
default:
return -EINVAL;
}
r = regulatory_hint_user(data, user_reg_hint_type);
return r; return r;
} }
...@@ -3965,6 +3987,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) ...@@ -3965,6 +3987,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
cfg80211_regdomain->dfs_region))) cfg80211_regdomain->dfs_region)))
goto nla_put_failure; goto nla_put_failure;
if (reg_last_request_cell_base() &&
nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
NL80211_USER_REG_HINT_CELL_BASE))
goto nla_put_failure;
nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
if (!nl_reg_rules) if (!nl_reg_rules)
goto nla_put_failure; goto nla_put_failure;
...@@ -6261,8 +6288,35 @@ nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { ...@@ -6261,8 +6288,35 @@ nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
[NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
[NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
[NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
}; };
static int nl80211_set_cqm_txe(struct genl_info *info,
u32 rate, u32 pkts, u32 intvl)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct wireless_dev *wdev;
struct net_device *dev = info->user_ptr[1];
if ((rate < 0 || rate > 100) ||
(intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL))
return -EINVAL;
wdev = dev->ieee80211_ptr;
if (!rdev->ops->set_cqm_txe_config)
return -EOPNOTSUPP;
if (wdev->iftype != NL80211_IFTYPE_STATION &&
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
return -EOPNOTSUPP;
return rdev->ops->set_cqm_txe_config(wdev->wiphy, dev,
rate, pkts, intvl);
}
static int nl80211_set_cqm_rssi(struct genl_info *info, static int nl80211_set_cqm_rssi(struct genl_info *info,
s32 threshold, u32 hysteresis) s32 threshold, u32 hysteresis)
{ {
...@@ -6310,6 +6364,14 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) ...@@ -6310,6 +6364,14 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
err = nl80211_set_cqm_rssi(info, threshold, hysteresis); err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
} else if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
attrs[NL80211_ATTR_CQM_TXE_PKTS] &&
attrs[NL80211_ATTR_CQM_TXE_INTVL]) {
u32 rate, pkts, intvl;
rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]);
pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]);
intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]);
err = nl80211_set_cqm_txe(info, rate, pkts, intvl);
} else } else
err = -EINVAL; err = -EINVAL;
...@@ -6466,8 +6528,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) ...@@ -6466,8 +6528,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
{ {
struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG]; struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
struct cfg80211_wowlan no_triggers = {};
struct cfg80211_wowlan new_triggers = {}; struct cfg80211_wowlan new_triggers = {};
struct cfg80211_wowlan *ntrig;
struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan; struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
int err, i; int err, i;
bool prev_enabled = rdev->wowlan; bool prev_enabled = rdev->wowlan;
...@@ -6475,8 +6537,11 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) ...@@ -6475,8 +6537,11 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
goto no_triggers; cfg80211_rdev_free_wowlan(rdev);
rdev->wowlan = NULL;
goto set_wakeup;
}
err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG, err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
...@@ -6587,22 +6652,15 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) ...@@ -6587,22 +6652,15 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
} }
} }
if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) { ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
struct cfg80211_wowlan *ntrig;
ntrig = kmemdup(&new_triggers, sizeof(new_triggers),
GFP_KERNEL);
if (!ntrig) { if (!ntrig) {
err = -ENOMEM; err = -ENOMEM;
goto error; goto error;
} }
cfg80211_rdev_free_wowlan(rdev); cfg80211_rdev_free_wowlan(rdev);
rdev->wowlan = ntrig; rdev->wowlan = ntrig;
} else {
no_triggers:
cfg80211_rdev_free_wowlan(rdev);
rdev->wowlan = NULL;
}
set_wakeup:
if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan) if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan)
rdev->ops->set_wakeup(&rdev->wiphy, rdev->wowlan); rdev->ops->set_wakeup(&rdev->wiphy, rdev->wowlan);
...@@ -8099,7 +8157,7 @@ static void nl80211_send_remain_on_chan_event( ...@@ -8099,7 +8157,7 @@ static void nl80211_send_remain_on_chan_event(
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
(wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
wdev->netdev->ifindex)) || wdev->netdev->ifindex)) ||
nla_put_u32(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) || nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) ||
nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
...@@ -8493,6 +8551,56 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, ...@@ -8493,6 +8551,56 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
nlmsg_free(msg); nlmsg_free(msg);
} }
void
nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer,
u32 num_packets, u32 rate, u32 intvl, gfp_t gfp)
{
struct sk_buff *msg;
struct nlattr *pinfoattr;
void *hdr;
msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
if (!msg)
return;
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
if (!hdr) {
nlmsg_free(msg);
return;
}
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
goto nla_put_failure;
pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
if (!pinfoattr)
goto nla_put_failure;
if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
goto nla_put_failure;
if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
goto nla_put_failure;
if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
goto nla_put_failure;
nla_nest_end(msg, pinfoattr);
genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
return;
nla_put_failure:
genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
void void
nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer, struct net_device *netdev, const u8 *peer,
......
...@@ -110,6 +110,11 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, ...@@ -110,6 +110,11 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer, struct net_device *netdev, const u8 *peer,
u32 num_packets, gfp_t gfp); u32 num_packets, gfp_t gfp);
void
nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer,
u32 num_packets, u32 rate, u32 intvl, gfp_t gfp);
void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid, struct net_device *netdev, const u8 *bssid,
const u8 *replay_ctr, gfp_t gfp); const u8 *replay_ctr, gfp_t gfp);
......
...@@ -97,9 +97,16 @@ const struct ieee80211_regdomain *cfg80211_regdomain; ...@@ -97,9 +97,16 @@ const struct ieee80211_regdomain *cfg80211_regdomain;
* - cfg80211_world_regdom * - cfg80211_world_regdom
* - cfg80211_regdom * - cfg80211_regdom
* - last_request * - last_request
* - reg_num_devs_support_basehint
*/ */
static DEFINE_MUTEX(reg_mutex); static DEFINE_MUTEX(reg_mutex);
/*
* Number of devices that registered to the core
* that support cellular base station regulatory hints
*/
static int reg_num_devs_support_basehint;
static inline void assert_reg_lock(void) static inline void assert_reg_lock(void)
{ {
lockdep_assert_held(&reg_mutex); lockdep_assert_held(&reg_mutex);
...@@ -911,6 +918,59 @@ static void handle_band(struct wiphy *wiphy, ...@@ -911,6 +918,59 @@ static void handle_band(struct wiphy *wiphy,
handle_channel(wiphy, initiator, band, i); handle_channel(wiphy, initiator, band, i);
} }
static bool reg_request_cell_base(struct regulatory_request *request)
{
if (request->initiator != NL80211_REGDOM_SET_BY_USER)
return false;
if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE)
return false;
return true;
}
bool reg_last_request_cell_base(void)
{
assert_cfg80211_lock();
mutex_lock(&reg_mutex);
return reg_request_cell_base(last_request);
mutex_unlock(&reg_mutex);
}
#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
/* Core specific check */
static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
{
if (!reg_num_devs_support_basehint)
return -EOPNOTSUPP;
if (reg_request_cell_base(last_request)) {
if (!regdom_changes(pending_request->alpha2))
return -EALREADY;
return 0;
}
return 0;
}
/* Device specific check */
static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
{
if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS))
return true;
return false;
}
#else
static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
{
return -EOPNOTSUPP;
}
static int reg_dev_ignore_cell_hint(struct wiphy *wiphy)
{
return true;
}
#endif
static bool ignore_reg_update(struct wiphy *wiphy, static bool ignore_reg_update(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator) enum nl80211_reg_initiator initiator)
{ {
...@@ -944,6 +1004,9 @@ static bool ignore_reg_update(struct wiphy *wiphy, ...@@ -944,6 +1004,9 @@ static bool ignore_reg_update(struct wiphy *wiphy,
return true; return true;
} }
if (reg_request_cell_base(last_request))
return reg_dev_ignore_cell_hint(wiphy);
return false; return false;
} }
...@@ -1169,14 +1232,6 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, ...@@ -1169,14 +1232,6 @@ static void wiphy_update_regulatory(struct wiphy *wiphy,
wiphy->reg_notifier(wiphy, last_request); wiphy->reg_notifier(wiphy, last_request);
} }
void regulatory_update(struct wiphy *wiphy,
enum nl80211_reg_initiator setby)
{
mutex_lock(&reg_mutex);
wiphy_update_regulatory(wiphy, setby);
mutex_unlock(&reg_mutex);
}
static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
{ {
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
...@@ -1307,6 +1362,13 @@ static int ignore_request(struct wiphy *wiphy, ...@@ -1307,6 +1362,13 @@ static int ignore_request(struct wiphy *wiphy,
return 0; return 0;
case NL80211_REGDOM_SET_BY_COUNTRY_IE: case NL80211_REGDOM_SET_BY_COUNTRY_IE:
if (reg_request_cell_base(last_request)) {
/* Trust a Cell base station over the AP's country IE */
if (regdom_changes(pending_request->alpha2))
return -EOPNOTSUPP;
return -EALREADY;
}
last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
if (unlikely(!is_an_alpha2(pending_request->alpha2))) if (unlikely(!is_an_alpha2(pending_request->alpha2)))
...@@ -1351,6 +1413,12 @@ static int ignore_request(struct wiphy *wiphy, ...@@ -1351,6 +1413,12 @@ static int ignore_request(struct wiphy *wiphy,
return REG_INTERSECT; return REG_INTERSECT;
case NL80211_REGDOM_SET_BY_USER: case NL80211_REGDOM_SET_BY_USER:
if (reg_request_cell_base(pending_request))
return reg_ignore_cell_hint(pending_request);
if (reg_request_cell_base(last_request))
return -EOPNOTSUPP;
if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
return REG_INTERSECT; return REG_INTERSECT;
/* /*
...@@ -1640,7 +1708,8 @@ static int regulatory_hint_core(const char *alpha2) ...@@ -1640,7 +1708,8 @@ static int regulatory_hint_core(const char *alpha2)
} }
/* User hints */ /* User hints */
int regulatory_hint_user(const char *alpha2) int regulatory_hint_user(const char *alpha2,
enum nl80211_user_reg_hint_type user_reg_hint_type)
{ {
struct regulatory_request *request; struct regulatory_request *request;
...@@ -1654,6 +1723,7 @@ int regulatory_hint_user(const char *alpha2) ...@@ -1654,6 +1723,7 @@ int regulatory_hint_user(const char *alpha2)
request->alpha2[0] = alpha2[0]; request->alpha2[0] = alpha2[0];
request->alpha2[1] = alpha2[1]; request->alpha2[1] = alpha2[1];
request->initiator = NL80211_REGDOM_SET_BY_USER; request->initiator = NL80211_REGDOM_SET_BY_USER;
request->user_reg_hint_type = user_reg_hint_type;
queue_regulatory_request(request); queue_regulatory_request(request);
...@@ -1906,7 +1976,7 @@ static void restore_regulatory_settings(bool reset_user) ...@@ -1906,7 +1976,7 @@ static void restore_regulatory_settings(bool reset_user)
* settings, user regulatory settings takes precedence. * settings, user regulatory settings takes precedence.
*/ */
if (is_an_alpha2(alpha2)) if (is_an_alpha2(alpha2))
regulatory_hint_user(user_alpha2); regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER);
if (list_empty(&tmp_reg_req_list)) if (list_empty(&tmp_reg_req_list))
return; return;
...@@ -2081,10 +2151,17 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) ...@@ -2081,10 +2151,17 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
else { else {
if (is_unknown_alpha2(rd->alpha2)) if (is_unknown_alpha2(rd->alpha2))
pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n");
else {
if (reg_request_cell_base(last_request))
pr_info("Regulatory domain changed "
"to country: %c%c by Cell Station\n",
rd->alpha2[0], rd->alpha2[1]);
else else
pr_info("Regulatory domain changed to country: %c%c\n", pr_info("Regulatory domain changed "
"to country: %c%c\n",
rd->alpha2[0], rd->alpha2[1]); rd->alpha2[0], rd->alpha2[1]);
} }
}
print_dfs_region(rd->dfs_region); print_dfs_region(rd->dfs_region);
print_rd_rules(rd); print_rd_rules(rd);
} }
...@@ -2128,7 +2205,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) ...@@ -2128,7 +2205,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
* checking if the alpha2 changes if CRDA was already called * checking if the alpha2 changes if CRDA was already called
*/ */
if (!regdom_changes(rd->alpha2)) if (!regdom_changes(rd->alpha2))
return -EINVAL; return -EALREADY;
} }
/* /*
...@@ -2248,6 +2325,9 @@ int set_regdom(const struct ieee80211_regdomain *rd) ...@@ -2248,6 +2325,9 @@ int set_regdom(const struct ieee80211_regdomain *rd)
/* Note that this doesn't update the wiphys, this is done below */ /* Note that this doesn't update the wiphys, this is done below */
r = __set_regdom(rd); r = __set_regdom(rd);
if (r) { if (r) {
if (r == -EALREADY)
reg_set_request_processed();
kfree(rd); kfree(rd);
mutex_unlock(&reg_mutex); mutex_unlock(&reg_mutex);
return r; return r;
...@@ -2290,8 +2370,22 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -2290,8 +2370,22 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
} }
#endif /* CONFIG_HOTPLUG */ #endif /* CONFIG_HOTPLUG */
void wiphy_regulatory_register(struct wiphy *wiphy)
{
assert_cfg80211_lock();
mutex_lock(&reg_mutex);
if (!reg_dev_ignore_cell_hint(wiphy))
reg_num_devs_support_basehint++;
wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE);
mutex_unlock(&reg_mutex);
}
/* Caller must hold cfg80211_mutex */ /* Caller must hold cfg80211_mutex */
void reg_device_remove(struct wiphy *wiphy) void wiphy_regulatory_deregister(struct wiphy *wiphy)
{ {
struct wiphy *request_wiphy = NULL; struct wiphy *request_wiphy = NULL;
...@@ -2299,6 +2393,9 @@ void reg_device_remove(struct wiphy *wiphy) ...@@ -2299,6 +2393,9 @@ void reg_device_remove(struct wiphy *wiphy)
mutex_lock(&reg_mutex); mutex_lock(&reg_mutex);
if (!reg_dev_ignore_cell_hint(wiphy))
reg_num_devs_support_basehint--;
kfree(wiphy->regd); kfree(wiphy->regd);
if (last_request) if (last_request)
...@@ -2364,7 +2461,8 @@ int __init regulatory_init(void) ...@@ -2364,7 +2461,8 @@ int __init regulatory_init(void)
* as a user hint. * as a user hint.
*/ */
if (!is_world_regdom(ieee80211_regdom)) if (!is_world_regdom(ieee80211_regdom))
regulatory_hint_user(ieee80211_regdom); regulatory_hint_user(ieee80211_regdom,
NL80211_USER_REG_HINT_USER);
return 0; return 0;
} }
......
...@@ -22,17 +22,19 @@ bool is_world_regdom(const char *alpha2); ...@@ -22,17 +22,19 @@ bool is_world_regdom(const char *alpha2);
bool reg_is_valid_request(const char *alpha2); bool reg_is_valid_request(const char *alpha2);
bool reg_supported_dfs_region(u8 dfs_region); bool reg_supported_dfs_region(u8 dfs_region);
int regulatory_hint_user(const char *alpha2); int regulatory_hint_user(const char *alpha2,
enum nl80211_user_reg_hint_type user_reg_hint_type);
int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env); int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env);
void reg_device_remove(struct wiphy *wiphy); void wiphy_regulatory_register(struct wiphy *wiphy);
void wiphy_regulatory_deregister(struct wiphy *wiphy);
int __init regulatory_init(void); int __init regulatory_init(void);
void regulatory_exit(void); void regulatory_exit(void);
int set_regdom(const struct ieee80211_regdomain *rd); int set_regdom(const struct ieee80211_regdomain *rd);
void regulatory_update(struct wiphy *wiphy, enum nl80211_reg_initiator setby); bool reg_last_request_cell_base(void);
/** /**
* regulatory_hint_found_beacon - hints a beacon was found on a channel * regulatory_hint_found_beacon - hints a beacon was found on a channel
......
...@@ -827,6 +827,8 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, ...@@ -827,6 +827,8 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
struct ieee80211_channel *chan;
enum nl80211_channel_type channel_type;
switch (wdev->iftype) { switch (wdev->iftype) {
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
...@@ -834,10 +836,13 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, ...@@ -834,10 +836,13 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
if (!rdev->monitor_channel) if (!rdev->ops->get_channel)
return -EINVAL; return -EINVAL;
freq->m = rdev->monitor_channel->center_freq; chan = rdev->ops->get_channel(wdev->wiphy, wdev, &channel_type);
if (!chan)
return -EINVAL;
freq->m = chan->center_freq;
freq->e = 6; freq->e = 6;
return 0; return 0;
default: default:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment