Example #1
0
void dfs_reset_arq(struct ath_dfs_host *dfs)
{
	struct dfs_event *event;

    if (dfs == NULL) {
        A_PRINTF("%s: sc_dfs is NULL\n", __func__);
        return;
    }
	ATH_ARQ_LOCK(dfs);
	ATH_DFSEVENTQ_LOCK(dfs);
	while (!STAILQ_EMPTY(&(dfs->dfs_arq))) {
		event = STAILQ_FIRST(&(dfs->dfs_arq));
		STAILQ_REMOVE_HEAD(&(dfs->dfs_arq), re_list);
		OS_MEMZERO(event, sizeof(struct dfs_event));
		STAILQ_INSERT_TAIL(&(dfs->dfs_eventq), event, re_list);
	}
	ATH_DFSEVENTQ_UNLOCK(dfs);
	ATH_ARQ_UNLOCK(dfs);
}
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
}
Example #3
0
void dfs_process_ar_event(struct ath_softc *sc, HAL_CHANNEL *chan)
{
	struct ath_dfs *dfs=sc->sc_dfs;
	struct dfs_ar_state *ar;
	struct dfs_event *re=NULL;
	u_int32_t sumpeak=0,numpeaks,rssi,width,origregionsum=0, i;
	u_int16_t thistimestamp;
	int empty;

	if (dfs == NULL) {
		DFS_DPRINTK(sc, ATH_DEBUG_DFS, "%s: sc_dfs is NULL\n",
			__func__);
		return;
	}
	ar = (struct dfs_ar_state *) &(dfs->dfs_ar_state);
	ATH_ARQ_LOCK(dfs);
	empty = STAILQ_EMPTY(&(dfs->dfs_arq));
	ATH_ARQ_UNLOCK(dfs);
	while (!empty) {
		ATH_ARQ_LOCK(dfs);
		re = STAILQ_FIRST(&(dfs->dfs_arq));
		if (re != NULL)
			STAILQ_REMOVE_HEAD(&(dfs->dfs_arq), re_list);
		ATH_ARQ_UNLOCK(dfs);
		if (re == NULL)
			return;

		thistimestamp = re->re_ts;
		rssi = re->re_rssi;
		width = re->re_dur;

		/* Return the dfs event to the free event list */
		OS_MEMZERO(re, sizeof(struct dfs_event));
		ATH_DFSEVENTQ_LOCK(dfs);
		STAILQ_INSERT_TAIL(&(dfs->dfs_eventq), re, re_list);
		ATH_DFSEVENTQ_UNLOCK(dfs);

		/* determine if current radar is an extension of previous radar */
		if (ar->ar_prevwidth == 255) {
			/* tag on previous width for consideraion of low data rate ACKs */
			ar->ar_prevwidth += width;
			width = (width == 255) ? 255 : ar->ar_prevwidth;
		} else if ((width == 255) &&
			   (ar->ar_prevwidth == 510 ||
			    ar->ar_prevwidth == 765 ||
			    ar->ar_prevwidth == 1020)) {
			/* Aggregate up to 5 consecuate max radar widths
			 * to consider 11Mbps long preamble 1500-byte pkts
			 */
			ar->ar_prevwidth += width;
		} else if (ar->ar_prevwidth == 1275 && width != 255) {
			/* Found 5th consecute maxed out radar, reset history */
			width += ar->ar_prevwidth;
			ar->ar_prevwidth = 0;
		} else if (ar->ar_prevwidth > 255) {
			/* Ignore if there are less than 5 consecutive maxed out radars */
			ar->ar_prevwidth = width;
			width = 255;
		} else {
			ar->ar_prevwidth = width;
		}
		/* For ignoring noises with radar duration in ranges of 3-30: AP4x */
		if ((width >= 257 && width <= 278) ||	/* Region 7 - 5.5Mbps (long pre) ACK = 270 = 216 us */
		    (width >= 295 && width <= 325) ||	/* Region 8 - 2Mbps (long pre) ACKC = 320 = 256us */
		    (width >= 1280 && width <= 1300)) {
			u_int16_t wraparoundadj=0;
			u_int16_t base = (width >= 1280) ? 1275 : 255;
			if (thistimestamp < ar->ar_prevtimestamp) {
				wraparoundadj = 32768;
			}
			if ((thistimestamp + wraparoundadj - ar->ar_prevtimestamp) !=
			    (width - base)) {
				width = 1;
			}
		}
		if (width <= 10) {
			ATH_ARQ_LOCK(dfs);
			empty = STAILQ_EMPTY(&(dfs->dfs_arq));
			ATH_ARQ_UNLOCK(dfs);
			continue;
		}
		/*
		 * Overloading the width=2 in: Store a count of radars w/max duration
		 * and high RSSI (not noise)
		 */
		if ((width == 255) && (rssi > DFS_AR_RSSI_THRESH_STRONG_PKTS))
			width = 2;
		/*
		 * Overloading the width=3 bin:
		 *   Double and store a count of rdars of durtaion that matches 11Mbps (long preamble)
		 *   TCP ACKs or 1500-byte data packets
		 */
		if ((width >= 1280 && width <= 1300) ||
		    (width >= 318 && width <= 325)) {
			width = 3;
			ar->ar_phyerrcount[3] += 2;
			ar->ar_acksum += 2;
		}
		/* build histogram of radar duration */
		if (width > 0 && width <= 510)
			ar->ar_phyerrcount[width]++;
		else {
			/* invalid radar width, throw it away */
			ATH_ARQ_LOCK(dfs);
			empty = STAILQ_EMPTY(&(dfs->dfs_arq));
			ATH_ARQ_UNLOCK(dfs);
			continue;
		}
		/* Received radar of interest (i.e., signature match), proceed to check if
		 * there is enough neighboring traffic to drop out of Turbo 
		 */
		if ((width >= 33 && width <= 38) ||          /* Region 0: 24Mbps ACK = 35 = 28us */
		    (width >= 39 && width <= 44) ||          /* Region 1: 12Mbps ACK = 40 = 32us */
		    (width >= 53 && width <= 58) ||          /* Region 2:  6Mbps ACK = 55 = 44us */
		    (width >= 126 && width <= 140) ||        /* Region 3: 11Mbps ACK = 135 = 108us */
		    (width >= 141 && width <= 160) ||        /* Region 4: 5.5Mbps ACK = 150 = 120us */
		    (width >= 189 && width <= 210) ||        /* Region 5:  2Mbps ACK = 200 = 160us */
		    (width >= 360 && width <= 380) ||        /* Region 6   1Mbps ACK = 400 = 320us */
		    (width >= 257 && width <= 270) ||        /* Region 7   5.5Mbps (Long Pre) ACK = 270 = 216us */
		    (width >= 295 && width <= 302) ||        /* Region 8   2Mbps (Long Pre) ACK = 320 = 256us */
		    /* Ignoring Region 9 due to overlap with 255 which is same as board noise */
		    /* Region 9  11Mbps (Long Pre) ACK = 255 = 204us */            
		    (width == 3)) {
			ar->ar_acksum++;
			/* double the count for strong radars that match one of the ACK signatures */
			if (rssi > DFS_AR_RSSI_DOUBLE_THRESHOLD) {
				ar->ar_phyerrcount[width]++;
				ar->ar_acksum++;
			}
			UPDATE_TOP_THREE_PEAKS(ar->ar_phyerrcount,
					       ar->ar_peaklist, width);
			/* sum the counts of these peaks */
			numpeaks = DFS_AR_MAX_NUM_PEAKS;
			origregionsum = ar->ar_acksum;
			for (i = 0; i < DFS_AR_MAX_NUM_PEAKS; i++) {
				if (ar->ar_peaklist[i] > 0) {
					if ((i==0) &&
					    (ar->ar_peaklist[i] == 3) &&
					    (ar->ar_phyerrcount[3] <
					     ar->ar_phyerrcount[2]) &&
					    (ar->ar_phyerrcount[3] > 6)) {
						/*
						 * If the top peak is one that
						 * maches the 11Mbps long
						 * preamble TCP Ack/1500-byte
						 * data, include the count for
						 * radars that hav emax
						 * duration and high rssi
						 * (width = 2) to boost the
						 * sum for the PAR test that
						 * follows */
						sumpeak += (ar->ar_phyerrcount[2]
							    + ar->ar_phyerrcount[3]);
						ar->ar_acksum += (ar->ar_phyerrcount[2]
								  + ar->ar_phyerrcount[3]);
					} else {
						sumpeak += ar->ar_phyerrcount[ar->ar_peaklist[i]];
					}
				} else 
					numpeaks--;
			}
			/*
			 * If sum of patterns matches exceeds packet threshold,
			 * perform comparison between peak-to-avg ratio against parThreshold
			 */
			if ((ar->ar_acksum > ar->ar_packetthreshold) &&
			    ((sumpeak * DFS_AR_REGION_WIDTH) > (ar->ar_parthreshold * numpeaks *
								ar->ar_acksum))) {
				/* neighboring traffic detected, get out of Turbo */
				chan->priv_flags |= CHANNEL_INTERFERENCE;
				OS_MEMZERO(ar->ar_peaklist, sizeof(ar->ar_peaklist));
				ar->ar_acksum = 0;
				OS_MEMZERO(ar->ar_phyerrcount, sizeof(ar->ar_phyerrcount));
			} else {
				/*
				 * reset sum of matches to discount the count of
				 * strong radars with max duration
				 */
				ar->ar_acksum = origregionsum;
			}
		}
		ar->ar_prevtimestamp = thistimestamp;
		ATH_ARQ_LOCK(dfs);
		empty = STAILQ_EMPTY(&(dfs->dfs_arq));
		ATH_ARQ_UNLOCK(dfs);
	}
}
Example #4
0
void
dfs_detach(struct ieee80211com *ic)
{
	struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs;
	int n, empty;

	if (dfs == NULL) {
	        DFS_DPRINTK(dfs, ATH_DEBUG_DFS1, "%s: ic_dfs is NULL\n", __func__);
		return;
	}
  
        /* Bug 29099 make sure all outstanding timers are cancelled*/

        if (dfs->ath_radar_tasksched) {
            OS_CANCEL_TIMER(&dfs->ath_dfs_task_timer);
            dfs->ath_radar_tasksched = 0;
        }

	if (dfs->ath_dfstest) {
		OS_CANCEL_TIMER(&dfs->ath_dfstesttimer);
		dfs->ath_dfstest = 0;
	}

#if 0        
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
        if (dfs->ic_dfswait) {
            OS_CANCEL_TIMER(&dfs->ic_dfswaittimer);
            dfs->ath_dfswait = 0;
        }

 		OS_CANCEL_TIMER(&dfs->sc_dfs_war_timer);
	if (dfs->dfs_nol != NULL) {
	    struct dfs_nolelem *nol, *next;
	    nol = dfs->dfs_nol;
                /* Bug 29099 - each NOL element has its own timer, cancel it and 
                   free the element*/
		while (nol != NULL) {
                       OS_CANCEL_TIMER(&nol->nol_timer);
		       next = nol->nol_next;
		       OS_FREE(nol);
		       nol = next;
		}
		dfs->dfs_nol = NULL;
	}
#endif
#endif
        /* Return radar events to free q*/
        dfs_reset_radarq(dfs);
	dfs_reset_alldelaylines(dfs);

        /* Free up pulse log*/
        if (dfs->pulses != NULL) {
                OS_FREE(dfs->pulses);
                dfs->pulses = NULL;
        }

	for (n=0; n<DFS_MAX_RADAR_TYPES;n++) {
		if (dfs->dfs_radarf[n] != NULL) {
			OS_FREE(dfs->dfs_radarf[n]);
			dfs->dfs_radarf[n] = NULL;
		}
	}


	if (dfs->dfs_radartable != NULL) {
		for (n=0; n<256; n++) {
			if (dfs->dfs_radartable[n] != NULL) {
				OS_FREE(dfs->dfs_radartable[n]);
				dfs->dfs_radartable[n] = NULL;
			}
		}
		OS_FREE(dfs->dfs_radartable);
		dfs->dfs_radartable = NULL;
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
		dfs->ath_dfs_isdfsregdomain = 0;
#endif
	}
        
	if (dfs->dfs_b5radars != NULL) {
		OS_FREE(dfs->dfs_b5radars);
		dfs->dfs_b5radars=NULL;
	}

	dfs_reset_ar(dfs);

	ATH_ARQ_LOCK(dfs);
	empty = STAILQ_EMPTY(&(dfs->dfs_arq));
	ATH_ARQ_UNLOCK(dfs);
	if (!empty) {
		dfs_reset_arq(dfs);
	}
        if (dfs->events != NULL) {
                OS_FREE(dfs->events);
                dfs->events = NULL;
        }
       dfs_nol_timer_cleanup(dfs);
	OS_FREE(dfs);

	/* XXX? */
        ic->ic_dfs = NULL;
}
Example #5
0
void
ath_process_phyerr(struct ath_softc *sc, struct ath_buf *bf, struct ath_rx_status *rxs, u_int64_t fulltsf)
{
#define EXT_CH_RADAR_FOUND 0x02
#define PRI_CH_RADAR_FOUND 0x01
#define EXT_CH_RADAR_EARLY_FOUND 0x04
        struct ath_dfs *dfs=sc->sc_dfs;
	HAL_CHANNEL *chan=&sc->sc_curchan;
	struct dfs_event *event;
	u_int8_t rssi;
	u_int8_t ext_rssi=0;
        u_int8_t pulse_bw_info=0, pulse_length_ext=0, pulse_length_pri=0;
	u_int32_t dur=0;
        u_int16_t datalen;
        int pri_found=1, ext_found=0, dc_found=0, early_ext=0, slope=0, add_dur=0;

	int empty;
        u_int32_t *last_word_ptr, *secondlast_word_ptr;
        u_int8_t *byte_ptr, last_byte_0, last_byte_1, last_byte_2, last_byte_3; 
        u_int8_t secondlast_byte_0, secondlast_byte_1, secondlast_byte_2, secondlast_byte_3; 

	if (((rxs->rs_phyerr != HAL_PHYERR_RADAR)) &&
	    ((rxs->rs_phyerr != HAL_PHYERR_FALSE_RADAR_EXT)))  {
		DFS_DPRINTK(sc, ATH_DEBUG_DFS3, "%s: rs_phyer=0x%x not a radar error\n",__func__, rxs->rs_phyerr);
        	return;
        }

        /* 
           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 (chan->priv_flags & CHANNEL_INTERFERENCE) {
                DFS_DPRINTK(sc, ATH_DEBUG_DFS1, "%s: Radar already found in the channel, do not queue radar data\n", __func__);
                return;
        } 

	if (dfs == NULL) {
		DFS_DPRINTK(sc, ATH_DEBUG_DFS, "%s: sc_dfs is NULL\n",__func__);
		return;
	}
        dfs->ath_dfs_stats.total_phy_errors++;
        datalen = rxs->rs_datalen;
        /* WAR: Never trust combined RSSI on radar pulses for <=
         * OWL2.0. For short pulses only the chain 0 rssi is present
         * and remaining descriptor data is all 0x80, for longer
         * pulses the descriptor is present, but the combined value is
         * inaccurate. This HW capability is queried in dfs_attach and stored in
         * the sc_dfs_combined_rssi_ok flag.*/

        if (sc->sc_dfs->sc_dfs_combined_rssi_ok) {
                rssi = (u_int8_t) rxs->rs_rssi;
        } else {
            rssi = (u_int8_t) rxs->rs_rssi_ctl0;
        }

        ext_rssi = (u_int8_t) rxs->rs_rssi_ext0;


        /* hardware stores this as 8 bit signed value.
         * we will cap it at 0 if it is a negative number 
         */

        if (rssi & 0x80)
            rssi = 0;

        if (ext_rssi & 0x80)
            ext_rssi = 0;

        last_word_ptr = (u_int32_t *)(((u_int8_t*)bf->bf_vdata) + datalen - (datalen%4));


        secondlast_word_ptr = last_word_ptr-1;

        byte_ptr = (u_int8_t*)last_word_ptr; 
        last_byte_0=(*(byte_ptr) & 0xff); 
        last_byte_1=(*(byte_ptr+1) & 0xff); 
        last_byte_2=(*(byte_ptr+2) & 0xff); 
        last_byte_3=(*(byte_ptr+3) & 0xff); 

        byte_ptr = (u_int8_t*)secondlast_word_ptr; 
        secondlast_byte_0=(*(byte_ptr) & 0xff); 
        secondlast_byte_1=(*(byte_ptr+1) & 0xff); 
        secondlast_byte_2=(*(byte_ptr+2) & 0xff); 
        secondlast_byte_3=(*(byte_ptr+3) & 0xff); 
       
        /* If radar can be detected on the extension channel (for SOWL onwards), we have to read radar data differently as the HW supplies bwinfo and duration for both primary and extension channel.*/
        if (sc->sc_dfs->sc_dfs_ext_chan_ok) {
        
        /* If radar can be detected on the extension channel, datalen zero pulses are bogus, discard them.*/
        if (!datalen) {
            dfs->ath_dfs_stats.datalen_discards++;
            return;
        }
        switch((datalen & 0x3)) {
        case 0:
            pulse_bw_info = secondlast_byte_3;
            pulse_length_ext = secondlast_byte_2;
            pulse_length_pri = secondlast_byte_1;
            break;
        case 1:
            pulse_bw_info = last_byte_0;
            pulse_length_ext = secondlast_byte_3;
            pulse_length_pri = secondlast_byte_2;
            break;
        case 2:
            pulse_bw_info = last_byte_1;
            pulse_length_ext = last_byte_0;
            pulse_length_pri = secondlast_byte_3;
           break;
        case 3:
            pulse_bw_info = last_byte_2;
            pulse_length_ext = last_byte_1;
            pulse_length_pri = last_byte_0;
            break;
        default:
            DFS_DPRINTK(sc, ATH_DEBUG_DFS, "datalen mod4=%d\n", (datalen%4));
        }

        /* Only the last 3 bits of the BW info are relevant, they indicate
        which channel the radar was detected in.*/
        pulse_bw_info &= 0x07;
        /* If pulse on DC, both primary and extension flags will be set */
        if (((pulse_bw_info & EXT_CH_RADAR_FOUND) && (pulse_bw_info & PRI_CH_RADAR_FOUND))) {

            /* Conducted testing, when pulse is on DC, both pri and ext durations are reported to be same
               Radiated testing, when pulse is on DC, different pri and ext durations are reported, so take the larger of the two */
            if (pulse_length_ext >= pulse_length_pri) {
                dur = pulse_length_ext;
                ext_found = 1;
            } else {
                dur = pulse_length_pri;
                pri_found = 1;
            }
            dfs->ath_dfs_stats.dc_phy_errors++;         

        } else {
        if (pulse_bw_info & EXT_CH_RADAR_FOUND) {
            dur = pulse_length_ext;
            pri_found = 0;
            ext_found = 1;
            dfs->ath_dfs_stats.ext_phy_errors++;         
        } 
        if (pulse_bw_info & PRI_CH_RADAR_FOUND) {
            dur = pulse_length_pri;
            pri_found = 1;
            ext_found = 0;
            dfs->ath_dfs_stats.pri_phy_errors++;         
        } 
        if (pulse_bw_info & EXT_CH_RADAR_EARLY_FOUND) {
             dur = pulse_length_ext;
             pri_found = 0;
             ext_found = 1; early_ext = 1;
             dfs->ath_dfs_stats.early_ext_phy_errors++;         
	     DFS_DPRINTK(sc, ATH_DEBUG_DFS2, "EARLY ext channel dur=%u rssi=%u datalen=%d\n",dur, rssi, datalen);
        } 
        if (!pulse_bw_info) {
	    DFS_DPRINTK(sc, ATH_DEBUG_DFS3, "ERROR channel dur=%u rssi=%u pulse_bw_info=0x%x datalen MOD 4 = %d\n",dur, rssi, pulse_bw_info, (datalen & 0x3));
            /* Bogus bandwidth info received in descriptor, 
            so ignore this PHY error */
            dfs->ath_dfs_stats.bwinfo_errors++;
            return; 
        }
    }

    if (sc->sc_dfs->sc_dfs_use_enhancement) {
        /*
         * for osprey (and Merlin) bw_info has implication for selecting RSSI value
         */

        switch (pulse_bw_info & 0x03) {
        case 0x00:
            /* No radar in ctrl or ext channel */
            rssi = 0;
            break;
        case 0x01:
            /* radar in ctrl channel */
            DFS_DPRINTK(sc, ATH_DEBUG_DFS3, "RAW RSSI: rssi=%u ext_rssi=%u\n", rssi, ext_rssi);
            if (ext_rssi >= (rssi + 3)) {
                /* cannot use ctrl channel RSSI if extension channel is stronger */
                rssi = 0;
            }
            break;
        case 0x02:
            /* radar in extension channel */
	    
            DFS_DPRINTK(sc, ATH_DEBUG_DFS3, "RAW RSSI: rssi=%u ext_rssi=%u\n", rssi, ext_rssi);
            if (rssi >= (ext_rssi + 12)) {
                /* cannot use extension channel RSSI if control channel is stronger */
                rssi = 0;
            } else {
                rssi = ext_rssi;
            }
            break;
        case 0x03:
           /* when both are present use stronger one */
            if (rssi < ext_rssi) {
                rssi = ext_rssi;
            }
            break;
        } 
    } else {
        /* Always use combined RSSI reported, unless RSSI reported on 
           extension is stronger */
        if ((ext_rssi > rssi) && (ext_rssi < 128)) {
                    rssi = ext_rssi;
        }
    }
        DFS_DPRINTK(sc, ATH_DEBUG_DFS1, "pulse_bw_info=0x%x pulse_length_ext=%u pulse_length_pri=%u rssi=%u ext_rssi=%u phyerr=0x%x\n", pulse_bw_info, pulse_length_ext, pulse_length_pri, rssi, ext_rssi, rxs->rs_phyerr);

        /* 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)) && 
            (dur >= MAYBE_BIN5_DUR) && (dur < MAX_BIN5_DUR)) {
            add_dur = dfs_check_chirping(sc, bf, rxs, pri_found, ext_found, &slope, &dc_found);
            if (add_dur) {
                DFS_DPRINTK(sc, ATH_DEBUG_DFS2, "old dur %d slope =%d\n", dur, slope);
                    // bump up to a random bin5 pulse duration
                    if (dur < MIN_BIN5_DUR) {
                        dur = dfs_get_random_bin5_dur(sc, fulltsf);
                    }
                DFS_DPRINTK(sc, ATH_DEBUG_DFS2, "new dur %d\n", dur);
            } else {
                dur = MAX_BIN5_DUR + 100;       /* set the duration so that it is rejected */
                DFS_DPRINTK(sc, ATH_DEBUG_DFS2,"is_chirping = %d dur=%d \n", add_dur, 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)) {
                if (dur >= MAX_BIN5_DUR) {
                    dur = MAX_BIN5_DUR + 50;        /* set the duration so that it is rejected */
                }
            }
        }
    } else {
        dfs->ath_dfs_stats.owl_phy_errors++;
     /* HW cannot detect extension channel radar so it only passes us primary channel radar data*/
            dur = (rxs->rs_datalen && bf->bf_vdata != NULL ?
               (u_int32_t)(*((u_int8_t *) bf->bf_vdata)) : 0) & 0xff;

            if ((rssi == 0) && (dur== 0)){
               return;
            }
            pri_found = 1;
            ext_found = 0;
        }

	ATH_DFSEVENTQ_LOCK(dfs);
	empty = STAILQ_EMPTY(&(dfs->dfs_eventq));
	ATH_DFSEVENTQ_UNLOCK(dfs);
	if (empty) {
		return;
        }

	if ((chan->channel_flags & CHANNEL_108G) == CHANNEL_108G) {
	        if (!(dfs->dfs_proc_phyerr & DFS_AR_EN)) {
                		DFS_DPRINTK(sc, 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(sc, 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 = rssi;
		event->re_dur = dur;
		event->re_full_ts = fulltsf;
		event->re_ts = (rxs->rs_tstamp) & DFS_TSMASK;
        	event->re_chanindex = dfs->dfs_curchan_radindex;
		ATH_ARQ_LOCK(dfs);
		STAILQ_INSERT_TAIL(&(dfs->dfs_arq), event, re_list);
		ATH_ARQ_UNLOCK(dfs);
	}
	else {
		if (chan->priv_flags & CHANNEL_DFS) {
	                if (!(dfs->dfs_proc_phyerr & DFS_RADAR_EN)) {
                		DFS_DPRINTK(sc, 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*/
                        if (sc->sc_dfs->sc_dfs_ext_chan_ok) {
			    if ((rssi < dfs->dfs_rinfo.rn_minrssithresh && (dur > 4))||
			        dur > (dfs->dfs_rinfo.rn_maxpulsedur) ) {
                                    dfs->ath_dfs_stats.rssi_discards++;
                		    DFS_DPRINTK(sc, ATH_DEBUG_DFS3, "Extension channel pulse is discarded %d, %d, %d, %d\n", 
						dur, dfs->dfs_rinfo.rn_maxpulsedur, rssi, dfs->dfs_rinfo.rn_minrssithresh);
				    return;
                            }
                        } else {

			    if (rssi < dfs->dfs_rinfo.rn_minrssithresh ||
			        dur > dfs->dfs_rinfo.rn_maxpulsedur) {
                                    dfs->ath_dfs_stats.rssi_discards++;
				    return;
                            }
                        }

			ATH_DFSEVENTQ_LOCK(dfs);
			event = STAILQ_FIRST(&(dfs->dfs_eventq));
			if (event == NULL) {
				ATH_DFSEVENTQ_UNLOCK(dfs);
				DFS_DPRINTK(sc, 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_dur = dur;
			event->re_full_ts = fulltsf;
			event->re_ts = (rxs->rs_tstamp) & DFS_TSMASK;
			event->re_rssi = rssi;
                        if (pri_found == 1) {
        		    event->re_chanindex = dfs->dfs_curchan_radindex;
                        } else {
                            if (dfs->dfs_extchan_radindex == -1) { 
                                DFS_DPRINTK(sc, ATH_DEBUG_DFS3, "%s - phyerr on ext channel\n", __func__);
                            }
        		    event->re_chanindex = dfs->dfs_extchan_radindex;
                            DFS_DPRINTK(sc, ATH_DEBUG_DFS3, "%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);
		}
    }
#undef EXT_CH_RADAR_FOUND
#undef PRI_CH_RADAR_FOUND
#undef EXT_CH_RADAR_EARLY_FOUND
}