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