/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2017 Intel Deutschland GmbH * Copyright (C) 2018 - 2025 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/** * DOC: Wireless regulatory infrastructure * * The usual implementation is for a driver to read a device EEPROM to * determine which regulatory domain it should be operating under, then * looking up the allowable channels in a driver-local table and finally * registering those channels in the wiphy structure. * * Another set of compliance enforcement is for drivers to use their * own compliance limits which can be stored on the EEPROM. The host * driver or firmware may ensure these are used. * * In addition to all this we provide an extra layer of regulatory * conformance. For drivers which do not have any regulatory * information CRDA provides the complete regulatory solution. * For others it provides a community effort on further restrictions * to enhance compliance. * * Note: When number of rules --> infinity we will not be able to * index on alpha2 any more, instead we'll probably have to * rely on some SHA1 checksum of the regdomain for example. *
*/
/* * Grace period we give before making sure all current interfaces reside on * channels allowed by the current regulatory domain.
*/ #define REG_ENFORCE_GRACE_MS 60000
/** * enum reg_request_treatment - regulatory request treatment * * @REG_REQ_OK: continue processing the regulatory request * @REG_REQ_IGNORE: ignore the regulatory request * @REG_REQ_INTERSECT: the regulatory domain resulting from this request should * be intersected with the current one. * @REG_REQ_ALREADY_SET: the regulatory request will not change the current * regulatory settings, and no further processing is required.
*/ enum reg_request_treatment {
REG_REQ_OK,
REG_REQ_IGNORE,
REG_REQ_INTERSECT,
REG_REQ_ALREADY_SET,
};
/* * Receipt of information from last regulatory request, * protected by RTNL (and can be accessed with RCU protection)
*/ staticstruct regulatory_request __rcu *last_request =
(void __force __rcu *)&core_request_world;
/* To trigger userspace events and load firmware */ staticstruct faux_device *reg_fdev;
/* * Central wireless core regulatory domains, we only need two, * the current one and a world regulatory domain in case we have no * information to give us an alpha2. * (protected by RTNL, can be read under RCU)
*/ conststruct ieee80211_regdomain __rcu *cfg80211_regdomain;
/* * Number of devices that registered to the core * that support cellular base station regulatory hints * (protected by RTNL)
*/ staticint reg_num_devs_support_basehint;
/* * State variable indicating if the platform on which the devices * are attached is operating in an indoor environment. The state variable * is relevant for all registered devices.
*/ staticbool reg_is_indoor; static DEFINE_SPINLOCK(reg_indoor_lock);
/* Used to track the userspace process controlling the indoor setting */ static u32 reg_is_indoor_portid;
if (wiphy_regd->dfs_region == regd->dfs_region) goto out;
pr_debug("%s: device specific dfs_region (%s) disagrees with cfg80211's central dfs_region (%s)\n",
dev_name(&wiphy->dev),
reg_dfs_region_str(wiphy_regd->dfs_region),
reg_dfs_region_str(regd->dfs_region));
/* avoid freeing static information or freeing something twice */ if (r == cfg80211_world_regdom)
r = NULL; if (cfg80211_world_regdom == &world_regdom)
cfg80211_world_regdom = NULL; if (r == &world_regdom)
r = NULL;
staticbool is_unknown_alpha2(constchar *alpha2)
{ if (!alpha2) returnfalse; /* * Special case where regulatory domain was built by driver * but a specific alpha2 cannot be determined
*/ return alpha2[0] == '9' && alpha2[1] == '9';
}
staticbool is_intersected_alpha2(constchar *alpha2)
{ if (!alpha2) returnfalse; /* * Special case where regulatory domain is the * result of an intersection between two regulatory domain * structures
*/ return alpha2[0] == '9' && alpha2[1] == '8';
}
if (!r) returntrue; return !alpha2_equal(r->alpha2, alpha2);
}
/* * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER * has ever been issued.
*/ staticbool is_user_regdom_saved(void)
{ if (user_alpha2[0] == '9' && user_alpha2[1] == '7') returnfalse;
/* This would indicate a mistake on the design */ if (WARN(!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2), "Unexpected user alpha2: %c%c\n",
user_alpha2[0], user_alpha2[1])) returnfalse;
/* * This lets us keep regulatory code which is updated on a regulatory * basis in userspace.
*/ staticint call_crda(constchar *alpha2)
{ char country[12]; char *env[] = { country, NULL }; int ret;
if (reg_crda_timeouts > REG_MAX_CRDA_TIMEOUTS) {
pr_debug("Exceeded CRDA call max attempts. Not calling CRDA\n"); return -EINVAL;
}
if (!is_world_regdom((char *) alpha2))
pr_debug("Calling CRDA for country: %c%c\n",
alpha2[0], alpha2[1]); else
pr_debug("Calling CRDA to update world regulatory domain\n");
ret = kobject_uevent_env(®_fdev->dev.kobj, KOBJ_CHANGE, env); if (ret) return ret;
/* code to directly load a firmware database through request_firmware */ staticconststruct fwdb_header *regdb;
struct fwdb_country {
u8 alpha2[2];
__be16 coll_ptr; /* this struct cannot be extended */
} __packed __aligned(4);
struct fwdb_collection {
u8 len;
u8 n_rules;
u8 dfs_region; /* no optional data yet */ /* aligned to 2, then followed by __be16 array of rule pointers */
} __packed __aligned(4);
if (hdr->magic != cpu_to_be32(FWDB_MAGIC)) returnfalse;
if (hdr->version != cpu_to_be32(FWDB_VERSION)) returnfalse;
if (!regdb_has_valid_signature(data, size)) returnfalse;
country = &hdr->country[0]; while ((u8 *)(country + 1) <= data + size) { if (!country->coll_ptr) break; if (!valid_country(data, size, country)) returnfalse;
country++;
}
int reg_query_regdb_wmm(char *alpha2, int freq, struct ieee80211_reg_rule *rule)
{ conststruct fwdb_header *hdr = regdb; conststruct fwdb_country *country;
if (!regdb) return -ENODATA;
if (IS_ERR(regdb)) return PTR_ERR(regdb);
country = &hdr->country[0]; while (country->coll_ptr) { if (alpha2_equal(alpha2, country->alpha2)) return __regdb_query_wmm(regdb, country, freq, rule);
country = &hdr->country[0]; while (country->coll_ptr) { if (alpha2_equal(alpha2, country->alpha2)) return regdb_query_country(regdb, country);
country++;
}
if (!fw) {
pr_info("failed to load regulatory.db\n");
set_error = -ENODATA;
} elseif (!valid_regdb(fw->data, fw->size)) {
pr_info("loaded regulatory.db is malformed or signature is missing/invalid\n");
set_error = -EINVAL;
}
rtnl_lock(); if (regdb && !IS_ERR(regdb)) { /* negative case - a bug * positive case - can happen due to race in case of multiple cb's in * queue, due to usage of asynchronous callback * * Either case, just restore and free new db.
*/
} elseif (set_error) {
regdb = ERR_PTR(set_error);
} elseif (fw) {
db = kmemdup(fw->data, fw->size, GFP_KERNEL); if (db) {
regdb = db;
restore = context && query_regdb(context);
} else {
restore = true;
}
}
if (restore)
restore_regulatory_settings(true, false);
rtnl_unlock();
kfree(context);
release_firmware(fw);
}
MODULE_FIRMWARE("regulatory.db");
staticint query_regdb_file(constchar *alpha2)
{ int err;
ASSERT_RTNL();
if (regdb) return query_regdb(alpha2);
alpha2 = kmemdup(alpha2, 2, GFP_KERNEL); if (!alpha2) return -ENOMEM;
/* * Follow the driver's regulatory domain, if present, unless a country * IE has been processed or a user wants to help compliance further
*/ if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
lr->initiator != NL80211_REGDOM_SET_BY_USER &&
wiphy->regd) return get_wiphy_regdom(wiphy);
if (rule->flags & NL80211_RRF_NO_320MHZ)
bw = min_t(unsignedint, bw, MHZ_TO_KHZ(160)); if (rule->flags & NL80211_RRF_NO_160MHZ)
bw = min_t(unsignedint, bw, MHZ_TO_KHZ(80)); if (rule->flags & NL80211_RRF_NO_80MHZ)
bw = min_t(unsignedint, bw, MHZ_TO_KHZ(40));
/* * HT40+/HT40- limits are handled per-channel. Only limit BW if both * are not allowed.
*/ if (rule->flags & NL80211_RRF_NO_HT40MINUS &&
rule->flags & NL80211_RRF_NO_HT40PLUS)
bw = min_t(unsignedint, bw, MHZ_TO_KHZ(20));
return bw;
}
/* Sanity check on a regulatory rule */ staticbool is_valid_reg_rule(conststruct ieee80211_reg_rule *rule)
{ conststruct ieee80211_freq_range *freq_range = &rule->freq_range;
u32 freq_diff;
if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0) returnfalse;
if (freq_range->start_freq_khz > freq_range->end_freq_khz) returnfalse;
if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) returnfalse;
for (i = 0; i < rd->n_reg_rules; i++) {
reg_rule = &rd->reg_rules[i]; if (!is_valid_reg_rule(reg_rule)) returnfalse;
}
returntrue;
}
/** * freq_in_rule_band - tells us if a frequency is in a frequency band * @freq_range: frequency rule we want to query * @freq_khz: frequency we are inquiring about * * This lets us know if a specific frequency rule is or is not relevant to * a specific frequency's band. Bands are device specific and artificial * definitions (the "2.4 GHz band", the "5 GHz band" and the "60GHz band"), * however it is safe for now to assume that a frequency rule should not be * part of a frequency's band if the start freq or end freq are off by more * than 2 GHz for the 2.4 and 5 GHz bands, and by more than 20 GHz for the * 60 GHz band. * This resolution can be lowered and should be considered as we add * regulatory rule support for other "bands". * * Returns: whether or not the frequency is in the range
*/ staticbool freq_in_rule_band(conststruct ieee80211_freq_range *freq_range,
u32 freq_khz)
{ /* * From 802.11ad: directional multi-gigabit (DMG): * Pertaining to operation in a frequency band containing a channel * with the Channel starting frequency above 45 GHz.
*/
u32 limit = freq_khz > 45 * KHZ_PER_GHZ ? 20 * KHZ_PER_GHZ : 2 * KHZ_PER_GHZ; if (abs(freq_khz - freq_range->start_freq_khz) <= limit) returntrue; if (abs(freq_khz - freq_range->end_freq_khz) <= limit) returntrue; returnfalse;
}
/* * Later on we can perhaps use the more restrictive DFS * region but we don't have information for that yet so * for now simply disallow conflicts.
*/ staticenum nl80211_dfs_regions
reg_intersect_dfs_region(constenum nl80211_dfs_regions dfs_region1, constenum nl80211_dfs_regions dfs_region2)
{ if (dfs_region1 != dfs_region2) return NL80211_DFS_UNSET; return dfs_region1;
}
/* * In case NL80211_RRF_AUTO_BW requested for both rules * set AUTO_BW in intersected rule also. Next we will * calculate BW correctly in handle_channel function. * In other case remove AUTO_BW flag while we calculate * maximum bandwidth correctly and auto calculation is * not required.
*/ if ((rule1->flags & NL80211_RRF_AUTO_BW) &&
(rule2->flags & NL80211_RRF_AUTO_BW))
intersected_rule->flags |= NL80211_RRF_AUTO_BW; else
intersected_rule->flags &= ~NL80211_RRF_AUTO_BW;
if (!is_valid_reg_rule(intersected_rule)) return -EINVAL;
return 0;
}
/* check whether old rule contains new rule */ staticbool rule_contains(struct ieee80211_reg_rule *r1, struct ieee80211_reg_rule *r2)
{ /* for simplicity, currently consider only same flags */ if (r1->flags != r2->flags) returnfalse;
/* verify r1 is more restrictive */ if ((r1->power_rule.max_antenna_gain >
r2->power_rule.max_antenna_gain) ||
r1->power_rule.max_eirp > r2->power_rule.max_eirp) returnfalse;
/* make sure r2's range is contained within r1 */ if (r1->freq_range.start_freq_khz > r2->freq_range.start_freq_khz ||
r1->freq_range.end_freq_khz < r2->freq_range.end_freq_khz) returnfalse;
/* and finally verify that r1.max_bw >= r2.max_bw */ if (r1->freq_range.max_bandwidth_khz <
r2->freq_range.max_bandwidth_khz) returnfalse;
returntrue;
}
/* add or extend current rules. do nothing if rule is already contained */ staticvoid add_rule(struct ieee80211_reg_rule *rule, struct ieee80211_reg_rule *reg_rules, u32 *n_rules)
{ struct ieee80211_reg_rule *tmp_rule; int i;
for (i = 0; i < *n_rules; i++) {
tmp_rule = ®_rules[i]; /* rule is already contained - do nothing */ if (rule_contains(tmp_rule, rule)) return;
/* extend rule if possible */ if (rule_contains(rule, tmp_rule)) {
memcpy(tmp_rule, rule, sizeof(*rule)); return;
}
}
/** * regdom_intersect - do the intersection between two regulatory domains * @rd1: first regulatory domain * @rd2: second regulatory domain * * Use this function to get the intersection between two regulatory domains. * Once completed we will mark the alpha2 for the rd as intersected, "98", * as no one single alpha2 can represent this regulatory domain. * * Returns a pointer to the regulatory domain structure which will hold the * resulting intersection of rules between rd1 and rd2. We will * kzalloc() this structure for you. * * Returns: the intersected regdomain
*/ staticstruct ieee80211_regdomain *
regdom_intersect(conststruct ieee80211_regdomain *rd1, conststruct ieee80211_regdomain *rd2)
{ int r; unsignedint x, y; unsignedint num_rules = 0; conststruct ieee80211_reg_rule *rule1, *rule2; struct ieee80211_reg_rule intersected_rule; struct ieee80211_regdomain *rd;
if (!rd1 || !rd2) return NULL;
/* * First we get a count of the rules we'll need, then we actually * build them. This is to so we can malloc() and free() a * regdomain once. The reason we use reg_rules_intersect() here * is it will return -EINVAL if the rule computed makes no sense. * All rules that do check out OK are valid.
*/
for (x = 0; x < rd1->n_reg_rules; x++) {
rule1 = &rd1->reg_rules[x]; for (y = 0; y < rd2->n_reg_rules; y++) {
rule2 = &rd2->reg_rules[y]; if (!reg_rules_intersect(rd1, rd2, rule1, rule2,
&intersected_rule))
num_rules++;
}
}
if (!num_rules) return NULL;
rd = kzalloc(struct_size(rd, reg_rules, num_rules), GFP_KERNEL); if (!rd) return NULL;
for (x = 0; x < rd1->n_reg_rules; x++) {
rule1 = &rd1->reg_rules[x]; for (y = 0; y < rd2->n_reg_rules; y++) {
rule2 = &rd2->reg_rules[y];
r = reg_rules_intersect(rd1, rd2, rule1, rule2,
&intersected_rule); /* * No need to memset here the intersected rule here as * we're not using the stack anymore
*/ if (r) continue;
for (i = 0; i < regd->n_reg_rules; i++) { conststruct ieee80211_reg_rule *rr; conststruct ieee80211_freq_range *fr = NULL;
rr = ®d->reg_rules[i];
fr = &rr->freq_range;
/* * We only need to know if one frequency rule was * in center_freq's band, that's enough, so let's * not overwrite it once found
*/ if (!band_rule_found)
band_rule_found = freq_in_rule_band(fr, center_freq);
max_bandwidth_khz = freq_range->max_bandwidth_khz;
center_freq_khz = ieee80211_channel_to_khz(chan); /* Check if auto calculation requested */ if (reg_rule->flags & NL80211_RRF_AUTO_BW)
max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
/* If we get a reg_rule we can assume that at least 5Mhz fit */ if (!cfg80211_does_bw_fit_range(freq_range,
center_freq_khz,
MHZ_TO_KHZ(10)))
bw_flags |= IEEE80211_CHAN_NO_10MHZ; if (!cfg80211_does_bw_fit_range(freq_range,
center_freq_khz,
MHZ_TO_KHZ(20)))
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
if (is_s1g) { /* S1G is strict about non overlapping channels. We can * calculate which bandwidth is allowed per channel by finding * the largest bandwidth which cleanly divides the freq_range.
*/ int edge_offset; int ch_bw = max_bandwidth_khz;
while (ch_bw) {
edge_offset = (center_freq_khz - ch_bw / 2) -
freq_range->start_freq_khz; if (edge_offset % ch_bw == 0) { switch (KHZ_TO_MHZ(ch_bw)) { case 1:
bw_flags |= IEEE80211_CHAN_1MHZ; break; case 2:
bw_flags |= IEEE80211_CHAN_2MHZ; break; case 4:
bw_flags |= IEEE80211_CHAN_4MHZ; break; case 8:
bw_flags |= IEEE80211_CHAN_8MHZ; break; case 16:
bw_flags |= IEEE80211_CHAN_16MHZ; break; default: /* If we got here, no bandwidths fit on * this frequency, ie. band edge.
*/
bw_flags |= IEEE80211_CHAN_DISABLED; break;
} break;
}
ch_bw /= 2;
}
} else { if (max_bandwidth_khz < MHZ_TO_KHZ(10))
bw_flags |= IEEE80211_CHAN_NO_10MHZ; if (max_bandwidth_khz < MHZ_TO_KHZ(20))
bw_flags |= IEEE80211_CHAN_NO_20MHZ; if (max_bandwidth_khz < MHZ_TO_KHZ(40))
bw_flags |= IEEE80211_CHAN_NO_HT40; if (max_bandwidth_khz < MHZ_TO_KHZ(80))
bw_flags |= IEEE80211_CHAN_NO_80MHZ; if (max_bandwidth_khz < MHZ_TO_KHZ(160))
bw_flags |= IEEE80211_CHAN_NO_160MHZ; if (max_bandwidth_khz < MHZ_TO_KHZ(320))
bw_flags |= IEEE80211_CHAN_NO_320MHZ;
} return bw_flags;
}
disable_chan: /* We will disable all channels that do not match our * received regulatory rule unless the hint is coming * from a Country IE and the Country IE had no information * about a band. The IEEE 802.11 spec allows for an AP * to send only a subset of the regulatory rules allowed, * so an AP in the US that only supports 2.4 GHz may only send * a country IE with information for the 2.4 GHz band * while 5 GHz is still supported.
*/ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
PTR_ERR(rrule) == -ERANGE) return;
if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) returntrue;
if (!lr) {
pr_debug("Ignoring regulatory request set by %s since last_request is not set\n",
reg_initiator_name(initiator)); returntrue;
}
if (initiator == NL80211_REGDOM_SET_BY_CORE &&
wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
pr_debug("Ignoring regulatory request set by %s since the driver uses its own custom regulatory domain\n",
reg_initiator_name(initiator)); returntrue;
}
/* * wiphy->regd will be set once the device has its own * desired regulatory domain set
*/ if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd &&
initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
!is_world_regdom(lr->alpha2)) {
pr_debug("Ignoring regulatory request set by %s since the driver requires its own regulatory domain to be set first\n",
reg_initiator_name(initiator)); returntrue;
}
if (reg_request_cell_base(lr)) return reg_dev_ignore_cell_hint(wiphy);
if (channel_changed) {
nl80211_send_beacon_hint_event(wiphy, &chan_before, chan); if (wiphy->flags & WIPHY_FLAG_CHANNEL_CHANGE_ON_BEACON)
reg_call_notifier(wiphy, lr);
}
}
/* * Called when a scan on a wiphy finds a beacon on * new channel
*/ staticvoid wiphy_update_new_beacon(struct wiphy *wiphy, struct reg_beacon *reg_beacon)
{ unsignedint i; struct ieee80211_supported_band *sband;
if (!wiphy->bands[reg_beacon->chan.band]) return;
sband = wiphy->bands[reg_beacon->chan.band];
for (i = 0; i < sband->n_channels; i++)
handle_reg_beacon(wiphy, i, reg_beacon);
}
/* * Called upon reg changes or a new wiphy is added
*/ staticvoid wiphy_update_beacon_reg(struct wiphy *wiphy)
{ unsignedint i; struct ieee80211_supported_band *sband; struct reg_beacon *reg_beacon;
list_for_each_entry(reg_beacon, ®_beacon_list, list) { if (!wiphy->bands[reg_beacon->chan.band]) continue;
sband = wiphy->bands[reg_beacon->chan.band]; for (i = 0; i < sband->n_channels; i++)
handle_reg_beacon(wiphy, i, reg_beacon);
}
}
/* Reap the advantages of previously found beacons */ staticvoid reg_process_beacons(struct wiphy *wiphy)
{ /* * Means we are just firing up cfg80211, so no beacons would * have been processed yet.
*/ if (!last_request) return;
wiphy_update_beacon_reg(wiphy);
}
staticbool is_ht40_allowed(struct ieee80211_channel *chan)
{ if (!chan) returnfalse; if (chan->flags & IEEE80211_CHAN_DISABLED) returnfalse; /* This would happen when regulatory rules disallow HT40 completely */ if ((chan->flags & IEEE80211_CHAN_NO_HT40) == IEEE80211_CHAN_NO_HT40) returnfalse; returntrue;
}
if (!is_ht40_allowed(channel)) {
channel->flags |= IEEE80211_CHAN_NO_HT40; return;
}
/* * We need to ensure the extension channels exist to * be able to use HT40- or HT40+, this finds them (or not)
*/ for (i = 0; i < sband->n_channels; i++) { struct ieee80211_channel *c = &sband->channels[i];
if (c->center_freq == (channel->center_freq - 20))
channel_before = c; if (c->center_freq == (channel->center_freq + 20))
channel_after = c;
}
/* * Please note that this assumes target bandwidth is 20 MHz, * if that ever changes we also need to change the below logic * to include that as well.
*/ if (!is_ht40_allowed(channel_before) ||
flags & NL80211_RRF_NO_HT40MINUS)
channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; else
channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
/* make sure the interface is active */ if (!wdev->netdev || !netif_running(wdev->netdev)) returntrue;
for (link = 0; link < ARRAY_SIZE(wdev->links); link++) { struct ieee80211_channel *chan;
if (!wdev->valid_links && link > 0) break; if (wdev->valid_links && !(wdev->valid_links & BIT(link))) continue; switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: if (!wdev->links[link].ap.beacon_interval) continue;
chandef = wdev->links[link].ap.chandef; break; case NL80211_IFTYPE_MESH_POINT: if (!wdev->u.mesh.beacon_interval) continue;
chandef = wdev->u.mesh.chandef; break; case NL80211_IFTYPE_ADHOC: if (!wdev->u.ibss.ssid_len) continue;
chandef = wdev->u.ibss.chandef; break; case NL80211_IFTYPE_STATION:
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet)
¤