#include <sys/cdefs.h>
#include "opt_inet.h"
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_media.h>
#include <net/if_private.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_adhoc.h>
#include <net80211/ieee80211_sta.h>
#include <net80211/ieee80211_hostap.h>
#include <net80211/ieee80211_wds.h>
#ifdef IEEE80211_SUPPORT_MESH
#include <net80211/ieee80211_mesh.h>
#endif
#include <net80211/ieee80211_monitor.h>
#include <net80211/ieee80211_input.h>
#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3
#define HIGH_PRI_SWITCH_THRESH 10
const char *mgt_subtype_name[] = {
"assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp",
"probe_req", "probe_resp", "timing_adv", "reserved#7",
"beacon", "atim", "disassoc", "auth",
"deauth", "action", "action_noack", "reserved#15"
};
const char *ctl_subtype_name[] = {
"reserved#0", "reserved#1", "reserved#2", "reserved#3",
"reserved#4", "reserved#5", "reserved#6", "control_wrap",
"bar", "ba", "ps_poll", "rts",
"cts", "ack", "cf_end", "cf_end_ack"
};
const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = {
"IBSS",
"STA",
"WDS",
"AHDEMO",
"HOSTAP",
"MONITOR",
"MBSS"
};
const char *ieee80211_state_name[IEEE80211_S_MAX] = {
"INIT",
"SCAN",
"AUTH",
"ASSOC",
"CAC",
"RUN",
"CSA",
"SLEEP",
};
const char *ieee80211_wme_acnames[] = {
"WME_AC_BE",
"WME_AC_BK",
"WME_AC_VI",
"WME_AC_VO",
"WME_UPSD",
};
const char *
ieee80211_reason_to_string(uint16_t reason)
{
switch (reason) {
case IEEE80211_REASON_UNSPECIFIED:
return ("unspecified");
case IEEE80211_REASON_AUTH_EXPIRE:
return ("previous authentication is expired");
case IEEE80211_REASON_AUTH_LEAVE:
return ("sending STA is leaving/has left IBSS or ESS");
case IEEE80211_REASON_ASSOC_EXPIRE:
return ("disassociated due to inactivity");
case IEEE80211_REASON_ASSOC_TOOMANY:
return ("too many associated STAs");
case IEEE80211_REASON_NOT_AUTHED:
return ("class 2 frame received from nonauthenticated STA");
case IEEE80211_REASON_NOT_ASSOCED:
return ("class 3 frame received from nonassociated STA");
case IEEE80211_REASON_ASSOC_LEAVE:
return ("sending STA is leaving/has left BSS");
case IEEE80211_REASON_ASSOC_NOT_AUTHED:
return ("STA requesting (re)association is not authenticated");
case IEEE80211_REASON_DISASSOC_PWRCAP_BAD:
return ("information in the Power Capability element is "
"unacceptable");
case IEEE80211_REASON_DISASSOC_SUPCHAN_BAD:
return ("information in the Supported Channels element is "
"unacceptable");
case IEEE80211_REASON_IE_INVALID:
return ("invalid element");
case IEEE80211_REASON_MIC_FAILURE:
return ("MIC failure");
case IEEE80211_REASON_4WAY_HANDSHAKE_TIMEOUT:
return ("4-Way handshake timeout");
case IEEE80211_REASON_GROUP_KEY_UPDATE_TIMEOUT:
return ("group key update timeout");
case IEEE80211_REASON_IE_IN_4WAY_DIFFERS:
return ("element in 4-Way handshake different from "
"(re)association request/probe response/beacon frame");
case IEEE80211_REASON_GROUP_CIPHER_INVALID:
return ("invalid group cipher");
case IEEE80211_REASON_PAIRWISE_CIPHER_INVALID:
return ("invalid pairwise cipher");
case IEEE80211_REASON_AKMP_INVALID:
return ("invalid AKMP");
case IEEE80211_REASON_UNSUPP_RSN_IE_VERSION:
return ("unsupported version in RSN IE");
case IEEE80211_REASON_INVALID_RSN_IE_CAP:
return ("invalid capabilities in RSN IE");
case IEEE80211_REASON_802_1X_AUTH_FAILED:
return ("IEEE 802.1X authentication failed");
case IEEE80211_REASON_CIPHER_SUITE_REJECTED:
return ("cipher suite rejected because of the security "
"policy");
case IEEE80211_REASON_UNSPECIFIED_QOS:
return ("unspecified (QoS-related)");
case IEEE80211_REASON_INSUFFICIENT_BW:
return ("QoS AP lacks sufficient bandwidth for this QoS STA");
case IEEE80211_REASON_TOOMANY_FRAMES:
return ("too many frames need to be acknowledged");
case IEEE80211_REASON_OUTSIDE_TXOP:
return ("STA is transmitting outside the limits of its TXOPs");
case IEEE80211_REASON_LEAVING_QBSS:
return ("requested from peer STA (the STA is "
"resetting/leaving the BSS)");
case IEEE80211_REASON_BAD_MECHANISM:
return ("requested from peer STA (it does not want to use "
"the mechanism)");
case IEEE80211_REASON_SETUP_NEEDED:
return ("requested from peer STA (setup is required for the "
"used mechanism)");
case IEEE80211_REASON_TIMEOUT:
return ("requested from peer STA (timeout)");
case IEEE80211_REASON_PEER_LINK_CANCELED:
return ("SME cancels the mesh peering instance (not related "
"to the maximum number of peer mesh STAs)");
case IEEE80211_REASON_MESH_MAX_PEERS:
return ("maximum number of peer mesh STAs was reached");
case IEEE80211_REASON_MESH_CPVIOLATION:
return ("the received information violates the Mesh "
"Configuration policy configured in the mesh STA "
"profile");
case IEEE80211_REASON_MESH_CLOSE_RCVD:
return ("the mesh STA has received a Mesh Peering Close "
"message requesting to close the mesh peering");
case IEEE80211_REASON_MESH_MAX_RETRIES:
return ("the mesh STA has resent dot11MeshMaxRetries Mesh "
"Peering Open messages, without receiving a Mesh "
"Peering Confirm message");
case IEEE80211_REASON_MESH_CONFIRM_TIMEOUT:
return ("the confirmTimer for the mesh peering instance times "
"out");
case IEEE80211_REASON_MESH_INVALID_GTK:
return ("the mesh STA fails to unwrap the GTK or the values "
"in the wrapped contents do not match");
case IEEE80211_REASON_MESH_INCONS_PARAMS:
return ("the mesh STA receives inconsistent information about "
"the mesh parameters between Mesh Peering Management "
"frames");
case IEEE80211_REASON_MESH_INVALID_SECURITY:
return ("the mesh STA fails the authenticated mesh peering "
"exchange because due to failure in selecting "
"pairwise/group ciphersuite");
case IEEE80211_REASON_MESH_PERR_NO_PROXY:
return ("the mesh STA does not have proxy information for "
"this external destination");
case IEEE80211_REASON_MESH_PERR_NO_FI:
return ("the mesh STA does not have forwarding information "
"for this destination");
case IEEE80211_REASON_MESH_PERR_DEST_UNREACH:
return ("the mesh STA determines that the link to the next "
"hop of an active path in its forwarding information "
"is no longer usable");
case IEEE80211_REASON_MESH_MAC_ALRDY_EXISTS_MBSS:
return ("the MAC address of the STA already exists in the "
"mesh BSS");
case IEEE80211_REASON_MESH_CHAN_SWITCH_REG:
return ("the mesh STA performs channel switch to meet "
"regulatory requirements");
case IEEE80211_REASON_MESH_CHAN_SWITCH_UNSPEC:
return ("the mesh STA performs channel switch with "
"unspecified reason");
default:
return ("reserved/unknown");
}
}
static void beacon_miss(void *, int);
static void beacon_swmiss(void *, int);
static void parent_updown(void *, int);
static void update_mcast(void *, int);
static void update_promisc(void *, int);
static void update_channel(void *, int);
static void update_chw(void *, int);
static void vap_update_wme(void *, int);
static void vap_update_slot(void *, int);
static void restart_vaps(void *, int);
static void vap_update_erp_protmode(void *, int);
static void vap_update_preamble(void *, int);
static void vap_update_ht_protmode(void *, int);
static void ieee80211_newstate_cb(void *, int);
static struct ieee80211_node *vap_update_bss(struct ieee80211vap *,
struct ieee80211_node *);
static int
null_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
const struct ieee80211_bpf_params *params)
{
ic_printf(ni->ni_ic, "missing ic_raw_xmit callback, drop frame\n");
m_freem(m);
return ENETDOWN;
}
void
ieee80211_proto_attach(struct ieee80211com *ic)
{
uint8_t hdrlen;
hdrlen = ic->ic_headroom
+ sizeof(struct ieee80211_qosframe_addr4)
+ IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
+ IEEE80211_WEP_EXTIVLEN;
max_linkhdr_grow(ALIGN(hdrlen));
TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ic);
TASK_INIT(&ic->ic_mcast_task, 0, update_mcast, ic);
TASK_INIT(&ic->ic_promisc_task, 0, update_promisc, ic);
TASK_INIT(&ic->ic_chan_task, 0, update_channel, ic);
TASK_INIT(&ic->ic_bmiss_task, 0, beacon_miss, ic);
TASK_INIT(&ic->ic_chw_task, 0, update_chw, ic);
TASK_INIT(&ic->ic_restart_task, 0, restart_vaps, ic);
ic->ic_wme.wme_hipri_switch_hysteresis =
AGGRESSIVE_MODE_SWITCH_HYSTERESIS;
ic->ic_send_mgmt = ieee80211_send_mgmt;
ic->ic_raw_xmit = null_raw_xmit;
ieee80211_adhoc_attach(ic);
ieee80211_sta_attach(ic);
ieee80211_wds_attach(ic);
ieee80211_hostap_attach(ic);
#ifdef IEEE80211_SUPPORT_MESH
ieee80211_mesh_attach(ic);
#endif
ieee80211_monitor_attach(ic);
}
void
ieee80211_proto_detach(struct ieee80211com *ic)
{
ieee80211_monitor_detach(ic);
#ifdef IEEE80211_SUPPORT_MESH
ieee80211_mesh_detach(ic);
#endif
ieee80211_hostap_detach(ic);
ieee80211_wds_detach(ic);
ieee80211_adhoc_detach(ic);
ieee80211_sta_detach(ic);
}
static void
null_update_beacon(struct ieee80211vap *vap, int item)
{
}
void
ieee80211_proto_vattach(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = vap->iv_ifp;
int i;
ifp->if_hdrlen = ic->ic_headroom
+ sizeof(struct ieee80211_qosframe_addr4)
+ IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
+ IEEE80211_WEP_EXTIVLEN;
vap->iv_rtsthreshold = IEEE80211_RTS_DEFAULT;
vap->iv_fragthreshold = IEEE80211_FRAG_DEFAULT;
vap->iv_bmiss_max = IEEE80211_BMISS_MAX;
callout_init_mtx(&vap->iv_swbmiss, IEEE80211_LOCK_OBJ(ic), 0);
callout_init(&vap->iv_mgtsend, 1);
for (i = 0; i < NET80211_IV_NSTATE_NUM; i++)
TASK_INIT(&vap->iv_nstate_task[i], 0, ieee80211_newstate_cb, vap);
TASK_INIT(&vap->iv_swbmiss_task, 0, beacon_swmiss, vap);
TASK_INIT(&vap->iv_wme_task, 0, vap_update_wme, vap);
TASK_INIT(&vap->iv_slot_task, 0, vap_update_slot, vap);
TASK_INIT(&vap->iv_erp_protmode_task, 0, vap_update_erp_protmode, vap);
TASK_INIT(&vap->iv_ht_protmode_task, 0, vap_update_ht_protmode, vap);
TASK_INIT(&vap->iv_preamble_task, 0, vap_update_preamble, vap);
for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) {
if (isclr(ic->ic_modecaps, i))
continue;
const struct ieee80211_rateset *rs = &ic->ic_sup_rates[i];
vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE;
#ifdef NOTYET
if (i == IEEE80211_MODE_11NA || i == IEEE80211_MODE_11NG) {
vap->iv_txparms[i].mgmtrate = 0 | IEEE80211_RATE_MCS;
vap->iv_txparms[i].mcastrate = 0 | IEEE80211_RATE_MCS;
} else {
vap->iv_txparms[i].mgmtrate =
rs->rs_rates[0] & IEEE80211_RATE_VAL;
vap->iv_txparms[i].mcastrate =
rs->rs_rates[0] & IEEE80211_RATE_VAL;
}
#endif
vap->iv_txparms[i].mgmtrate = rs->rs_rates[0] & IEEE80211_RATE_VAL;
vap->iv_txparms[i].mcastrate = rs->rs_rates[0] & IEEE80211_RATE_VAL;
vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT;
}
vap->iv_roaming = IEEE80211_ROAMING_AUTO;
vap->iv_update_beacon = null_update_beacon;
vap->iv_deliver_data = ieee80211_deliver_data;
vap->iv_protmode = IEEE80211_PROT_CTSONLY;
vap->iv_update_bss = vap_update_bss;
ic->ic_vattach[vap->iv_opmode](vap);
}
void
ieee80211_proto_vdetach(struct ieee80211vap *vap)
{
#define FREEAPPIE(ie) do { \
if (ie != NULL) \
IEEE80211_FREE(ie, M_80211_NODE_IE); \
} while (0)
if (vap->iv_opdetach != NULL)
vap->iv_opdetach(vap);
if (vap->iv_auth->ia_detach != NULL)
vap->iv_auth->ia_detach(vap);
if (vap->iv_acl != NULL)
vap->iv_acl->iac_detach(vap);
FREEAPPIE(vap->iv_appie_beacon);
FREEAPPIE(vap->iv_appie_probereq);
FREEAPPIE(vap->iv_appie_proberesp);
FREEAPPIE(vap->iv_appie_assocreq);
FREEAPPIE(vap->iv_appie_assocresp);
FREEAPPIE(vap->iv_appie_wpa);
#undef FREEAPPIE
}
#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1)
static const char *auth_modnames[IEEE80211_AUTH_MAX] = {
"wlan_internal",
"wlan_internal",
"wlan_internal",
"wlan_xauth",
"wlan_internal",
"wlan_xauth",
};
static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX];
static const struct ieee80211_authenticator auth_internal = {
.ia_name = "wlan_internal",
.ia_attach = NULL,
.ia_detach = NULL,
.ia_node_join = NULL,
.ia_node_leave = NULL,
};
static void
ieee80211_auth_setup(void)
{
ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal);
ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal);
ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal);
}
SYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL);
const struct ieee80211_authenticator *
ieee80211_authenticator_get(int auth)
{
if (auth >= IEEE80211_AUTH_MAX)
return NULL;
if (authenticators[auth] == NULL)
ieee80211_load_module(auth_modnames[auth]);
return authenticators[auth];
}
void
ieee80211_authenticator_register(int type,
const struct ieee80211_authenticator *auth)
{
if (type >= IEEE80211_AUTH_MAX)
return;
authenticators[type] = auth;
}
void
ieee80211_authenticator_unregister(int type)
{
if (type >= IEEE80211_AUTH_MAX)
return;
authenticators[type] = NULL;
}
static const struct ieee80211_aclator *acl = NULL;
void
ieee80211_aclator_register(const struct ieee80211_aclator *iac)
{
net80211_printf("wlan: %s acl policy registered\n", iac->iac_name);
acl = iac;
}
void
ieee80211_aclator_unregister(const struct ieee80211_aclator *iac)
{
if (acl == iac)
acl = NULL;
net80211_printf("wlan: %s acl policy unregistered\n", iac->iac_name);
}
const struct ieee80211_aclator *
ieee80211_aclator_get(const char *name)
{
if (acl == NULL)
ieee80211_load_module("wlan_acl");
return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL;
}
void
ieee80211_print_essid(const uint8_t *essid, int len)
{
const uint8_t *p;
int i;
if (len > IEEE80211_NWID_LEN)
len = IEEE80211_NWID_LEN;
for (i = 0, p = essid; i < len; i++, p++) {
if (*p < ' ' || *p > 0x7e)
break;
}
if (i == len) {
net80211_printf("\"");
for (i = 0, p = essid; i < len; i++, p++)
net80211_printf("%c", *p);
net80211_printf("\"");
} else {
net80211_printf("0x");
for (i = 0, p = essid; i < len; i++, p++)
net80211_printf("%02x", *p);
}
}
void
ieee80211_dump_pkt(struct ieee80211com *ic,
const uint8_t *buf, int len, int rate, int rssi)
{
const struct ieee80211_frame *wh;
int i;
wh = (const struct ieee80211_frame *)buf;
switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
case IEEE80211_FC1_DIR_NODS:
net80211_printf("NODS %s", ether_sprintf(wh->i_addr2));
net80211_printf("->%s", ether_sprintf(wh->i_addr1));
net80211_printf("(%s)", ether_sprintf(wh->i_addr3));
break;
case IEEE80211_FC1_DIR_TODS:
net80211_printf("TODS %s", ether_sprintf(wh->i_addr2));
net80211_printf("->%s", ether_sprintf(wh->i_addr3));
net80211_printf("(%s)", ether_sprintf(wh->i_addr1));
break;
case IEEE80211_FC1_DIR_FROMDS:
net80211_printf("FRDS %s", ether_sprintf(wh->i_addr3));
net80211_printf("->%s", ether_sprintf(wh->i_addr1));
net80211_printf("(%s)", ether_sprintf(wh->i_addr2));
break;
case IEEE80211_FC1_DIR_DSTODS:
net80211_printf("DSDS %s", ether_sprintf((const uint8_t *)&wh[1]));
net80211_printf("->%s", ether_sprintf(wh->i_addr3));
net80211_printf("(%s", ether_sprintf(wh->i_addr2));
net80211_printf("->%s)", ether_sprintf(wh->i_addr1));
break;
}
switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
case IEEE80211_FC0_TYPE_DATA:
net80211_printf(" data");
break;
case IEEE80211_FC0_TYPE_MGT:
net80211_printf(" %s", ieee80211_mgt_subtype_name(wh->i_fc[0]));
break;
default:
net80211_printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
break;
}
if (IEEE80211_QOS_HAS_SEQ(wh)) {
const struct ieee80211_qosframe *qwh =
(const struct ieee80211_qosframe *)buf;
net80211_printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID,
qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : "");
}
if (IEEE80211_IS_PROTECTED(wh)) {
int off;
off = ieee80211_anyhdrspace(ic, wh);
net80211_printf(" WEP [IV %.02x %.02x %.02x",
buf[off+0], buf[off+1], buf[off+2]);
if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV)
net80211_printf(" %.02x %.02x %.02x",
buf[off+4], buf[off+5], buf[off+6]);
net80211_printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6);
}
if (rate >= 0)
net80211_printf(" %dM", rate / 2);
if (rssi >= 0)
net80211_printf(" +%d", rssi);
net80211_printf("\n");
if (len > 0) {
for (i = 0; i < len; i++) {
if ((i & 1) == 0)
net80211_printf(" ");
net80211_printf("%02x", buf[i]);
}
net80211_printf("\n");
}
}
static __inline int
findrix(const struct ieee80211_rateset *rs, int r)
{
int i;
for (i = 0; i < rs->rs_nrates; i++)
if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r)
return i;
return -1;
}
int
ieee80211_fix_rate(struct ieee80211_node *ni,
struct ieee80211_rateset *nrs, int flags)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
int i, j, rix, error;
int okrate, badrate, fixedrate, ucastrate;
const struct ieee80211_rateset *srs;
uint8_t r;
error = 0;
okrate = badrate = 0;
ucastrate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].ucastrate;
if (ucastrate != IEEE80211_FIXED_RATE_NONE) {
if (ucastrate & 0x80) {
if (flags & IEEE80211_F_DOFRATE)
flags &= ~IEEE80211_F_DOFRATE;
} else if ((ucastrate & 0x80) == 0) {
if (flags & IEEE80211_F_DOFMCS)
flags &= ~IEEE80211_F_DOFMCS;
}
ucastrate &= IEEE80211_RATE_VAL;
}
fixedrate = IEEE80211_FIXED_RATE_NONE;
if (flags & IEEE80211_F_DOBRS)
srs = (const struct ieee80211_rateset *)
ieee80211_get_suphtrates(ic, ni->ni_chan);
else
srs = ieee80211_get_suprates(ic, ni->ni_chan);
for (i = 0; i < nrs->rs_nrates; ) {
if (flags & IEEE80211_F_DOSORT) {
for (j = i + 1; j < nrs->rs_nrates; j++) {
if (IEEE80211_RV(nrs->rs_rates[i]) >
IEEE80211_RV(nrs->rs_rates[j])) {
r = nrs->rs_rates[i];
nrs->rs_rates[i] = nrs->rs_rates[j];
nrs->rs_rates[j] = r;
}
}
}
r = nrs->rs_rates[i] & IEEE80211_RATE_VAL;
badrate = r;
if (r == ucastrate)
fixedrate = r;
rix = findrix(srs, r);
if (flags & IEEE80211_F_DONEGO) {
if (rix < 0) {
if ((flags & IEEE80211_F_JOIN) &&
(nrs->rs_rates[i] & IEEE80211_RATE_BASIC))
error++;
} else if ((flags & IEEE80211_F_JOIN) == 0) {
nrs->rs_rates[i] = srs->rs_rates[rix];
}
}
if ((flags & IEEE80211_F_DODEL) && rix < 0) {
nrs->rs_nrates--;
for (j = i; j < nrs->rs_nrates; j++)
nrs->rs_rates[j] = nrs->rs_rates[j + 1];
nrs->rs_rates[j] = 0;
continue;
}
if (rix >= 0)
okrate = nrs->rs_rates[i];
i++;
}
if (okrate == 0 || error != 0 ||
((flags & (IEEE80211_F_DOFRATE|IEEE80211_F_DOFMCS)) &&
fixedrate != ucastrate)) {
IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
"%s: flags 0x%x okrate %d error %d fixedrate 0x%x "
"ucastrate %x\n", __func__, fixedrate, ucastrate, flags);
return badrate | IEEE80211_RATE_BASIC;
} else
return IEEE80211_RV(okrate);
}
void
ieee80211_vap_reset_erp(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
vap->iv_nonerpsta = 0;
vap->iv_longslotsta = 0;
vap->iv_flags &= ~IEEE80211_F_USEPROT;
if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
(vap->iv_caps & IEEE80211_C_SHPREAMBLE)) {
vap->iv_flags |= IEEE80211_F_SHPREAMBLE;
vap->iv_flags &= ~IEEE80211_F_USEBARKER;
} else {
vap->iv_flags &= ~IEEE80211_F_SHPREAMBLE;
vap->iv_flags |= IEEE80211_F_USEBARKER;
}
ieee80211_vap_set_shortslottime(vap,
IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
IEEE80211_IS_CHAN_HT(ic->ic_curchan) ||
(IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
vap->iv_opmode == IEEE80211_M_HOSTAP &&
(ic->ic_caps & IEEE80211_C_SHSLOT)));
}
void
ieee80211_reset_erp(struct ieee80211com *ic)
{
#if 0
ic->ic_flags &= ~IEEE80211_F_USEPROT;
if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
(ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
ic->ic_flags &= ~IEEE80211_F_USEBARKER;
} else {
ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
ic->ic_flags |= IEEE80211_F_USEBARKER;
}
#endif
}
static struct ieee80211_node *
vap_update_bss(struct ieee80211vap *vap, struct ieee80211_node *ni)
{
struct ieee80211_node *obss;
IEEE80211_LOCK_ASSERT(vap->iv_ic);
obss = vap->iv_bss;
vap->iv_bss = ni;
return (obss);
}
static void
vap_update_slot(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211vap *iv;
int num_shslot = 0, num_lgslot = 0;
if (vap->iv_updateslot != NULL) {
vap->iv_updateslot(vap);
return;
}
IEEE80211_LOCK(ic);
TAILQ_FOREACH(iv, &ic->ic_vaps, iv_next) {
if (iv->iv_flags & IEEE80211_F_SHSLOT)
num_shslot++;
else
num_lgslot++;
}
if (num_shslot == 0)
ic->ic_flags &= ~IEEE80211_F_SHSLOT;
else if (num_lgslot == 0)
ic->ic_flags |= IEEE80211_F_SHSLOT;
IEEE80211_UNLOCK(ic);
if (ic->ic_updateslot != NULL)
ic->ic_updateslot(ic);
}
static void
vap_update_erp_protmode(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211vap *iv;
int enable_protmode = 0;
int non_erp_present = 0;
IEEE80211_LOCK(ic);
TAILQ_FOREACH(iv, &ic->ic_vaps, iv_next) {
if (iv->iv_flags & IEEE80211_F_USEPROT)
enable_protmode = 1;
if (iv->iv_flags_ext & IEEE80211_FEXT_NONERP_PR)
non_erp_present = 1;
}
if (enable_protmode)
ic->ic_flags |= IEEE80211_F_USEPROT;
else
ic->ic_flags &= ~IEEE80211_F_USEPROT;
if (non_erp_present)
ic->ic_flags_ext |= IEEE80211_FEXT_NONERP_PR;
else
ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
ieee80211_notify_erp_locked(ic);
IEEE80211_UNLOCK(ic);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: called; enable_protmode=%d, non_erp_present=%d\n",
__func__, enable_protmode, non_erp_present);
if (vap->iv_erp_protmode_update != NULL)
vap->iv_erp_protmode_update(vap);
}
static void
vap_update_preamble(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211vap *iv;
int barker_count = 0, short_preamble_count = 0, count = 0;
IEEE80211_LOCK(ic);
TAILQ_FOREACH(iv, &ic->ic_vaps, iv_next) {
if (iv->iv_flags & IEEE80211_F_USEBARKER)
barker_count++;
if (iv->iv_flags & IEEE80211_F_SHPREAMBLE)
short_preamble_count++;
count++;
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: called; barker_count=%d, short_preamble_count=%d\n",
__func__, barker_count, short_preamble_count);
if (barker_count == 0 && short_preamble_count == count) {
ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
ic->ic_flags &= ~IEEE80211_F_USEBARKER;
} else {
ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
ic->ic_flags |= IEEE80211_F_USEBARKER;
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: global barker=%d preamble=%d\n",
__func__,
!! (ic->ic_flags & IEEE80211_F_USEBARKER),
!! (ic->ic_flags & IEEE80211_F_SHPREAMBLE));
ieee80211_notify_erp_locked(ic);
IEEE80211_UNLOCK(ic);
if (vap->iv_preamble_update != NULL)
vap->iv_preamble_update(vap);
}
static void
vap_update_ht_protmode(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
struct ieee80211vap *iv;
struct ieee80211com *ic = vap->iv_ic;
int num_vaps = 0, num_pure = 0;
int num_optional = 0, num_ht2040 = 0, num_nonht = 0;
int num_ht_sta = 0, num_ht40_sta = 0, num_sta = 0;
int num_nonhtpr = 0;
IEEE80211_LOCK(ic);
TAILQ_FOREACH(iv, &ic->ic_vaps, iv_next) {
num_vaps++;
if (iv->iv_flags_ht & IEEE80211_FHT_NONHT_PR)
num_nonht++;
if (iv->iv_curhtprotmode & IEEE80211_HTINFO_NONHT_PRESENT)
num_nonhtpr++;
switch (iv->iv_curhtprotmode & IEEE80211_HTINFO_OPMODE) {
case IEEE80211_HTINFO_OPMODE_PURE:
num_pure++;
break;
case IEEE80211_HTINFO_OPMODE_PROTOPT:
num_optional++;
break;
case IEEE80211_HTINFO_OPMODE_HT20PR:
num_ht2040++;
break;
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N,
"%s: vap %s: nonht_pr=%d, curhtprotmode=0x%02x\n",
__func__,
ieee80211_get_vap_ifname(iv),
!! (iv->iv_flags_ht & IEEE80211_FHT_NONHT_PR),
iv->iv_curhtprotmode);
num_ht_sta += iv->iv_ht_sta_assoc;
num_ht40_sta += iv->iv_ht40_sta_assoc;
num_sta += iv->iv_sta_assoc;
}
if (num_nonht > 0)
ic->ic_flags_ht |= IEEE80211_FHT_NONHT_PR;
else
ic->ic_flags_ht &= ~IEEE80211_FHT_NONHT_PR;
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_MIXED;
if (num_pure == num_vaps)
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
if (num_optional == num_vaps)
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PROTOPT;
if ((num_ht2040 > 0) ||
((num_ht_sta > 0) && (num_ht40_sta > 0) &&
(num_ht_sta != num_ht40_sta)))
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_HT20PR;
if (num_ht_sta != num_sta)
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_MIXED;
if (num_nonhtpr > 0)
ic->ic_curhtprotmode |= IEEE80211_HTINFO_NONHT_PRESENT;
TAILQ_FOREACH(iv, &ic->ic_vaps, iv_next)
ieee80211_htinfo_notify(iv);
IEEE80211_UNLOCK(ic);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N,
"%s: global: nonht_pr=%d ht_opmode=0x%02x\n",
__func__,
!! (ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR),
ic->ic_curhtprotmode);
if (vap->iv_ht_protmode_update != NULL)
vap->iv_ht_protmode_update(vap);
}
void
ieee80211_vap_set_shortslottime(struct ieee80211vap *vap, int onoff)
{
struct ieee80211com *ic = vap->iv_ic;
if (onoff)
vap->iv_flags |= IEEE80211_F_SHSLOT;
else
vap->iv_flags &= ~IEEE80211_F_SHSLOT;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: called; onoff=%d\n", __func__, onoff);
ieee80211_runtask(ic, &vap->iv_slot_task);
}
void
ieee80211_vap_update_preamble(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: called\n", __func__);
ieee80211_runtask(ic, &vap->iv_preamble_task);
}
void
ieee80211_vap_update_erp_protmode(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: called\n", __func__);
ieee80211_runtask(ic, &vap->iv_erp_protmode_task);
}
void
ieee80211_vap_update_ht_protmode(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: called\n", __func__);
ieee80211_runtask(ic, &vap->iv_ht_protmode_task);
}
int
ieee80211_iserp_rateset(const struct ieee80211_rateset *rs)
{
static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 };
int i, j;
if (rs->rs_nrates < nitems(rates))
return 0;
for (i = 0; i < nitems(rates); i++) {
for (j = 0; j < rs->rs_nrates; j++) {
int r = rs->rs_rates[j] & IEEE80211_RATE_VAL;
if (rates[i] == r)
goto next;
if (r > rates[i])
return 0;
}
return 0;
next:
;
}
return 1;
}
static void
setbasicrates(struct ieee80211_rateset *rs,
enum ieee80211_phymode mode, int add)
{
static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_11A] = { 3, { 12, 24, 48 } },
[IEEE80211_MODE_11B] = { 2, { 2, 4 } },
[IEEE80211_MODE_11G] = { 4, { 2, 4, 11, 22 } },
[IEEE80211_MODE_TURBO_A] = { 3, { 12, 24, 48 } },
[IEEE80211_MODE_TURBO_G] = { 4, { 2, 4, 11, 22 } },
[IEEE80211_MODE_STURBO_A] = { 3, { 12, 24, 48 } },
[IEEE80211_MODE_HALF] = { 3, { 6, 12, 24 } },
[IEEE80211_MODE_QUARTER] = { 3, { 3, 6, 12 } },
[IEEE80211_MODE_11NA] = { 3, { 12, 24, 48 } },
[IEEE80211_MODE_11NG] = { 4, { 2, 4, 11, 22 } },
[IEEE80211_MODE_VHT_2GHZ] = { 4, { 2, 4, 11, 22 } },
[IEEE80211_MODE_VHT_5GHZ] = { 3, { 12, 24, 48 } },
};
int i, j;
for (i = 0; i < rs->rs_nrates; i++) {
if (!add)
rs->rs_rates[i] &= IEEE80211_RATE_VAL;
for (j = 0; j < basic[mode].rs_nrates; j++)
if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
break;
}
}
}
void
ieee80211_setbasicrates(struct ieee80211_rateset *rs,
enum ieee80211_phymode mode)
{
setbasicrates(rs, mode, 0);
}
void
ieee80211_addbasicrates(struct ieee80211_rateset *rs,
enum ieee80211_phymode mode)
{
setbasicrates(rs, mode, 1);
}
typedef struct phyParamType {
uint8_t aifsn;
uint8_t logcwmin;
uint8_t logcwmax;
uint16_t txopLimit;
uint8_t acm;
} paramType;
static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = { 3, 4, 6, 0, 0 },
[IEEE80211_MODE_11A] = { 3, 4, 6, 0, 0 },
[IEEE80211_MODE_11B] = { 3, 4, 6, 0, 0 },
[IEEE80211_MODE_11G] = { 3, 4, 6, 0, 0 },
[IEEE80211_MODE_FH] = { 3, 4, 6, 0, 0 },
[IEEE80211_MODE_TURBO_A]= { 2, 3, 5, 0, 0 },
[IEEE80211_MODE_TURBO_G]= { 2, 3, 5, 0, 0 },
[IEEE80211_MODE_STURBO_A]={ 2, 3, 5, 0, 0 },
[IEEE80211_MODE_HALF] = { 3, 4, 6, 0, 0 },
[IEEE80211_MODE_QUARTER]= { 3, 4, 6, 0, 0 },
[IEEE80211_MODE_11NA] = { 3, 4, 6, 0, 0 },
[IEEE80211_MODE_11NG] = { 3, 4, 6, 0, 0 },
[IEEE80211_MODE_VHT_2GHZ] = { 3, 4, 6, 0, 0 },
[IEEE80211_MODE_VHT_5GHZ] = { 3, 4, 6, 0, 0 },
};
static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = { 7, 4, 10, 0, 0 },
[IEEE80211_MODE_11A] = { 7, 4, 10, 0, 0 },
[IEEE80211_MODE_11B] = { 7, 4, 10, 0, 0 },
[IEEE80211_MODE_11G] = { 7, 4, 10, 0, 0 },
[IEEE80211_MODE_FH] = { 7, 4, 10, 0, 0 },
[IEEE80211_MODE_TURBO_A]= { 7, 3, 10, 0, 0 },
[IEEE80211_MODE_TURBO_G]= { 7, 3, 10, 0, 0 },
[IEEE80211_MODE_STURBO_A]={ 7, 3, 10, 0, 0 },
[IEEE80211_MODE_HALF] = { 7, 4, 10, 0, 0 },
[IEEE80211_MODE_QUARTER]= { 7, 4, 10, 0, 0 },
[IEEE80211_MODE_11NA] = { 7, 4, 10, 0, 0 },
[IEEE80211_MODE_11NG] = { 7, 4, 10, 0, 0 },
[IEEE80211_MODE_VHT_2GHZ] = { 7, 4, 10, 0, 0 },
[IEEE80211_MODE_VHT_5GHZ] = { 7, 4, 10, 0, 0 },
};
static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = { 1, 3, 4, 94, 0 },
[IEEE80211_MODE_11A] = { 1, 3, 4, 94, 0 },
[IEEE80211_MODE_11B] = { 1, 3, 4, 188, 0 },
[IEEE80211_MODE_11G] = { 1, 3, 4, 94, 0 },
[IEEE80211_MODE_FH] = { 1, 3, 4, 188, 0 },
[IEEE80211_MODE_TURBO_A]= { 1, 2, 3, 94, 0 },
[IEEE80211_MODE_TURBO_G]= { 1, 2, 3, 94, 0 },
[IEEE80211_MODE_STURBO_A]={ 1, 2, 3, 94, 0 },
[IEEE80211_MODE_HALF] = { 1, 3, 4, 94, 0 },
[IEEE80211_MODE_QUARTER]= { 1, 3, 4, 94, 0 },
[IEEE80211_MODE_11NA] = { 1, 3, 4, 94, 0 },
[IEEE80211_MODE_11NG] = { 1, 3, 4, 94, 0 },
[IEEE80211_MODE_VHT_2GHZ] = { 1, 3, 4, 94, 0 },
[IEEE80211_MODE_VHT_5GHZ] = { 1, 3, 4, 94, 0 },
};
static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = { 1, 2, 3, 47, 0 },
[IEEE80211_MODE_11A] = { 1, 2, 3, 47, 0 },
[IEEE80211_MODE_11B] = { 1, 2, 3, 102, 0 },
[IEEE80211_MODE_11G] = { 1, 2, 3, 47, 0 },
[IEEE80211_MODE_FH] = { 1, 2, 3, 102, 0 },
[IEEE80211_MODE_TURBO_A]= { 1, 2, 2, 47, 0 },
[IEEE80211_MODE_TURBO_G]= { 1, 2, 2, 47, 0 },
[IEEE80211_MODE_STURBO_A]={ 1, 2, 2, 47, 0 },
[IEEE80211_MODE_HALF] = { 1, 2, 3, 47, 0 },
[IEEE80211_MODE_QUARTER]= { 1, 2, 3, 47, 0 },
[IEEE80211_MODE_11NA] = { 1, 2, 3, 47, 0 },
[IEEE80211_MODE_11NG] = { 1, 2, 3, 47, 0 },
[IEEE80211_MODE_VHT_2GHZ] = { 1, 2, 3, 47, 0 },
[IEEE80211_MODE_VHT_5GHZ] = { 1, 2, 3, 47, 0 },
};
static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = { 3, 4, 10, 0, 0 },
[IEEE80211_MODE_11A] = { 3, 4, 10, 0, 0 },
[IEEE80211_MODE_11B] = { 3, 4, 10, 0, 0 },
[IEEE80211_MODE_11G] = { 3, 4, 10, 0, 0 },
[IEEE80211_MODE_FH] = { 3, 4, 10, 0, 0 },
[IEEE80211_MODE_TURBO_A]= { 2, 3, 10, 0, 0 },
[IEEE80211_MODE_TURBO_G]= { 2, 3, 10, 0, 0 },
[IEEE80211_MODE_STURBO_A]={ 2, 3, 10, 0, 0 },
[IEEE80211_MODE_HALF] = { 3, 4, 10, 0, 0 },
[IEEE80211_MODE_QUARTER]= { 3, 4, 10, 0, 0 },
[IEEE80211_MODE_11NA] = { 3, 4, 10, 0, 0 },
[IEEE80211_MODE_11NG] = { 3, 4, 10, 0, 0 },
};
static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = { 2, 3, 4, 94, 0 },
[IEEE80211_MODE_11A] = { 2, 3, 4, 94, 0 },
[IEEE80211_MODE_11B] = { 2, 3, 4, 188, 0 },
[IEEE80211_MODE_11G] = { 2, 3, 4, 94, 0 },
[IEEE80211_MODE_FH] = { 2, 3, 4, 188, 0 },
[IEEE80211_MODE_TURBO_A]= { 2, 2, 3, 94, 0 },
[IEEE80211_MODE_TURBO_G]= { 2, 2, 3, 94, 0 },
[IEEE80211_MODE_STURBO_A]={ 2, 2, 3, 94, 0 },
[IEEE80211_MODE_HALF] = { 2, 3, 4, 94, 0 },
[IEEE80211_MODE_QUARTER]= { 2, 3, 4, 94, 0 },
[IEEE80211_MODE_11NA] = { 2, 3, 4, 94, 0 },
[IEEE80211_MODE_11NG] = { 2, 3, 4, 94, 0 },
};
static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = { 2, 2, 3, 47, 0 },
[IEEE80211_MODE_11A] = { 2, 2, 3, 47, 0 },
[IEEE80211_MODE_11B] = { 2, 2, 3, 102, 0 },
[IEEE80211_MODE_11G] = { 2, 2, 3, 47, 0 },
[IEEE80211_MODE_FH] = { 2, 2, 3, 102, 0 },
[IEEE80211_MODE_TURBO_A]= { 1, 2, 2, 47, 0 },
[IEEE80211_MODE_TURBO_G]= { 1, 2, 2, 47, 0 },
[IEEE80211_MODE_STURBO_A]={ 1, 2, 2, 47, 0 },
[IEEE80211_MODE_HALF] = { 2, 2, 3, 47, 0 },
[IEEE80211_MODE_QUARTER]= { 2, 2, 3, 47, 0 },
[IEEE80211_MODE_11NA] = { 2, 2, 3, 47, 0 },
[IEEE80211_MODE_11NG] = { 2, 2, 3, 47, 0 },
};
static void
_setifsparams(struct wmeParams *wmep, const paramType *phy)
{
wmep->wmep_aifsn = phy->aifsn;
wmep->wmep_logcwmin = phy->logcwmin;
wmep->wmep_logcwmax = phy->logcwmax;
wmep->wmep_txopLimit = phy->txopLimit;
}
static void
setwmeparams(struct ieee80211vap *vap, const char *type, int ac,
struct wmeParams *wmep, const paramType *phy)
{
wmep->wmep_acm = phy->acm;
_setifsparams(wmep, phy);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"set %s (%s) [acm %u aifsn %u logcwmin %u logcwmax %u txop %u]\n",
ieee80211_wme_acnames[ac], type,
wmep->wmep_acm, wmep->wmep_aifsn, wmep->wmep_logcwmin,
wmep->wmep_logcwmax, wmep->wmep_txopLimit);
}
static void
ieee80211_wme_initparams_locked(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
const paramType *pPhyParam, *pBssPhyParam;
struct wmeParams *wmep;
enum ieee80211_phymode mode;
int i;
IEEE80211_LOCK_ASSERT(ic);
if ((ic->ic_caps & IEEE80211_C_WME) == 0 || ic->ic_nrunning > 1)
return;
wme->wme_wmeChanParams.cap_info = 0xfe;
if (ic->ic_bsschan != IEEE80211_CHAN_ANYC)
mode = ieee80211_chan2mode(ic->ic_bsschan);
else
mode = IEEE80211_MODE_AUTO;
for (i = 0; i < WME_NUM_AC; i++) {
switch (i) {
case WME_AC_BK:
pPhyParam = &phyParamForAC_BK[mode];
pBssPhyParam = &phyParamForAC_BK[mode];
break;
case WME_AC_VI:
pPhyParam = &phyParamForAC_VI[mode];
pBssPhyParam = &bssPhyParamForAC_VI[mode];
break;
case WME_AC_VO:
pPhyParam = &phyParamForAC_VO[mode];
pBssPhyParam = &bssPhyParamForAC_VO[mode];
break;
case WME_AC_BE:
default:
pPhyParam = &phyParamForAC_BE[mode];
pBssPhyParam = &bssPhyParamForAC_BE[mode];
break;
}
wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
setwmeparams(vap, "chan", i, wmep, pPhyParam);
} else {
setwmeparams(vap, "chan", i, wmep, pBssPhyParam);
}
wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
setwmeparams(vap, "bss ", i, wmep, pBssPhyParam);
}
if (vap->iv_bss != NULL) {
wme->wme_hipri_switch_thresh =
(HIGH_PRI_SWITCH_THRESH * vap->iv_bss->ni_intval) / 100;
wme->wme_flags &= ~WME_F_AGGRMODE;
ieee80211_wme_updateparams(vap);
}
}
void
ieee80211_wme_initparams(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
IEEE80211_LOCK(ic);
ieee80211_wme_initparams_locked(vap);
IEEE80211_UNLOCK(ic);
}
void
ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
{
static const paramType aggrParam[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = { 2, 4, 10, 64, 0 },
[IEEE80211_MODE_11A] = { 2, 4, 10, 64, 0 },
[IEEE80211_MODE_11B] = { 2, 5, 10, 64, 0 },
[IEEE80211_MODE_11G] = { 2, 4, 10, 64, 0 },
[IEEE80211_MODE_FH] = { 2, 5, 10, 64, 0 },
[IEEE80211_MODE_TURBO_A] = { 1, 3, 10, 64, 0 },
[IEEE80211_MODE_TURBO_G] = { 1, 3, 10, 64, 0 },
[IEEE80211_MODE_STURBO_A] = { 1, 3, 10, 64, 0 },
[IEEE80211_MODE_HALF] = { 2, 4, 10, 64, 0 },
[IEEE80211_MODE_QUARTER] = { 2, 4, 10, 64, 0 },
[IEEE80211_MODE_11NA] = { 2, 4, 10, 64, 0 },
[IEEE80211_MODE_11NG] = { 2, 4, 10, 64, 0 },
[IEEE80211_MODE_VHT_2GHZ] = { 2, 4, 10, 64, 0 },
[IEEE80211_MODE_VHT_5GHZ] = { 2, 4, 10, 64, 0 },
};
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
const struct wmeParams *wmep;
struct wmeParams *chanp, *bssp;
enum ieee80211_phymode mode;
int i;
int do_aggrmode = 0;
for (i = 0; i < WME_NUM_AC; i++) {
chanp = &wme->wme_chanParams.cap_wmeParams[i];
wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
chanp->wmep_aifsn = wmep->wmep_aifsn;
chanp->wmep_logcwmin = wmep->wmep_logcwmin;
chanp->wmep_logcwmax = wmep->wmep_logcwmax;
chanp->wmep_txopLimit = wmep->wmep_txopLimit;
chanp = &wme->wme_bssChanParams.cap_wmeParams[i];
wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
chanp->wmep_aifsn = wmep->wmep_aifsn;
chanp->wmep_logcwmin = wmep->wmep_logcwmin;
chanp->wmep_logcwmax = wmep->wmep_logcwmax;
chanp->wmep_txopLimit = wmep->wmep_txopLimit;
}
if (ic->ic_bsschan != IEEE80211_CHAN_ANYC)
mode = ieee80211_chan2mode(ic->ic_bsschan);
else
mode = IEEE80211_MODE_AUTO;
if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
(wme->wme_flags & WME_F_AGGRMODE) != 0)
do_aggrmode = 1;
else if ((vap->iv_opmode == IEEE80211_M_STA &&
(vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0))
do_aggrmode = 1;
else if ((vap->iv_opmode == IEEE80211_M_IBSS) &&
(vap->iv_flags & IEEE80211_F_WME))
do_aggrmode = 1;
if ((vap->iv_flags & IEEE80211_F_WME) == 0)
do_aggrmode = 1;
if (do_aggrmode) {
chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
chanp->wmep_aifsn = bssp->wmep_aifsn = aggrParam[mode].aifsn;
chanp->wmep_logcwmin = bssp->wmep_logcwmin =
aggrParam[mode].logcwmin;
chanp->wmep_logcwmax = bssp->wmep_logcwmax =
aggrParam[mode].logcwmax;
chanp->wmep_txopLimit = bssp->wmep_txopLimit =
(vap->iv_flags & IEEE80211_F_BURST) ?
aggrParam[mode].txopLimit : 0;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"update %s (chan+bss) [acm %u aifsn %u logcwmin %u "
"logcwmax %u txop %u]\n", ieee80211_wme_acnames[WME_AC_BE],
chanp->wmep_acm, chanp->wmep_aifsn, chanp->wmep_logcwmin,
chanp->wmep_logcwmax, chanp->wmep_txopLimit);
}
if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
vap->iv_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) {
static const uint8_t logCwMin[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = 3,
[IEEE80211_MODE_11A] = 3,
[IEEE80211_MODE_11B] = 4,
[IEEE80211_MODE_11G] = 3,
[IEEE80211_MODE_FH] = 4,
[IEEE80211_MODE_TURBO_A] = 3,
[IEEE80211_MODE_TURBO_G] = 3,
[IEEE80211_MODE_STURBO_A] = 3,
[IEEE80211_MODE_HALF] = 3,
[IEEE80211_MODE_QUARTER] = 3,
[IEEE80211_MODE_11NA] = 3,
[IEEE80211_MODE_11NG] = 3,
[IEEE80211_MODE_VHT_2GHZ] = 3,
[IEEE80211_MODE_VHT_5GHZ] = 3,
};
chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode];
IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"update %s (chan+bss) logcwmin %u\n",
ieee80211_wme_acnames[WME_AC_BE], chanp->wmep_logcwmin);
}
ieee80211_runtask(ic, &vap->iv_wme_task);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: WME params updated, cap_info 0x%x\n", __func__,
vap->iv_opmode == IEEE80211_M_STA ?
wme->wme_wmeChanParams.cap_info :
wme->wme_bssChanParams.cap_info);
}
void
ieee80211_wme_updateparams(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
if (ic->ic_caps & IEEE80211_C_WME) {
IEEE80211_LOCK(ic);
ieee80211_wme_updateparams_locked(vap);
IEEE80211_UNLOCK(ic);
}
}
void
ieee80211_wme_vap_getparams(struct ieee80211vap *vap, struct chanAccParams *wp)
{
memcpy(wp, &vap->iv_ic->ic_wme.wme_chanParams, sizeof(*wp));
}
void
ieee80211_wme_ic_getparams(struct ieee80211com *ic, struct chanAccParams *wp)
{
memcpy(wp, &ic->ic_wme.wme_chanParams, sizeof(*wp));
}
int
ieee80211_wme_vap_ac_is_noack(struct ieee80211vap *vap, int ac)
{
if (ac < 0 || ac >= WME_NUM_AC)
return (0);
return (!! vap->iv_ic->ic_wme.wme_chanParams.cap_wmeParams[ac].wmep_noackPolicy);
}
static void
parent_updown(void *arg, int npending)
{
struct ieee80211com *ic = arg;
ic->ic_parent(ic);
}
static void
update_mcast(void *arg, int npending)
{
struct ieee80211com *ic = arg;
ic->ic_update_mcast(ic);
}
static void
update_promisc(void *arg, int npending)
{
struct ieee80211com *ic = arg;
ic->ic_update_promisc(ic);
}
static void
update_channel(void *arg, int npending)
{
struct ieee80211com *ic = arg;
ic->ic_set_channel(ic);
ieee80211_radiotap_chan_change(ic);
}
static void
update_chw(void *arg, int npending)
{
struct ieee80211com *ic = arg;
ic->ic_update_chw(ic);
}
static void
vap_update_wme(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
if (vap->iv_wme_update != NULL)
vap->iv_wme_update(vap,
ic->ic_wme.wme_chanParams.cap_wmeParams);
else
ic->ic_wme.wme_update(ic);
IEEE80211_LOCK(ic);
if (vap->iv_opmode == IEEE80211_M_HOSTAP
|| vap->iv_opmode == IEEE80211_M_IBSS) {
wme->wme_bssChanParams.cap_info =
(wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT;
ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME);
}
IEEE80211_UNLOCK(ic);
}
static void
restart_vaps(void *arg, int npending)
{
struct ieee80211com *ic = arg;
ieee80211_suspend_all(ic);
ieee80211_resume_all(ic);
}
void
ieee80211_waitfor_parent(struct ieee80211com *ic)
{
taskqueue_block(ic->ic_tq);
ieee80211_draintask(ic, &ic->ic_parent_task);
ieee80211_draintask(ic, &ic->ic_mcast_task);
ieee80211_draintask(ic, &ic->ic_promisc_task);
ieee80211_draintask(ic, &ic->ic_chan_task);
ieee80211_draintask(ic, &ic->ic_bmiss_task);
ieee80211_draintask(ic, &ic->ic_chw_task);
taskqueue_unblock(ic->ic_tq);
}
static int
ieee80211_start_check_reset_chan(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
if ((vap->iv_opmode == IEEE80211_M_IBSS &&
IEEE80211_IS_CHAN_NOADHOC(ic->ic_curchan)) ||
(vap->iv_opmode == IEEE80211_M_HOSTAP &&
IEEE80211_IS_CHAN_NOHOSTAP(ic->ic_curchan)))
return (1);
return (0);
}
static void
ieee80211_start_reset_chan(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
ic->ic_curchan = &ic->ic_channels[0];
}
void
ieee80211_start_locked(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
IEEE80211_LOCK_ASSERT(ic);
IEEE80211_DPRINTF(vap,
IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
"start running, %d vaps running\n", ic->ic_nrunning);
if (!ieee80211_vap_ifp_check_is_running(vap)) {
ieee80211_vap_ifp_set_running_state(vap, true);
ieee80211_notify_ifnet_change(vap, IFF_DRV_RUNNING);
if (ic->ic_nrunning++ == 0) {
if (ieee80211_start_check_reset_chan(vap))
ieee80211_start_reset_chan(vap);
IEEE80211_DPRINTF(vap,
IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
"%s: up parent %s\n", __func__, ic->ic_name);
ieee80211_runtask(ic, &ic->ic_parent_task);
return;
}
}
if (vap->iv_roaming != IEEE80211_ROAMING_MANUAL) {
if (vap->iv_opmode == IEEE80211_M_STA) {
#if 0
if (vap->iv_state >= IEEE80211_S_RUN)
ieee80211_new_state_locked(vap,
IEEE80211_S_ASSOC, 1);
else
#endif
ieee80211_new_state_locked(vap,
IEEE80211_S_SCAN, 0);
} else {
vap->iv_flags_ext |= IEEE80211_FEXT_REINIT;
if (vap->iv_opmode == IEEE80211_M_MONITOR ||
vap->iv_opmode == IEEE80211_M_WDS)
ieee80211_new_state_locked(vap,
IEEE80211_S_RUN, -1);
else
ieee80211_new_state_locked(vap,
IEEE80211_S_SCAN, 0);
}
}
}
void
ieee80211_init(void *arg)
{
struct ieee80211vap *vap = arg;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
"%s\n", __func__);
IEEE80211_LOCK(vap->iv_ic);
ieee80211_start_locked(vap);
IEEE80211_UNLOCK(vap->iv_ic);
}
void
ieee80211_start_all(struct ieee80211com *ic)
{
struct ieee80211vap *vap;
IEEE80211_LOCK(ic);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
struct ifnet *ifp = vap->iv_ifp;
if (IFNET_IS_UP_RUNNING(ifp))
ieee80211_start_locked(vap);
}
IEEE80211_UNLOCK(ic);
}
void
ieee80211_stop_locked(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
IEEE80211_LOCK_ASSERT(ic);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
"stop running, %d vaps running\n", ic->ic_nrunning);
ieee80211_new_state_locked(vap, IEEE80211_S_INIT, -1);
if (ieee80211_vap_ifp_check_is_running(vap)) {
ieee80211_vap_ifp_set_running_state(vap, false);
ieee80211_notify_ifnet_change(vap, IFF_DRV_RUNNING);
if (--ic->ic_nrunning == 0) {
IEEE80211_DPRINTF(vap,
IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
"down parent %s\n", ic->ic_name);
ieee80211_runtask(ic, &ic->ic_parent_task);
}
}
}
void
ieee80211_stop(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
IEEE80211_LOCK(ic);
ieee80211_stop_locked(vap);
IEEE80211_UNLOCK(ic);
}
void
ieee80211_stop_all(struct ieee80211com *ic)
{
struct ieee80211vap *vap;
IEEE80211_LOCK(ic);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
struct ifnet *ifp = vap->iv_ifp;
if (IFNET_IS_UP_RUNNING(ifp))
ieee80211_stop_locked(vap);
}
IEEE80211_UNLOCK(ic);
ieee80211_waitfor_parent(ic);
}
void
ieee80211_suspend_all(struct ieee80211com *ic)
{
struct ieee80211vap *vap;
IEEE80211_LOCK(ic);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
struct ifnet *ifp = vap->iv_ifp;
if (IFNET_IS_UP_RUNNING(ifp)) {
vap->iv_flags_ext |= IEEE80211_FEXT_RESUME;
ieee80211_stop_locked(vap);
}
}
IEEE80211_UNLOCK(ic);
ieee80211_waitfor_parent(ic);
}
void
ieee80211_resume_all(struct ieee80211com *ic)
{
struct ieee80211vap *vap;
IEEE80211_LOCK(ic);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
struct ifnet *ifp = vap->iv_ifp;
if (!IFNET_IS_UP_RUNNING(ifp) &&
(vap->iv_flags_ext & IEEE80211_FEXT_RESUME)) {
vap->iv_flags_ext &= ~IEEE80211_FEXT_RESUME;
ieee80211_start_locked(vap);
}
}
IEEE80211_UNLOCK(ic);
}
void
ieee80211_restart_all(struct ieee80211com *ic)
{
taskqueue_enqueue(taskqueue_thread, &ic->ic_restart_task);
}
void
ieee80211_beacon_miss(struct ieee80211com *ic)
{
IEEE80211_LOCK(ic);
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
ieee80211_runtask(ic, &ic->ic_bmiss_task);
}
IEEE80211_UNLOCK(ic);
}
static void
beacon_miss(void *arg, int npending)
{
struct ieee80211com *ic = arg;
struct ieee80211vap *vap;
IEEE80211_LOCK(ic);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap->iv_opmode == IEEE80211_M_STA &&
vap->iv_state >= IEEE80211_S_RUN &&
vap->iv_bmiss != NULL)
vap->iv_bmiss(vap);
}
IEEE80211_UNLOCK(ic);
}
static void
beacon_swmiss(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
struct ieee80211com *ic = vap->iv_ic;
IEEE80211_LOCK(ic);
if (vap->iv_state >= IEEE80211_S_RUN) {
vap->iv_bmiss(vap);
}
IEEE80211_UNLOCK(ic);
}
void
ieee80211_swbmiss(void *arg)
{
struct ieee80211vap *vap = arg;
struct ieee80211com *ic = vap->iv_ic;
IEEE80211_LOCK_ASSERT(ic);
KASSERT(vap->iv_state >= IEEE80211_S_RUN,
("wrong state %d", vap->iv_state));
if (ic->ic_flags & IEEE80211_F_SCAN) {
vap->iv_swbmiss_count = 0;
} else if (vap->iv_swbmiss_count == 0) {
if (vap->iv_bmiss != NULL)
ieee80211_runtask(ic, &vap->iv_swbmiss_task);
} else
vap->iv_swbmiss_count = 0;
callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
ieee80211_swbmiss, vap);
}
void
ieee80211_csa_startswitch(struct ieee80211com *ic,
struct ieee80211_channel *c, int mode, int count)
{
struct ieee80211vap *vap;
IEEE80211_LOCK_ASSERT(ic);
ic->ic_csa_newchan = c;
ic->ic_csa_mode = mode;
ic->ic_csa_count = count;
ic->ic_flags |= IEEE80211_F_CSAPENDING;
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
vap->iv_opmode == IEEE80211_M_IBSS ||
vap->iv_opmode == IEEE80211_M_MBSS)
ieee80211_beacon_notify(vap, IEEE80211_BEACON_CSA);
if (vap->iv_state == IEEE80211_S_RUN)
ieee80211_new_state_locked(vap, IEEE80211_S_CSA, 0);
}
ieee80211_notify_csa(ic, c, mode, count);
}
static void
csa_completeswitch(struct ieee80211com *ic)
{
struct ieee80211vap *vap;
ic->ic_csa_newchan = NULL;
ic->ic_flags &= ~IEEE80211_F_CSAPENDING;
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
if (vap->iv_state == IEEE80211_S_CSA)
ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
}
void
ieee80211_csa_completeswitch(struct ieee80211com *ic)
{
struct ieee80211vap *vap;
IEEE80211_LOCK_ASSERT(ic);
KASSERT(ic->ic_flags & IEEE80211_F_CSAPENDING, ("csa not pending"));
ieee80211_setcurchan(ic, ic->ic_csa_newchan);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
if (vap->iv_state == IEEE80211_S_CSA)
vap->iv_bss->ni_chan = ic->ic_curchan;
csa_completeswitch(ic);
}
void
ieee80211_csa_cancelswitch(struct ieee80211com *ic)
{
IEEE80211_LOCK_ASSERT(ic);
csa_completeswitch(ic);
}
void
ieee80211_cac_completeswitch(struct ieee80211vap *vap0)
{
struct ieee80211com *ic = vap0->iv_ic;
struct ieee80211vap *vap;
IEEE80211_LOCK(ic);
KASSERT(vap0->iv_state == IEEE80211_S_CAC,
("wrong state %d", vap0->iv_state));
ieee80211_new_state_locked(vap0, IEEE80211_S_RUN, 0);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
if (vap->iv_state == IEEE80211_S_CAC && vap != vap0)
ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
IEEE80211_UNLOCK(ic);
}
static void
markwaiting(struct ieee80211vap *vap0)
{
struct ieee80211com *ic = vap0->iv_ic;
struct ieee80211vap *vap;
IEEE80211_LOCK_ASSERT(ic);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap == vap0)
continue;
if (vap->iv_state != IEEE80211_S_INIT) {
vap->iv_newstate(vap, IEEE80211_S_INIT, 0);
IEEE80211_LOCK_ASSERT(ic);
vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
}
}
}
static void
wakeupwaiting(struct ieee80211vap *vap0)
{
struct ieee80211com *ic = vap0->iv_ic;
struct ieee80211vap *vap;
IEEE80211_LOCK_ASSERT(ic);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap == vap0)
continue;
if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) {
vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT;
vap->iv_newstate(vap,
vap->iv_opmode == IEEE80211_M_STA ?
IEEE80211_S_SCAN : IEEE80211_S_RUN, 0);
IEEE80211_LOCK_ASSERT(ic);
}
}
}
static int
_ieee80211_newstate_get_next_empty_slot(struct ieee80211vap *vap)
{
int nstate_num;
IEEE80211_LOCK_ASSERT(vap->iv_ic);
if (vap->iv_nstate_n >= NET80211_IV_NSTATE_NUM)
return (-1);
nstate_num = vap->iv_nstate_b + vap->iv_nstate_n;
nstate_num %= NET80211_IV_NSTATE_NUM;
vap->iv_nstate_n++;
return (nstate_num);
}
static int
_ieee80211_newstate_get_next_pending_slot(struct ieee80211vap *vap)
{
int nstate_num;
IEEE80211_LOCK_ASSERT(vap->iv_ic);
KASSERT(vap->iv_nstate_n > 0, ("%s: vap %p iv_nstate_n %d\n",
__func__, vap, vap->iv_nstate_n));
nstate_num = vap->iv_nstate_b;
vap->iv_nstate_b++;
if (vap->iv_nstate_b >= NET80211_IV_NSTATE_NUM)
vap->iv_nstate_b = 0;
vap->iv_nstate_n--;
return (nstate_num);
}
static int
_ieee80211_newstate_get_npending(struct ieee80211vap *vap)
{
IEEE80211_LOCK_ASSERT(vap->iv_ic);
return (vap->iv_nstate_n);
}
static void
ieee80211_newstate_cb(void *xvap, int npending)
{
struct ieee80211vap *vap = xvap;
struct ieee80211com *ic = vap->iv_ic;
enum ieee80211_state nstate, ostate;
int arg, rc, nstate_num;
KASSERT(npending == 1, ("%s: vap %p with npending %d != 1\n",
__func__, vap, npending));
IEEE80211_LOCK(ic);
nstate_num = _ieee80211_newstate_get_next_pending_slot(vap);
vap->iv_nstate = nstate = vap->iv_nstates[nstate_num];
arg = vap->iv_nstate_args[nstate_num];
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s:%d: running state update %s -> %s (%d)\n",
__func__, __LINE__,
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate],
npending);
if (vap->iv_flags_ext & IEEE80211_FEXT_REINIT) {
vap->iv_nstate = IEEE80211_S_INIT;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s: %s -> %s arg %d -> %s arg %d\n", __func__,
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[vap->iv_nstate], 0,
ieee80211_state_name[nstate], arg);
vap->iv_newstate(vap, vap->iv_nstate, 0);
IEEE80211_LOCK_ASSERT(ic);
vap->iv_flags_ext &= ~(IEEE80211_FEXT_REINIT |
IEEE80211_FEXT_STATEWAIT);
ieee80211_new_state_locked(vap, nstate, arg);
goto done;
}
ostate = vap->iv_state;
if (nstate == IEEE80211_S_SCAN && ostate != IEEE80211_S_INIT) {
markwaiting(vap);
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s: %s -> %s arg %d\n", __func__,
ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg);
rc = vap->iv_newstate(vap, nstate, arg);
IEEE80211_LOCK_ASSERT(ic);
vap->iv_flags_ext &= ~IEEE80211_FEXT_STATEWAIT;
if (rc != 0) {
KASSERT(rc != EINPROGRESS, ("iv_newstate was deferred"));
KASSERT(nstate != IEEE80211_S_INIT,
("INIT state change failed"));
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s: %s returned error %d\n", __func__,
ieee80211_state_name[nstate], rc);
goto done;
}
if (nstate == IEEE80211_S_RUN) {
vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
}
if (ostate == nstate)
goto done;
if (nstate == IEEE80211_S_RUN) {
wakeupwaiting(vap);
} else if (nstate == IEEE80211_S_INIT) {
ieee80211_scan_flush(vap);
}
done:
IEEE80211_UNLOCK(ic);
}
int
ieee80211_new_state_locked(struct ieee80211vap *vap,
enum ieee80211_state nstate, int arg)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211vap *vp;
enum ieee80211_state ostate;
int nrunning, nscanning, nstate_num;
IEEE80211_LOCK_ASSERT(ic);
if (vap->iv_flags_ext & IEEE80211_FEXT_STATEWAIT) {
if (vap->iv_nstate == IEEE80211_S_INIT ||
((vap->iv_state == IEEE80211_S_INIT ||
(vap->iv_flags_ext & IEEE80211_FEXT_REINIT)) &&
vap->iv_nstate == IEEE80211_S_SCAN &&
nstate > IEEE80211_S_SCAN)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s:%d: %s -> %s (%s) transition discarded\n",
__func__, __LINE__,
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate],
ieee80211_state_name[vap->iv_nstate]);
return -1;
}
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s:%d: starting state update %s -> %s (%s)\n",
__func__, __LINE__,
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[vap->iv_nstate],
ieee80211_state_name[nstate]);
nrunning = nscanning = 0;
TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) {
if (vp != vap) {
if (vp->iv_state >= IEEE80211_S_RUN)
nrunning++;
else if (vp->iv_state > IEEE80211_S_INIT)
nscanning++;
}
}
if (vap->iv_nstate_n == 0) {
ostate = vap->iv_state;
} else {
nstate_num = (vap->iv_nstate_b + vap->iv_nstate_n - 1) % NET80211_IV_NSTATE_NUM;
ostate = vap->iv_nstates[nstate_num];
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s: %s -> %s (arg %d) (nrunning %d nscanning %d)\n", __func__,
ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg,
nrunning, nscanning);
switch (nstate) {
case IEEE80211_S_SCAN:
if (ostate == IEEE80211_S_INIT) {
KASSERT(!(nscanning && nrunning),
("%d scanning and %d running", nscanning, nrunning));
if (nscanning) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s: defer %s -> %s\n",
__func__, ieee80211_state_name[ostate],
ieee80211_state_name[nstate]);
vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
return 0;
}
if (nrunning) {
if (vap->iv_opmode == IEEE80211_M_STA)
nstate = IEEE80211_S_SCAN;
else
nstate = IEEE80211_S_RUN;
#ifdef IEEE80211_DEBUG
if (nstate != IEEE80211_S_SCAN) {
IEEE80211_DPRINTF(vap,
IEEE80211_MSG_STATE,
"%s: override, now %s -> %s\n",
__func__,
ieee80211_state_name[ostate],
ieee80211_state_name[nstate]);
}
#endif
}
}
break;
case IEEE80211_S_RUN:
if (vap->iv_opmode == IEEE80211_M_WDS &&
(vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) &&
nscanning) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s: defer %s -> %s (legacy WDS)\n", __func__,
ieee80211_state_name[ostate],
ieee80211_state_name[nstate]);
vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
return 0;
}
if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
(vap->iv_flags_ext & IEEE80211_FEXT_DFS) &&
!IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) {
nstate = IEEE80211_S_CAC;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s: override %s -> %s (DFS)\n", __func__,
ieee80211_state_name[ostate],
ieee80211_state_name[nstate]);
}
break;
case IEEE80211_S_INIT:
ieee80211_cancel_scan(vap);
if (ostate == IEEE80211_S_INIT ) {
vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT;
}
default:
break;
}
nstate_num = _ieee80211_newstate_get_next_empty_slot(vap);
if (nstate_num == -1) {
ic_printf(ic, "%s:%d: pending %s -> %s (now to %s) "
"transition lost. %d/%d pending state changes:\n",
__func__, __LINE__,
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[vap->iv_nstate],
ieee80211_state_name[nstate],
_ieee80211_newstate_get_npending(vap),
NET80211_IV_NSTATE_NUM);
return (EAGAIN);
}
vap->iv_nstates[nstate_num] = nstate;
vap->iv_nstate_args[nstate_num] = arg;
vap->iv_flags_ext |= IEEE80211_FEXT_STATEWAIT;
ieee80211_runtask(ic, &vap->iv_nstate_task[nstate_num]);
return EINPROGRESS;
}
int
ieee80211_new_state(struct ieee80211vap *vap,
enum ieee80211_state nstate, int arg)
{
struct ieee80211com *ic = vap->iv_ic;
int rc;
IEEE80211_LOCK(ic);
rc = ieee80211_new_state_locked(vap, nstate, arg);
IEEE80211_UNLOCK(ic);
return rc;
}