X-Git-Url: http://git.cascardo.info/?p=cascardo%2Flinux.git;a=blobdiff_plain;f=drivers%2Fnet%2Fwireless%2Fath%2Fath10k%2Fwmi.c;h=f9925708f588e690a5e8c5b7d22aabcbf18cf2ad;hp=ccf3597fd9e2511c11cce44e136c3d1c50c59d06;hb=2fe5288c11dae71c178a9d948347933b0b76a5f5;hpb=4fbf888accb39af423f271111d44e8186f053723 diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index ccf3597fd9e2..f9925708f588 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -16,6 +16,7 @@ */ #include +#include #include "core.h" #include "htc.h" @@ -674,10 +675,8 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) /* Send the management frame buffer to the target */ ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid); - if (ret) { - dev_kfree_skb_any(skb); + if (ret) return ret; - } /* TODO: report tx status to mac80211 - temporary just ACK */ info->flags |= IEEE80211_TX_STAT_ACK; @@ -877,6 +876,7 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) 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; @@ -909,6 +909,11 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) 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; @@ -924,7 +929,25 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) 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); @@ -934,7 +957,11 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) 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 & @@ -1383,9 +1410,259 @@ static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, 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) @@ -1400,9 +1677,37 @@ static void ath10k_wmi_event_profile_match(struct ath10k *ar, } 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) @@ -2062,6 +2367,7 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar, { struct wmi_set_channel_cmd *cmd; struct sk_buff *skb; + u32 ch_flags = 0; if (arg->passive) return -EINVAL; @@ -2070,10 +2376,14 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar, 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; @@ -2211,7 +2521,7 @@ static int ath10k_wmi_main_cmd_init(struct ath10k *ar) } ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", - __cpu_to_le32(ar->wmi.num_mem_chunks)); + ar->wmi.num_mem_chunks); cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); @@ -2224,10 +2534,10 @@ static int ath10k_wmi_main_cmd_init(struct ath10k *ar) __cpu_to_le32(ar->wmi.mem_chunks[i].req_id); ath10k_dbg(ATH10K_DBG_WMI, - "wmi chunk %d len %d requested, addr 0x%x\n", + "wmi chunk %d len %d requested, addr 0x%llx\n", i, - cmd->host_mem_chunks[i].size, - cmd->host_mem_chunks[i].ptr); + ar->wmi.mem_chunks[i].len, + (unsigned long long)ar->wmi.mem_chunks[i].paddr); } out: memcpy(&cmd->resource_config, &config, sizeof(config)); @@ -2302,7 +2612,7 @@ static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) } ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", - __cpu_to_le32(ar->wmi.num_mem_chunks)); + ar->wmi.num_mem_chunks); cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); @@ -2315,10 +2625,10 @@ static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) __cpu_to_le32(ar->wmi.mem_chunks[i].req_id); ath10k_dbg(ATH10K_DBG_WMI, - "wmi chunk %d len %d requested, addr 0x%x\n", + "wmi chunk %d len %d requested, addr 0x%llx\n", i, - cmd->host_mem_chunks[i].size, - cmd->host_mem_chunks[i].ptr); + ar->wmi.mem_chunks[i].len, + (unsigned long long)ar->wmi.mem_chunks[i].paddr); } out: memcpy(&cmd->resource_config, &config, sizeof(config)); @@ -2622,6 +2932,7 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, 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) @@ -2648,6 +2959,8 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, 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); @@ -2669,6 +2982,7 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, __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; @@ -2676,9 +2990,10 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, 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); } @@ -3012,6 +3327,8 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar, 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); @@ -3094,6 +3411,7 @@ int ath10k_wmi_beacon_send_nowait(struct ath10k *ar, { struct wmi_bcn_tx_cmd *cmd; struct sk_buff *skb; + int ret; skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->bcn_len); if (!skb) @@ -3106,7 +3424,11 @@ int ath10k_wmi_beacon_send_nowait(struct ath10k *ar, 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,