/*
 * Process an Owl-style phy error.
 *
 * Return 1 on success or 0 on failure.
 */
int
dfs_process_phyerr_owl(struct ath_dfs *dfs, void *buf, uint16_t datalen,
		       uint8_t rssi, uint8_t ext_rssi, uint32_t rs_tstamp,
		       uint64_t fulltsf, struct dfs_phy_err *e)
{
	const char *cbuf = (const char *)buf;
	uint8_t dur;
	int event_width;

	/* XXX this shouldn't be kept count here */
	dfs->ath_dfs_stats.owl_phy_errors++;

	/*
	 * HW cannot detect extension channel radar so it only passes us
	 * primary channel radar data
	 */
	if (datalen == 0)
		dur = 0;
	else
		dur = ((uint8_t *) cbuf)[0];

	/*
	 * This is a spurious event; toss.
	 */
	if (rssi == 0 && dur == 0)
		dfs->ath_dfs_stats.datalen_discards++;
	return 0;

	/*
	 * Fill out dfs_phy_err with the information we have
	 * at hand.
	 */
	OS_MEMSET(e, 0, sizeof(*e));
	e->rssi = rssi;
	e->dur = dur;
	e->is_pri = 1;
	e->is_ext = 0;
	e->is_dc = 0;
	e->is_early = 1;
	e->fulltsf = fulltsf;
	e->rs_tstamp = rs_tstamp;

	/*
	 * Owl only ever reports events on the primary channel;
	 * it doesn't even see events on the secondary channel.
	 */
	event_width = dfs_get_event_freqwidth(dfs);
	e->freq = dfs_get_event_freqcentre(dfs, 1, 0, 0) * 1000;
	e->freq_lo = e->freq - (event_width / 2) * 1000;
	e->freq_hi = e->freq + (event_width / 2) * 1000;

	DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR_SUM,
		"%s: rssi=%u dur=%u,freq=%dMHz, freq_lo=%dMHz, freq_hi=%dMHz\n",
		__func__, rssi, dur, e->freq / 1000, e->freq_lo / 1000,
		e->freq_hi / 1000);

	return 1;
}
Example #2
0
/*
 * Return the centre frequency for the current operating channel and
 * event.
 *
 * This is for post-Owl 11n chips which report pri/extension channel
 * events.
 */
static inline uint16_t
dfs_get_event_freqcentre(struct ath_dfs *dfs, int is_pri, int is_ext, int is_dc)
{
   struct ieee80211com *ic;
   int chan_offset = 0, chan_width;

   /* Handle edge cases during startup/transition, shouldn't happen! */
   if (dfs == NULL)
      return (0);
   if (dfs->ic == NULL || dfs->ic->ic_curchan == NULL)
      return (0);

   ic = dfs->ic;

   /*
    *
    * For wide channels, DC and ext frequencies need a bit of hand-holding
    * based on whether it's an upper or lower channel.
    */
   chan_width = dfs_get_event_freqwidth(dfs);

   if (IEEE80211_IS_CHAN_11N_HT40PLUS(ic->ic_curchan))
      chan_offset = chan_width;
   else if (IEEE80211_IS_CHAN_11N_HT40MINUS(ic->ic_curchan))
      chan_offset = -chan_width;
   else
      chan_offset = 0;

   /*
    * Check for DC events first - the sowl code may just set all
    * the bits together..
    */
   if (is_dc) {
      /*
       * XXX TODO: Should DC events be considered 40MHz wide here?
       */
      return (ieee80211_chan2freq(ic, ic->ic_curchan) +
          (chan_offset / 2));
   }

   /*
    * For non-wide channels, the centre frequency is just ic_freq.
    * The centre frequency for pri events is still ic_freq.
    */
   if (is_pri) {
      return (ieee80211_chan2freq(ic, ic->ic_curchan));
   }

   if (is_ext) {
      return (ieee80211_chan2freq(ic, ic->ic_curchan) + chan_width);
   }

   /* XXX shouldn't get here */
   return (ieee80211_chan2freq(ic, ic->ic_curchan));
}
/*
 * Process a Sowl/Howl style phy error.
 */
int
dfs_process_phyerr_sowl(struct ath_dfs *dfs, void *buf, uint16_t datalen,
			uint8_t rssi, uint8_t ext_rssi, uint32_t rs_tstamp,
			uint64_t fulltsf, struct dfs_phy_err *e)
{
#define EXT_CH_RADAR_FOUND 0x02
#define PRI_CH_RADAR_FOUND 0x01
#define EXT_CH_RADAR_EARLY_FOUND 0x04
	const char *cbuf = (const char *)buf;
	uint8_t dur = 0;
	uint8_t pulse_bw_info, pulse_length_ext, pulse_length_pri;
	int pri_found = 0, ext_found = 0;
	int early_ext = 0;
	int event_width;

	/*
	 * 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 0;
	}

	/* Ensure that we have at least three bytes of payload */
	if (datalen < 3) {
		DFS_DPRINTK(dfs, ATH_DEBUG_DFS,
			    "%s: short error frame (%d bytes)\n",
			    __func__, datalen);
		dfs->ath_dfs_stats.datalen_discards++;
		return 0;
	}

	/*
	 * Fetch the payload directly - the compiler will happily generate
	 * byte-read instructions with a const char * cbuf pointer.
	 */
	pulse_length_pri = cbuf[datalen - 3];
	pulse_length_ext = cbuf[datalen - 2];
	pulse_bw_info = cbuf[datalen - 1];

	/*
	 * 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(dfs, ATH_DEBUG_DFS_PHYERR,
				    "EARLY ext channel dur=%u rssi=%u datalen=%d\n",
				    dur, rssi, datalen);
		}
		if (!pulse_bw_info) {
			DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
				"dur=%u rssi=%u bw_info=0x%x datalen = %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 0;
		}
	}

	/*
	 * Always use combined RSSI reported, unless RSSI reported on
	 * extension is stronger
	 */
	if ((ext_rssi > rssi) && (ext_rssi < 128))
		rssi = ext_rssi;

	/*
	 * Fill out the rssi/duration fields from above.
	 */
	OS_MEMSET(e, 0, sizeof(*e));
	e->rssi = rssi;
	e->dur = dur;
	e->is_pri = pri_found;
	e->is_ext = ext_found;
	e->is_dc = !!(((pulse_bw_info & EXT_CH_RADAR_FOUND) &&
		       (pulse_bw_info & PRI_CH_RADAR_FOUND)));
	e->is_early = early_ext;
	e->fulltsf = fulltsf;
	e->rs_tstamp = rs_tstamp;

	/*
	 * Sowl and later can report pri/ext events.
	 */
	event_width = dfs_get_event_freqwidth(dfs);
	e->freq = dfs_get_event_freqcentre(dfs, e->is_pri, e->is_ext, e->is_dc)
		  * 1000;
	e->freq_lo = e->freq - (event_width / 2) * 1000;
	e->freq_hi = e->freq + (event_width / 2) * 1000;

	DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR_SUM,
		    "%s: pulse_bw_info=0x%x pulse_length_ext=%u pulse_length_pri=%u "
		    "rssi=%u ext_rssi=%u, freq=%d MHz, freq_lo=%d MHz, "
		    "freq_hi=%d MHz\n",
		    __func__,
		    pulse_bw_info,
		    pulse_length_ext,
		    pulse_length_pri,
		    rssi,
		    ext_rssi,
		    e->freq / 1000, e->freq_lo / 1000, e->freq_hi / 1000);

	return 1;
}