struct ieee80211_supported_band *sband;
        const struct cfg80211_chan_def *chandef = &wdev->chandef;
        struct ieee80211_channel *chan;
-       struct qtnf_chan_stats stats;
        int ret;
 
        sband = wiphy->bands[NL80211_BAND_2GHZ];
                return -ENOENT;
 
        chan = &sband->channels[idx];
-       memset(&stats, 0, sizeof(stats));
-
        survey->channel = chan;
        survey->filled = 0x0;
 
-       if (chandef->chan) {
-               if (chan->hw_value == chandef->chan->hw_value)
-                       survey->filled = SURVEY_INFO_IN_USE;
-       }
+       if (chan == chandef->chan)
+               survey->filled = SURVEY_INFO_IN_USE;
 
-       ret = qtnf_cmd_get_chan_stats(mac, chan->hw_value, &stats);
-       switch (ret) {
-       case 0:
-               if (unlikely(stats.chan_num != chan->hw_value)) {
-                       pr_err("received stats for channel %d instead of %d\n",
-                              stats.chan_num, chan->hw_value);
-                       ret = -EINVAL;
-                       break;
-               }
-
-               survey->filled |= SURVEY_INFO_TIME |
-                                SURVEY_INFO_TIME_SCAN |
-                                SURVEY_INFO_TIME_BUSY |
-                                SURVEY_INFO_TIME_RX |
-                                SURVEY_INFO_TIME_TX |
-                                SURVEY_INFO_NOISE_DBM;
-
-               survey->time_scan = stats.cca_try;
-               survey->time = stats.cca_try;
-               survey->time_tx = stats.cca_tx;
-               survey->time_rx = stats.cca_rx;
-               survey->time_busy = stats.cca_busy;
-               survey->noise = stats.chan_noise;
-               break;
-       case -ENOENT:
-               pr_debug("no stats for channel %u\n", chan->hw_value);
-               ret = 0;
-               break;
-       default:
+       ret = qtnf_cmd_get_chan_stats(mac, chan->center_freq, survey);
+       if (ret)
                pr_debug("failed to get chan(%d) stats from card\n",
                         chan->hw_value);
-               break;
-       }
 
        return ret;
 }
 
        return ret;
 }
 
-static int
-qtnf_cmd_resp_proc_chan_stat_info(struct qtnf_chan_stats *stats,
-                                 const u8 *payload, size_t payload_len)
-{
-       struct qlink_chan_stats *qlink_stats;
-       const struct qlink_tlv_hdr *tlv;
-       size_t tlv_full_len;
-       u16 tlv_value_len;
-       u16 tlv_type;
-
-       tlv = (struct qlink_tlv_hdr *)payload;
-       while (payload_len >= sizeof(struct qlink_tlv_hdr)) {
-               tlv_type = le16_to_cpu(tlv->type);
-               tlv_value_len = le16_to_cpu(tlv->len);
-               tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr);
-               if (tlv_full_len > payload_len) {
-                       pr_warn("malformed TLV 0x%.2X; LEN: %u\n",
-                               tlv_type, tlv_value_len);
-                       return -EINVAL;
-               }
-               switch (tlv_type) {
-               case QTN_TLV_ID_CHANNEL_STATS:
-                       if (unlikely(tlv_value_len != sizeof(*qlink_stats))) {
-                               pr_err("invalid CHANNEL_STATS entry size\n");
-                               return -EINVAL;
-                       }
-
-                       qlink_stats = (void *)tlv->val;
-
-                       stats->chan_num = le32_to_cpu(qlink_stats->chan_num);
-                       stats->cca_tx = le32_to_cpu(qlink_stats->cca_tx);
-                       stats->cca_rx = le32_to_cpu(qlink_stats->cca_rx);
-                       stats->cca_busy = le32_to_cpu(qlink_stats->cca_busy);
-                       stats->cca_try = le32_to_cpu(qlink_stats->cca_try);
-                       stats->chan_noise = qlink_stats->chan_noise;
-
-                       pr_debug("chan(%u) try(%u) busy(%u) noise(%d)\n",
-                                stats->chan_num, stats->cca_try,
-                                stats->cca_busy, stats->chan_noise);
-                       break;
-               default:
-                       pr_warn("Unknown TLV type: %#x\n",
-                               le16_to_cpu(tlv->type));
-               }
-               payload_len -= tlv_full_len;
-               tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
-       }
-
-       if (payload_len) {
-               pr_warn("malformed TLV buf; bytes left: %zu\n", payload_len);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac)
 {
        struct sk_buff *cmd_skb, *resp_skb = NULL;
        return ret;
 }
 
