Example #1
0
/* returns 1 if this was a spectral frame, even if not handled. */
int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
		    struct ath_rx_status *rs, u64 tsf)
{
	struct ath_hw *ah = sc->sc_ah;
	u8 num_bins, *bins, *vdata = (u8 *)hdr;
	struct fft_sample_ht20 fft_sample_20;
	struct fft_sample_ht20_40 fft_sample_40;
	struct fft_sample_tlv *tlv;
	struct ath_radar_info *radar_info;
	int len = rs->rs_datalen;
	int dc_pos;
	u16 fft_len, length, freq = ah->curchan->chan->center_freq;
	enum nl80211_channel_type chan_type;

	/* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer
	 * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT
	 * yet, but this is supposed to be possible as well.
	 */
	if (rs->rs_phyerr != ATH9K_PHYERR_RADAR &&
	    rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT &&
	    rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL)
		return 0;

	/* check if spectral scan bit is set. This does not have to be checked
	 * if received through a SPECTRAL phy error, but shouldn't hurt.
	 */
	radar_info = ((struct ath_radar_info *)&vdata[len]) - 1;
	if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
		return 0;

	chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef);
	if ((chan_type == NL80211_CHAN_HT40MINUS) ||
	    (chan_type == NL80211_CHAN_HT40PLUS)) {
		fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;
		num_bins = SPECTRAL_HT20_40_NUM_BINS;
		bins = (u8 *)fft_sample_40.data;
	} else {
		fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN;
		num_bins = SPECTRAL_HT20_NUM_BINS;
		bins = (u8 *)fft_sample_20.data;
	}

	/* Variation in the data length is possible and will be fixed later */
	if ((len > fft_len + 2) || (len < fft_len - 1))
		return 1;

	switch (len - fft_len) {
	case 0:
		/* length correct, nothing to do. */
		memcpy(bins, vdata, num_bins);
		break;
	case -1:
		/* first byte missing, duplicate it. */
		memcpy(&bins[1], vdata, num_bins - 1);
		bins[0] = vdata[0];
		break;
	case 2:
		/* MAC added 2 extra bytes at bin 30 and 32, remove them. */
		memcpy(bins, vdata, 30);
		bins[30] = vdata[31];
		memcpy(&bins[31], &vdata[33], num_bins - 31);
		break;
	case 1:
		/* MAC added 2 extra bytes AND first byte is missing. */
		bins[0] = vdata[0];
		memcpy(&bins[1], vdata, 30);
		bins[31] = vdata[31];
		memcpy(&bins[32], &vdata[33], num_bins - 32);
		break;
	default:
		return 1;
	}

	/* DC value (value in the middle) is the blind spot of the spectral
	 * sample and invalid, interpolate it.
	 */
	dc_pos = num_bins / 2;
	bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2;

	if ((chan_type == NL80211_CHAN_HT40MINUS) ||
	    (chan_type == NL80211_CHAN_HT40PLUS)) {
		s8 lower_rssi, upper_rssi;
		s16 ext_nf;
		u8 lower_max_index, upper_max_index;
		u8 lower_bitmap_w, upper_bitmap_w;
		u16 lower_mag, upper_mag;
		struct ath9k_hw_cal_data *caldata = ah->caldata;
		struct ath_ht20_40_mag_info *mag_info;

		if (caldata)
			ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan,
					caldata->nfCalHist[3].privNF);
		else
			ext_nf = ATH_DEFAULT_NOISE_FLOOR;

		length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv);
		fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40;
		fft_sample_40.tlv.length = __cpu_to_be16(length);
		fft_sample_40.freq = __cpu_to_be16(freq);
		fft_sample_40.channel_type = chan_type;

		if (chan_type == NL80211_CHAN_HT40PLUS) {
			lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
			upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);

			fft_sample_40.lower_noise = ah->noise;
			fft_sample_40.upper_noise = ext_nf;
		} else {
			lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);
			upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);

			fft_sample_40.lower_noise = ext_nf;
			fft_sample_40.upper_noise = ah->noise;
		}
		fft_sample_40.lower_rssi = lower_rssi;
		fft_sample_40.upper_rssi = upper_rssi;

		mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1;
		lower_mag = spectral_max_magnitude(mag_info->lower_bins);
		upper_mag = spectral_max_magnitude(mag_info->upper_bins);
		fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);
		fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);
		lower_max_index = spectral_max_index(mag_info->lower_bins);
		upper_max_index = spectral_max_index(mag_info->upper_bins);
		fft_sample_40.lower_max_index = lower_max_index;
		fft_sample_40.upper_max_index = upper_max_index;
		lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins);
		upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins);
		fft_sample_40.lower_bitmap_weight = lower_bitmap_w;
		fft_sample_40.upper_bitmap_weight = upper_bitmap_w;
		fft_sample_40.max_exp = mag_info->max_exp & 0xf;

		fft_sample_40.tsf = __cpu_to_be64(tsf);

		tlv = (struct fft_sample_tlv *)&fft_sample_40;
	} else {
		u8 max_index, bitmap_w;
		u16 magnitude;
		struct ath_ht20_mag_info *mag_info;

		length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv);
		fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20;
		fft_sample_20.tlv.length = __cpu_to_be16(length);
		fft_sample_20.freq = __cpu_to_be16(freq);

		fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
		fft_sample_20.noise = ah->noise;

		mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1;
		magnitude = spectral_max_magnitude(mag_info->all_bins);
		fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);
		max_index = spectral_max_index(mag_info->all_bins);
		fft_sample_20.max_index = max_index;
		bitmap_w = spectral_bitmap_weight(mag_info->all_bins);
		fft_sample_20.bitmap_weight = bitmap_w;
		fft_sample_20.max_exp = mag_info->max_exp & 0xf;

		fft_sample_20.tsf = __cpu_to_be64(tsf);

		tlv = (struct fft_sample_tlv *)&fft_sample_20;
	}

	ath_debug_send_fft_sample(sc, tlv);

	return 1;
}
Example #2
0
void
ath_process_spectraldata(struct ath_spectral *spectral, 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
#define SPECTRAL_SCAN_DATA          0x10
#define DEFAULT_CHAN_NOISE_FLOOR    -110

    int i = 0;
    struct samp_msg_params params;

    u_int8_t rssi             = 0;
    u_int8_t control_rssi     = 0;
    u_int8_t extension_rssi   = 0;
    u_int8_t combined_rssi    = 0;
    u_int8_t max_exp          = 0;
    u_int8_t max_scale        = 0;
    u_int8_t dc_index         = 0;
    u_int8_t lower_dc         = 0;
    u_int8_t upper_dc         = 0;
    u_int8_t ext_rssi         = 0;

    int8_t inv_control_rssi     = 0;
    int8_t inv_combined_rssi    = 0;
    int8_t inv_extension_rssi   = 0;
    int8_t temp_nf              = 0;


    u_int8_t pulse_bw_info      = 0;
    u_int8_t pulse_length_ext   = 0;
    u_int8_t pulse_length_pri   = 0;

    u_int32_t tstamp            = 0;

    u_int16_t datalen           = 0;
    u_int16_t max_mag           = 0;
    u_int16_t newdatalen        = 0;
    u_int16_t already_copied    = 0;
    u_int16_t maxmag_upper      = 0;

    u_int8_t maxindex_upper     = 0;
    u_int8_t max_index          = 0;
    
    int bin_pwr_count   = 0;

    u_int32_t *last_word_ptr        = NULL;
    u_int32_t *secondlast_word_ptr  = NULL;

    u_int8_t *byte_ptr          = NULL;
    u_int8_t *fft_data_end_ptr  = NULL;

    u_int8_t last_byte_0 = 0;
    u_int8_t last_byte_1 = 0;
    u_int8_t last_byte_2 = 0;
    u_int8_t last_byte_3 = 0;

    u_int8_t secondlast_byte_0 = 0;
    u_int8_t secondlast_byte_1 = 0;
    u_int8_t secondlast_byte_2 = 0;
    u_int8_t secondlast_byte_3 = 0;

    HT20_FFT_PACKET fft_20;
    HT40_FFT_PACKET fft_40;

    u_int8_t *fft_data_ptr  = NULL;
    u_int8_t *fft_src_ptr   = NULL;
    u_int8_t *data_ptr      = NULL;

    int8_t cmp_rssi = -110;
    int8_t rssi_up  = 0;
    int8_t rssi_low = 0;

    static u_int64_t last_tsf       = 0;
    static u_int64_t send_tstamp    = 0;

    int8_t nfc_control_rssi     = 0;
    int8_t nfc_extension_rssi   = 0;

    int peak_freq   = 0;
    int nb_lower    = 0;
    int nb_upper    = 0;

    int8_t ctl_chan_noise_floor = DEFAULT_CHAN_NOISE_FLOOR;
    int8_t ext_chan_noise_floor = DEFAULT_CHAN_NOISE_FLOOR;
    struct ath_softc* sc        = NULL;
    SPECTRAL_OPS* p_sops        = NULL;

    if (((rxs->rs_phyerr != HAL_PHYERR_RADAR)) &&
        ((rxs->rs_phyerr != HAL_PHYERR_FALSE_RADAR_EXT)) &&
        ((rxs->rs_phyerr != HAL_PHYERR_SPECTRAL))) {
        printk("%s : Unknown PHY error (0x%x)\n", __func__, rxs->rs_phyerr);
        return;
    }

    if (spectral == NULL) {
        printk("Spectral Module not attached\n");
        return;
    }

    sc     = GET_SPECTRAL_ATHSOFTC(spectral);
    p_sops = GET_SPECTRAL_OPS(spectral);

    spectral->ath_spectral_stats.total_phy_errors++;

    /* Get pointer to data & timestamp*/
    datalen = rxs->rs_datalen;
    tstamp  = (rxs->rs_tstamp & SPECTRAL_TSMASK);

    /* check for valid data length */
    if ((!datalen) || (datalen < spectral->spectral_data_len - 1))  {
        spectral->ath_spectral_stats.datalen_discards++;
        return;
    }


    /* 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 spectral_attach and stored in
     * the sc_spectral_combined_rssi_ok flag.*/

    if (spectral->sc_spectral_combined_rssi_ok) {
        rssi = (u_int8_t) rxs->rs_rssi;
    } else {
        rssi = (u_int8_t) rxs->rs_rssi_ctl0;
    }

    /* get rssi values */
    combined_rssi   = rssi;
    control_rssi    = (u_int8_t) rxs->rs_rssi_ctl0;
    extension_rssi  = (u_int8_t) rxs->rs_rssi_ext0;
    ext_rssi        = (u_int8_t) rxs->rs_rssi_ext0;

    /*
     * If the combined RSSI is less than a particular threshold, this pulse is of no
     * interest to the classifier, so discard it here itself
     * except when noise power cal is required (then we want all rssi values)
     */
    inv_combined_rssi = fix_rssi_inv_only(combined_rssi);

    if (inv_combined_rssi < 5 && !spectral->sc_spectral_noise_pwr_cal) {
        return;
    }

    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);


    switch((datalen & 0x3)) {
        case 0:
            pulse_bw_info       = secondlast_byte_3;
            pulse_length_ext    = secondlast_byte_2;
            pulse_length_pri    = secondlast_byte_1;
            byte_ptr            = (u_int8_t*)secondlast_word_ptr;
            fft_data_end_ptr    = (byte_ptr);
            break;
        case 1:
            pulse_bw_info       = last_byte_0;
            pulse_length_ext    = secondlast_byte_3;
            pulse_length_pri    = secondlast_byte_2;
            byte_ptr            = (u_int8_t*)secondlast_word_ptr;
            fft_data_end_ptr    = (byte_ptr+2);
            break;
        case 2:
            pulse_bw_info       = last_byte_1;
            pulse_length_ext    = last_byte_0;
            pulse_length_pri    = secondlast_byte_3;
            byte_ptr            = (u_int8_t*)secondlast_word_ptr;
            fft_data_end_ptr    = (byte_ptr+3);
            break;
        case 3:
            pulse_bw_info       = last_byte_2;
            pulse_length_ext    = last_byte_1;
            pulse_length_pri    = last_byte_0;
            byte_ptr            = (u_int8_t*)last_word_ptr;
            fft_data_end_ptr    = (byte_ptr);
            break;
        default:
             printk(  "datalen mod4=%d spectral_data_len=%d\n", (datalen%4), spectral->spectral_data_len);
    }

    /*
     * Only the last 3 bits of the BW info are relevant, 
     * they indicate which channel the radar was detected in.
     */
    pulse_bw_info &= 0x17;

    if (pulse_bw_info & SPECTRAL_SCAN_DATA) {

        if (datalen > spectral->spectral_data_len + 2) {
            //printk("Invalid spectral scan datalen = %d\n", datalen);
            return;
        }

        if (spectral->num_spectral_data == 0) {
            spectral->first_tstamp = tstamp;
            spectral->max_rssi = -110;
            //printk( "First FFT data tstamp = %u rssi=%d\n", tstamp, fix_rssi_inv_only(combined_rssi));
        }

        spectral->num_spectral_data++;

        OS_MEMZERO(&fft_40, sizeof (fft_40));
        OS_MEMZERO(&fft_20, sizeof (fft_20));

        if (spectral->sc_spectral_20_40_mode) {
            fft_data_ptr = (u_int8_t*)&fft_40.lower_bins.bin_magnitude[0];
        } else {
            fft_data_ptr = (u_int8_t*)&fft_20.lower_bins.bin_magnitude[0];
        }

        byte_ptr = fft_data_ptr;

        if (datalen == spectral->spectral_data_len) {

            if (spectral->sc_spectral_20_40_mode) {
                // HT40 packet, correct length
                OS_MEMCPY(&fft_40, (u_int8_t*)(bf->bf_vdata), datalen);
            } else {
                // HT20 packet, correct length
                OS_MEMCPY(&fft_20, (u_int8_t*)(bf->bf_vdata), datalen);
            }

        }

      /* This happens when there is a missing byte because CCK is enabled.
       * This may happen with or without the second bug of the MAC inserting
       * 2 bytes
       */
      if ((datalen == (spectral->spectral_data_len - 1)) ||
          (datalen == (spectral->spectral_data_len + 1))) {

          printk(  "%s %d missing 1 byte datalen=%d expected=%d\n", __func__, __LINE__, datalen, spectral->spectral_data_len);

          already_copied++;

          if (spectral->sc_spectral_20_40_mode) {
              // HT40 packet, missing 1 byte
              // Use the beginning byte as byte 0 and byte 1
              fft_40.lower_bins.bin_magnitude[0]=*(u_int8_t*)(bf->bf_vdata);
              // Now copy over the rest
              fft_data_ptr = (u_int8_t*)&fft_40.lower_bins.bin_magnitude[1];
              OS_MEMCPY(fft_data_ptr, (u_int8_t*)(bf->bf_vdata), datalen);
          } else {
              // HT20 packet, missing 1 byte
              // Use the beginning byte as byte 0 and byte 1
              fft_20.lower_bins.bin_magnitude[0]=*(u_int8_t*)(bf->bf_vdata);
              // Now copy over the rest
              fft_data_ptr = (u_int8_t*)&fft_20.lower_bins.bin_magnitude[1];
              OS_MEMCPY(fft_data_ptr, (u_int8_t*)(bf->bf_vdata), datalen);
          }
      }

      if ((datalen == (spectral->spectral_data_len + 1)) ||
          (datalen == (spectral->spectral_data_len + 2))) {

          //printk(  "%s %d extra bytes datalen=%d expected=%d\n", __func__, __LINE__, datalen, spectral->spectral_data_len);

          if (spectral->sc_spectral_20_40_mode) {// HT40 packet, MAC added 2 extra bytes
              fft_src_ptr = (u_int8_t*)(bf->bf_vdata);

              fft_data_ptr = (u_int8_t*)&fft_40.lower_bins.bin_magnitude[already_copied];

              for( i = 0, newdatalen=0; newdatalen < (SPECTRAL_HT40_DATA_LEN - already_copied); i++) {
                if (i == 30 || i == 32) {
                  continue;
                }

                *(fft_data_ptr+newdatalen)=*(fft_src_ptr+i);
                newdatalen++;
              }
          } else { //HT20 packet, MAC added 2 extra bytes
              fft_src_ptr = (u_int8_t*)(bf->bf_vdata);
              fft_data_ptr = (u_int8_t*)&fft_20.lower_bins.bin_magnitude[already_copied];

              for(i=0, newdatalen=0; newdatalen < (SPECTRAL_HT20_DATA_LEN - already_copied); i++) {
                if (i == 30 || i == 32)
                  continue;

                *(fft_data_ptr+newdatalen)=*(fft_src_ptr+i);
                newdatalen++;
              }
          }
      }

      spectral->total_spectral_data++;

      dc_index = spectral->spectral_dc_index;

      if (spectral->sc_spectral_20_40_mode) {
            max_exp     = (fft_40.max_exp & 0x07);
            byte_ptr    = (u_int8_t*)&fft_40.lower_bins.bin_magnitude[0];

             /* Correct the DC bin value */
            lower_dc = *(byte_ptr+dc_index-1);
            upper_dc = *(byte_ptr+dc_index+1);
            *(byte_ptr+dc_index)=((upper_dc + lower_dc)/2);

       } else {
             max_exp    = (fft_20.max_exp & 0x07);
             byte_ptr   = (u_int8_t*)&fft_20.lower_bins.bin_magnitude[0];

            /* Correct the DC bin value */
            lower_dc = *(byte_ptr+dc_index-1);
            upper_dc = *(byte_ptr+dc_index+1);
            *(byte_ptr+dc_index)=((upper_dc + lower_dc)/2);

       }

        if (p_sops->get_ent_spectral_mask(spectral)) {
            *(byte_ptr+dc_index) &=
                ~((1 << p_sops->get_ent_spectral_mask(spectral)) - 1);
        }

        if (max_exp < 1) {
            max_scale = 1;
        } else {
            max_scale = (2) << (max_exp - 1);
        }

        bin_pwr_count = spectral->spectral_numbins;

        if ((last_tsf  > fulltsf) && (!spectral->classify_scan)) {
            spectral->send_single_packet = 1;
            last_tsf = fulltsf;
        }

        inv_combined_rssi   = fix_rssi_inv_only(combined_rssi);
        inv_control_rssi    = fix_rssi_inv_only(control_rssi);
        inv_extension_rssi  = fix_rssi_inv_only(extension_rssi);

        {
            if (spectral->upper_is_control)
              rssi_up = control_rssi;
            else
              rssi_up = extension_rssi;

            if (spectral->lower_is_control)
              rssi_low = control_rssi;
            else
              rssi_low = extension_rssi;

            nfc_control_rssi = get_nfc_ctl_rssi(spectral, inv_control_rssi, &temp_nf);
            ctl_chan_noise_floor = temp_nf;
            rssi_low = fix_rssi_for_classifier(spectral, rssi_low, spectral->lower_is_control, spectral->lower_is_extension);

            if (spectral->sc_spectral_20_40_mode) {
              rssi_up = fix_rssi_for_classifier(spectral, rssi_up,spectral->upper_is_control,spectral->upper_is_extension);
              nfc_extension_rssi = get_nfc_ext_rssi(spectral, inv_extension_rssi, &temp_nf);
              ext_chan_noise_floor = temp_nf;
            }
        }
        if(sc->sc_ieee_ops->spectral_eacs_update) {
            sc->sc_ieee_ops->spectral_eacs_update(sc->sc_ieee, nfc_control_rssi,
                                                  nfc_extension_rssi, ctl_chan_noise_floor, 
                                                                       ext_chan_noise_floor);
        }

        if (!spectral->sc_spectral_20_40_mode) {
            rssi_up             = 0;
            extension_rssi      = 0;
            inv_extension_rssi  = 0;
            nb_upper            = 0;
            maxindex_upper      = 0;
            maxmag_upper        = 0;
        }

        params.rssi         = inv_combined_rssi;
        params.lower_rssi   = rssi_low;
        params.upper_rssi   = rssi_up;

        if (spectral->sc_spectral_noise_pwr_cal) {
            params.chain_ctl_rssi[0] = fix_rssi_inv_only(rxs->rs_rssi_ctl0);
            params.chain_ctl_rssi[1] = fix_rssi_inv_only(rxs->rs_rssi_ctl1);
            params.chain_ctl_rssi[2] = fix_rssi_inv_only(rxs->rs_rssi_ctl2);
            params.chain_ext_rssi[0] = fix_rssi_inv_only(rxs->rs_rssi_ext0);
            params.chain_ext_rssi[1] = fix_rssi_inv_only(rxs->rs_rssi_ext1);
            params.chain_ext_rssi[2] = fix_rssi_inv_only(rxs->rs_rssi_ext2);
        }

        params.bwinfo       = pulse_bw_info;
        params.tstamp       = tstamp;
        params.max_mag      = max_mag;
        params.max_index    = max_index;
        params.max_exp      = max_scale;
        params.peak         = peak_freq;
        params.pwr_count    = bin_pwr_count;
        params.bin_pwr_data = &byte_ptr;
        params.freq         = p_sops->get_current_channel(spectral);
        if(sc->sc_ieee_ops->spectral_get_freq_loading) {
            params.freq_loading = sc->sc_ieee_ops->spectral_get_freq_loading(sc->sc_ieee);
        }
        else {
            params.freq_loading = 0;
        }
        params.interf_list.count = 0;
        params.max_lower_index   = 0;//maxindex_lower;
        params.max_upper_index   = 0;//maxindex_upper;
        params.nb_lower          = nb_lower;
        params.nb_upper          = nb_upper;
        params.last_tstamp       = spectral->last_tstamp;

        get_nfc_ctl_rssi(spectral, inv_control_rssi, &temp_nf);

        params.noise_floor = (int16_t)temp_nf;

        OS_MEMCPY(&params.classifier_params, &spectral->classifier_params, sizeof(SPECTRAL_CLASSIFIER_PARAMS));

        cmp_rssi = fix_rssi_inv_only (combined_rssi);
        spectral->send_single_packet = 0;

#ifdef SPECTRAL_DEBUG_TIMER
        OS_CANCEL_TIMER(&spectral->debug_timer);
#endif
        spectral->spectral_sent_msg++;
        params.datalen = datalen;

        if (spectral->sc_spectral_20_40_mode) {
            data_ptr = (u_int8_t*)&fft_40.lower_bins.bin_magnitude;
        } else {
            data_ptr = (u_int8_t*)&fft_20.lower_bins.bin_magnitude;
        }

        byte_ptr = (u_int8_t*)data_ptr;
        params.bin_pwr_data = &byte_ptr;

        send_tstamp = p_sops->get_tsf64(spectral);
        spectral_create_samp_msg(spectral, &params);

#ifdef SPECTRAL_CLASSIFIER_IN_KERNEL

        if (spectral->classify_scan && maxindex_lower != (SPECTRAL_HT20_DC_INDEX + 1)) {

            classifier(&spectral->bd_lower, tstamp, spectral->last_tstamp, rssi_low, nb_lower, maxindex_lower);

            if (spectral->sc_spectral_20_40_mode && maxindex_upper != (SPECTRAL_HT40_DC_INDEX)) {
                classifier(&spectral->bd_upper, tstamp, spectral->last_tstamp, rssi_up, nb_upper, maxindex_upper);
            }

            print_detection(sc);
        }

#endif /* SPECTRAL_CLASSIFIER_IN_KERNEL */

        spectral->last_tstamp=tstamp;

        return;
  } else {

      /*
       *  Add a HAL capability that tests if chip is capable of spectral scan.
       *  Probably just a check if its a Merlin and above.
       */
      printk("Non Spectral error\n");
      spectral->ath_spectral_stats.owl_phy_errors++;
  }
#undef EXT_CH_RADAR_FOUND
#undef PRI_CH_RADAR_FOUND
#undef EXT_CH_RADAR_EARLY_FOUND
#undef SPECTRAL_SCAN_DATA
}