aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/realtek/rtw88/debug.h1
-rw-r--r--drivers/net/wireless/realtek/rtw88/fw.c388
-rw-r--r--drivers/net/wireless/realtek/rtw88/fw.h142
-rw-r--r--drivers/net/wireless/realtek/rtw88/mac80211.c79
-rw-r--r--drivers/net/wireless/realtek/rtw88/main.c63
-rw-r--r--drivers/net/wireless/realtek/rtw88/main.h45
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822c.c10
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822c.h4
-rw-r--r--drivers/net/wireless/realtek/rtw88/rx.c10
9 files changed, 709 insertions, 33 deletions
diff --git a/drivers/net/wireless/realtek/rtw88/debug.h b/drivers/net/wireless/realtek/rtw88/debug.h
index 47c57f395f52..61f8369fe2d6 100644
--- a/drivers/net/wireless/realtek/rtw88/debug.h
+++ b/drivers/net/wireless/realtek/rtw88/debug.h
@@ -22,6 +22,7 @@ enum rtw_debug_mask {
RTW_DBG_CFO = 0x00002000,
RTW_DBG_PATH_DIV = 0x00004000,
RTW_DBG_ADAPTIVITY = 0x00008000,
+ RTW_DBG_HW_SCAN = 0x00010000,
RTW_DBG_ALL = 0xffffffff
};
diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c
index 0c4f2a2f2d7f..2f7c036f9022 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.c
+++ b/drivers/net/wireless/realtek/rtw88/fw.c
@@ -28,6 +28,12 @@ static void rtw_fw_c2h_cmd_handle_ext(struct rtw_dev *rtwdev,
case C2H_CCX_RPT:
rtw_tx_report_handle(rtwdev, skb, C2H_CCX_RPT);
break;
+ case C2H_SCAN_STATUS_RPT:
+ rtw_hw_scan_status_report(rtwdev, skb);
+ break;
+ case C2H_CHAN_SWITCH:
+ rtw_hw_scan_chan_switch(rtwdev, skb);
+ break;
default:
break;
}
@@ -1777,3 +1783,385 @@ void rtw_fw_scan_notify(struct rtw_dev *rtwdev, bool start)
rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
}
+
+static void rtw_append_probe_req_ie(struct rtw_dev *rtwdev, struct sk_buff *skb,
+ struct sk_buff_head *list,
+ struct rtw_vif *rtwvif)
+{
+ struct ieee80211_scan_ies *ies = rtwvif->scan_ies;
+ struct rtw_chip_info *chip = rtwdev->chip;
+ struct sk_buff *new;
+ u8 idx;
+
+ for (idx = NL80211_BAND_2GHZ; idx < NUM_NL80211_BANDS; idx++) {
+ if (!(BIT(idx) & chip->band))
+ continue;
+ new = skb_copy(skb, GFP_KERNEL);
+ skb_put_data(new, ies->ies[idx], ies->len[idx]);
+ skb_put_data(new, ies->common_ies, ies->common_ie_len);
+ skb_queue_tail(list, new);
+ }
+}
+
+static int _rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev, u8 num_ssids,
+ struct sk_buff_head *probe_req_list)
+{
+ struct rtw_chip_info *chip = rtwdev->chip;
+ struct sk_buff *skb, *tmp;
+ u8 page_offset = 1, *buf, page_size = chip->page_size;
+ u8 pages = page_offset + num_ssids * RTW_PROBE_PG_CNT;
+ u16 pg_addr = rtwdev->fifo.rsvd_h2c_info_addr, loc;
+ u16 buf_offset = page_size * page_offset;
+ u8 tx_desc_sz = chip->tx_pkt_desc_sz;
+ unsigned int pkt_len;
+ int ret;
+
+ buf = kzalloc(page_size * pages, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf_offset -= tx_desc_sz;
+ skb_queue_walk_safe(probe_req_list, skb, tmp) {
+ skb_unlink(skb, probe_req_list);
+ rtw_fill_rsvd_page_desc(rtwdev, skb, RSVD_PROBE_REQ);
+ if (skb->len > page_size * RTW_PROBE_PG_CNT) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ memcpy(buf + buf_offset, skb->data, skb->len);
+ pkt_len = skb->len - tx_desc_sz;
+ loc = pg_addr - rtwdev->fifo.rsvd_boundary + page_offset;
+ __rtw_fw_update_pkt(rtwdev, RTW_PACKET_PROBE_REQ, pkt_len, loc);
+
+ buf_offset += RTW_PROBE_PG_CNT * page_size;
+ page_offset += RTW_PROBE_PG_CNT;
+ kfree_skb(skb);
+ }
+
+ ret = rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, buf_offset);
+ if (ret) {
+ rtw_err(rtwdev, "Download probe request to firmware failed\n");
+ goto out;
+ }
+
+ rtwdev->scan_info.probe_pg_size = page_offset;
+out:
+ kfree(buf);
+
+ return ret;
+}
+
+static int rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev,
+ struct rtw_vif *rtwvif)
+{
+ struct cfg80211_scan_request *req = rtwvif->scan_req;
+ struct sk_buff_head list;
+ struct sk_buff *skb;
+ u8 num = req->n_ssids, i;
+
+ skb_queue_head_init(&list);
+ for (i = 0; i < num; i++) {
+ skb = ieee80211_probereq_get(rtwdev->hw, rtwvif->mac_addr,
+ req->ssids[i].ssid,
+ req->ssids[i].ssid_len,
+ req->ie_len);
+ rtw_append_probe_req_ie(rtwdev, skb, &list, rtwvif);
+ kfree_skb(skb);
+ }
+
+ return _rtw_hw_scan_update_probe_req(rtwdev, num, &list);
+}
+
+static int rtw_add_chan_info(struct rtw_dev *rtwdev, struct rtw_chan_info *info,
+ struct rtw_chan_list *list, u8 *buf)
+{
+ u8 *chan = &buf[list->size];
+ u8 info_size = RTW_CH_INFO_SIZE;
+
+ if (list->size > list->buf_size)
+ return -ENOMEM;
+
+ CH_INFO_SET_CH(chan, info->channel);
+ CH_INFO_SET_PRI_CH_IDX(chan, info->pri_ch_idx);
+ CH_INFO_SET_BW(chan, info->bw);
+ CH_INFO_SET_TIMEOUT(chan, info->timeout);
+ CH_INFO_SET_ACTION_ID(chan, info->action_id);
+ CH_INFO_SET_EXTRA_INFO(chan, info->extra_info);
+ if (info->extra_info) {
+ EXTRA_CH_INFO_SET_ID(chan, RTW_SCAN_EXTRA_ID_DFS);
+ EXTRA_CH_INFO_SET_INFO(chan, RTW_SCAN_EXTRA_ACTION_SCAN);
+ EXTRA_CH_INFO_SET_SIZE(chan, RTW_EX_CH_INFO_SIZE -
+ RTW_EX_CH_INFO_HDR_SIZE);
+ EXTRA_CH_INFO_SET_DFS_EXT_TIME(chan, RTW_DFS_CHAN_TIME);
+ info_size += RTW_EX_CH_INFO_SIZE;
+ }
+ list->size += info_size;
+ list->ch_num++;
+
+ return 0;
+}
+
+static int rtw_add_chan_list(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif,
+ struct rtw_chan_list *list, u8 *buf)
+{
+ struct cfg80211_scan_request *req = rtwvif->scan_req;
+ struct rtw_fifo_conf *fifo = &rtwdev->fifo;
+ struct ieee80211_channel *channel;
+ int i, ret = 0;
+
+ for (i = 0; i < req->n_channels; i++) {
+ struct rtw_chan_info ch_info = {0};
+
+ channel = req->channels[i];
+ ch_info.channel = channel->hw_value;
+ ch_info.bw = RTW_SCAN_WIDTH;
+ ch_info.pri_ch_idx = RTW_PRI_CH_IDX;
+ ch_info.timeout = req->duration_mandatory ?
+ req->duration : RTW_CHANNEL_TIME;
+
+ if (channel->flags & (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IR)) {
+ ch_info.action_id = RTW_CHANNEL_RADAR;
+ ch_info.extra_info = 1;
+ /* Overwrite duration for passive scans if necessary */
+ ch_info.timeout = ch_info.timeout > RTW_PASS_CHAN_TIME ?
+ ch_info.timeout : RTW_PASS_CHAN_TIME;
+ } else {
+ ch_info.action_id = RTW_CHANNEL_ACTIVE;
+ }
+
+ ret = rtw_add_chan_info(rtwdev, &ch_info, list, buf);
+ if (ret)
+ return ret;
+ }
+
+ if (list->size > fifo->rsvd_pg_num << TX_PAGE_SIZE_SHIFT) {
+ rtw_err(rtwdev, "List exceeds rsvd page total size\n");
+ return -EINVAL;
+ }
+
+ list->addr = fifo->rsvd_h2c_info_addr + rtwdev->scan_info.probe_pg_size;
+ ret = rtw_fw_write_data_rsvd_page(rtwdev, list->addr, buf, list->size);
+ if (ret)
+ rtw_err(rtwdev, "Download channel list failed\n");
+
+ return ret;
+}
+
+static void rtw_fw_set_scan_offload(struct rtw_dev *rtwdev,
+ struct rtw_ch_switch_option *opt,
+ struct rtw_vif *rtwvif,
+ struct rtw_chan_list *list)
+{
+ struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
+ struct cfg80211_scan_request *req = rtwvif->scan_req;
+ struct rtw_fifo_conf *fifo = &rtwdev->fifo;
+ /* reserve one dummy page at the beginning for tx descriptor */
+ u8 pkt_loc = fifo->rsvd_h2c_info_addr - fifo->rsvd_boundary + 1;
+ bool random_seq = req->flags & NL80211_SCAN_FLAG_RANDOM_SN;
+ u8 h2c_pkt[H2C_PKT_SIZE] = {0};
+
+ rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_SCAN_OFFLOAD);
+ SET_PKT_H2C_TOTAL_LEN(h2c_pkt, H2C_PKT_CH_SWITCH_LEN);
+
+ SCAN_OFFLOAD_SET_START(h2c_pkt, opt->switch_en);
+ SCAN_OFFLOAD_SET_BACK_OP_EN(h2c_pkt, opt->back_op_en);
+ SCAN_OFFLOAD_SET_RANDOM_SEQ_EN(h2c_pkt, random_seq);
+ SCAN_OFFLOAD_SET_NO_CCK_EN(h2c_pkt, req->no_cck);
+ SCAN_OFFLOAD_SET_CH_NUM(h2c_pkt, list->ch_num);
+ SCAN_OFFLOAD_SET_CH_INFO_SIZE(h2c_pkt, list->size);
+ SCAN_OFFLOAD_SET_CH_INFO_LOC(h2c_pkt, list->addr - fifo->rsvd_boundary);
+ SCAN_OFFLOAD_SET_OP_CH(h2c_pkt, scan_info->op_chan);
+ SCAN_OFFLOAD_SET_OP_PRI_CH_IDX(h2c_pkt, scan_info->op_pri_ch_idx);
+ SCAN_OFFLOAD_SET_OP_BW(h2c_pkt, scan_info->op_bw);
+ SCAN_OFFLOAD_SET_OP_PORT_ID(h2c_pkt, rtwvif->port);
+ SCAN_OFFLOAD_SET_OP_DWELL_TIME(h2c_pkt, req->duration_mandatory ?
+ req->duration : RTW_CHANNEL_TIME);
+ SCAN_OFFLOAD_SET_OP_GAP_TIME(h2c_pkt, RTW_OFF_CHAN_TIME);
+ SCAN_OFFLOAD_SET_SSID_NUM(h2c_pkt, req->n_ssids);
+ SCAN_OFFLOAD_SET_PKT_LOC(h2c_pkt, pkt_loc);
+
+ rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
+}
+
+void rtw_hw_scan_start(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *scan_req)
+{
+ struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
+ struct cfg80211_scan_request *req = &scan_req->req;
+ u8 mac_addr[ETH_ALEN];
+
+ rtwdev->scan_info.scanning_vif = vif;
+ rtwvif->scan_ies = &scan_req->ies;
+ rtwvif->scan_req = req;
+
+ ieee80211_stop_queues(rtwdev->hw);
+ if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
+ get_random_mask_addr(mac_addr, req->mac_addr,
+ req->mac_addr_mask);
+ else
+ ether_addr_copy(mac_addr, vif->addr);
+
+ rtw_core_scan_start(rtwdev, rtwvif, mac_addr, true);
+
+ rtwdev->hal.rcr &= ~BIT_CBSSID_BCN;
+ rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr);
+}
+
+void rtw_hw_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
+ bool aborted)
+{
+ struct cfg80211_scan_info info = {
+ .aborted = aborted,
+ };
+ struct rtw_vif *rtwvif;
+
+ if (!vif)
+ return;
+
+ rtwdev->hal.rcr |= BIT_CBSSID_BCN;
+ rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr);
+
+ rtw_core_scan_complete(rtwdev, vif);
+
+ ieee80211_wake_queues(rtwdev->hw);
+ ieee80211_scan_completed(rtwdev->hw, &info);
+
+ rtwvif = (struct rtw_vif *)vif->drv_priv;
+ rtwvif->scan_req = NULL;
+ rtwvif->scan_ies = NULL;
+ rtwdev->scan_info.scanning_vif = NULL;
+}
+
+static int rtw_hw_scan_prehandle(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif,
+ struct rtw_chan_list *list)
+{
+ struct cfg80211_scan_request *req = rtwvif->scan_req;
+ int size = req->n_channels * (RTW_CH_INFO_SIZE + RTW_EX_CH_INFO_SIZE);
+ u8 *buf;
+ int ret;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = rtw_hw_scan_update_probe_req(rtwdev, rtwvif);
+ if (ret) {
+ rtw_err(rtwdev, "Update probe request failed\n");
+ goto out;
+ }
+
+ list->buf_size = size;
+ list->size = 0;
+ list->ch_num = 0;
+ ret = rtw_add_chan_list(rtwdev, rtwvif, list, buf);
+out:
+ kfree(buf);
+
+ return ret;
+}
+
+int rtw_hw_scan_offload(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
+ bool enable)
+{
+ struct rtw_vif *rtwvif = vif ? (struct rtw_vif *)vif->drv_priv : NULL;
+ struct rtw_ch_switch_option cs_option = {0};
+ struct rtw_chan_list chan_list = {0};
+ int ret = 0;
+
+ if (!rtwvif)
+ return -EINVAL;
+
+ cs_option.switch_en = enable;
+ cs_option.back_op_en = rtwvif->net_type == RTW_NET_MGD_LINKED;
+ if (enable) {
+ ret = rtw_hw_scan_prehandle(rtwdev, rtwvif, &chan_list);
+ if (ret)
+ goto out;
+ }
+ rtw_fw_set_scan_offload(rtwdev, &cs_option, rtwvif, &chan_list);
+out:
+ return ret;
+}
+
+void rtw_hw_scan_abort(struct rtw_dev *rtwdev, struct ieee80211_vif *vif)
+{
+ if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD))
+ return;
+
+ rtw_hw_scan_offload(rtwdev, vif, false);
+ rtw_hw_scan_complete(rtwdev, vif, true);
+}
+
+void rtw_hw_scan_status_report(struct rtw_dev *rtwdev, struct sk_buff *skb)
+{
+ struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif;
+ struct rtw_c2h_cmd *c2h;
+ bool aborted;
+ u8 rc;
+
+ if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
+ return;
+
+ c2h = get_c2h_from_skb(skb);
+ rc = GET_SCAN_REPORT_RETURN_CODE(c2h->payload);
+ aborted = rc != RTW_SCAN_REPORT_SUCCESS;
+ rtw_hw_scan_complete(rtwdev, vif, aborted);
+
+ if (aborted)
+ rtw_info(rtwdev, "HW scan aborted with code: %d\n", rc);
+}
+
+void rtw_store_op_chan(struct rtw_dev *rtwdev)
+{
+ struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
+ struct rtw_hal *hal = &rtwdev->hal;
+
+ scan_info->op_chan = hal->current_channel;
+ scan_info->op_bw = hal->current_band_width;
+ scan_info->op_pri_ch_idx = hal->current_primary_channel_index;
+}
+
+static bool rtw_is_op_chan(struct rtw_dev *rtwdev, u8 channel)
+{
+ struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
+
+ return channel == scan_info->op_chan;
+}
+
+void rtw_hw_scan_chan_switch(struct rtw_dev *rtwdev, struct sk_buff *skb)
+{
+ struct rtw_hal *hal = &rtwdev->hal;
+ struct rtw_c2h_cmd *c2h;
+ enum rtw_scan_notify_id id;
+ u8 chan, status;
+
+ c2h = get_c2h_from_skb(skb);
+ chan = GET_CHAN_SWITCH_CENTRAL_CH(c2h->payload);
+ id = GET_CHAN_SWITCH_ID(c2h->payload);
+ status = GET_CHAN_SWITCH_STATUS(c2h->payload);
+
+ if (id == RTW_SCAN_NOTIFY_ID_POSTSWITCH) {
+ if (rtw_is_op_chan(rtwdev, chan))
+ ieee80211_wake_queues(rtwdev->hw);
+ hal->current_channel = chan;
+ hal->current_band_type = chan > 14 ? RTW_BAND_5G : RTW_BAND_2G;
+ } else if (id == RTW_SCAN_NOTIFY_ID_PRESWITCH) {
+ if (IS_CH_5G_BAND(chan)) {
+ rtw_coex_switchband_notify(rtwdev, COEX_SWITCH_TO_5G);
+ } else if (IS_CH_2G_BAND(chan)) {
+ u8 chan_type;
+
+ if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
+ chan_type = COEX_SWITCH_TO_24G;
+ else
+ chan_type = COEX_SWITCH_TO_24G_NOFORSCAN;
+ rtw_coex_switchband_notify(rtwdev, chan_type);
+ }
+ if (rtw_is_op_chan(rtwdev, chan))
+ ieee80211_stop_queues(rtwdev->hw);
+ }
+
+ rtw_dbg(rtwdev, RTW_DBG_HW_SCAN,
+ "Chan switch: %x, id: %x, status: %x\n", chan, id, status);
+}
diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h
index 09c7afb99e63..5de67308889c 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.h
+++ b/drivers/net/wireless/realtek/rtw88/fw.h
@@ -32,6 +32,17 @@
#define SCAN_NOTIFY_TIMEOUT msecs_to_jiffies(10)
+#define RTW_CHANNEL_TIME 45
+#define RTW_OFF_CHAN_TIME 100
+#define RTW_PASS_CHAN_TIME 105
+#define RTW_DFS_CHAN_TIME 20
+#define RTW_CH_INFO_SIZE 4
+#define RTW_EX_CH_INFO_SIZE 3
+#define RTW_EX_CH_INFO_HDR_SIZE 2
+#define RTW_SCAN_WIDTH 0
+#define RTW_PRI_CH_IDX 1
+#define RTW_PROBE_PG_CNT 2
+
enum rtw_c2h_cmd_id {
C2H_CCX_TX_RPT = 0x03,
C2H_BT_INFO = 0x09,
@@ -48,7 +59,9 @@ enum rtw_c2h_cmd_id {
};
enum rtw_c2h_cmd_id_ext {
+ C2H_SCAN_STATUS_RPT = 0x3,
C2H_CCX_RPT = 0x0f,
+ C2H_CHAN_SWITCH = 0x22,
};
struct rtw_c2h_cmd {
@@ -101,6 +114,7 @@ enum rtw_fw_feature {
FW_FEATURE_BCN_FILTER = BIT(5),
FW_FEATURE_NOTIFY_SCAN = BIT(6),
FW_FEATURE_ADAPTIVITY = BIT(7),
+ FW_FEATURE_SCAN_OFFLOAD = BIT(8),
FW_FEATURE_MAX = BIT(31),
};
@@ -196,6 +210,51 @@ struct rtw_fw_wow_disconnect_para {
u8 retry_count;
};
+enum rtw_channel_type {
+ RTW_CHANNEL_PASSIVE,
+ RTW_CHANNEL_ACTIVE,
+ RTW_CHANNEL_RADAR,
+};
+
+enum rtw_scan_extra_id {
+ RTW_SCAN_EXTRA_ID_DFS,
+};
+
+enum rtw_scan_extra_info {
+ RTW_SCAN_EXTRA_ACTION_SCAN,
+};
+
+enum rtw_scan_report_code {
+ RTW_SCAN_REPORT_SUCCESS = 0x00,
+ RTW_SCAN_REPORT_ERR_PHYDM = 0x01,
+ RTW_SCAN_REPORT_ERR_ID = 0x02,
+ RTW_SCAN_REPORT_ERR_TX = 0x03,
+ RTW_SCAN_REPORT_CANCELED = 0x10,
+ RTW_SCAN_REPORT_CANCELED_EXT = 0x11,
+ RTW_SCAN_REPORT_FW_DISABLED = 0xF0,
+};
+
+enum rtw_scan_notify_id {
+ RTW_SCAN_NOTIFY_ID_PRESWITCH = 0x00,
+ RTW_SCAN_NOTIFY_ID_POSTSWITCH = 0x01,
+ RTW_SCAN_NOTIFY_ID_PROBE_PRETX = 0x02,
+ RTW_SCAN_NOTIFY_ID_PROBE_ISSUETX = 0x03,
+ RTW_SCAN_NOTIFY_ID_NULL0_PRETX = 0x04,
+ RTW_SCAN_NOTIFY_ID_NULL0_ISSUETX = 0x05,
+ RTW_SCAN_NOTIFY_ID_NULL0_POSTTX = 0x06,
+ RTW_SCAN_NOTIFY_ID_NULL1_PRETX = 0x07,
+ RTW_SCAN_NOTIFY_ID_NULL1_ISSUETX = 0x08,
+ RTW_SCAN_NOTIFY_ID_NULL1_POSTTX = 0x09,
+ RTW_SCAN_NOTIFY_ID_DWELLEXT = 0x0A,
+};
+
+enum rtw_scan_notify_status {
+ RTW_SCAN_NOTIFY_STATUS_SUCCESS = 0x00,
+ RTW_SCAN_NOTIFY_STATUS_FAILURE = 0x01,
+ RTW_SCAN_NOTIFY_STATUS_RESOURCE = 0x02,
+ RTW_SCAN_NOTIFY_STATUS_TIMEOUT = 0x03,
+};
+
struct rtw_ch_switch_option {
u8 periodic_option;
u32 tsf_high;
@@ -209,6 +268,8 @@ struct rtw_ch_switch_option {
u8 slow_period;
u8 slow_period_sel;
u8 nlo_en;
+ bool switch_en;
+ bool back_op_en;
};
struct rtw_fw_hdr {
@@ -265,6 +326,11 @@ struct rtw_fw_hdr_legacy {
#define GET_CCX_REPORT_SEQNUM_V1(c2h_payload) (c2h_payload[8] & 0xfc)
#define GET_CCX_REPORT_STATUS_V1(c2h_payload) (c2h_payload[9] & 0xc0)
+#define GET_SCAN_REPORT_RETURN_CODE(c2h_payload) (c2h_payload[2] & 0xff)
+
+#define GET_CHAN_SWITCH_CENTRAL_CH(c2h_payload) (c2h_payload[2])
+#define GET_CHAN_SWITCH_ID(c2h_payload) (c2h_payload[3])
+#define GET_CHAN_SWITCH_STATUS(c2h_payload) (c2h_payload[4])
#define GET_RA_REPORT_RATE(c2h_payload) (c2h_payload[0] & 0x7f)
#define GET_RA_REPORT_SGI(c2h_payload) ((c2h_payload[0] & 0x80) >> 7)
#define GET_RA_REPORT_BW(c2h_payload) (c2h_payload[6])
@@ -284,6 +350,7 @@ struct rtw_fw_hdr_legacy {
#define H2C_PKT_CH_SWITCH 0x02
#define H2C_PKT_UPDATE_PKT 0x0C
+#define H2C_PKT_SCAN_OFFLOAD 0x19
#define H2C_PKT_CH_SWITCH_LEN 0x20
#define H2C_PKT_UPDATE_PKT_LEN 0x4
@@ -334,6 +401,30 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)
le32p_replace_bits((__le32 *)(pkt) + 0x00, value, GENMASK(23, 16))
#define CHSW_INFO_SET_ACTION_ID(pkt, value) \
le32p_replace_bits((__le32 *)(pkt) + 0x00, value, GENMASK(30, 24))
+#define CHSW_INFO_SET_EXTRA_INFO(pkt, value) \
+ le32p_replace_bits((__le32 *)(pkt) + 0x00, value, BIT(31))
+
+#define CH_INFO_SET_CH(pkt, value) \
+ u8p_replace_bits((u8 *)(pkt) + 0x00, value, GENMASK(7, 0))
+#define CH_INFO_SET_PRI_CH_IDX(pkt, value) \
+ u8p_replace_bits((u8 *)(pkt) + 0x01, value, GENMASK(3, 0))
+#define CH_INFO_SET_BW(pkt, value) \
+ u8p_replace_bits((u8 *)(pkt) + 0x01, value, GENMASK(7, 4))
+#define CH_INFO_SET_TIMEOUT(pkt, value) \
+ u8p_replace_bits((u8 *)(pkt) + 0x02, value, GENMASK(7, 0))
+#define CH_INFO_SET_ACTION_ID(pkt, value) \
+ u8p_replace_bits((u8 *)(pkt) + 0x03, value, GENMASK(6, 0))
+#define CH_INFO_SET_EXTRA_INFO(pkt, value) \
+ u8p_replace_bits((u8 *)(pkt) + 0x03, value, BIT(7))
+
+#define EXTRA_CH_INFO_SET_ID(pkt, value) \
+ u8p_replace_bits((u8 *)(pkt) + 0x04, value, GENMASK(6, 0))
+#define EXTRA_CH_INFO_SET_INFO(pkt, value) \
+ u8p_replace_bits((u8 *)(pkt) + 0x04, value, BIT(7))
+#define EXTRA_CH_INFO_SET_SIZE(pkt, value) \
+ u8p_replace_bits((u8 *)(pkt) + 0x05, value, GENMASK(7, 0))
+#define EXTRA_CH_INFO_SET_DFS_EXT_TIME(pkt, value) \
+ u8p_replace_bits((u8 *)(pkt) + 0x06, value, GENMASK(7, 0))
#define UPDATE_PKT_SET_SIZE(h2c_pkt, value) \
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(15, 0))
@@ -350,12 +441,18 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(2))
#define CH_SWITCH_SET_PERIODIC_OPT(h2c_pkt, value) \
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(4, 3))
+#define CH_SWITCH_SET_SCAN_MODE(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(5))
+#define CH_SWITCH_SET_BACK_OP_EN(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(6))
#define CH_SWITCH_SET_INFO_LOC(h2c_pkt, value) \
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(15, 8))
#define CH_SWITCH_SET_CH_NUM(h2c_pkt, value) \
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(23, 16))
#define CH_SWITCH_SET_PRI_CH_IDX(h2c_pkt, value) \
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(27, 24))
+#define CH_SWITCH_SET_DEST_BW(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(31, 28))
#define CH_SWITCH_SET_DEST_CH(h2c_pkt, value) \
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(7, 0))
#define CH_SWITCH_SET_NORMAL_PERIOD(h2c_pkt, value) \
@@ -375,6 +472,41 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)
#define CH_SWITCH_SET_INFO_SIZE(h2c_pkt, value) \
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x06, value, GENMASK(15, 0))
+#define SCAN_OFFLOAD_SET_START(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(0))
+#define SCAN_OFFLOAD_SET_BACK_OP_EN(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(1))
+#define SCAN_OFFLOAD_SET_RANDOM_SEQ_EN(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(2))
+#define SCAN_OFFLOAD_SET_NO_CCK_EN(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(3))
+#define SCAN_OFFLOAD_SET_VERBOSE(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(4))
+#define SCAN_OFFLOAD_SET_CH_NUM(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(15, 8))
+#define SCAN_OFFLOAD_SET_CH_INFO_SIZE(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(31, 16))
+#define SCAN_OFFLOAD_SET_CH_INFO_LOC(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(7, 0))
+#define SCAN_OFFLOAD_SET_OP_CH(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(15, 8))
+#define SCAN_OFFLOAD_SET_OP_PRI_CH_IDX(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(19, 16))
+#define SCAN_OFFLOAD_SET_OP_BW(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(23, 20))
+#define SCAN_OFFLOAD_SET_OP_PORT_ID(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(26, 24))
+#define SCAN_OFFLOAD_SET_OP_DWELL_TIME(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x04, value, GENMASK(15, 0))
+#define SCAN_OFFLOAD_SET_OP_GAP_TIME(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x04, value, GENMASK(31, 16))
+#define SCAN_OFFLOAD_SET_MODE(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x05, value, GENMASK(3, 0))
+#define SCAN_OFFLOAD_SET_SSID_NUM(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x05, value, GENMASK(7, 4))
+#define SCAN_OFFLOAD_SET_PKT_LOC(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x05, value, GENMASK(15, 8))
+
/* Command H2C */
#define H2C_CMD_RSVD_PAGE 0x0
#define H2C_CMD_MEDIA_STATUS_RPT 0x01
@@ -686,4 +818,14 @@ int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size,
u32 *buffer);
void rtw_fw_scan_notify(struct rtw_dev *rtwdev, bool start);
void rtw_fw_adaptivity(struct rtw_dev *rtwdev);
+void rtw_store_op_chan(struct rtw_dev *rtwdev);
+void rtw_hw_scan_start(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *req);
+void rtw_hw_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
+ bool aborted);
+int rtw_hw_scan_offload(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
+ bool enable);
+void rtw_hw_scan_status_report(struct rtw_dev *rtwdev, struct sk_buff *skb);
+void rtw_hw_scan_chan_switch(struct rtw_dev *rtwdev, struct sk_buff *skb);
+void rtw_hw_scan_abort(struct rtw_dev *rtwdev, struct ieee80211_vif *vif);
#endif
diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c
index 6f5629852416..5c01c40f248f 100644
--- a/drivers/net/wireless/realtek/rtw88/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
@@ -161,6 +161,7 @@ static int rtw_ops_add_interface(struct ieee80211_hw *hw,
rtwvif->stats.rx_unicast = 0;
rtwvif->stats.tx_cnt = 0;
rtwvif->stats.rx_cnt = 0;
+ rtwvif->scan_req = NULL;
memset(&rtwvif->bfee, 0, sizeof(struct rtw_bfee));
rtwvif->conf = &rtw_vif_port[port];
rtw_txq_init(rtwdev, vif->txq);
@@ -372,9 +373,15 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,
rtw_coex_media_status_notify(rtwdev, conf->assoc);
if (rtw_bf_support)
rtw_bf_assoc(rtwdev, vif, conf);
+ rtw_store_op_chan(rtwdev);
} else {
rtw_leave_lps(rtwdev);
rtw_bf_disassoc(rtwdev, vif, conf);
+ /* Abort ongoing scan if cancel_scan isn't issued
+ * when disconnected by peer
+ */
+ if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
+ rtw_hw_scan_abort(rtwdev, vif);
}
config |= PORT_SET_NET_TYPE;
@@ -594,22 +601,9 @@ static void rtw_ops_sw_scan_start(struct ieee80211_hw *hw,
{
struct rtw_dev *rtwdev = hw->priv;
struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
- u32 config = 0;
mutex_lock(&rtwdev->mutex);
-
- rtw_leave_lps(rtwdev);
-
- ether_addr_copy(rtwvif->mac_addr, mac_addr);
- config |= PORT_SET_MAC_ADDR;
- rtw_vif_port_config(rtwdev, rtwvif, config);
-
- rtw_coex_scan_notify(rtwdev, COEX_SCAN_START);
- rtw_core_fw_scan_notify(rtwdev, true);
-
- set_bit(RTW_FLAG_DIG_DISABLE, rtwdev->flags);
- set_bit(RTW_FLAG_SCANNING, rtwdev->flags);
-
+ rtw_core_scan_start(rtwdev, rtwvif, mac_addr, false);
mutex_unlock(&rtwdev->mutex);
}
@@ -617,22 +611,9 @@ static void rtw_ops_sw_scan_complete(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct rtw_dev *rtwdev = hw->priv;
- struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
- u32 config = 0;
mutex_lock(&rtwdev->mutex);
-
- clear_bit(RTW_FLAG_SCANNING, rtwdev->flags);
- clear_bit(RTW_FLAG_DIG_DISABLE, rtwdev->flags);
-
- rtw_core_fw_scan_notify(rtwdev, false);
-
- ether_addr_copy(rtwvif->mac_addr, vif->addr);
- config |= PORT_SET_MAC_ADDR;
- rtw_vif_port_config(rtwdev, rtwvif, config);
-
- rtw_coex_scan_notify(rtwdev, COEX_SCAN_FINISH);
-
+ rtw_core_scan_complete(rtwdev, vif);
mutex_unlock(&rtwdev->mutex);
}
@@ -815,6 +796,46 @@ static void rtw_reconfig_complete(struct ieee80211_hw *hw,
mutex_unlock(&rtwdev->mutex);
}
+static int rtw_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *req)
+{
+ struct rtw_dev *rtwdev = hw->priv;
+ int ret;
+
+ if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD))
+ return 1;
+
+ if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
+ return -EBUSY;
+
+ mutex_lock(&rtwdev->mutex);
+ rtw_hw_scan_start(rtwdev, vif, req);
+ ret = rtw_hw_scan_offload(rtwdev, vif, true);
+ if (ret) {
+ rtw_hw_scan_abort(rtwdev, vif);
+ rtw_err(rtwdev, "HW scan failed with status: %d\n", ret);
+ }
+ mutex_unlock(&rtwdev->mutex);
+
+ return ret;
+}
+
+static void rtw_ops_cancel_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct rtw_dev *rtwdev = hw->priv;
+
+ if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD))
+ return;
+
+ if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
+ return;
+
+ mutex_lock(&rtwdev->mutex);
+ rtw_hw_scan_abort(rtwdev, vif);
+ mutex_unlock(&rtwdev->mutex);
+}
+
const struct ieee80211_ops rtw_ops = {
.tx = rtw_ops_tx,
.wake_tx_queue = rtw_ops_wake_tx_queue,
@@ -842,6 +863,8 @@ const struct ieee80211_ops rtw_ops = {
.set_antenna = rtw_ops_set_antenna,
.get_antenna = rtw_ops_get_antenna,
.reconfig_complete = rtw_reconfig_complete,
+ .hw_scan = rtw_ops_hw_scan,
+ .cancel_hw_scan = rtw_ops_cancel_hw_scan,
#ifdef CONFIG_PM
.suspend = rtw_ops_suspend,
.resume = rtw_ops_resume,
diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index 274e0af95917..b569a67cfb01 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -637,6 +637,19 @@ static void rtw_txq_ba_work(struct work_struct *work)
rtw_iterate_stas_atomic(rtwdev, rtw_txq_ba_iter, &data);
}
+void rtw_set_rx_freq_band(struct rtw_rx_pkt_stat *pkt_stat, u8 channel)
+{
+ if (IS_CH_2G_BAND(channel))
+ pkt_stat->band = NL80211_BAND_2GHZ;
+ else if (IS_CH_5G_BAND(channel))
+ pkt_stat->band = NL80211_BAND_5GHZ;
+ else
+ return;
+
+ pkt_stat->freq = ieee80211_channel_to_frequency(channel, pkt_stat->band);
+}
+EXPORT_SYMBOL(rtw_set_rx_freq_band);
+
void rtw_get_channel_params(struct cfg80211_chan_def *chandef,
struct rtw_channel_params *chan_params)
{
@@ -735,6 +748,7 @@ void rtw_set_channel(struct rtw_dev *rtwdev)
hal->current_band_width = bandwidth;
hal->current_channel = center_chan;
+ hal->current_primary_channel_index = primary_chan_idx;
hal->current_band_type = center_chan > 14 ? RTW_BAND_5G : RTW_BAND_2G;
for (i = RTW_CHANNEL_WIDTH_20; i <= RTW_MAX_CHANNEL_WIDTH; i++)
@@ -1278,6 +1292,50 @@ void rtw_core_fw_scan_notify(struct rtw_dev *rtwdev, bool start)
}
}
+void rtw_core_scan_start(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif,
+ const u8 *mac_addr, bool hw_scan)
+{
+ u32 config = 0;
+ int ret = 0;
+
+ rtw_leave_lps(rtwdev);
+
+ if (hw_scan && rtwvif->net_type == RTW_NET_NO_LINK) {
+ ret = rtw_leave_ips(rtwdev);
+ if (ret) {
+ rtw_err(rtwdev, "failed to leave idle state\n");
+ return;
+ }
+ }
+
+ ether_addr_copy(rtwvif->mac_addr, mac_addr);
+ config |= PORT_SET_MAC_ADDR;
+ rtw_vif_port_config(rtwdev, rtwvif, config);
+
+ rtw_coex_scan_notify(rtwdev, COEX_SCAN_START);
+ rtw_core_fw_scan_notify(rtwdev, true);
+
+ set_bit(RTW_FLAG_DIG_DISABLE, rtwdev->flags);
+ set_bit(RTW_FLAG_SCANNING, rtwdev->flags);
+}
+
+void rtw_core_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif)
+{
+ struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
+ u32 config = 0;
+
+ clear_bit(RTW_FLAG_SCANNING, rtwdev->flags);
+ clear_bit(RTW_FLAG_DIG_DISABLE, rtwdev->flags);
+
+ rtw_core_fw_scan_notify(rtwdev, false);
+
+ ether_addr_copy(rtwvif->mac_addr, vif->addr);
+ config |= PORT_SET_MAC_ADDR;
+ rtw_vif_port_config(rtwdev, rtwvif, config);
+
+ rtw_coex_scan_notify(rtwdev, COEX_SCAN_FINISH);
+}
+
int rtw_core_start(struct rtw_dev *rtwdev)
{
int ret;
@@ -1952,6 +2010,7 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
ieee80211_hw_set(hw, HAS_RATE_CONTROL);
ieee80211_hw_set(hw, TX_AMSDU);
+ ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) |
@@ -1964,8 +2023,12 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
hw->wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+ hw->wiphy->max_scan_ssids = RTW_SCAN_MAX_SSIDS;
+ hw->wiphy->max_scan_ie_len = RTW_SCAN_MAX_IE_LEN;
wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
+ wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_SCAN_RANDOM_SN);
+ wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL);
#ifdef CONFIG_PM
hw->wiphy->wowlan = rtwdev->chip->wowlan_stub;
diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h
index 5e043101ee74..ff7570c28c4f 100644
--- a/drivers/net/wireless/realtek/rtw88/main.h
+++ b/drivers/net/wireless/realtek/rtw88/main.h
@@ -22,6 +22,9 @@
#define RTW_MAX_SEC_CAM_NUM 32
#define MAX_PG_CAM_BACKUP_NUM 8
+#define RTW_SCAN_MAX_SSIDS 4
+#define RTW_SCAN_MAX_IE_LEN 128
+
#define RTW_MAX_PATTERN_NUM 12
#define RTW_MAX_PATTERN_MASK_SIZE 16
#define RTW_MAX_PATTERN_SIZE 128
@@ -81,11 +84,9 @@ struct rtw_hci {
IS_CH_5G_BAND_3(channel) || IS_CH_5G_BAND_4(channel))
enum rtw_supported_band {
- RTW_BAND_2G = 1 << 0,
- RTW_BAND_5G = 1 << 1,
- RTW_BAND_60G = 1 << 2,
-
- RTW_BAND_MAX,
+ RTW_BAND_2G = BIT(NL80211_BAND_2GHZ),
+ RTW_BAND_5G = BIT(NL80211_BAND_5GHZ),
+ RTW_BAND_60G = BIT(NL80211_BAND_60GHZ),
};
/* now, support upto 80M bw */
@@ -630,6 +631,8 @@ struct rtw_rx_pkt_stat {
s8 rx_snr[RTW_RF_PATH_MAX];
u8 rx_evm[RTW_RF_PATH_MAX];
s8 cfo_tail[RTW_RF_PATH_MAX];
+ u16 freq;
+ u8 band;
struct rtw_sta_info *si;
struct ieee80211_vif *vif;
@@ -800,6 +803,8 @@ struct rtw_vif {
struct list_head rsvd_page_list;
struct ieee80211_tx_queue_params tx_params[IEEE80211_NUM_ACS];
const struct rtw_vif_port *conf;
+ struct cfg80211_scan_request *scan_req;
+ struct ieee80211_scan_ies *scan_ies;
struct rtw_traffic_stats stats;
@@ -1819,6 +1824,7 @@ struct rtw_hal {
u8 ps_mode;
u8 current_channel;
+ u8 current_primary_channel_index;
u8 current_band_width;
u8 current_band_type;
@@ -1865,12 +1871,37 @@ struct rtw_path_div {
u16 path_b_cnt;
};
+struct rtw_chan_info {
+ int pri_ch_idx;
+ int action_id;
+ int bw;
+ u8 extra_info;
+ u8 channel;
+ u16 timeout;
+};
+
+struct rtw_chan_list {
+ u32 buf_size;
+ u32 ch_num;
+ u32 size;
+ u16 addr;
+};
+
+struct rtw_hw_scan_info {
+ struct ieee80211_vif *scanning_vif;
+ u8 probe_pg_size;
+ u8 op_pri_ch_idx;
+ u8 op_chan;
+ u8 op_bw;
+};
+
struct rtw_dev {
struct ieee80211_hw *hw;
struct device *dev;
struct rtw_hci hci;
+ struct rtw_hw_scan_info scan_info;
struct rtw_chip_info *chip;
struct rtw_hal hal;
struct rtw_fifo_conf fifo;
@@ -2023,6 +2054,7 @@ static inline int rtw_chip_dump_fw_crash(struct rtw_dev *rtwdev)
return 0;
}
+void rtw_set_rx_freq_band(struct rtw_rx_pkt_stat *pkt_stat, u8 channel);
void rtw_get_channel_params(struct cfg80211_chan_def *chandef,
struct rtw_channel_params *ch_param);
bool check_hw_ready(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 target);
@@ -2037,6 +2069,9 @@ void rtw_vif_port_config(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif,
u32 config);
void rtw_tx_report_purge_timer(struct timer_list *t);
void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si);
+void rtw_core_scan_start(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif,
+ const u8 *mac_addr, bool hw_scan);
+void rtw_core_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif);
int rtw_core_start(struct rtw_dev *rtwdev);
void rtw_core_stop(struct rtw_dev *rtwdev);
int rtw_chip_info_setup(struct rtw_dev *rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
index e01d61b5d104..35c46e5209de 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
@@ -2535,6 +2535,7 @@ static void query_phy_status_page0(struct rtw_dev *rtwdev, u8 *phy_status,
s8 rx_power[RTW_RF_PATH_MAX];
s8 min_rx_power = -120;
u8 rssi;
+ u8 channel;
int path;
rx_power[RF_PATH_A] = GET_PHY_STAT_P0_PWDB_A(phy_status);
@@ -2555,6 +2556,11 @@ static void query_phy_status_page0(struct rtw_dev *rtwdev, u8 *phy_status,
rx_power[RF_PATH_A] -= 110;
rx_power[RF_PATH_B] -= 110;
+ channel = GET_PHY_STAT_P0_CHANNEL(phy_status);
+ if (channel == 0)
+ channel = rtwdev->hal.current_channel;
+ rtw_set_rx_freq_band(pkt_stat, channel);
+
pkt_stat->rx_power[RF_PATH_A] = rx_power[RF_PATH_A];
pkt_stat->rx_power[RF_PATH_B] = rx_power[RF_PATH_B];
@@ -2580,6 +2586,7 @@ static void query_phy_status_page1(struct rtw_dev *rtwdev, u8 *phy_status,
u8 evm_dbm = 0;
u8 rssi;
int path;
+ u8 channel;
if (pkt_stat->rate > DESC_RATE11M && pkt_stat->rate < DESC_RATEMCS0)
rxsc = GET_PHY_STAT_P1_L_RXSC(phy_status);
@@ -2593,6 +2600,9 @@ static void query_phy_status_page1(struct rtw_dev *rtwdev, u8 *phy_status,
else
bw = RTW_CHANNEL_WIDTH_20;
+ channel = GET_PHY_STAT_P1_CHANNEL(phy_status);
+ rtw_set_rx_freq_band(pkt_stat, channel);
+
pkt_stat->rx_power[RF_PATH_A] = GET_PHY_STAT_P1_PWDB_A(phy_status) - 110;
pkt_stat->rx_power[RF_PATH_B] = GET_PHY_STAT_P1_PWDB_B(phy_status) - 110;
pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 2);
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.h b/drivers/net/wireless/realtek/rtw88/rtw8822c.h
index 3df627419d81..8201955e1f21 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822c.h
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.h
@@ -137,6 +137,8 @@ const struct rtw_table name ## _tbl = { \
le32_get_bits(*((__le32 *)(phy_stat) + 0x04), GENMASK(7, 0))
#define GET_PHY_STAT_P0_GAIN_A(phy_stat) \
le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(21, 16))
+#define GET_PHY_STAT_P0_CHANNEL(phy_stat) \
+ le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(23, 16))
#define GET_PHY_STAT_P0_GAIN_B(phy_stat) \
le32_get_bits(*((__le32 *)(phy_stat) + 0x04), GENMASK(29, 24))
@@ -149,6 +151,8 @@ const struct rtw_table name ## _tbl = { \
le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(11, 8))
#define GET_PHY_STAT_P1_HT_RXSC(phy_stat) \
le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(15, 12))
+#define GET_PHY_STAT_P1_CHANNEL(phy_stat) \
+ le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(23, 16))
#define GET_PHY_STAT_P1_RXEVM_A(phy_stat) \
le32_get_bits(*((__le32 *)(phy_stat) + 0x04), GENMASK(7, 0))
#define GET_PHY_STAT_P1_RXEVM_B(phy_stat) \
diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c
index 7087e385a9b3..d2d607e22198 100644
--- a/drivers/net/wireless/realtek/rtw88/rx.c
+++ b/drivers/net/wireless/realtek/rtw88/rx.c
@@ -6,6 +6,7 @@
#include "rx.h"
#include "ps.h"
#include "debug.h"
+#include "fw.h"
void rtw_rx_stats(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
struct sk_buff *skb)
@@ -138,6 +139,13 @@ static void rtw_rx_addr_match(struct rtw_dev *rtwdev,
rtw_iterate_vifs_atomic(rtwdev, rtw_rx_addr_match_iter, &data);
}
+static void rtw_set_rx_freq_by_pktstat(struct rtw_rx_pkt_stat *pkt_stat,
+ struct ieee80211_rx_status *rx_status)
+{
+ rx_status->freq = pkt_stat->freq;
+ rx_status->band = pkt_stat->band;
+}
+
void rtw_rx_fill_rx_status(struct rtw_dev *rtwdev,
struct rtw_rx_pkt_stat *pkt_stat,
struct ieee80211_hdr *hdr,
@@ -150,6 +158,8 @@ void rtw_rx_fill_rx_status(struct rtw_dev *rtwdev,
memset(rx_status, 0, sizeof(*rx_status));
rx_status->freq = hw->conf.chandef.chan->center_freq;
rx_status->band = hw->conf.chandef.chan->band;
+ if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD))
+ rtw_set_rx_freq_by_pktstat(pkt_stat, rx_status);
if (pkt_stat->crc_err)
rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
if (pkt_stat->decrypted)