-int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel,
-                           struct qtnf_chan_stats *stats)
+static int
+qtnf_cmd_resp_proc_chan_stat_info(struct survey_info *survey,
+                                 const u8 *payload, size_t payload_len)
+{
+       const struct qlink_chan_stats *stats = NULL;
+       const struct qlink_tlv_hdr *tlv;
+       size_t tlv_full_len;
+       u16 tlv_value_len;
+       u16 tlv_type;
+       const u8 *map = NULL;
+       unsigned int map_len = 0;
+       unsigned int stats_len = 0;
+
+       tlv = (struct qlink_tlv_hdr *)payload;
+
+       while (payload_len >= sizeof(*tlv)) {
+               tlv_type = le16_to_cpu(tlv->type);
+               tlv_value_len = le16_to_cpu(tlv->len);
+               tlv_full_len = tlv_value_len + sizeof(*tlv);
+
+               if (tlv_full_len > payload_len) {
+                       pr_warn("malformed TLV 0x%.2X; LEN: %u\n",
+                               tlv_type, tlv_value_len);
+                       return -ENOSPC;
+               }
+
+               switch (tlv_type) {
+               case QTN_TLV_ID_BITMAP:
+                       map = tlv->val;
+                       map_len = tlv_value_len;
+                       break;
+               case QTN_TLV_ID_CHANNEL_STATS:
+                       stats = (struct qlink_chan_stats *)tlv->val;
+                       stats_len = tlv_value_len;
+                       break;
+               default:
+                       pr_info("Unknown TLV type: %#x\n", tlv_type);
+                       break;
+               }
+
+               payload_len -= tlv_full_len;
+               tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
+       }
+
+       if (payload_len) {
+               pr_warn("malformed TLV buf; bytes left: %zu\n", payload_len);
+               return -EINVAL;
+       }
+
+       if (!map || !stats)
+               return 0;
+
+#define qtnf_chan_stat_avail(stat_name, bitn)  \
+       (qtnf_utils_is_bit_set(map, bitn, map_len) && \
+        (offsetofend(struct qlink_chan_stats, stat_name) <= stats_len))
+
+       if (qtnf_chan_stat_avail(time_on, QLINK_CHAN_STAT_TIME_ON)) {
+               survey->filled |= SURVEY_INFO_TIME;
+               survey->time = le64_to_cpu(stats->time_on);
+       }
+
+       if (qtnf_chan_stat_avail(time_tx, QLINK_CHAN_STAT_TIME_TX)) {
+               survey->filled |= SURVEY_INFO_TIME_TX;
+               survey->time_tx = le64_to_cpu(stats->time_tx);
+       }
+
+       if (qtnf_chan_stat_avail(time_rx, QLINK_CHAN_STAT_TIME_RX)) {
+               survey->filled |= SURVEY_INFO_TIME_RX;
+               survey->time_rx = le64_to_cpu(stats->time_rx);
+       }
+
+       if (qtnf_chan_stat_avail(cca_busy, QLINK_CHAN_STAT_CCA_BUSY)) {
+               survey->filled |= SURVEY_INFO_TIME_BUSY;
+               survey->time_busy = le64_to_cpu(stats->cca_busy);
+       }
+
+       if (qtnf_chan_stat_avail(cca_busy_ext, QLINK_CHAN_STAT_CCA_BUSY_EXT)) {
+               survey->filled |= SURVEY_INFO_TIME_EXT_BUSY;
+               survey->time_ext_busy = le64_to_cpu(stats->cca_busy_ext);
+       }
+
+       if (qtnf_chan_stat_avail(time_scan, QLINK_CHAN_STAT_TIME_SCAN)) {
+               survey->filled |= SURVEY_INFO_TIME_SCAN;
+               survey->time_scan = le64_to_cpu(stats->time_scan);
+       }
+
+       if (qtnf_chan_stat_avail(chan_noise, QLINK_CHAN_STAT_CHAN_NOISE)) {
+               survey->filled |= SURVEY_INFO_NOISE_DBM;
+               survey->noise = stats->chan_noise;
+       }
+
+#undef qtnf_chan_stat_avail
+
+       return 0;
+}
+
+int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u32 chan_freq,
+                           struct survey_info *survey)
 {
        struct sk_buff *cmd_skb, *resp_skb = NULL;
        struct qlink_cmd_get_chan_stats *cmd;
        if (!cmd_skb)
                return -ENOMEM;
 
-       qtnf_bus_lock(mac->bus);
-
        cmd = (struct qlink_cmd_get_chan_stats *)cmd_skb->data;
-       cmd->channel = cpu_to_le16(channel);
+       cmd->channel_freq = cpu_to_le32(chan_freq);
 
+       qtnf_bus_lock(mac->bus);
        ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb,
                                       sizeof(*resp), &var_data_len);
+       qtnf_bus_unlock(mac->bus);
+
        if (ret)
                goto out;
 
        resp = (struct qlink_resp_get_chan_stats *)resp_skb->data;
