*/
#include <linux/skbuff.h>
+#include <linux/ctype.h>
#include "core.h"
#include "htc.h"
struct wmi_mgmt_rx_event_v2 *ev_v2;
struct wmi_mgmt_rx_hdr_v1 *ev_hdr;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_channel *ch;
struct ieee80211_hdr *hdr;
u32 rx_status;
u32 channel;
ath10k_dbg(ATH10K_DBG_MGMT,
"event mgmt rx status %08x\n", rx_status);
+ if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) {
dev_kfree_skb(skb);
return 0;
if (rx_status & WMI_RX_STATUS_ERR_MIC)
status->flag |= RX_FLAG_MMIC_ERROR;
- status->band = phy_mode_to_band(phy_mode);
+ /* HW can Rx CCK rates on 5GHz. In that case phy_mode is set to
+ * MODE_11B. This means phy_mode is not a reliable source for the band
+ * of mgmt rx. */
+
+ ch = ar->scan_channel;
+ if (!ch)
+ ch = ar->rx_channel;
+
+ if (ch) {
+ status->band = ch->band;
+
+ if (phy_mode == MODE_11B &&
+ status->band == IEEE80211_BAND_5GHZ)
+ ath10k_dbg(ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n");
+ } else {
+ ath10k_warn("using (unreliable) phy_mode to extract band for mgmt rx\n");
+ status->band = phy_mode_to_band(phy_mode);
+ }
+
status->freq = ieee80211_channel_to_frequency(channel, status->band);
status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR;
status->rate_idx = get_rate_idx(rate, status->band);
hdr = (struct ieee80211_hdr *)skb->data;
fc = le16_to_cpu(hdr->frame_control);
- if (fc & IEEE80211_FCTL_PROTECTED) {
+ /* FW delivers WEP Shared Auth frame with Protected Bit set and
+ * encrypted payload. However in case of PMF it delivers decrypted
+ * frames with Protected Bit set. */
+ if (ieee80211_has_protected(hdr->frame_control) &&
+ !ieee80211_is_auth(hdr->frame_control)) {
status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED |
RX_FLAG_MMIC_STRIPPED;
hdr->frame_control = __cpu_to_le16(fc &
ath10k_dbg(ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n");
}
+static void ath10k_dfs_radar_report(struct ath10k *ar,
+ struct wmi_single_phyerr_rx_event *event,
+ struct phyerr_radar_report *rr,
+ u64 tsf)
+{
+ u32 reg0, reg1, tsf32l;
+ struct pulse_event pe;
+ u64 tsf64;
+ u8 rssi, width;
+
+ reg0 = __le32_to_cpu(rr->reg0);
+ reg1 = __le32_to_cpu(rr->reg1);
+
+ ath10k_dbg(ATH10K_DBG_REGULATORY,
+ "wmi phyerr radar report chirp %d max_width %d agc_total_gain %d pulse_delta_diff %d\n",
+ MS(reg0, RADAR_REPORT_REG0_PULSE_IS_CHIRP),
+ MS(reg0, RADAR_REPORT_REG0_PULSE_IS_MAX_WIDTH),
+ MS(reg0, RADAR_REPORT_REG0_AGC_TOTAL_GAIN),
+ MS(reg0, RADAR_REPORT_REG0_PULSE_DELTA_DIFF));
+ ath10k_dbg(ATH10K_DBG_REGULATORY,
+ "wmi phyerr radar report pulse_delta_pean %d pulse_sidx %d fft_valid %d agc_mb_gain %d subchan_mask %d\n",
+ MS(reg0, RADAR_REPORT_REG0_PULSE_DELTA_PEAK),
+ MS(reg0, RADAR_REPORT_REG0_PULSE_SIDX),
+ MS(reg1, RADAR_REPORT_REG1_PULSE_SRCH_FFT_VALID),
+ MS(reg1, RADAR_REPORT_REG1_PULSE_AGC_MB_GAIN),
+ MS(reg1, RADAR_REPORT_REG1_PULSE_SUBCHAN_MASK));
+ ath10k_dbg(ATH10K_DBG_REGULATORY,
+ "wmi phyerr radar report pulse_tsf_offset 0x%X pulse_dur: %d\n",
+ MS(reg1, RADAR_REPORT_REG1_PULSE_TSF_OFFSET),
+ MS(reg1, RADAR_REPORT_REG1_PULSE_DUR));
+
+ if (!ar->dfs_detector)
+ return;
+
+ /* report event to DFS pattern detector */
+ tsf32l = __le32_to_cpu(event->hdr.tsf_timestamp);
+ tsf64 = tsf & (~0xFFFFFFFFULL);
+ tsf64 |= tsf32l;
+
+ width = MS(reg1, RADAR_REPORT_REG1_PULSE_DUR);
+ rssi = event->hdr.rssi_combined;
+
+ /* hardware store this as 8 bit signed value,
+ * set to zero if negative number
+ */
+ if (rssi & 0x80)
+ rssi = 0;
+
+ pe.ts = tsf64;
+ pe.freq = ar->hw->conf.chandef.chan->center_freq;
+ pe.width = width;
+ pe.rssi = rssi;
+
+ ath10k_dbg(ATH10K_DBG_REGULATORY,
+ "dfs add pulse freq: %d, width: %d, rssi %d, tsf: %llX\n",
+ pe.freq, pe.width, pe.rssi, pe.ts);
+
+ ATH10K_DFS_STAT_INC(ar, pulses_detected);
+
+ if (!ar->dfs_detector->add_pulse(ar->dfs_detector, &pe)) {
+ ath10k_dbg(ATH10K_DBG_REGULATORY,
+ "dfs no pulse pattern detected, yet\n");
+ return;
+ }
+
+ ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs radar detected\n");
+ ATH10K_DFS_STAT_INC(ar, radar_detected);
+
+ /* Control radar events reporting in debugfs file
+ dfs_block_radar_events */
+ if (ar->dfs_block_radar_events) {
+ ath10k_info("DFS Radar detected, but ignored as requested\n");
+ return;
+ }
+
+ ieee80211_radar_detected(ar->hw);
+}
+
+static int ath10k_dfs_fft_report(struct ath10k *ar,
+ struct wmi_single_phyerr_rx_event *event,
+ struct phyerr_fft_report *fftr,
+ u64 tsf)
+{
+ u32 reg0, reg1;
+ u8 rssi, peak_mag;
+
+ reg0 = __le32_to_cpu(fftr->reg0);
+ reg1 = __le32_to_cpu(fftr->reg1);
+ rssi = event->hdr.rssi_combined;
+
+ ath10k_dbg(ATH10K_DBG_REGULATORY,
+ "wmi phyerr fft report total_gain_db %d base_pwr_db %d fft_chn_idx %d peak_sidx %d\n",
+ MS(reg0, SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB),
+ MS(reg0, SEARCH_FFT_REPORT_REG0_BASE_PWR_DB),
+ MS(reg0, SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX),
+ MS(reg0, SEARCH_FFT_REPORT_REG0_PEAK_SIDX));
+ ath10k_dbg(ATH10K_DBG_REGULATORY,
+ "wmi phyerr fft report rel_pwr_db %d avgpwr_db %d peak_mag %d num_store_bin %d\n",
+ MS(reg1, SEARCH_FFT_REPORT_REG1_RELPWR_DB),
+ MS(reg1, SEARCH_FFT_REPORT_REG1_AVGPWR_DB),
+ MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG),
+ MS(reg1, SEARCH_FFT_REPORT_REG1_NUM_STR_BINS_IB));
+
+ peak_mag = MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG);
+
+ /* false event detection */
+ if (rssi == DFS_RSSI_POSSIBLY_FALSE &&
+ peak_mag < 2 * DFS_PEAK_MAG_THOLD_POSSIBLY_FALSE) {
+ ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs false pulse detected\n");
+ ATH10K_DFS_STAT_INC(ar, pulses_discarded);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void ath10k_wmi_event_dfs(struct ath10k *ar,
+ struct wmi_single_phyerr_rx_event *event,
+ u64 tsf)
+{
+ int buf_len, tlv_len, res, i = 0;
+ struct phyerr_tlv *tlv;
+ struct phyerr_radar_report *rr;
+ struct phyerr_fft_report *fftr;
+ u8 *tlv_buf;
+
+ buf_len = __le32_to_cpu(event->hdr.buf_len);
+ ath10k_dbg(ATH10K_DBG_REGULATORY,
+ "wmi event dfs err_code %d rssi %d tsfl 0x%X tsf64 0x%llX len %d\n",
+ event->hdr.phy_err_code, event->hdr.rssi_combined,
+ __le32_to_cpu(event->hdr.tsf_timestamp), tsf, buf_len);
+
+ /* Skip event if DFS disabled */
+ if (!config_enabled(CONFIG_ATH10K_DFS_CERTIFIED))
+ return;
+
+ ATH10K_DFS_STAT_INC(ar, pulses_total);
+
+ while (i < buf_len) {
+ if (i + sizeof(*tlv) > buf_len) {
+ ath10k_warn("too short buf for tlv header (%d)\n", i);
+ return;
+ }
+
+ tlv = (struct phyerr_tlv *)&event->bufp[i];
+ tlv_len = __le16_to_cpu(tlv->len);
+ tlv_buf = &event->bufp[i + sizeof(*tlv)];
+ ath10k_dbg(ATH10K_DBG_REGULATORY,
+ "wmi event dfs tlv_len %d tlv_tag 0x%02X tlv_sig 0x%02X\n",
+ tlv_len, tlv->tag, tlv->sig);
+
+ switch (tlv->tag) {
+ case PHYERR_TLV_TAG_RADAR_PULSE_SUMMARY:
+ if (i + sizeof(*tlv) + sizeof(*rr) > buf_len) {
+ ath10k_warn("too short radar pulse summary (%d)\n",
+ i);
+ return;
+ }
+
+ rr = (struct phyerr_radar_report *)tlv_buf;
+ ath10k_dfs_radar_report(ar, event, rr, tsf);
+ break;
+ case PHYERR_TLV_TAG_SEARCH_FFT_REPORT:
+ if (i + sizeof(*tlv) + sizeof(*fftr) > buf_len) {
+ ath10k_warn("too short fft report (%d)\n", i);
+ return;
+ }
+
+ fftr = (struct phyerr_fft_report *)tlv_buf;
+ res = ath10k_dfs_fft_report(ar, event, fftr, tsf);
+ if (res)
+ return;
+ break;
+ }
+
+ i += sizeof(*tlv) + tlv_len;
+ }
+}
+
+static void ath10k_wmi_event_spectral_scan(struct ath10k *ar,
+ struct wmi_single_phyerr_rx_event *event,
+ u64 tsf)
+{
+ ath10k_dbg(ATH10K_DBG_WMI, "wmi event spectral scan\n");
+}
+
static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
{
- ath10k_dbg(ATH10K_DBG_WMI, "WMI_PHYERR_EVENTID\n");
+ struct wmi_comb_phyerr_rx_event *comb_event;
+ struct wmi_single_phyerr_rx_event *event;
+ u32 count, i, buf_len, phy_err_code;
+ u64 tsf;
+ int left_len = skb->len;
+
+ ATH10K_DFS_STAT_INC(ar, phy_errors);
+
+ /* Check if combined event available */
+ if (left_len < sizeof(*comb_event)) {
+ ath10k_warn("wmi phyerr combined event wrong len\n");
+ return;
+ }
+
+ left_len -= sizeof(*comb_event);
+
+ /* Check number of included events */
+ comb_event = (struct wmi_comb_phyerr_rx_event *)skb->data;
+ count = __le32_to_cpu(comb_event->hdr.num_phyerr_events);
+
+ tsf = __le32_to_cpu(comb_event->hdr.tsf_u32);
+ tsf <<= 32;
+ tsf |= __le32_to_cpu(comb_event->hdr.tsf_l32);
+
+ ath10k_dbg(ATH10K_DBG_WMI,
+ "wmi event phyerr count %d tsf64 0x%llX\n",
+ count, tsf);
+
+ event = (struct wmi_single_phyerr_rx_event *)comb_event->bufp;
+ for (i = 0; i < count; i++) {
+ /* Check if we can read event header */
+ if (left_len < sizeof(*event)) {
+ ath10k_warn("single event (%d) wrong head len\n", i);
+ return;
+ }
+
+ left_len -= sizeof(*event);
+
+ buf_len = __le32_to_cpu(event->hdr.buf_len);
+ phy_err_code = event->hdr.phy_err_code;
+
+ if (left_len < buf_len) {
+ ath10k_warn("single event (%d) wrong buf len\n", i);
+ return;
+ }
+
+ left_len -= buf_len;
+
+ switch (phy_err_code) {
+ case PHY_ERROR_RADAR:
+ ath10k_wmi_event_dfs(ar, event, tsf);
+ break;
+ case PHY_ERROR_SPECTRAL_SCAN:
+ ath10k_wmi_event_spectral_scan(ar, event, tsf);
+ break;
+ case PHY_ERROR_FALSE_RADAR_EXT:
+ ath10k_wmi_event_dfs(ar, event, tsf);
+ ath10k_wmi_event_spectral_scan(ar, event, tsf);
+ break;
+ default:
+ break;
+ }
+
+ event += sizeof(*event) + buf_len;
+ }
}
static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb)
}
static void ath10k_wmi_event_debug_print(struct ath10k *ar,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
- ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_PRINT_EVENTID\n");
+ char buf[101], c;
+ int i;
+
+ for (i = 0; i < sizeof(buf) - 1; i++) {
+ if (i >= skb->len)
+ break;
+
+ c = skb->data[i];
+
+ if (c == '\0')
+ break;
+
+ if (isascii(c) && isprint(c))
+ buf[i] = c;
+ else
+ buf[i] = '.';
+ }
+
+ if (i == sizeof(buf) - 1)
+ ath10k_warn("wmi debug print truncated: %d\n", skb->len);
+
+ /* for some reason the debug prints end with \n, remove that */
+ if (skb->data[i - 1] == '\n')
+ i--;
+
+ /* the last byte is always reserved for the null character */
+ buf[i] = '\0';
+
+ ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug print '%s'\n", buf);
}
static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
{
struct wmi_set_channel_cmd *cmd;
struct sk_buff *skb;
+ u32 ch_flags = 0;
if (arg->passive)
return -EINVAL;
if (!skb)
return -ENOMEM;
+ if (arg->chan_radar)
+ ch_flags |= WMI_CHAN_FLAG_DFS;
+
cmd = (struct wmi_set_channel_cmd *)skb->data;
cmd->chan.mhz = __cpu_to_le32(arg->freq);
cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq);
cmd->chan.mode = arg->mode;
+ cmd->chan.flags |= __cpu_to_le32(ch_flags);
cmd->chan.min_power = arg->min_power;
cmd->chan.max_power = arg->max_power;
cmd->chan.reg_power = arg->max_reg_power;
struct sk_buff *skb;
const char *cmdname;
u32 flags = 0;
+ u32 ch_flags = 0;
if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid &&
cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid)
flags |= WMI_VDEV_START_HIDDEN_SSID;
if (arg->pmf_enabled)
flags |= WMI_VDEV_START_PMF_ENABLED;
+ if (arg->channel.chan_radar)
+ ch_flags |= WMI_CHAN_FLAG_DFS;
cmd = (struct wmi_vdev_start_request_cmd *)skb->data;
cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
__cpu_to_le32(arg->channel.band_center_freq1);
cmd->chan.mode = arg->channel.mode;
+ cmd->chan.flags |= __cpu_to_le32(ch_flags);
cmd->chan.min_power = arg->channel.min_power;
cmd->chan.max_power = arg->channel.max_power;
cmd->chan.reg_power = arg->channel.max_reg_power;
cmd->chan.antenna_max = arg->channel.max_antenna_gain;
ath10k_dbg(ATH10K_DBG_WMI,
- "wmi vdev %s id 0x%x freq %d, mode %d, ch_flags: 0x%0X,"
- "max_power: %d\n", cmdname, arg->vdev_id, arg->channel.freq,
- arg->channel.mode, flags, arg->channel.max_power);
+ "wmi vdev %s id 0x%x flags: 0x%0X, freq %d, mode %d, "
+ "ch_flags: 0x%0X, max_power: %d\n", cmdname, arg->vdev_id,
+ flags, arg->channel.freq, arg->channel.mode,
+ cmd->chan.flags, arg->channel.max_power);
return ath10k_wmi_cmd_send(ar, skb, cmd_id);
}
cmd = (struct wmi_vdev_up_cmd *)skb->data;
cmd->vdev_id = __cpu_to_le32(vdev_id);
cmd->vdev_assoc_id = __cpu_to_le32(aid);
- memcpy(&cmd->vdev_bssid.addr, bssid, 6);
+ memcpy(&cmd->vdev_bssid.addr, bssid, ETH_ALEN);
ath10k_dbg(ATH10K_DBG_WMI,
"wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n",
cmd->vdev_id = __cpu_to_le32(vdev_id);
cmd->param_id = __cpu_to_le32(param_id);
cmd->param_value = __cpu_to_le32(param_value);
- memcpy(&cmd->peer_macaddr.addr, peer_addr, 6);
+ memcpy(&cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
ath10k_dbg(ATH10K_DBG_WMI,
"wmi vdev %d peer 0x%pM set param %d value %d\n",
flags |= WMI_CHAN_FLAG_ALLOW_VHT;
if (ch->ht40plus)
flags |= WMI_CHAN_FLAG_HT40_PLUS;
+ if (ch->chan_radar)
+ flags |= WMI_CHAN_FLAG_DFS;
ci->mhz = __cpu_to_le32(ch->freq);
ci->band_center_freq1 = __cpu_to_le32(ch->freq);
{
struct wmi_bcn_tx_cmd *cmd;
struct sk_buff *skb;
+ int ret;
skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->bcn_len);
if (!skb)
cmd->hdr.bcn_len = __cpu_to_le32(arg->bcn_len);
memcpy(cmd->bcn, arg->bcn, arg->bcn_len);
- return ath10k_wmi_cmd_send_nowait(ar, skb, ar->wmi.cmd->bcn_tx_cmdid);
+ ret = ath10k_wmi_cmd_send_nowait(ar, skb, ar->wmi.cmd->bcn_tx_cmdid);
+ if (ret)
+ dev_kfree_skb(skb);
+
+ return ret;
}
static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,