diff --git a/net80211/ieee80211_proto.c b/net80211/ieee80211_proto.c index 7ffff4e..d94dfc9 100644 --- a/net80211/ieee80211_proto.c +++ b/net80211/ieee80211_proto.c @@ -972,27 +972,46 @@ ieee80211_init(struct net_device *dev, int forcescan) * 802.11 state machine as appropriate. * XXX parent should always be up+running */ - if (IS_RUNNING(ic->ic_dev) && - ic->ic_roaming != IEEE80211_ROAMING_MANUAL) { + if (IS_RUNNING(ic->ic_dev)) { if (vap->iv_opmode == IEEE80211_M_STA) { - /* - * Try to be intelligent about clocking the state - * machine. If we're currently in RUN state then - * we should be able to apply any new state/parameters - * simply by re-associating. Otherwise we need to - * re-scan to select an appropriate ap. - */ - if (vap->iv_state != IEEE80211_S_RUN || forcescan) - ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); - else - ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1); + if(ic->ic_roaming != IEEE80211_ROAMING_MANUAL) { + /* + * Try to be intelligent about clocking the state + * machine. If we're currently in RUN state then + * we should be able to apply any new state/parameters + * simply by re-associating. Otherwise we need to + * re-scan to select an appropriate ap. + */ + if (vap->iv_state != IEEE80211_S_RUN || forcescan) { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "Bringing vap %p[%s] to %s\n", + vap, vap->iv_nickname, + ieee80211_state_name[IEEE80211_S_SCAN]); + ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + } + else { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "Bringing vap %p[%s] to %s\n", + vap, vap->iv_nickname, + ieee80211_state_name[IEEE80211_S_ASSOC]); + ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1); + } + } } else { /* * When the old state is running the vap must * be brought to init. */ - if (vap->iv_state == IEEE80211_S_RUN) + if (vap->iv_state == IEEE80211_S_RUN) { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "Bringing vap %p[%s] to %s\n", + vap, vap->iv_nickname, + ieee80211_state_name[IEEE80211_S_INIT]); ieee80211_new_state(vap, IEEE80211_S_INIT, -1); + } /* * For monitor+wds modes there's nothing to do but * start running. Otherwise, if this is the first @@ -1002,9 +1021,20 @@ ieee80211_init(struct net_device *dev, int forcescan) */ if (vap->iv_opmode == IEEE80211_M_MONITOR || vap->iv_opmode == IEEE80211_M_WDS) { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "Bringing vap %p[%s] to %s\n", + vap, vap->iv_nickname, + ieee80211_state_name[IEEE80211_S_RUN]); ieee80211_new_state(vap, IEEE80211_S_RUN, -1); - } else + } else { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "Bringing vap %p[%s] to %s\n", + vap, vap->iv_nickname, + ieee80211_state_name[IEEE80211_S_SCAN]); ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + } } } return 0; @@ -1263,8 +1293,6 @@ __ieee80211_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int enum ieee80211_state ostate; ostate = vap->iv_state; - IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, - ieee80211_state_name[ostate], ieee80211_state_name[nstate]); vap->iv_state = nstate; /* state transition */ del_timer(&vap->iv_mgtsend); if ((vap->iv_opmode != IEEE80211_M_HOSTAP) && @@ -1610,104 +1638,234 @@ __ieee80211_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int return 0; } +/* Get the dominant state of the device (init, running, or scanning (and/or associating)) */ +static int get_dominant_state(struct ieee80211com *ic) { + int nscanning = 0; + int nrunning = 0; + struct ieee80211vap *tmpvap; + + TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) { + if (tmpvap->iv_opmode == IEEE80211_M_MONITOR) + /* skip monitor vaps as their + * S_RUN shouldn't have any + * influence on modifying state + * transition */ + continue; + if (tmpvap->iv_state == IEEE80211_S_RUN) + nrunning++; + else if (tmpvap->iv_state == IEEE80211_S_SCAN || + tmpvap->iv_state == IEEE80211_S_AUTH || + tmpvap->iv_state == IEEE80211_S_ASSOC) + { + KASSERT((nscanning <= 1), ("Two VAPs cannot scan at the same time\n")); + nscanning++; + } + } + KASSERT(!(nscanning && nrunning), ("SCAN and RUN can't happen at the same time\n")); + KASSERT((nscanning <= 1), ("Two VAPs must not SCAN at the same time\n")); + + if (nrunning > 0) + return IEEE80211_S_RUN; + else if (nscanning > 0) + return IEEE80211_S_SCAN; + else + return IEEE80211_S_INIT; +} + +static void +dump_vap_states(struct ieee80211com *ic, struct ieee80211vap* highlighed) +{ + /* RE-count the number of VAPs in RUN, SCAN states */ + int nrunning = 0; + int nscanning = 0; + struct ieee80211vap *tmpvap; + TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) { + IEEE80211_DPRINTF(tmpvap, IEEE80211_MSG_STATE, + "%s: VAP %s%p[%24s]%s = %s%s%s.\n", + __func__, + (highlighed == tmpvap ? "*" : " "), + tmpvap, tmpvap->iv_nickname, (highlighed == tmpvap ? "*" : " "), + ieee80211_state_name[tmpvap->iv_state], + tmpvap->iv_state == IEEE80211_S_RUN ? "[RUNNING]" : "", + (tmpvap->iv_state == IEEE80211_S_SCAN || + tmpvap->iv_state == IEEE80211_S_AUTH || + tmpvap->iv_state == IEEE80211_S_ASSOC) ? "[SCANNING]" : "" + ); + if (tmpvap->iv_opmode == IEEE80211_M_MONITOR) + { + /* skip monitor vaps as their + * S_RUN shouldn't have any + * influence on modifying state + * transition */ + continue; + } + if (tmpvap->iv_state == IEEE80211_S_RUN) + { + KASSERT((nscanning == 0), ("SCAN and RUN can't happen at the same time\n")); + nrunning++; + } + if (tmpvap->iv_state == IEEE80211_S_SCAN || + tmpvap->iv_state == IEEE80211_S_AUTH || /* STA in WDS/Repeater */ + tmpvap->iv_state == IEEE80211_S_ASSOC) + { + KASSERT((nscanning == 0), ("Two VAPs cannot scan at the same time\n")); + KASSERT((nrunning == 0), ("SCAN and RUN can't happen at the same time\n")); + nscanning++; + } + } +} + static int ieee80211_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; enum ieee80211_state ostate; + enum ieee80211_state dstate; + int blocked = 0; struct ieee80211vap *tmpvap; ostate = vap->iv_state; + dstate = get_dominant_state(ic); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: %p[%s] %s -> %s (dominant %s)\n", + __func__, vap, vap->iv_nickname, + ieee80211_state_name[ostate], + ieee80211_state_name[nstate], + ieee80211_state_name[dstate]); + switch (nstate) { + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: case IEEE80211_S_SCAN: - if (ostate == IEEE80211_S_INIT) { - int nrunning, nscanning; - - nrunning = nscanning = 0; - TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) { - if (vap != tmpvap) { - if (tmpvap->iv_opmode == IEEE80211_M_MONITOR) - /* skip monitor vaps as their - * S_RUN shouldn't have any - * influence on modifying state - * transition */ - continue; - if (tmpvap->iv_state == IEEE80211_S_RUN) - nrunning++; - else if (tmpvap->iv_state == IEEE80211_S_SCAN || - tmpvap->iv_state == IEEE80211_S_AUTH || /* STA in WDS/Repeater */ - tmpvap->iv_state == IEEE80211_S_ASSOC) - nscanning++; - } + switch (dstate) { + case IEEE80211_S_RUN: + if(vap->iv_opmode == IEEE80211_M_MONITOR || + vap->iv_opmode == IEEE80211_M_WDS || + vap->iv_opmode == IEEE80211_M_HOSTAP) + { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: Jumping directly to RUN on VAP %p [%s].\n", + __func__, vap, vap->iv_nickname); + /* One or more VAPs are running, so non-station VAPs + * can skip SCAN/AUTH/ASSOC states and just run. */ + __ieee80211_newstate(vap, IEEE80211_S_RUN, arg); } - - KASSERT(!(nscanning && nrunning), ("SCAN and RUN can't happen at the same time\n")); - - if (!nscanning && !nrunning) { - /* when no one is running or scanning, start a new scan */ + else { + /* We'll use this flag briefly to mark transition in progress */ + ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_PENDING; + /* IEEE80211_M_IBSS or IEEE80211_M_STA VAP + * is forced to scan, we need to change + * all other VAPs state to S_INIT and pend for + * the scan completion */ + TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) { + if (vap != tmpvap && tmpvap->iv_opmode != IEEE80211_M_MONITOR) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: Setting SCAN_PENDING flag on VAP %p [%s].\n", + __func__, tmpvap, tmpvap->iv_nickname); + tmpvap->iv_flags_ext |= IEEE80211_FEXT_SCAN_PENDING; + if(tmpvap->iv_state != IEEE80211_S_INIT) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: Forcing INIT state on VAP %p [%s].\n", + __func__, tmpvap, tmpvap->iv_nickname); + tmpvap->iv_newstate(tmpvap, IEEE80211_S_INIT, 0); + } + else { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: NOT forcing INIT state on VAP %p [%s].\n", + __func__, tmpvap, tmpvap->iv_nickname); + } + } + } + /* We used this flag briefly to mark transition in progress */ + ic->ic_flags_ext &= ~IEEE80211_FEXT_SCAN_PENDING; + /* Transition S_INIT -> S_SCAN */ __ieee80211_newstate(vap, nstate, arg); - } else if (!nscanning && nrunning) { - /* when no one is scanning but someone is running, bypass - * scan and go to run state immediately */ - if (vap->iv_opmode == IEEE80211_M_MONITOR || - vap->iv_opmode == IEEE80211_M_WDS || - vap->iv_opmode == IEEE80211_M_HOSTAP) { - __ieee80211_newstate(vap, IEEE80211_S_RUN, arg); - } else { - /* MW: avoid invalid S_INIT -> S_RUN transition */ - __ieee80211_newstate(vap, nstate, arg); - } - } else if (nscanning && !nrunning) { - /* when someone is scanning and no one is running, set - * the scan pending flag. Don't go through state machine */ - IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, - "%s: %s -> %s with SCAN_PENDING\n", - __func__, - ieee80211_state_name[ostate], - ieee80211_state_name[nstate]); + break; + } + break; + case IEEE80211_S_SCAN: + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + /* this VAP was scanning */ + if (ostate == IEEE80211_S_SCAN || /* STA in WDS/Repeater needs to bring up other VAPs */ + ostate == IEEE80211_S_AUTH || + ostate == IEEE80211_S_ASSOC) + { + /* Transition (S_SCAN|S_AUTH|S_ASSOC) -> S_SCAN */ + __ieee80211_newstate(vap, nstate, arg); + } + else { + /* Someone else is scanning, so block the transition */ vap->iv_flags_ext |= IEEE80211_FEXT_SCAN_PENDING; + __ieee80211_newstate(vap, IEEE80211_S_INIT, arg); + blocked = 1; } - } else { - /* the VAP is forced to scan, we need to change all other VAPs state - * to INIT and pend for the scan completion */ - TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) { - if (vap != tmpvap && tmpvap->iv_state != IEEE80211_S_INIT) { - tmpvap->iv_newstate(tmpvap, IEEE80211_S_INIT, 0); - tmpvap->iv_flags_ext |= IEEE80211_FEXT_SCAN_PENDING; - } - } - - /* start the new scan */ + break; + case IEEE80211_S_INIT: + /* Transition S_INIT -> S_SCAN */ __ieee80211_newstate(vap, nstate, arg); + break; } break; case IEEE80211_S_RUN: - if ((ostate == IEEE80211_S_SCAN) || /* AP coming out of scan */ - (vap->iv_opmode == IEEE80211_M_STA)) { /* STA in WDS/Repeater needs to bring up other VAPs */ + /* this VAP was scanning */ + if (ostate == IEEE80211_S_SCAN || /* STA in WDS/Repeater needs to bring up other VAPs */ + ostate == IEEE80211_S_AUTH || + ostate == IEEE80211_S_ASSOC) + { + /* Transition (S_SCAN|S_AUTH|S_ASSOC) -> S_RUN */ __ieee80211_newstate(vap, nstate, arg); - - /* bring up all other vaps pending on the scan */ - TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) { - if (vap != tmpvap) { - if (tmpvap->iv_flags_ext & IEEE80211_FEXT_SCAN_PENDING) { - tmpvap->iv_flags_ext &= ~IEEE80211_FEXT_SCAN_PENDING; - tmpvap->iv_newstate(tmpvap, IEEE80211_S_RUN, 0); + /* Then bring up all other vaps pending on the scan*/ + dstate = get_dominant_state(ic); + if (dstate == IEEE80211_S_RUN) { + TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) { + if (vap != tmpvap && + tmpvap->iv_opmode != IEEE80211_M_MONITOR && + (tmpvap->iv_flags_ext & IEEE80211_FEXT_SCAN_PENDING)) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: Clearing SCAN_PENDING flag from VAP %p [%s] and transitioning to RUN state.\n", + __func__, tmpvap, tmpvap->iv_nickname); + tmpvap->iv_flags_ext &= ~IEEE80211_FEXT_SCAN_PENDING; + if(tmpvap->iv_state != IEEE80211_S_RUN) { + tmpvap->iv_newstate(tmpvap, IEEE80211_S_RUN, 0); + } + else if(tmpvap->iv_opmode == IEEE80211_M_HOSTAP) { + /* Force other AP through -> INIT -> RUN to make + * sure beacons are reallocated */ + tmpvap->iv_newstate(tmpvap, IEEE80211_S_INIT, 0); + tmpvap->iv_newstate(tmpvap, IEEE80211_S_RUN, 0); + } } } } - } else + } else if (dstate == IEEE80211_S_SCAN) { + /* Force to scan pending... someone is scanning */ + vap->iv_flags_ext |= IEEE80211_FEXT_SCAN_PENDING; + __ieee80211_newstate(vap, IEEE80211_S_INIT, arg); + blocked = 1; + } + else { __ieee80211_newstate(vap, nstate, arg); + } break; - - case IEEE80211_S_INIT: - if (ostate == IEEE80211_S_INIT) - vap->iv_flags_ext &= ~IEEE80211_FEXT_SCAN_PENDING; - /* fall through */ - default: __ieee80211_newstate(vap, nstate, arg); } + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: %s requested transition %s -> %s on VAP %p [%s]. " + "Dominant state is %s.\n", + __func__, + (blocked ? "BLOCKED" : "ALLOWED"), + ieee80211_state_name[ostate], + ieee80211_state_name[nstate], + vap, + vap->iv_nickname, + ieee80211_state_name[dstate]); + + dump_vap_states(ic, vap); return 0; }