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; struct ieee80211_supported_band *sband;
u8 n;
sband = ieee80211_get_sband(sdata); if (!sband) return 0;
he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); if (!he_cap) return 0;
n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem); return 2 + 1 + sizeof(he_cap->he_cap_elem) + n +
ieee80211_he_ppe_size(he_cap->ppe_thres[0],
he_cap->he_cap_elem.phy_cap_info);
}
if (!conn)
conn = &ieee80211_conn_settings_unlimited;
he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); if (!he_cap) return 0;
/* modify on stack first to calculate 'n' and 'ie_len' correctly */
ieee80211_get_adjusted_he_cap(conn, he_cap, &elem);
n = ieee80211_he_mcs_nss_size(&elem);
ie_len = 2 + 1 + sizeof(he_cap->he_cap_elem) + n +
ieee80211_he_ppe_size(he_cap->ppe_thres[0],
he_cap->he_cap_elem.phy_cap_info);
if (skb_tailroom(skb) < ie_len) return -ENOBUFS;
skb_put_u8(skb, WLAN_EID_EXTENSION);
len = skb_put(skb, 1); /* We'll set the size later below */
skb_put_u8(skb, WLAN_EID_EXT_HE_CAPABILITY);
/* Fixed data */
skb_put_data(skb, &elem, sizeof(elem));
skb_put_data(skb, &he_cap->he_mcs_nss_supp, n);
/* Check if PPE Threshold should be present */ if ((he_cap->he_cap_elem.phy_cap_info[6] &
IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) == 0) goto end;
/* * Calculate how many PPET16/PPET8 pairs are to come. Algorithm: * (NSS_M1 + 1) x (num of 1 bits in RU_INDEX_BITMASK)
*/
n = hweight8(he_cap->ppe_thres[0] &
IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK);
n *= (1 + ((he_cap->ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) >>
IEEE80211_PPE_THRES_NSS_POS));
/* * Each pair is 6 bits, and we need to add the 7 "header" bits to the * total size.
*/
n = (n * IEEE80211_PPE_THRES_INFO_PPET_SIZE * 2) + 7;
n = DIV_ROUND_UP(n, 8);
/* Copy PPE Thresholds */
skb_put_data(skb, &he_cap->ppe_thres, n);
iftd = ieee80211_get_sband_iftype_data(sband, iftype); if (!iftd) return 0;
/* Check for device HE 6 GHz capability before adding element */ if (!iftd->he_6ghz_capa.capa) return 0;
cap = iftd->he_6ghz_capa.capa;
cap &= cpu_to_le16(~IEEE80211_HE_6GHZ_CAP_SM_PS);
switch (smps_mode) { case IEEE80211_SMPS_AUTOMATIC: case IEEE80211_SMPS_NUM_MODES:
WARN_ON(1);
fallthrough; case IEEE80211_SMPS_OFF:
cap |= le16_encode_bits(WLAN_HT_CAP_SM_PS_DISABLED,
IEEE80211_HE_6GHZ_CAP_SM_PS); break; case IEEE80211_SMPS_STATIC:
cap |= le16_encode_bits(WLAN_HT_CAP_SM_PS_STATIC,
IEEE80211_HE_6GHZ_CAP_SM_PS); break; case IEEE80211_SMPS_DYNAMIC:
cap |= le16_encode_bits(WLAN_HT_CAP_SM_PS_DYNAMIC,
IEEE80211_HE_6GHZ_CAP_SM_PS); break;
}
if (skb_tailroom(skb) < 2 + 1 + sizeof(cap)) return -ENOBUFS;
/* It seems that Basic MCS set and Supported MCS set
are identical for the first 10 bytes */
memset(&ht_oper->basic_set, 0, 16);
memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10);
void ieee80211_ie_build_wide_bw_cs(u8 *pos, conststruct cfg80211_chan_def *chandef)
{
*pos++ = WLAN_EID_WIDE_BW_CHANNEL_SWITCH; /* EID */
*pos++ = 3; /* IE length */ /* New channel width */ switch (chandef->width) { case NL80211_CHAN_WIDTH_80:
*pos++ = IEEE80211_VHT_CHANWIDTH_80MHZ; break; case NL80211_CHAN_WIDTH_160:
*pos++ = IEEE80211_VHT_CHANWIDTH_160MHZ; break; case NL80211_CHAN_WIDTH_80P80:
*pos++ = IEEE80211_VHT_CHANWIDTH_80P80MHZ; break; case NL80211_CHAN_WIDTH_320: /* The behavior is not defined for 320 MHz channels */
WARN_ON(1);
fallthrough; default:
*pos++ = IEEE80211_VHT_CHANWIDTH_USE_HT;
}
/* new center frequency segment 0 */
*pos++ = ieee80211_frequency_to_channel(chandef->center_freq1); /* new center frequency segment 1 */ if (chandef->center_freq2)
*pos++ = ieee80211_frequency_to_channel(chandef->center_freq2); else
*pos++ = 0;
}
switch (chandef->width) { case NL80211_CHAN_WIDTH_160: /* * Convert 160 MHz channel width to new style as interop * workaround.
*/
vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
vht_oper->center_freq_seg1_idx = vht_oper->center_freq_seg0_idx; if (chandef->chan->center_freq < chandef->center_freq1)
vht_oper->center_freq_seg0_idx -= 8; else
vht_oper->center_freq_seg0_idx += 8; break; case NL80211_CHAN_WIDTH_80P80: /* * Convert 80+80 MHz channel width to new style as interop * workaround.
*/
vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; break; case NL80211_CHAN_WIDTH_80:
vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; break; case NL80211_CHAN_WIDTH_320: /* VHT information element should not be included on 6GHz */
WARN_ON(1); return pos; default:
vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; break;
}
/* if not supported, parse as though we didn't understand it */ if (!ieee80211_hw_check(hw, SUPPORTS_VHT_EXT_NSS_BW))
ext_nss_bw_supp = 0;
/* * Cf. IEEE 802.11 Table 9-250 * * We really just consider that because it's inefficient to connect * at a higher bandwidth than we'll actually be able to use.
*/ switch ((supp_chwidth << 4) | ext_nss_bw_supp) { default: case 0x00:
ccf1 = 0;
support_160 = false;
support_80_80 = false; break; case 0x01:
support_80_80 = false;
fallthrough; case 0x02: case 0x03:
ccf1 = ccfs2; break; case 0x10:
ccf1 = ccfs1; break; case 0x11: case 0x12: if (!ccfs1)
ccf1 = ccfs2; else
ccf1 = ccfs1; break; case 0x13: case 0x20: case 0x23:
ccf1 = ccfs1; break;
}
switch (oper->chan_width) { case IEEE80211_VHT_CHANWIDTH_USE_HT: /* just use HT information directly */ break; case IEEE80211_VHT_CHANWIDTH_80MHZ: new.width = NL80211_CHAN_WIDTH_80; new.center_freq1 = cf0; /* If needed, adjust based on the newer interop workaround. */ if (ccf1) { unsignedint diff;
if (chandef->chan->band != NL80211_BAND_6GHZ) returntrue;
if (!he_oper) returnfalse;
he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper); if (!he_6ghz_oper) returnfalse;
/* * The EHT operation IE does not contain the primary channel so the * primary channel frequency should be taken from the 6 GHz operation * information.
*/
freq = ieee80211_channel_to_frequency(he_6ghz_oper->primary,
NL80211_BAND_6GHZ);
he_chandef.chan = ieee80211_get_channel(local->hw.wiphy, freq);
if (!he_chandef.chan) returnfalse;
if (!eht_oper ||
!(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) { switch (u8_get_bits(he_6ghz_oper->control,
IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH)) { case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ:
he_chandef.width = NL80211_CHAN_WIDTH_20; break; case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ:
he_chandef.width = NL80211_CHAN_WIDTH_40; break; case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ:
he_chandef.width = NL80211_CHAN_WIDTH_80; break; case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ:
he_chandef.width = NL80211_CHAN_WIDTH_80; if (!he_6ghz_oper->ccfs1) break; if (abs(he_6ghz_oper->ccfs1 - he_6ghz_oper->ccfs0) == 8)
he_chandef.width = NL80211_CHAN_WIDTH_160; else
he_chandef.width = NL80211_CHAN_WIDTH_80P80; break;
}
u8 ieee80211_mcs_to_chains(conststruct ieee80211_mcs_info *mcs)
{ if (!mcs) return 1;
/* TODO: consider rx_highest */
if (mcs->rx_mask[3]) return 4; if (mcs->rx_mask[2]) return 3; if (mcs->rx_mask[1]) return 2; return 1;
}
/** * ieee80211_calculate_rx_timestamp - calculate timestamp in frame * @local: mac80211 hw info struct * @status: RX status * @mpdu_len: total MPDU length (including FCS) * @mpdu_offset: offset into MPDU to calculate timestamp at * * This function calculates the RX timestamp at the given MPDU offset, taking * into account what the RX timestamp was. An offset of 0 will just normalize * the timestamp to TSF at beginning of MPDU reception. * * Returns: the calculated timestamp
*/
u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, struct ieee80211_rx_status *status, unsignedint mpdu_len, unsignedint mpdu_offset)
{
u64 ts = status->mactime; bool mactime_plcp_start; struct rate_info ri;
u16 rate;
u8 n_ltf;
if (WARN_ON(!ieee80211_have_rx_timestamp(status))) return 0;
/* Fill cfg80211 rate info */ switch (status->encoding) { case RX_ENC_EHT:
ri.flags |= RATE_INFO_FLAGS_EHT_MCS;
ri.mcs = status->rate_idx;
ri.nss = status->nss;
ri.eht_ru_alloc = status->eht.ru; if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
ri.flags |= RATE_INFO_FLAGS_SHORT_GI; /* TODO/FIXME: is this right? handle other PPDUs */ if (mactime_plcp_start) {
mpdu_offset += 2;
ts += 36;
} break; case RX_ENC_HE:
ri.flags |= RATE_INFO_FLAGS_HE_MCS;
ri.mcs = status->rate_idx;
ri.nss = status->nss;
ri.he_ru_alloc = status->he_ru; if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
/* * See P802.11ax_D6.0, section 27.3.4 for * VHT PPDU format.
*/ if (mactime_plcp_start) {
mpdu_offset += 2;
ts += 36;
/* * TODO: * For HE MU PPDU, add the HE-SIG-B. * For HE ER PPDU, add 8us for the HE-SIG-A. * For HE TB PPDU, add 4us for the HE-STF. * Add the HE-LTF durations - variable.
*/
}
break; case RX_ENC_HT:
ri.mcs = status->rate_idx;
ri.flags |= RATE_INFO_FLAGS_MCS; if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
/* * See P802.11REVmd_D3.0, section 19.3.2 for * HT PPDU format.
*/ if (mactime_plcp_start) {
mpdu_offset += 2; if (status->enc_flags & RX_ENC_FLAG_HT_GF)
ts += 24; else
ts += 32;
/* rewind from end of MPDU */ if ((status->flag & RX_FLAG_MACTIME) == RX_FLAG_MACTIME_END)
ts -= mpdu_len * 8 * 10 / rate;
ts += mpdu_offset * 8 * 10 / rate;
return ts;
}
/* Cancel CAC for the interfaces under the specified @local. If @ctx is * also provided, only the interfaces using that ctx will be canceled.
*/ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local, struct ieee80211_chanctx *ctx)
{ struct ieee80211_sub_if_data *sdata; struct cfg80211_chan_def chandef; struct ieee80211_link_data *link; struct ieee80211_chanctx_conf *chanctx_conf; unsignedint link_id;
lockdep_assert_wiphy(local->hw.wiphy);
list_for_each_entry(sdata, &local->interfaces, list) { for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS;
link_id++) {
link = sdata_dereference(sdata->link[link_id],
sdata); if (!link) continue;
/* * With an 80 MHz channel, we might have the puncturing in the primary * 40 Mhz channel, but that's not valid when downgraded to 40 MHz width. * In that case, downgrade again.
*/ if (!cfg80211_chandef_valid(c) && c->punctured) goto again;
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
}
int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, struct cfg80211_csa_settings *csa_settings)
{ struct sk_buff *skb; struct ieee80211_mgmt *mgmt; struct ieee80211_local *local = sdata->local; int freq; int hdr_len = offsetofend(struct ieee80211_mgmt,
u.action.u.chan_switch);
u8 *pos;
if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
sdata->vif.type != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP;
staticbool
ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i)
{
s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1); int skip;
if (end > 0) returnfalse;
/* One shot NOA */ if (data->count[i] == 1) returnfalse;
if (data->desc[i].interval == 0) returnfalse;
/* End time is in the past, check for repetitions */
skip = DIV_ROUND_UP(-end, data->desc[i].interval); if (data->count[i] < 255) { if (data->count[i] <= skip) {
data->count[i] = 0; returnfalse;
}
staticbool
ieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf,
s32 *offset)
{ bool ret = false; int i;
for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
s32 cur;
if (!data->count[i]) continue;
if (ieee80211_extend_noa_desc(data, tsf + *offset, i))
ret = true;
cur = data->desc[i].start - tsf; if (cur > *offset) continue;
cur = data->desc[i].start + data->desc[i].duration - tsf; if (cur > *offset)
*offset = cur;
}
return ret;
}
static u32
ieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf)
{
s32 offset = 0; int tries = 0; /* * arbitrary limit, used to avoid infinite loops when combined NoA * descriptors cover the full time period.
*/ int max_tries = 5;
ieee80211_extend_absent_time(data, tsf, &offset); do { if (!ieee80211_extend_absent_time(data, tsf, &offset)) break;
/* * actually finds last dtim_count, mac80211 will update in * __beacon_add_tim(). * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period
*/
do_div(tsf, beacon_int);
bcns_from_dtim = do_div(tsf, dtim_period); /* just had a DTIM */ if (!bcns_from_dtim)
dtim_count = 0; else
dtim_count = dtim_period - bcns_from_dtim;
ps->dtim_count = dtim_count;
}
/* * Given a long beacon period, calculate the current index into * that period to determine the number of TSBTTs until the next TBTT. * It is completely valid to have a short beacon period that differs * from the dtim period (i.e a TBTT thats not a DTIM).
*/ void ieee80211_recalc_sb_count(struct ieee80211_sub_if_data *sdata, u64 tsf)
{
u32 sb_idx; struct ps_data *ps = &sdata->bss->ps;
u8 lb_period = sdata->vif.bss_conf.s1g_long_beacon_period;
u32 beacon_int = sdata->vif.bss_conf.beacon_int * 1024;
/* No mesh / IBSS support for short beaconing */ if (tsf == -1ULL || !lb_period ||
(sdata->vif.type != NL80211_IFTYPE_AP &&
sdata->vif.type != NL80211_IFTYPE_AP_VLAN)) return;
/* find the current TSBTT index in our lb_period */
do_div(tsf, beacon_int);
sb_idx = do_div(tsf, lb_period);
/* num TSBTTs until the next TBTT */
ps->sb_count = sb_idx ? lb_period - sb_idx : 0;
}
if (WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)) return 0;
list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list) if (link->reserved_radar_required)
radar_detect |= BIT(link->reserved.oper.width);
/* * An in-place reservation context should not have any assigned vifs * until it replaces the other context.
*/
WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER &&
!list_empty(&ctx->assigned_links));
list_for_each_entry(link, &ctx->assigned_links, assigned_chanctx_list) { if (!link->radar_required) continue;
bool ieee80211_is_radio_idx_in_scan_req(struct wiphy *wiphy, struct cfg80211_scan_request *scan_req, int radio_idx)
{ struct ieee80211_channel *chan; int i, chan_radio_idx;
for (i = 0; i < scan_req->n_channels; i++) {
chan = scan_req->channels[i];
chan_radio_idx = cfg80211_get_radio_idx_by_chan(wiphy, chan); /* * The chan_radio_idx should be valid since it's taken from a * valid scan request. * However, if chan_radio_idx is unexpectedly invalid (negative), * we take a conservative approach and assume the scan request * might use the specified radio_idx. Hence, return true.
*/ if (WARN_ON(chan_radio_idx < 0)) returntrue;
if (WARN_ON(hweight32(radar_detect) > 1)) return -EINVAL;
if (WARN_ON(chandef && chanmode == IEEE80211_CHANCTX_SHARED &&
!chandef->chan)) return -EINVAL;
if (WARN_ON(iftype >= NUM_NL80211_IFTYPES)) return -EINVAL;
if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_MESH_POINT) { /* * always passing this is harmless, since it'll be the * same value that cfg80211 finds if it finds the same * interface ... and that's always allowed
*/
params.new_beacon_int = sdata->vif.bss_conf.beacon_int;
}
/* Always allow software iftypes */ if (cfg80211_iftype_allowed(local->hw.wiphy, iftype, 0, 1)) { if (radar_detect) return -EINVAL; return 0;
}
if (chandef)
params.num_different_channels = 1;
if (iftype != NL80211_IFTYPE_UNSPECIFIED)
params.iftype_num[iftype] = 1;
total = ieee80211_fill_ifcomb_params(local, ¶ms,
shared ? chandef : NULL,
sdata); if (total == 1 && !params.radar_detect) return 0;
/* error if there is a remainder. Should've been checked by user */
WARN_ON_ONCE(ui > IEEE80211_MAX_UI);
listen_interval = FIELD_PREP(LISTEN_INT_USF, usf) |
FIELD_PREP(LISTEN_INT_UI, ui);
return (u16) listen_interval;
}
/* this may return more than ieee80211_put_eht_cap() will need */
u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata)
{ conststruct ieee80211_sta_he_cap *he_cap; conststruct ieee80211_sta_eht_cap *eht_cap; struct ieee80211_supported_band *sband; bool is_ap;
u8 n;
sband = ieee80211_get_sband(sdata); if (!sband) return 0;
if (mcs_nss_len == 4 && orig_mcs_nss_len != 4) { /* * If the (non-AP) STA became 20 MHz only, then convert from * <=80 to 20-MHz-only format, where MCSes are indicated in * the groups 0-7, 8-9, 10-11, 12-13 rather than just 0-9, * 10-11, 12-13. Thus, use 0-9 for 0-7 and 8-9.
*/
skb_put_u8(skb, eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs9_max_nss);
skb_put_u8(skb, eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs9_max_nss);
skb_put_u8(skb, eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs11_max_nss);
skb_put_u8(skb, eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_mcs13_max_nss);
} else {
skb_put_data(skb, &eht_cap->eht_mcs_nss_supp, mcs_nss_len);
}
if (ppet_len)
skb_put_data(skb, &eht_cap->eht_ppe_thres, ppet_len);
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.80Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-26)
¤
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.