/* inform AP that we are awake again */ staticvoid ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
{ struct ieee80211_local *local = sdata->local;
list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue;
if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
sdata->vif.type == NL80211_IFTYPE_NAN) continue;
if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
/* Check to see if we should disable beaconing. */ if (sdata->vif.bss_conf.enable_beacon) {
set_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED,
&sdata->state);
sdata->vif.bss_conf.enable_beacon = false;
ieee80211_link_info_change_notify(
sdata, &sdata->deflink,
BSS_CHANGED_BEACON_ENABLED);
}
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
sdata->u.mgd.associated)
ieee80211_offchannel_ps_enable(sdata);
}
}
/* In case of HW ROC, it is possible that the HW finished the *ROCsessionbeforetheactualrequestedtime.Insuchacase *endtheROCsession(disregardingtheremainingtime).
*/ if (roc->abort || roc->hw_begun || remaining <= 0)
ieee80211_roc_notify_destroy(roc); else
remaining_dur_min = min(remaining_dur_min, remaining);
}
if (local->ops->remain_on_channel) {
_ieee80211_start_next_roc(local);
} else { /* delay it a bit */
wiphy_delayed_work_queue(local->hw.wiphy, &local->roc_work,
round_jiffies_relative(HZ / 2));
}
}
/* *Inthesoftwareimplementationcanjustcontinuewiththe *interruptionduetoreconfig,roc_workisstillqueuedif *needed.
*/ if (!local->ops->remain_on_channel) return;
/* flush work so nothing from the driver is still pending */
wiphy_work_flush(local->hw.wiphy, &local->hw_roc_start);
wiphy_work_flush(local->hw.wiphy, &local->hw_roc_done);
list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { if (!roc->started) break;
if (!roc->hw_begun) { /* it didn't start in HW yet, so we can restart it */
roc->started = false; continue;
}
/* otherwise destroy it and tell userspace */
ieee80211_roc_notify_destroy(roc);
}
/* if it was scheduled in the hardware, but not started yet, *wecanonlycombineiftheolderonehadalongerduration
*/ if (!cur_roc->hw_begun && new_roc->duration > cur_roc->duration) returnfalse;
/* if it doesn't fit entirely, schedule a new one */ if (new_roc->duration > jiffies_to_msecs(remaining)) returnfalse;
/* add just after the current one so we combine their finish later */
list_add(&new_roc->list, &cur_roc->list);
/* if the existing one has already begun then let this one also *begin,otherwisethey'llbothbemarkedproperlybythework *structthatrunsoncethedrivernotifiesusofthebeginning
*/ if (cur_roc->hw_begun) {
new_roc->hw_begun = true;
ieee80211_handle_roc_started(new_roc, now);
}
/* if there's no need to queue, handle it immediately */ if (list_empty(&local->roc_list) &&
!local->scanning && !ieee80211_is_radar_required(local, req)) { /* if not HW assist, just queue & schedule work */ if (!local->ops->remain_on_channel) {
list_add_tail(&roc->list, &local->roc_list);
wiphy_delayed_work_queue(local->hw.wiphy,
&local->roc_work, 0);
} else { /* otherwise actually kick it off here *(forerrorhandling)
*/
ret = drv_remain_on_channel(local, sdata, channel,
duration, type); if (ret) {
kfree(roc); return ret;
}
roc->started = true;
list_add_tail(&roc->list, &local->roc_list);
}
queued = ieee80211_coalesce_hw_started_roc(local, roc, tmp); if (queued) break; /* if it wasn't queued, perhaps it can be combined with *anotherthatalsocouldn'tgetcombinedpreviously, *butnoneedtocheckforalreadystartedones,since *thatcan'twork.
*/
combine_started = false;
}
if (!queued)
list_add_tail(&roc->list, &local->roc_list);
/* TODO: *ifmultipleitemswerecombinedherethenwereallyshouldn't *cancelthemall-weshouldwaitforasmuchtimeasneeded *forthelongestremainingone,andonlythencancel...
*/
list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { if (!roc->started) break; if (roc == found)
found = NULL;
ieee80211_roc_notify_destroy(roc);
}
/* that really must not happen - it was started */
WARN_ON(found);
ieee80211_start_next_roc(local);
} else { /* go through work struct to return to the operating channel */
found->abort = true;
wiphy_delayed_work_queue(local->hw.wiphy, &local->roc_work, 0);
}
if (!sta) {
rcu_read_unlock(); return -ENOLINK;
} if (params->link_id >= 0 &&
!(sta->sta.valid_links & BIT(params->link_id))) {
rcu_read_unlock(); return -ENOLINK;
}
link_id = params->link_id;
rcu_read_unlock(); break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: if (!sdata->u.mgd.associated ||
(params->offchan && params->wait &&
local->ops->remain_on_channel &&
memcmp(sdata->vif.cfg.ap_addr, mgmt->bssid, ETH_ALEN))) {
need_offchan = true;
} elseif (sdata->u.mgd.associated &&
ether_addr_equal(sdata->vif.cfg.ap_addr, mgmt->da)) {
sta = sta_info_get_bss(sdata, mgmt->da);
mlo_sta = sta && sta->sta.mlo;
} break; case NL80211_IFTYPE_P2P_DEVICE:
need_offchan = true; break; case NL80211_IFTYPE_NAN: default: return -EOPNOTSUPP;
}
/* configurations requiring offchan cannot work if no channel has been *specified
*/ if (need_offchan && !params->chan) return -EINVAL;
/* Check if the operating channel is the requested channel */ if (!params->chan && mlo_sta) {
need_offchan = false;
} elseif (!need_offchan) { struct ieee80211_chanctx_conf *chanctx_conf = NULL; int i;
rcu_read_lock(); /* Check all the links first */ for (i = 0; i < ARRAY_SIZE(sdata->vif.link_conf); i++) { struct ieee80211_bss_conf *conf;
conf = rcu_dereference(sdata->vif.link_conf[i]); if (!conf) continue;
chanctx_conf = rcu_dereference(conf->chanctx_conf); if (!chanctx_conf) continue;
if (ether_addr_equal(conf->addr, mgmt->sa)) { /* If userspace requested Tx on a specific link *usethesamelinkidifthelinkbssismatching *therequestedchan.
*/ if (sdata->vif.valid_links &&
params->link_id >= 0 && params->link_id == i &&
params->chan == chanctx_conf->def.chan)
link_id = i;
if (!params->dont_wait_for_ack) { /* make a copy to preserve the frame contents *incaseofencryption.
*/
ret = ieee80211_attach_ack_skb(local, skb, cookie, GFP_KERNEL); if (ret) {
kfree_skb(skb); goto out_unlock;
}
} else { /* Assign a dummy non-zero cookie, it's not sent to *userspaceinthiscasebutwerelyonitsvalue *internallyintheneed_offchancasetodistinguish *mgmt-txfromremain-on-channel.
*/
*cookie = 0xffffffff;
}
if (!need_offchan) {
ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
ret = 0; goto out_unlock;
}
/* This will handle all kinds of coalescing and immediate TX */
ret = ieee80211_start_roc_work(local, sdata, params->chan,
params->wait, cookie, skb,
IEEE80211_ROC_TYPE_MGMT_TX); if (ret)
ieee80211_free_txskb(&local->hw, skb);
out_unlock: return ret;
}
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.