Merge from madwifi-dfs r3150. Fixes for AP+STA mode state machine logic

git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@3152 0192ed92-7a03-0410-a25b-9323aeb14dbd
This commit is contained in:
mtaylor 2008-01-16 01:04:01 +00:00
parent e3d16ecc8f
commit ec46fcf728

View File

@ -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;
}