-       ret = qtnf_cmd_resp_proc_chan_stat_info(stats, resp->info,
+
+       if (le32_to_cpu(resp->chan_freq) != chan_freq) {
+               pr_err("[MAC%u] channel stats freq %u != requested %u\n",
+                      mac->macid, le32_to_cpu(resp->chan_freq), chan_freq);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = qtnf_cmd_resp_proc_chan_stat_info(survey, resp->info,
                                                var_data_len);
 
 out:
-       qtnf_bus_unlock(mac->bus);
        consume_skb(resp_skb);
 
        return ret;
 
 /**
  * struct qlink_cmd_get_chan_stats - data for QLINK_CMD_CHAN_STATS command
  *
- * @channel: channel number according to 802.11 17.3.8.3.2 and Annex J
+ * @channel_freq: channel center frequency
  */
 struct qlink_cmd_get_chan_stats {
        struct qlink_cmd chdr;
-       __le16 channel;
+       __le32 channel_freq;
 } __packed;
 
 /**
  *
  * Response data containing statistics for specified STA.
  *
- * @filled: a bitmask of &enum qlink_sta_info, specifies which info in response
- *     is valid.
  * @sta_addr: MAC address of STA the response carries statistic for.
  * @info: variable statistics for specified STA.
  */
 /**
  * struct qlink_resp_get_chan_stats - response for QLINK_CMD_CHAN_STATS cmd
  *
+ * @chan_freq: center frequency for a channel the report is sent for.
  * @info: variable-length channel info.
  */
 struct qlink_resp_get_chan_stats {
-       struct qlink_cmd rhdr;
+       struct qlink_resp rhdr;
+       __le32 chan_freq;
        u8 info[0];
 } __packed;
 
  *     QTN_TLV_ID_STA_STATS is valid.
  *     &enum qlink_hw_capab listing wireless card capabilities.
  *     &enum qlink_driver_capab listing driver/host system capabilities.
+ *     &enum qlink_chan_stat used to indicate which statistic carried in
+ *     QTN_TLV_ID_CHANNEL_STATS is valid.
  * @QTN_TLV_ID_STA_STATS: per-STA statistics as defined by
  *     &struct qlink_sta_stats. Valid values are marked as such in a bitmap
  *     carried by QTN_TLV_ID_BITMAP.
        struct qlink_sband_iftype_data iftype_data[0];
 } __packed;
 
+/**
+ * enum qlink_chan_stat - channel statistics bitmap
+ *
+ * Used to indicate which statistics values in &struct qlink_chan_stats
+ * are valid. Individual values are used to fill a bitmap carried in a
+ * payload of QTN_TLV_ID_BITMAP.
+ *
+ * @QLINK_CHAN_STAT_TIME_ON: time_on value is valid.
+ * @QLINK_CHAN_STAT_TIME_TX: time_tx value is valid.
+ * @QLINK_CHAN_STAT_TIME_RX: time_rx value is valid.
+ * @QLINK_CHAN_STAT_CCA_BUSY: cca_busy value is valid.
+ * @QLINK_CHAN_STAT_CCA_BUSY_EXT: cca_busy_ext value is valid.
+ * @QLINK_CHAN_STAT_TIME_SCAN: time_scan value is valid.
+ * @QLINK_CHAN_STAT_CHAN_NOISE: chan_noise value is valid.
+ */
+enum qlink_chan_stat {
+       QLINK_CHAN_STAT_TIME_ON,
+       QLINK_CHAN_STAT_TIME_TX,
+       QLINK_CHAN_STAT_TIME_RX,
+       QLINK_CHAN_STAT_CCA_BUSY,
+       QLINK_CHAN_STAT_CCA_BUSY_EXT,
+       QLINK_CHAN_STAT_TIME_SCAN,
+       QLINK_CHAN_STAT_CHAN_NOISE,
+       QLINK_CHAN_STAT_NUM,
+};
+
+/**
+ * struct qlink_chan_stats - data for QTN_TLV_ID_CHANNEL_STATS
+ *
+ * Carries a per-channel statistics. Not all fields may be filled with
+ * valid values. Valid fields should be indicated as such using a bitmap of
+ * &enum qlink_chan_stat. Bitmap is carried separately in a payload of
+ * QTN_TLV_ID_BITMAP.
+ *
+ * @time_on: amount of time radio operated on that channel.
+ * @time_tx: amount of time radio spent transmitting on the channel.
+ * @time_rx: amount of time radio spent receiving on the channel.
+ * @cca_busy: amount of time the the primary channel was busy.
+ * @cca_busy_ext: amount of time the the secondary channel was busy.
+ * @time_scan: amount of radio spent scanning on the channel.
+ * @chan_noise: channel noise.
+ */
 struct qlink_chan_stats {
-       __le32 chan_num;
-       __le32 cca_tx;
-       __le32 cca_rx;
-       __le32 cca_busy;
-       __le32 cca_try;
+       __le64 time_on;
+       __le64 time_tx;
+       __le64 time_rx;
+       __le64 cca_busy;
+       __le64 cca_busy_ext;
+       __le64 time_scan;
        s8 chan_noise;
+       u8 rsvd[3];
 } __packed;
 
 /**