if (ieee80211_is_data(fc)) { if (len < 24) /* drop incorrect hdr len (data) */ return NULL;
if (ieee80211_has_a4(fc)) return NULL; if (ieee80211_has_tods(fc)) return hdr->addr1; if (ieee80211_has_fromds(fc)) return hdr->addr2;
return hdr->addr3;
}
if (ieee80211_is_s1g_beacon(fc)) { struct ieee80211_ext *ext = (void *) hdr;
return ext->u.s1g_beacon.sa;
}
if (ieee80211_is_mgmt(fc)) { if (len < 24) /* drop incorrect hdr len (mgmt) */ return NULL; return hdr->addr3;
}
if (ieee80211_is_ctl(fc)) { if (ieee80211_is_pspoll(fc)) return hdr->addr1;
if (ieee80211_is_back_req(fc)) { switch (type) { case NL80211_IFTYPE_STATION: return hdr->addr2; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: return hdr->addr1; default: break; /* fall through to the return */
}
}
}
int ieee80211_frame_duration(enum nl80211_band band, size_t len, int rate, int erp, int short_preamble)
{ int dur;
/* calculate duration (in microseconds, rounded up to next higher * integer if it includes a fractional microsecond) to send frame of * len bytes (does not include FCS) at the given rate. Duration will * also include SIFS. * * rate is in 100 kbps, so divident is multiplied by 10 in the * DIV_ROUND_UP() operations.
*/
if (local->queue_stop_reasons[queue] != 0) /* someone still has this queue stopped */ return;
if (!skb_queue_empty(&local->pending[queue]))
tasklet_schedule(&local->tx_pending_tasklet);
/* * Calling _ieee80211_wake_txqs here can be a problem because it may * release queue_stop_reason_lock which has been taken by * __ieee80211_wake_queue's caller. It is certainly not very nice to * release someone's lock, but it is fine because all the callers of * __ieee80211_wake_queue call it right before releasing the lock.
*/ if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
tasklet_schedule(&local->wake_txqs_tasklet); else
_ieee80211_wake_txqs(local, flags);
}
for (i = 0; i < hw->queues; i++)
__ieee80211_wake_queue(hw, i,
IEEE80211_QUEUE_STOP_REASON_SKB_ADD, false, &flags);
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
}
/* * If no queue was set, or if the HW doesn't support * IEEE80211_HW_QUEUE_CONTROL - flush all queues
*/ if (!queues || !ieee80211_hw_check(&local->hw, QUEUE_CONTROL))
queues = ieee80211_get_vif_queues(local, sdata);
/* Purge the queues, so the frames on them won't be * sent during __ieee80211_wake_queue()
*/
list_for_each_entry(sta, &local->sta_list, list) { if (sdata != sta->sdata) continue;
ieee80211_purge_sta_txqs(sta);
}
}
if (local->ops->flush)
drv_flush(local, sdata, queues, drop);
/* * Nothing should have been stuffed into the workqueue during * the suspend->resume cycle. Since we can't check each caller * of this function if we are already quiescing / suspended, * check here and don't WARN since this can actually happen when * the rx path (for example) is racing against __ieee80211_suspend * and suspending / quiescing was set after the rx path checked * them.
*/ staticbool ieee80211_can_queue_work(struct ieee80211_local *local)
{ if (local->quiescing || (local->suspended && !local->resuming)) {
pr_warn("queueing ieee80211 work while going to suspend\n"); returnfalse;
}
/* insert custom IEs that go before VHT */ if (ie && ie_len) { staticconst u8 before_vht[] = { /* * no need to list the ones split off already * (or generated here)
*/
WLAN_EID_BSS_COEX_2040,
WLAN_EID_EXT_CAPABILITY,
WLAN_EID_SSID_LIST,
WLAN_EID_CHANNEL_USAGE,
WLAN_EID_INTERWORKING,
WLAN_EID_MESH_ID, /* 60 GHz (Multi-band, DMG, MMS) can't happen */
};
noffset = ieee80211_ie_split(ie, ie_len,
before_vht, ARRAY_SIZE(before_vht),
*offset); if (skb_tailroom(skb) < noffset - *offset) return -ENOBUFS;
skb_put_data(skb, ie + *offset, noffset - *offset);
*offset = noffset;
}
/* Check if any channel in this sband supports at least 80 MHz */ for (i = 0; i < sband->n_channels; i++) { if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
IEEE80211_CHAN_NO_80MHZ)) continue;
have_80mhz = true; break;
}
if (sband->vht_cap.vht_supported && have_80mhz) {
u8 *pos;
if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_cap)) return -ENOBUFS;
/* insert custom IEs that go before HE */ if (ie && ie_len) { staticconst u8 before_he[] = { /* * no need to list the ones split off before VHT * or generated here
*/
WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_REQ_PARAMS,
WLAN_EID_AP_CSN, /* TODO: add 11ah/11aj/11ak elements */
};
noffset = ieee80211_ie_split(ie, ie_len,
before_he, ARRAY_SIZE(before_he),
*offset); if (skb_tailroom(skb) < noffset - *offset) return -ENOBUFS;
skb_put_data(skb, ie + *offset, noffset - *offset);
*offset = noffset;
}
if (cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
IEEE80211_CHAN_NO_HE)) {
err = ieee80211_put_he_cap(skb, sdata, sband, NULL); if (err) return err;
}
if (cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
IEEE80211_CHAN_NO_HE |
IEEE80211_CHAN_NO_EHT)) {
err = ieee80211_put_eht_cap(skb, sdata, sband, NULL); if (err) return err;
}
err = ieee80211_put_he_6ghz_cap(skb, sdata, IEEE80211_SMPS_OFF); if (err) return err;
/* * If adding more here, adjust code in main.c * that calculates local->scan_ies_len.
*/
/* * Do not send DS Channel parameter for directed probe requests * in order to maximize the chance that we get a response. Some * badly-behaved APs don't respond when this parameter is included.
*/
chandef.width = sdata->vif.bss_conf.chanreq.oper.width; if (flags & IEEE80211_PROBE_FLAG_DIRECTED)
chandef.chan = NULL; else
chandef.chan = chan;
staticvoid ieee80211_flush_completed_scan(struct ieee80211_local *local, bool aborted)
{ /* It's possible that we don't handle the scan completion in * time during suspend, so if it's still marked as completed * here, queue the work and flush it to clean things up. * Instead of calling the worker function directly here, we * really queue it to avoid potential races with other flows * scheduling the same work.
*/ if (test_bit(SCAN_COMPLETED, &local->scanning)) { /* If coming from reconfiguration failure, abort the scan so * we don't attempt to continue a partial HW scan - which is * possible otherwise if (e.g.) the 2.4 GHz portion was the * completed scan, and a 5 GHz portion is still pending.
*/ if (aborted)
set_bit(SCAN_ABORTED, &local->scanning);
wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, 0);
wiphy_delayed_work_flush(local->hw.wiphy, &local->scan_work);
}
}
/* * We get here if during resume the device can't be restarted properly. * We might also get here if this happens during HW reset, which is a * slightly different situation and we need to drop all connections in * the latter case. * * Ask cfg80211 to turn off all interfaces, this will result in more * warnings but at least we'll then get into a clean stopped state.
*/
/* Mark channel contexts as not being in the driver any more to avoid * removing them from the driver during the shutdown process...
*/
list_for_each_entry(ctx, &local->chanctx_list, list)
ctx->driver_present = false;
}
/* Add all the functions: * This is a little bit ugly. We need to call a potentially sleeping * callback for each NAN function, so we can't hold the spinlock.
*/
spin_lock_bh(&sdata->u.nan.func_lock);
for (i = 0; funcs[i]; i++) {
res = drv_add_nan_func(sdata->local, sdata, funcs[i]); if (WARN_ON(res))
ieee80211_nan_func_terminated(&sdata->vif,
funcs[i]->instance_id,
NL80211_NAN_FUNC_TERM_REASON_ERROR,
GFP_KERNEL);
}
/* nothing to do if HW shouldn't run */ if (!local->open_count) goto wake_up;
#ifdef CONFIG_PM if (suspended)
local->resuming = true;
if (local->wowlan) { /* * In the wowlan case, both mac80211 and the device * are functional when the resume op is called, so * clear local->suspended so the device could operate * normally (e.g. pass rx frames).
*/
local->suspended = false;
res = drv_resume(local);
local->wowlan = false; if (res < 0) {
local->resuming = false; return res;
} if (res == 0) goto wake_up;
WARN_ON(res > 1); /* * res is 1, which means the driver requested * to go through a regular reset on wakeup. * restore local->suspended in this case.
*/
reconfig_due_to_wowlan = true;
local->suspended = true;
} #endif
/* * In case of hw_restart during suspend (without wowlan), * cancel restart work, as we are reconfiguring the device * anyway. * Note that restart_work is scheduled on a frozen workqueue, * so we can't deadlock in this case.
*/ if (suspended && local->in_reconfig && !reconfig_due_to_wowlan)
cancel_work_sync(&local->restart_work);
local->started = false;
/* * Upon resume hardware can sometimes be goofy due to * various platform / driver / bus issues, so restarting * the device may at times not work immediately. Propagate * the error.
*/
res = drv_start(local); if (res) { if (suspended)
WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n"); else
WARN(1, "Hardware became unavailable during restart.\n");
ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_SUSPEND, false);
ieee80211_handle_reconfig_failure(local); return res;
}
/* add interfaces */
sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata); if (sdata && ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) { /* in HW restart it exists already */
WARN_ON(local->resuming);
res = drv_add_interface(local, sdata); if (WARN_ON(res)) {
RCU_INIT_POINTER(local->monitor_sdata, NULL);
synchronize_net();
kfree(sdata);
}
}
list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) continue; if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
ieee80211_sdata_running(sdata)) {
res = drv_add_interface(local, sdata); if (WARN_ON(res)) break;
}
}
/* If adding any of the interfaces failed above, roll back and * report failure.
*/ if (res) {
list_for_each_entry_continue_reverse(sdata, &local->interfaces,
list) { if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) continue; if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
ieee80211_sdata_running(sdata))
drv_remove_interface(local, sdata);
}
ieee80211_handle_reconfig_failure(local); return res;
}
if (sdata->vif.type == NL80211_IFTYPE_STATION) { /* start with a single active link */
active_links = sdata->vif.active_links;
link_id = ffs(active_links) - 1;
sdata->vif.active_links = BIT(link_id);
}
for (link_id = 0;
link_id < ARRAY_SIZE(sdata->vif.link_conf);
link_id++) { if (!ieee80211_vif_link_active(&sdata->vif, link_id)) continue;
link = sdata_dereference(sdata->link[link_id], sdata); if (!link) continue;
ieee80211_assign_chanctx(local, sdata, link);
}
switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: break; case NL80211_IFTYPE_ADHOC: if (sdata->vif.cfg.ibss_joined)
WARN_ON(drv_join_ibss(local, sdata));
fallthrough; default:
ieee80211_reconfig_stations(sdata);
fallthrough; case NL80211_IFTYPE_AP: /* AP stations are handled later */ for (i = 0; i < IEEE80211_NUM_ACS; i++)
drv_conf_tx(local, &sdata->deflink, i,
&sdata->deflink.tx_conf[i]); break;
}
if (sdata->vif.bss_conf.mu_mimo_owner)
changed |= BSS_CHANGED_MU_GROUPS;
if (!ieee80211_vif_is_mld(&sdata->vif))
changed |= BSS_CHANGED_IDLE;
switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: if (!ieee80211_vif_is_mld(&sdata->vif)) {
changed |= BSS_CHANGED_ASSOC |
BSS_CHANGED_ARP_FILTER |
BSS_CHANGED_PS;
/* Re-send beacon info report to the driver */ if (sdata->deflink.u.mgd.have_beacon)
changed |= BSS_CHANGED_BEACON_INFO;
if (sdata->vif.bss_conf.max_idle_period ||
sdata->vif.bss_conf.protected_keep_alive)
changed |= BSS_CHANGED_KEEP_ALIVE;
if (ieee80211_vif_is_mld(&sdata->vif))
ieee80211_vif_cfg_change_notify(sdata,
BSS_CHANGED_SSID); else
changed |= BSS_CHANGED_SSID;
if (sdata->vif.bss_conf.ftm_responder == 1 &&
wiphy_ext_feature_isset(sdata->local->hw.wiphy,
NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER))
changed |= BSS_CHANGED_FTM_RESPONDER;
if (sdata->vif.type == NL80211_IFTYPE_AP) {
changed |= BSS_CHANGED_AP_PROBE_RESP;
if (ieee80211_vif_is_mld(&sdata->vif)) {
ieee80211_reconfig_ap_links(local,
sdata,
changed); break;
}
if (rcu_access_pointer(sdata->deflink.u.ap.beacon))
drv_start_ap(local, sdata,
sdata->deflink.conf);
}
fallthrough; case NL80211_IFTYPE_MESH_POINT: if (sdata->vif.bss_conf.enable_beacon) {
changed |= BSS_CHANGED_BEACON |
BSS_CHANGED_BEACON_ENABLED;
ieee80211_bss_info_change_notify(sdata, changed);
} break; case NL80211_IFTYPE_NAN:
res = ieee80211_reconfig_nan(sdata); if (res < 0) {
ieee80211_handle_reconfig_failure(local); return res;
} break; case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_P2P_DEVICE: /* nothing to do */ break; case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_WDS:
WARN_ON(1); break;
}
}
ieee80211_recalc_ps(local);
/* * The sta might be in psm against the ap (e.g. because * this was the state before a hw restart), so we * explicitly send a null packet in order to make sure * it'll sync against the ap (and get out of psm).
*/ if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) {
list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type != NL80211_IFTYPE_STATION) continue; if (!sdata->u.mgd.associated) continue;
ieee80211_send_nullfunc(local, sdata, false);
}
}
/* APs are now beaconing, add back stations */
list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue;
switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP:
ieee80211_reconfig_stations(sdata); break; default: break;
}
}
/* add back keys */
list_for_each_entry(sdata, &local->interfaces, list)
ieee80211_reenable_keys(sdata);
/* re-enable multi-link for client interfaces */
list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->restart_active_links)
ieee80211_set_active_links(&sdata->vif,
sdata->restart_active_links); /* * If a link switch was scheduled before the restart, and ran * before reconfig, it will do nothing, so re-schedule.
*/ if (sdata->desired_active_links)
wiphy_work_queue(sdata->local->hw.wiphy,
&sdata->activate_links_work);
}
/* Reconfigure sched scan if it was interrupted by FW restart */
sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
lockdep_is_held(&local->hw.wiphy->mtx));
sched_scan_req = rcu_dereference_protected(local->sched_scan_req,
lockdep_is_held(&local->hw.wiphy->mtx)); if (sched_scan_sdata && sched_scan_req) /* * Sched scan stopped, but we don't want to report it. Instead, * we're trying to reschedule. However, if more than one scan * plan was set, we cannot reschedule since we don't know which * scan plan was currently running (and some scan plans may have * already finished).
*/ if (sched_scan_req->n_scan_plans > 1 ||
__ieee80211_request_sched_scan_start(sched_scan_sdata,
sched_scan_req)) {
RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
RCU_INIT_POINTER(local->sched_scan_req, NULL);
sched_scan_stopped = true;
}
if (sched_scan_stopped)
cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0);
wake_up: /* * Clear the WLAN_STA_BLOCK_BA flag so new aggregation * sessions can be established after a resume. * * Also tear down aggregation sessions since reconfiguring * them in a hardware restart scenario is not easily done * right now, and the hardware will have lost information * about the sessions, but we and the AP still think they * are active. This is really a workaround though.
*/ if (ieee80211_hw_check(hw, AMPDU_AGGREGATION)) {
list_for_each_entry(sta, &local->sta_list, list) { if (!local->resuming)
ieee80211_sta_tear_down_BA_sessions(
sta, AGG_STOP_LOCAL_REQUEST);
clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
}
}
/* * If this is for hw restart things are still running. * We may want to change that later, however.
*/ if (local->open_count && (!suspended || reconfig_due_to_wowlan))
drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
if (local->in_reconfig) {
in_reconfig = local->in_reconfig;
local->in_reconfig = false;
barrier();
ieee80211_reconfig_roc(local);
/* Requeue all works */
list_for_each_entry(sdata, &local->interfaces, list) { if (ieee80211_sdata_running(sdata))
wiphy_work_queue(local->hw.wiphy, &sdata->work);
}
}
/* * This function can be called from a work, thus it may be possible * that the chanctx_conf is removed (due to a disconnection, for * example). * So nothing should be done in such case.
*/ if (!chanctx_conf) return;
void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata, int link_id)
{ struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx *chanctx; int i;
lockdep_assert_wiphy(local->hw.wiphy);
for (i = 0; i < ARRAY_SIZE(sdata->vif.link_conf); i++) { struct ieee80211_bss_conf *bss_conf;
if (link_id >= 0 && link_id != i) continue;
rcu_read_lock();
bss_conf = rcu_dereference(sdata->vif.link_conf[i]); if (!bss_conf) {
rcu_read_unlock(); continue;
}
chanctx_conf = rcu_dereference_protected(bss_conf->chanctx_conf,
lockdep_is_held(&local->hw.wiphy->mtx)); /* * Since we hold the wiphy mutex (checked above) * we can take the chanctx_conf pointer out of the * RCU critical section, it cannot go away without * the mutex. Just the way we reached it could - in * theory - go away, but we don't really care and * it really shouldn't happen anyway.
*/
rcu_read_unlock();
/* this may return more than ieee80211_put_he_6ghz_cap() will need */
u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata)
{ conststruct ieee80211_sta_he_cap *he_cap;
--> --------------------
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.