return 0;
}
-static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- u16 frame_type, bool reg)
+static void ath6kl_update_mgmt_frame_registrations(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct mgmt_frame_regs *upd)
{
struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
- ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
- __func__, frame_type, reg);
- if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
- /*
- * Note: This notification callback is not allowed to sleep, so
- * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
- * hardcode target to report Probe Request frames all the time.
- */
- vif->probe_req_report = reg;
- }
+ /*
+ * FIXME: send WMI_PROBE_REQ_REPORT_CMD here instead of hardcoding
+ * the reporting in the target all the time, this callback
+ * *is* allowed to sleep after all.
+ */
+ vif->probe_req_report =
+ upd->interface_stypes & BIT(IEEE80211_STYPE_PROBE_REQ >> 4);
}
static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
.remain_on_channel = ath6kl_remain_on_channel,
.cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
.mgmt_tx = ath6kl_mgmt_tx,
- .mgmt_frame_register = ath6kl_mgmt_frame_register,
+ .update_mgmt_frame_registrations =
+ ath6kl_update_mgmt_frame_registrations,
.get_antenna = ath6kl_get_antenna,
.sched_scan_start = ath6kl_cfg80211_sscan_start,
.sched_scan_stop = ath6kl_cfg80211_sscan_stop,
}
static void
-brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- u16 frame_type, bool reg)
+brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct mgmt_frame_regs *upd)
{
struct brcmf_cfg80211_vif *vif;
- u16 mgmt_type;
- brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
-
- mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
- if (reg)
- vif->mgmt_rx_reg |= BIT(mgmt_type);
- else
- vif->mgmt_rx_reg &= ~BIT(mgmt_type);
+
+ vif->mgmt_rx_reg = upd->interface_stypes;
}
.change_station = brcmf_cfg80211_change_station,
.sched_scan_start = brcmf_cfg80211_sched_scan_start,
.sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
- .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
+ .update_mgmt_frame_registrations =
+ brcmf_cfg80211_update_mgmt_frame_registrations,
.mgmt_tx = brcmf_cfg80211_mgmt_tx,
.remain_on_channel = brcmf_p2p_remain_on_channel,
.cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
* CFG802.11 operation handler to register a mgmt frame.
*/
static void
-mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- u16 frame_type, bool reg)
+mwifiex_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct mgmt_frame_regs *upd)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
- u32 mask;
-
- if (reg)
- mask = priv->mgmt_frame_mask | BIT(frame_type >> 4);
- else
- mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4);
+ u32 mask = upd->interface_stypes;
if (mask != priv->mgmt_frame_mask) {
priv->mgmt_frame_mask = mask;
.del_key = mwifiex_cfg80211_del_key,
.set_default_mgmt_key = mwifiex_cfg80211_set_default_mgmt_key,
.mgmt_tx = mwifiex_cfg80211_mgmt_tx,
- .mgmt_frame_register = mwifiex_cfg80211_mgmt_frame_register,
+ .update_mgmt_frame_registrations =
+ mwifiex_cfg80211_update_mgmt_frame_registrations,
.remain_on_channel = mwifiex_cfg80211_remain_on_channel,
.cancel_remain_on_channel = mwifiex_cfg80211_cancel_remain_on_channel,
.set_default_key = mwifiex_cfg80211_set_default_key,
}
static void
-qtnf_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev,
- u16 frame_type, bool reg)
+qtnf_update_mgmt_frame_registrations(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct mgmt_frame_regs *upd)
{
struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev);
- u16 mgmt_type;
- u16 new_mask;
- u16 qlink_frame_type = 0;
+ u16 new_mask = upd->interface_stypes;
+ u16 old_mask = vif->mgmt_frames_bitmask;
+ static const struct {
+ u16 mask, qlink_type;
+ } updates[] = {
+ {
+ .mask = BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_ASSOC_REQ >> 4),
+ .qlink_type = QLINK_MGMT_FRAME_ASSOC_REQ,
+ },
+ {
+ .mask = BIT(IEEE80211_STYPE_AUTH >> 4),
+ .qlink_type = QLINK_MGMT_FRAME_AUTH,
+ },
+ {
+ .mask = BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
+ .qlink_type = QLINK_MGMT_FRAME_PROBE_REQ,
+ },
+ {
+ .mask = BIT(IEEE80211_STYPE_ACTION >> 4),
+ .qlink_type = QLINK_MGMT_FRAME_ACTION,
+ },
+ };
+ unsigned int i;
- mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
+ if (new_mask == old_mask)
+ return;
- if (reg)
- new_mask = vif->mgmt_frames_bitmask | BIT(mgmt_type);
- else
- new_mask = vif->mgmt_frames_bitmask & ~BIT(mgmt_type);
+ for (i = 0; i < ARRAY_SIZE(updates); i++) {
+ u16 mask = updates[i].mask;
+ u16 qlink_frame_type = updates[i].qlink_type;
+ bool reg;
- if (new_mask == vif->mgmt_frames_bitmask)
- return;
+ /* the ! are here due to the assoc/reassoc merge */
+ if (!(new_mask & mask) == !(old_mask & mask))
+ continue;
- switch (frame_type & IEEE80211_FCTL_STYPE) {
- case IEEE80211_STYPE_REASSOC_REQ:
- case IEEE80211_STYPE_ASSOC_REQ:
- qlink_frame_type = QLINK_MGMT_FRAME_ASSOC_REQ;
- break;
- case IEEE80211_STYPE_AUTH:
- qlink_frame_type = QLINK_MGMT_FRAME_AUTH;
- break;
- case IEEE80211_STYPE_PROBE_REQ:
- qlink_frame_type = QLINK_MGMT_FRAME_PROBE_REQ;
- break;
- case IEEE80211_STYPE_ACTION:
- qlink_frame_type = QLINK_MGMT_FRAME_ACTION;
- break;
- default:
- pr_warn("VIF%u.%u: unsupported frame type: %X\n",
- vif->mac->macid, vif->vifid,
- (frame_type & IEEE80211_FCTL_STYPE) >> 4);
- return;
- }
+ reg = new_mask & mask;
- if (qtnf_cmd_send_register_mgmt(vif, qlink_frame_type, reg)) {
- pr_warn("VIF%u.%u: failed to %sregister mgmt frame type 0x%x\n",
- vif->mac->macid, vif->vifid, reg ? "" : "un",
- frame_type);
- return;
+ if (qtnf_cmd_send_register_mgmt(vif, qlink_frame_type, reg))
+ pr_warn("VIF%u.%u: failed to %sregister qlink frame type 0x%x\n",
+ vif->mac->macid, vif->vifid, reg ? "" : "un",
+ qlink_frame_type);
}
vif->mgmt_frames_bitmask = new_mask;
- pr_debug("VIF%u.%u: %sregistered mgmt frame type 0x%x\n",
- vif->mac->macid, vif->vifid, reg ? "" : "un", frame_type);
}
static int
.change_beacon = qtnf_change_beacon,
.stop_ap = qtnf_stop_ap,
.set_wiphy_params = qtnf_set_wiphy_params,
- .mgmt_frame_register = qtnf_mgmt_frame_register,
+ .update_mgmt_frame_registrations =
+ qtnf_update_mgmt_frame_registrations,
.mgmt_tx = qtnf_mgmt_tx,
.change_station = qtnf_change_station,
.del_station = qtnf_del_station,
};
/**
+ * struct mgmt_frame_regs - management frame registrations data
+ * @global_stypes: bitmap of management frame subtypes registered
+ * for the entire device
+ * @interface_stypes: bitmap of management frame subtypes registered
+ * for the given interface
+ */
+struct mgmt_frame_regs {
+ u32 global_stypes, interface_stypes;
+};
+
+/**
* struct cfg80211_ops - backend description for wireless configuration
*
* This struct is registered by fullmac card drivers and/or wireless stacks
* The driver should not call cfg80211_sched_scan_stopped() for a requested
* stop (when this method returns 0).
*
- * @mgmt_frame_register: Notify driver that a management frame type was
- * registered. The callback is allowed to sleep.
+ * @update_mgmt_frame_registrations: Notify the driver that management frame
+ * registrations were updated. The callback is allowed to sleep.
*
* @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device.
* Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may
struct net_device *dev,
u32 rate, u32 pkts, u32 intvl);
- void (*mgmt_frame_register)(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- u16 frame_type, bool reg);
+ void (*update_mgmt_frame_registrations)(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct mgmt_frame_regs *upd);
int (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant);
int (*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant);
* by cfg80211 on change_interface
* @mgmt_registrations: list of registrations for management frames
* @mgmt_registrations_lock: lock for the list
+ * @mgmt_registrations_update_wk: update work to defer from atomic context
* @mtx: mutex used to lock data in this struct, may be used by drivers
* and some API functions require it held
* @beacon_interval: beacon interval used on this device for transmitting
struct list_head mgmt_registrations;
spinlock_t mgmt_registrations_lock;
+ struct work_struct mgmt_registrations_update_wk;
struct mutex mtx;
struct dentry *debugfs_dir;
#endif
- unsigned int probe_req_reg;
+ bool probe_req_reg;
bool txqs_stopped[IEEE80211_NUM_ACS];
return 0;
}
-static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
+static void
+ieee80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
struct wireless_dev *wdev,
- u16 frame_type, bool reg)
+ struct mgmt_frame_regs *upd)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ u32 preq_mask = BIT(IEEE80211_STYPE_PROBE_REQ >> 4);
+ bool global_change, intf_change;
- switch (frame_type) {
- case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ:
- if (reg) {
- local->probe_req_reg++;
- sdata->vif.probe_req_reg++;
- } else {
- if (local->probe_req_reg)
- local->probe_req_reg--;
+ global_change =
+ local->probe_req_reg != !!(upd->global_stypes & preq_mask);
+ local->probe_req_reg = upd->global_stypes & preq_mask;
- if (sdata->vif.probe_req_reg)
- sdata->vif.probe_req_reg--;
- }
+ intf_change = sdata->vif.probe_req_reg !=
+ !!(upd->interface_stypes & preq_mask);
+ sdata->vif.probe_req_reg = upd->interface_stypes & preq_mask;
- if (!local->open_count)
- break;
+ if (!local->open_count)
+ return;
- if (ieee80211_sdata_running(sdata)) {
- if (sdata->vif.probe_req_reg == 1)
- drv_config_iface_filter(local, sdata,
- FIF_PROBE_REQ,
- FIF_PROBE_REQ);
- else if (sdata->vif.probe_req_reg == 0)
- drv_config_iface_filter(local, sdata, 0,
- FIF_PROBE_REQ);
- }
+ if (intf_change && ieee80211_sdata_running(sdata))
+ drv_config_iface_filter(local, sdata,
+ sdata->vif.probe_req_reg ?
+ FIF_PROBE_REQ : 0,
+ FIF_PROBE_REQ);
+ if (global_change)
ieee80211_configure_filter(local);
- break;
- default:
- break;
- }
}
static int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
.mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,
.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
.set_cqm_rssi_range_config = ieee80211_set_cqm_rssi_range_config,
- .mgmt_frame_register = ieee80211_mgmt_frame_register,
+ .update_mgmt_frame_registrations =
+ ieee80211_update_mgmt_frame_registrations,
.set_antenna = ieee80211_set_antenna,
.get_antenna = ieee80211_get_antenna,
.set_rekey_data = ieee80211_set_rekey_data,
/* number of interfaces with corresponding FIF_ flags */
int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll,
fif_probe_req;
- int probe_req_reg;
+ bool probe_req_reg;
unsigned int filter_flags; /* FIF_* */
bool wiphy_ciphers_allocated;
INIT_LIST_HEAD(&rdev->bss_list);
INIT_LIST_HEAD(&rdev->sched_scan_req_list);
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
- INIT_LIST_HEAD(&rdev->mlme_unreg);
- spin_lock_init(&rdev->mlme_unreg_lock);
- INIT_WORK(&rdev->mlme_unreg_wk, cfg80211_mlme_unreg_wk);
INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk,
cfg80211_dfs_channels_update_work);
#ifdef CONFIG_CFG80211_WEXT
cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
flush_work(&rdev->destroy_work);
flush_work(&rdev->sched_scan_stop_wk);
- flush_work(&rdev->mlme_unreg_wk);
flush_work(&rdev->propagate_radar_detect_wk);
flush_work(&rdev->propagate_cac_done_wk);
rdev->devlist_generation++;
cfg80211_mlme_purge_registrations(wdev);
+ flush_work(&wdev->mgmt_registrations_update_wk);
switch (wdev->iftype) {
case NL80211_IFTYPE_P2P_DEVICE:
spin_lock_init(&wdev->event_lock);
INIT_LIST_HEAD(&wdev->mgmt_registrations);
spin_lock_init(&wdev->mgmt_registrations_lock);
+ INIT_WORK(&wdev->mgmt_registrations_update_wk,
+ cfg80211_mgmt_registrations_update_wk);
INIT_LIST_HEAD(&wdev->pmsr_list);
spin_lock_init(&wdev->pmsr_lock);
INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
struct list_head beacon_registrations;
spinlock_t beacon_registrations_lock;
- struct list_head mlme_unreg;
- spinlock_t mlme_unreg_lock;
- struct work_struct mlme_unreg_wk;
-
/* protected by RTNL only */
int num_running_ifaces;
int num_running_monitor_ifaces;
int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
u16 frame_type, const u8 *match_data,
int match_len, struct netlink_ext_ack *extack);
-void cfg80211_mlme_unreg_wk(struct work_struct *wk);
+void cfg80211_mgmt_registrations_update_wk(struct work_struct *wk);
void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
u8 match[];
};
-static void
-cfg80211_process_mlme_unregistrations(struct cfg80211_registered_device *rdev)
+static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev)
{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct wireless_dev *tmp;
struct cfg80211_mgmt_registration *reg;
+ struct mgmt_frame_regs upd = {};
ASSERT_RTNL();
- spin_lock_bh(&rdev->mlme_unreg_lock);
- while ((reg = list_first_entry_or_null(&rdev->mlme_unreg,
- struct cfg80211_mgmt_registration,
- list))) {
- list_del(®->list);
- spin_unlock_bh(&rdev->mlme_unreg_lock);
-
- if (rdev->ops->mgmt_frame_register) {
- u16 frame_type = le16_to_cpu(reg->frame_type);
+ rcu_read_lock();
+ list_for_each_entry_rcu(tmp, &rdev->wiphy.wdev_list, list) {
+ list_for_each_entry_rcu(reg, &tmp->mgmt_registrations, list) {
+ u32 mask = BIT(le16_to_cpu(reg->frame_type) >> 4);
- rdev_mgmt_frame_register(rdev, reg->wdev,
- frame_type, false);
+ upd.global_stypes |= mask;
+ if (tmp == wdev)
+ upd.interface_stypes |= mask;
}
-
- kfree(reg);
-
- spin_lock_bh(&rdev->mlme_unreg_lock);
}
- spin_unlock_bh(&rdev->mlme_unreg_lock);
+ rcu_read_unlock();
+
+ rdev_update_mgmt_frame_registrations(rdev, wdev, &upd);
}
-void cfg80211_mlme_unreg_wk(struct work_struct *wk)
+void cfg80211_mgmt_registrations_update_wk(struct work_struct *wk)
{
- struct cfg80211_registered_device *rdev;
-
- rdev = container_of(wk, struct cfg80211_registered_device,
- mlme_unreg_wk);
+ struct wireless_dev *wdev = container_of(wk, struct wireless_dev,
+ mgmt_registrations_update_wk);
rtnl_lock();
- cfg80211_process_mlme_unregistrations(rdev);
+ cfg80211_mgmt_registrations_update(wdev);
rtnl_unlock();
}
u16 frame_type, const u8 *match_data,
int match_len, struct netlink_ext_ack *extack)
{
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
struct cfg80211_mgmt_registration *reg, *nreg;
int err = 0;
u16 mgmt_type;
}
}
- if (err) {
- kfree(nreg);
+ if (err)
goto out;
- }
memcpy(nreg->match, match_data, match_len);
nreg->match_len = match_len;
list_add(&nreg->list, &wdev->mgmt_registrations);
spin_unlock_bh(&wdev->mgmt_registrations_lock);
- /* process all unregistrations to avoid driver confusion */
- cfg80211_process_mlme_unregistrations(rdev);
-
- if (rdev->ops->mgmt_frame_register)
- rdev_mgmt_frame_register(rdev, wdev, frame_type, true);
+ cfg80211_mgmt_registrations_update(wdev);
return 0;
out:
+ kfree(nreg);
spin_unlock_bh(&wdev->mgmt_registrations_lock);
return err;
continue;
list_del(®->list);
- spin_lock(&rdev->mlme_unreg_lock);
- list_add_tail(®->list, &rdev->mlme_unreg);
- spin_unlock(&rdev->mlme_unreg_lock);
+ kfree(reg);
- schedule_work(&rdev->mlme_unreg_wk);
+ schedule_work(&wdev->mgmt_registrations_update_wk);
}
spin_unlock_bh(&wdev->mgmt_registrations_lock);
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
{
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_mgmt_registration *reg, *tmp;
spin_lock_bh(&wdev->mgmt_registrations_lock);
- spin_lock(&rdev->mlme_unreg_lock);
- list_splice_tail_init(&wdev->mgmt_registrations, &rdev->mlme_unreg);
- spin_unlock(&rdev->mlme_unreg_lock);
+ list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
+ list_del(®->list);
+ kfree(reg);
+ }
spin_unlock_bh(&wdev->mgmt_registrations_lock);
- cfg80211_process_mlme_unregistrations(rdev);
+ cfg80211_mgmt_registrations_update(wdev);
}
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
}
static inline void
-rdev_mgmt_frame_register(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev, u16 frame_type, bool reg)
+rdev_update_mgmt_frame_registrations(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct mgmt_frame_regs *upd)
{
might_sleep();
- trace_rdev_mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
- rdev->ops->mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
+ trace_rdev_update_mgmt_frame_registrations(&rdev->wiphy, wdev, upd);
+ if (rdev->ops->update_mgmt_frame_registrations)
+ rdev->ops->update_mgmt_frame_registrations(&rdev->wiphy, wdev,
+ upd);
trace_rdev_return_void(&rdev->wiphy);
}
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer))
);
-TRACE_EVENT(rdev_mgmt_frame_register,
+TRACE_EVENT(rdev_update_mgmt_frame_registrations,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
- u16 frame_type, bool reg),
- TP_ARGS(wiphy, wdev, frame_type, reg),
+ struct mgmt_frame_regs *upd),
+ TP_ARGS(wiphy, wdev, upd),
TP_STRUCT__entry(
WIPHY_ENTRY
WDEV_ENTRY
- __field(u16, frame_type)
- __field(bool, reg)
+ __field(u16, global_stypes)
+ __field(u16, interface_stypes)
),
TP_fast_assign(
WIPHY_ASSIGN;
WDEV_ASSIGN;
- __entry->frame_type = frame_type;
- __entry->reg = reg;
+ __entry->global_stypes = upd->global_stypes;
+ __entry->interface_stypes = upd->interface_stypes;
),
- TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", frame_type: 0x%.2x, reg: %s ",
- WIPHY_PR_ARG, WDEV_PR_ARG, __entry->frame_type,
- __entry->reg ? "true" : "false")
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", global: 0x%.2x, intf: 0x%.2x",
+ WIPHY_PR_ARG, WDEV_PR_ARG,
+ __entry->global_stypes, __entry->interface_stypes)
);
TRACE_EVENT(rdev_return_int_tx_rx,