static void print_chaninfo(const struct ieee80211_channel *c) { char buf[14]; buf[0] = '\0'; if (IEEE80211_IS_CHAN_FHSS(c)) strlcat(buf, " FHSS", sizeof(buf)); if (IEEE80211_IS_CHAN_A(c)) strlcat(buf, " 11a", sizeof(buf)); /* XXX 11g schizophrenia */ if (IEEE80211_IS_CHAN_G(c) || IEEE80211_IS_CHAN_PUREG(c)) strlcat(buf, " 11g", sizeof(buf)); else if (IEEE80211_IS_CHAN_B(c)) strlcat(buf, " 11b", sizeof(buf)); if (IEEE80211_IS_CHAN_STURBO(c)) strlcat(buf, " Static", sizeof(buf)); if (IEEE80211_IS_CHAN_DTURBO(c)) strlcat(buf, " Dynamic", sizeof(buf)); if (IEEE80211_IS_CHAN_HALF(c)) strlcat(buf, " Half", sizeof(buf)); if (IEEE80211_IS_CHAN_QUARTER(c)) strlcat(buf, " Quarter", sizeof(buf)); printf("Channel %3u : %u%c%c Mhz%-14.14s", c->ic_ieee, c->ic_freq, IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ', IEEE80211_IS_CHAN_RADAR(c) ? '!' : ' ', buf); }
static void cac_timeout_callout(void *arg) { struct ieee80211vap *vap = arg; struct ieee80211com *ic; struct ieee80211_dfs_state *dfs; int i; wlan_serialize_enter(); ic = vap->iv_ic; dfs = &ic->ic_dfs; if (vap->iv_state != IEEE80211_S_CAC) { /* NB: just in case */ wlan_serialize_exit(); return; } /* * When radar is detected during a CAC we are woken * up prematurely to switch to a new channel. * Check the channel to decide how to act. */ if (IEEE80211_IS_CHAN_RADAR(ic->ic_curchan)) { ieee80211_notify_cac(ic, ic->ic_curchan, IEEE80211_NOTIFY_CAC_RADAR); if_printf(vap->iv_ifp, "CAC timer on channel %u (%u MHz) stopped due to radar\n", ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq); /* XXX clobbers any existing desired channel */ /* NB: dfs->newchan may be NULL, that's ok */ vap->iv_des_chan = dfs->newchan; /* XXX recursive lock need ieee80211_new_state_locked */ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); } else { if_printf(vap->iv_ifp, "CAC timer on channel %u (%u MHz) expired; " "no radar detected\n", ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq); /* * Mark all channels with the current frequency * as having completed CAC; this keeps us from * doing it again until we change channels. */ for (i = 0; i < ic->ic_nchans; i++) { struct ieee80211_channel *c = &ic->ic_channels[i]; if (c->ic_freq == ic->ic_curchan->ic_freq) c->ic_state |= IEEE80211_CHANSTATE_CACDONE; } ieee80211_notify_cac(ic, ic->ic_curchan, IEEE80211_NOTIFY_CAC_EXPIRE); ieee80211_cac_completeswitch(vap); } wlan_serialize_exit(); }
struct ieee80211_channel * ieee80211_dfs_pickchannel(struct ieee80211com *ic) { struct ieee80211_channel *c; int i, flags; uint16_t v; /* * Consult the scan cache first. */ flags = ic->ic_curchan->ic_flags & IEEE80211_CHAN_ALL; /* * XXX if curchan is HT this will never find a channel * XXX 'cuz we scan only legacy channels */ c = ieee80211_scan_pickchannel(ic, flags); if (c != NULL) return c; /* * No channel found in scan cache; select a compatible * one at random (skipping channels where radar has * been detected). */ get_random_bytes(&v, sizeof(v)); v %= ic->ic_nchans; for (i = v; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; if (!IEEE80211_IS_CHAN_RADAR(c) && (c->ic_flags & flags) == flags) return c; } for (i = 0; i < v; i++) { c = &ic->ic_channels[i]; if (!IEEE80211_IS_CHAN_RADAR(c) && (c->ic_flags & flags) == flags) return c; } if_printf(ic->ic_ifp, "HELP, no channel located to switch to!\n"); return NULL; }
static void dfs_timeout(void *arg) { struct ieee80211com *ic = arg; struct ieee80211_dfs_state *dfs = &ic->ic_dfs; struct ieee80211_channel *c; int i, oldest, now; IEEE80211_LOCK_ASSERT(ic); now = oldest = ticks; for (i = 0; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; if (IEEE80211_IS_CHAN_RADAR(c)) { if (time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) { c->ic_state &= ~IEEE80211_CHANSTATE_RADAR; if (c->ic_state & IEEE80211_CHANSTATE_NORADAR) { /* * NB: do this here so we get only one * msg instead of one for every channel * table entry. */ if_printf(ic->ic_ifp, "radar on channel" " %u (%u MHz) cleared after timeout\n", c->ic_ieee, c->ic_freq); /* notify user space */ c->ic_state &= ~IEEE80211_CHANSTATE_NORADAR; ieee80211_notify_radar(ic, c); } } else if (dfs->nol_event[i] < oldest) oldest = dfs->nol_event[i]; } } if (oldest != now) { /* arrange to process next channel up for a status change */ callout_schedule_dfly(&dfs->nol_timer, oldest + NOL_TIMEOUT - now, dfs_timeout, ic); } }
/* * IEEE80211_M_IBSS+IEEE80211_M_AHDEMO vap state machine handler. */ static int adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; enum ieee80211_state ostate; IEEE80211_LOCK_ASSERT(vap->iv_ic); ostate = vap->iv_state; IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg); vap->iv_state = nstate; /* state transition */ if (ostate != IEEE80211_S_SCAN) ieee80211_cancel_scan(vap); /* background scan */ ni = vap->iv_bss; /* NB: no reference held */ switch (nstate) { case IEEE80211_S_INIT: switch (ostate) { case IEEE80211_S_SCAN: ieee80211_cancel_scan(vap); break; default: break; } if (ostate != IEEE80211_S_INIT) { /* NB: optimize INIT -> INIT case */ ieee80211_reset_bss(vap); } break; case IEEE80211_S_SCAN: switch (ostate) { case IEEE80211_S_RUN: /* beacon miss */ /* purge station table; entries are stale */ ieee80211_iterate_nodes(&ic->ic_sta, sta_leave, vap); /* fall thru... */ case IEEE80211_S_INIT: if (vap->iv_des_chan != IEEE80211_CHAN_ANYC && !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { /* * Already have a channel; bypass the * scan and startup immediately. */ ieee80211_create_ibss(vap, ieee80211_ht_adjust_channel(ic, vap->iv_des_chan, vap->iv_flags_ht)); break; } /* * Initiate a scan. We can come here as a result * of an IEEE80211_IOC_SCAN_REQ too in which case * the vap will be marked with IEEE80211_FEXT_SCANREQ * and the scan request parameters will be present * in iv_scanreq. Otherwise we do the default. */ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { ieee80211_check_scan(vap, vap->iv_scanreq_flags, vap->iv_scanreq_duration, vap->iv_scanreq_mindwell, vap->iv_scanreq_maxdwell, vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; } else ieee80211_check_scan_current(vap); break; case IEEE80211_S_SCAN: /* * This can happen because of a change in state * that requires a reset. Trigger a new scan * unless we're in manual roaming mode in which * case an application must issue an explicit request. */ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) ieee80211_check_scan_current(vap); break; default: goto invalid; } break; case IEEE80211_S_RUN: if (vap->iv_flags & IEEE80211_F_WPA) { /* XXX validate prerequisites */ } switch (ostate) { case IEEE80211_S_SCAN: #ifdef IEEE80211_DEBUG if (ieee80211_msg_debug(vap)) { ieee80211_note(vap, "synchronized with %s ssid ", ether_sprintf(ni->ni_bssid)); ieee80211_print_essid(vap->iv_bss->ni_essid, ni->ni_esslen); /* XXX MCS/HT */ printf(" channel %d start %uMb\n", ieee80211_chan2ieee(ic, ic->ic_curchan), IEEE80211_RATE2MBS(ni->ni_txrate)); } #endif break; case IEEE80211_S_RUN: /* IBSS merge */ break; default: goto invalid; } /* * When 802.1x is not in use mark the port authorized * at this point so traffic can flow. */ if (ni->ni_authmode != IEEE80211_AUTH_8021X) ieee80211_node_authorize(ni); /* * Fake association when joining an existing bss. */ if (!IEEE80211_ADDR_EQ(ni->ni_macaddr, vap->iv_myaddr) && ic->ic_newassoc != NULL) ic->ic_newassoc(ni, ostate != IEEE80211_S_RUN); break; case IEEE80211_S_SLEEP: vap->iv_sta_ps(vap, 0); break; default: invalid: IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: unexpected state transition %s -> %s\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate]); break; } return 0; }
void dfs_process_phyerr(struct ieee80211com *ic, void *buf, uint16_t datalen, uint8_t r_rssi, uint8_t r_ext_rssi, uint32_t r_rs_tstamp, uint64_t r_fulltsf, bool enable_log) { struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs; struct dfs_ieee80211_channel *chan = ic->ic_curchan; struct dfs_event *event; struct dfs_phy_err e; int empty; if (dfs == NULL) { CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_ERROR, "%s: sc_dfs is NULL\n", __func__); return; } dfs->dfs_phyerr_count++; dump_phyerr_contents(buf, datalen); /* * XXX The combined_rssi_ok support has been removed. * This was only clear for Owl. * * XXX TODO: re-add this; it requires passing in the ctl/ext * RSSI set from the RX status descriptor. * * XXX TODO TODO: this may be done for us from the legacy * phy error path in ath_dev; please review that code. */ /* * At this time we have a radar pulse that we need to examine and * queue. But if dfs_process_radarevent already detected radar and set * CHANNEL_INTERFERENCE flag then do not queue any more radar data. * When we are in a new channel this flag will be clear and we will * start queueing data for new channel. (EV74162) */ if (dfs->dfs_debug_mask & ATH_DEBUG_DFS_PHYERR_PKT) dump_phyerr_contents(buf, datalen); if (chan == NULL) { CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_ERROR, "%s: chan is NULL\n", __func__); return; } cdf_spin_lock_bh(&ic->chan_lock); if (IEEE80211_IS_CHAN_RADAR(chan)) { cdf_spin_unlock_bh(&ic->chan_lock); DFS_DPRINTK(dfs, ATH_DEBUG_DFS1, "%s: Radar already found in the channel, " " do not queue radar data\n", __func__); return; } cdf_spin_unlock_bh(&ic->chan_lock); dfs->ath_dfs_stats.total_phy_errors++; DFS_DPRINTK(dfs, ATH_DEBUG_DFS2, "%s[%d] phyerr %d len %d\n", __func__, __LINE__, dfs->ath_dfs_stats.total_phy_errors, datalen); /* * hardware stores this as 8 bit signed value. * we will cap it at 0 if it is a negative number */ if (r_rssi & 0x80) r_rssi = 0; if (r_ext_rssi & 0x80) r_ext_rssi = 0; OS_MEMSET(&e, 0, sizeof(e)); /* * This is a bit evil - instead of just passing in * the chip version, the existing code uses a set * of HAL capability bits to determine what is * possible. * * The way I'm decoding it is thus: * * + DFS enhancement? Merlin or later * + DFS extension channel? Sowl or later. (Howl?) * + otherwise, Owl (and legacy.) */ if (dfs->dfs_caps.ath_chip_is_bb_tlv) { if (dfs_process_phyerr_bb_tlv(dfs, buf, datalen, r_rssi, r_ext_rssi, r_rs_tstamp, r_fulltsf, &e, enable_log) == 0) { dfs->dfs_phyerr_reject_count++; return; } else { if (dfs->dfs_phyerr_freq_min > e.freq) dfs->dfs_phyerr_freq_min = e.freq; if (dfs->dfs_phyerr_freq_max < e.freq) dfs->dfs_phyerr_freq_max = e.freq; } } else if (dfs->dfs_caps.ath_dfs_use_enhancement) { if (dfs_process_phyerr_merlin(dfs, buf, datalen, r_rssi, r_ext_rssi, r_rs_tstamp, r_fulltsf, &e) == 0) { return; } } else if (dfs->dfs_caps.ath_dfs_ext_chan_ok) { if (dfs_process_phyerr_sowl(dfs, buf, datalen, r_rssi, r_ext_rssi, r_rs_tstamp, r_fulltsf, &e) == 0) { return; } } else { if (dfs_process_phyerr_owl(dfs, buf, datalen, r_rssi, r_ext_rssi, r_rs_tstamp, r_fulltsf, &e) == 0) { return; } } CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO, "\n %s: Frequency at which the phyerror was injected = %d", __func__, e.freq); /* * If the hardware supports radar reporting on the extension channel * it will supply FFT data for longer radar pulses. * * TLV chips don't go through this software check - the hardware * check should be enough. If we want to do software checking * later on then someone will have to craft an FFT parser * suitable for the TLV FFT data format. */ if ((!dfs->dfs_caps.ath_chip_is_bb_tlv) && dfs->dfs_caps.ath_dfs_ext_chan_ok) { /* * HW has a known issue with chirping pulses injected at or * around DC in 40MHz mode. Such pulses are reported with * much lower durations and SW then discards them because * they do not fit the minimum bin5 pulse duration. * * To work around this issue, if a pulse is within a 10us * range of the bin5 min duration, check if the pulse is * chirping. If the pulse is chirping, bump up the duration * to the minimum bin5 duration. * * This makes sure that a valid chirping pulse will not be * discarded because of incorrect low duration. * * TBD - Is it possible to calculate the 'real' duration of * the pulse using the slope of the FFT data? * * TBD - Use FFT data to differentiate between radar pulses * and false PHY errors. * This will let us reduce the number of false alarms seen. * * BIN 5 chirping pulses are only for FCC or Japan MMK4 domain */ if (((dfs->dfsdomain == DFS_FCC_DOMAIN) || (dfs->dfsdomain == DFS_MKK4_DOMAIN)) && (e.dur >= MAYBE_BIN5_DUR) && (e.dur < MAX_BIN5_DUR)) { int add_dur; int slope = 0, dc_found = 0; /* * Set the event chirping flags; as we're doing * an actual chirp check. */ e.do_check_chirp = 1; e.is_hw_chirp = 0; e.is_sw_chirp = 0; /* * dfs_check_chirping() expects is_pri and is_ext * to be '1' for true and '0' for false for now, * as the function itself uses these values in * constructing things rather than testing them * for 'true' or 'false'. */ add_dur = dfs_check_chirping(dfs, buf, datalen, (e.is_pri ? 1 : 0), (e.is_ext ? 1 : 0), &slope, &dc_found); if (add_dur) { DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "old dur %d slope =%d\n", e.dur, slope); e.is_sw_chirp = 1; /* bump up to a random bin5 pulse duration */ if (e.dur < MIN_BIN5_DUR) { e.dur = dfs_get_random_bin5_dur(dfs, e.fulltsf); } DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "new dur %d\n", e.dur); } else { /* set the duration so that it is rejected */ e.is_sw_chirp = 0; e.dur = MAX_BIN5_DUR + 100; DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "is_chirping = %d dur=%d\n", add_dur, e.dur); } } else { /* * We have a pulse that is either bigger than * MAX_BIN5_DUR or * less than MAYBE_BIN5_DUR */ if ((dfs->dfsdomain == DFS_FCC_DOMAIN) || (dfs->dfsdomain == DFS_MKK4_DOMAIN)) { /* * XXX Would this result in very large pulses * wrapping around to become short pulses? */ if (e.dur >= MAX_BIN5_DUR) { /* * set the duration so that it is * rejected */ e.dur = MAX_BIN5_DUR + 50; } } } } /* * Add the parsed, checked and filtered entry to the radar pulse * event list. This is then checked by dfs_radar_processevent(). * * XXX TODO: some filtering is still done below this point - fix * XXX this! */ ATH_DFSEVENTQ_LOCK(dfs); empty = STAILQ_EMPTY(&(dfs->dfs_eventq)); ATH_DFSEVENTQ_UNLOCK(dfs); if (empty) { return; } /* * If the channel is a turbo G channel, then the event is * for the adaptive radio (AR) pattern matching rather than * radar detection. */ cdf_spin_lock_bh(&ic->chan_lock); if ((chan->ic_flags & CHANNEL_108G) == CHANNEL_108G) { cdf_spin_unlock_bh(&ic->chan_lock); if (!(dfs->dfs_proc_phyerr & DFS_AR_EN)) { DFS_DPRINTK(dfs, ATH_DEBUG_DFS2, "%s: DFS_AR_EN not enabled\n", __func__); return; } ATH_DFSEVENTQ_LOCK(dfs); event = STAILQ_FIRST(&(dfs->dfs_eventq)); if (event == NULL) { ATH_DFSEVENTQ_UNLOCK(dfs); DFS_DPRINTK(dfs, ATH_DEBUG_DFS, "%s: no more events space left\n", __func__); return; } STAILQ_REMOVE_HEAD(&(dfs->dfs_eventq), re_list); ATH_DFSEVENTQ_UNLOCK(dfs); event->re_rssi = e.rssi; event->re_dur = e.dur; event->re_full_ts = e.fulltsf; event->re_ts = (e.rs_tstamp) & DFS_TSMASK; event->re_chanindex = dfs->dfs_curchan_radindex; event->re_flags = 0; event->sidx = e.sidx; /* * Handle chirp flags. */ if (e.do_check_chirp) { event->re_flags |= DFS_EVENT_CHECKCHIRP; if (e.is_hw_chirp) event->re_flags |= DFS_EVENT_HW_CHIRP; if (e.is_sw_chirp) event->re_flags |= DFS_EVENT_SW_CHIRP; } ATH_ARQ_LOCK(dfs); STAILQ_INSERT_TAIL(&(dfs->dfs_arq), event, re_list); ATH_ARQ_UNLOCK(dfs); } else { if (IEEE80211_IS_CHAN_DFS(chan)) { cdf_spin_unlock_bh(&ic->chan_lock); if (!(dfs->dfs_proc_phyerr & DFS_RADAR_EN)) { DFS_DPRINTK(dfs, ATH_DEBUG_DFS3, "%s: DFS_RADAR_EN not enabled\n", __func__); return; } /* * rssi is not accurate for short pulses, so do * not filter based on that for short duration pulses * * XXX do this filtering above? */ if (dfs->dfs_caps.ath_dfs_ext_chan_ok) { if ((e.rssi < dfs->dfs_rinfo.rn_minrssithresh && (e.dur > 4)) || e.dur > (dfs->dfs_rinfo.rn_maxpulsedur)) { dfs->ath_dfs_stats.rssi_discards++; DFS_DPRINTK(dfs, ATH_DEBUG_DFS1, "Extension channel pulse is " "discarded: dur=%d, " "maxpulsedur=%d, rssi=%d, " "minrssi=%d\n", e.dur, dfs->dfs_rinfo. rn_maxpulsedur, e.rssi, dfs->dfs_rinfo. rn_minrssithresh); return; } } else { if (e.rssi < dfs->dfs_rinfo.rn_minrssithresh || e.dur > dfs->dfs_rinfo.rn_maxpulsedur) { /* XXX TODO add a debug statement? */ dfs->ath_dfs_stats.rssi_discards++; return; } } /* * Add the event to the list, if there's space. */ ATH_DFSEVENTQ_LOCK(dfs); event = STAILQ_FIRST(&(dfs->dfs_eventq)); if (event == NULL) { ATH_DFSEVENTQ_UNLOCK(dfs); DFS_DPRINTK(dfs, ATH_DEBUG_DFS, "%s: no more events space left\n", __func__); return; } STAILQ_REMOVE_HEAD(&(dfs->dfs_eventq), re_list); ATH_DFSEVENTQ_UNLOCK(dfs); dfs->dfs_phyerr_queued_count++; dfs->dfs_phyerr_w53_counter++; event->re_dur = e.dur; event->re_full_ts = e.fulltsf; event->re_ts = (e.rs_tstamp) & DFS_TSMASK; event->re_rssi = e.rssi; event->sidx = e.sidx; /* * Handle chirp flags. */ if (e.do_check_chirp) { event->re_flags |= DFS_EVENT_CHECKCHIRP; if (e.is_hw_chirp) event->re_flags |= DFS_EVENT_HW_CHIRP; if (e.is_sw_chirp) event->re_flags |= DFS_EVENT_SW_CHIRP; } /* * Correctly set which channel is being reported on */ if (e.is_pri) { event->re_chanindex = dfs->dfs_curchan_radindex; } else { if (dfs->dfs_extchan_radindex == -1) { DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s - phyerr on ext channel\n", __func__); } event->re_chanindex = dfs->dfs_extchan_radindex; DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s New extension channel event is added " "to queue\n", __func__); } ATH_DFSQ_LOCK(dfs); STAILQ_INSERT_TAIL(&(dfs->dfs_radarq), event, re_list); ATH_DFSQ_UNLOCK(dfs); } else { cdf_spin_unlock_bh(&ic->chan_lock); } } /* * Schedule the radar/AR task as appropriate. * * XXX isn't a lock needed for ath_radar_tasksched? */ /* * Commenting out the dfs_process_ar_event() since the function is never * called at run time as dfs_arq will be empty and the function * dfs_process_ar_event is obsolete and function definition is removed * as part of dfs_ar.c file * * if (!STAILQ_EMPTY(&dfs->dfs_arq)) * // XXX shouldn't this be a task/timer too? * dfs_process_ar_event(dfs, ic->ic_curchan); */ if (!STAILQ_EMPTY(&dfs->dfs_radarq) && !dfs->ath_radar_tasksched) { dfs->ath_radar_tasksched = 1; OS_SET_TIMER(&dfs->ath_dfs_task_timer, 0); } #undef EXT_CH_RADAR_FOUND #undef PRI_CH_RADAR_FOUND #undef EXT_CH_RADAR_EARLY_FOUND }