Jon West | 96501cc | 2021-04-06 13:09:18 -0400 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
| 2 | /* Copyright(c) 2018-2019 Realtek Corporation |
| 3 | */ |
| 4 | |
| 5 | #include "main.h" |
| 6 | #include "fw.h" |
| 7 | #include "wow.h" |
| 8 | #include "reg.h" |
| 9 | #include "debug.h" |
| 10 | #include "mac.h" |
| 11 | #include "ps.h" |
| 12 | |
| 13 | static void rtw_wow_show_wakeup_reason(struct rtw_dev *rtwdev) |
| 14 | { |
| 15 | u8 reason; |
| 16 | |
| 17 | reason = rtw_read8(rtwdev, REG_WOWLAN_WAKE_REASON); |
| 18 | |
| 19 | if (reason == RTW_WOW_RSN_RX_DEAUTH) |
| 20 | rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx deauth\n"); |
| 21 | else if (reason == RTW_WOW_RSN_DISCONNECT) |
| 22 | rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: AP is off\n"); |
| 23 | else if (reason == RTW_WOW_RSN_RX_MAGIC_PKT) |
| 24 | rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx magic packet\n"); |
| 25 | else if (reason == RTW_WOW_RSN_RX_GTK_REKEY) |
| 26 | rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx gtk rekey\n"); |
| 27 | else if (reason == RTW_WOW_RSN_RX_PTK_REKEY) |
| 28 | rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx ptk rekey\n"); |
| 29 | else if (reason == RTW_WOW_RSN_RX_PATTERN_MATCH) |
| 30 | rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx pattern match packet\n"); |
| 31 | else if (reason == RTW_WOW_RSN_RX_NLO) |
| 32 | rtw_dbg(rtwdev, RTW_DBG_WOW, "Rx NLO\n"); |
| 33 | else |
| 34 | rtw_warn(rtwdev, "Unknown wakeup reason %x\n", reason); |
| 35 | } |
| 36 | |
| 37 | static void rtw_wow_pattern_write_cam(struct rtw_dev *rtwdev, u8 addr, |
| 38 | u32 wdata) |
| 39 | { |
| 40 | rtw_write32(rtwdev, REG_WKFMCAM_RWD, wdata); |
| 41 | rtw_write32(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1 | |
| 42 | BIT_WKFCAM_WE | BIT_WKFCAM_ADDR_V2(addr)); |
| 43 | |
| 44 | if (!check_hw_ready(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1, 0)) |
| 45 | rtw_err(rtwdev, "failed to write pattern cam\n"); |
| 46 | } |
| 47 | |
| 48 | static void rtw_wow_pattern_write_cam_ent(struct rtw_dev *rtwdev, u8 id, |
| 49 | struct rtw_wow_pattern *rtw_pattern) |
| 50 | { |
| 51 | int i; |
| 52 | u8 addr; |
| 53 | u32 wdata; |
| 54 | |
| 55 | for (i = 0; i < RTW_MAX_PATTERN_MASK_SIZE / 4; i++) { |
| 56 | addr = (id << 3) + i; |
| 57 | wdata = rtw_pattern->mask[i * 4]; |
| 58 | wdata |= rtw_pattern->mask[i * 4 + 1] << 8; |
| 59 | wdata |= rtw_pattern->mask[i * 4 + 2] << 16; |
| 60 | wdata |= rtw_pattern->mask[i * 4 + 3] << 24; |
| 61 | rtw_wow_pattern_write_cam(rtwdev, addr, wdata); |
| 62 | } |
| 63 | |
| 64 | wdata = rtw_pattern->crc; |
| 65 | addr = (id << 3) + RTW_MAX_PATTERN_MASK_SIZE / 4; |
| 66 | |
| 67 | switch (rtw_pattern->type) { |
| 68 | case RTW_PATTERN_BROADCAST: |
| 69 | wdata |= BIT_WKFMCAM_BC | BIT_WKFMCAM_VALID; |
| 70 | break; |
| 71 | case RTW_PATTERN_MULTICAST: |
| 72 | wdata |= BIT_WKFMCAM_MC | BIT_WKFMCAM_VALID; |
| 73 | break; |
| 74 | case RTW_PATTERN_UNICAST: |
| 75 | wdata |= BIT_WKFMCAM_UC | BIT_WKFMCAM_VALID; |
| 76 | break; |
| 77 | default: |
| 78 | break; |
| 79 | } |
| 80 | rtw_wow_pattern_write_cam(rtwdev, addr, wdata); |
| 81 | } |
| 82 | |
| 83 | /* RTK internal CRC16 for Pattern Cam */ |
| 84 | static u16 __rtw_cal_crc16(u8 data, u16 crc) |
| 85 | { |
| 86 | u8 shift_in, data_bit; |
| 87 | u8 crc_bit4, crc_bit11, crc_bit15; |
| 88 | u16 crc_result; |
| 89 | int index; |
| 90 | |
| 91 | for (index = 0; index < 8; index++) { |
| 92 | crc_bit15 = ((crc & BIT(15)) ? 1 : 0); |
| 93 | data_bit = (data & (BIT(0) << index) ? 1 : 0); |
| 94 | shift_in = crc_bit15 ^ data_bit; |
| 95 | |
| 96 | crc_result = crc << 1; |
| 97 | |
| 98 | if (shift_in == 0) |
| 99 | crc_result &= (~BIT(0)); |
| 100 | else |
| 101 | crc_result |= BIT(0); |
| 102 | |
| 103 | crc_bit11 = ((crc & BIT(11)) ? 1 : 0) ^ shift_in; |
| 104 | |
| 105 | if (crc_bit11 == 0) |
| 106 | crc_result &= (~BIT(12)); |
| 107 | else |
| 108 | crc_result |= BIT(12); |
| 109 | |
| 110 | crc_bit4 = ((crc & BIT(4)) ? 1 : 0) ^ shift_in; |
| 111 | |
| 112 | if (crc_bit4 == 0) |
| 113 | crc_result &= (~BIT(5)); |
| 114 | else |
| 115 | crc_result |= BIT(5); |
| 116 | |
| 117 | crc = crc_result; |
| 118 | } |
| 119 | return crc; |
| 120 | } |
| 121 | |
| 122 | static u16 rtw_calc_crc(u8 *pdata, int length) |
| 123 | { |
| 124 | u16 crc = 0xffff; |
| 125 | int i; |
| 126 | |
| 127 | for (i = 0; i < length; i++) |
| 128 | crc = __rtw_cal_crc16(pdata[i], crc); |
| 129 | |
| 130 | /* get 1' complement */ |
| 131 | return ~crc; |
| 132 | } |
| 133 | |
| 134 | static void rtw_wow_pattern_generate(struct rtw_dev *rtwdev, |
| 135 | struct rtw_vif *rtwvif, |
| 136 | const struct cfg80211_pkt_pattern *pkt_pattern, |
| 137 | struct rtw_wow_pattern *rtw_pattern) |
| 138 | { |
| 139 | const u8 *mask; |
| 140 | const u8 *pattern; |
| 141 | u8 mask_hw[RTW_MAX_PATTERN_MASK_SIZE] = {0}; |
| 142 | u8 content[RTW_MAX_PATTERN_SIZE] = {0}; |
| 143 | u8 mac_addr[ETH_ALEN] = {0}; |
| 144 | u8 mask_len; |
| 145 | u16 count; |
| 146 | int len; |
| 147 | int i; |
| 148 | |
| 149 | pattern = pkt_pattern->pattern; |
| 150 | len = pkt_pattern->pattern_len; |
| 151 | mask = pkt_pattern->mask; |
| 152 | |
| 153 | ether_addr_copy(mac_addr, rtwvif->mac_addr); |
| 154 | memset(rtw_pattern, 0, sizeof(*rtw_pattern)); |
| 155 | |
| 156 | mask_len = DIV_ROUND_UP(len, 8); |
| 157 | |
| 158 | if (is_broadcast_ether_addr(pattern)) |
| 159 | rtw_pattern->type = RTW_PATTERN_BROADCAST; |
| 160 | else if (is_multicast_ether_addr(pattern)) |
| 161 | rtw_pattern->type = RTW_PATTERN_MULTICAST; |
| 162 | else if (ether_addr_equal(pattern, mac_addr)) |
| 163 | rtw_pattern->type = RTW_PATTERN_UNICAST; |
| 164 | else |
| 165 | rtw_pattern->type = RTW_PATTERN_INVALID; |
| 166 | |
| 167 | /* translate mask from os to mask for hw |
| 168 | * pattern from OS uses 'ethenet frame', like this: |
| 169 | * | 6 | 6 | 2 | 20 | Variable | 4 | |
| 170 | * |--------+--------+------+-----------+------------+-----| |
| 171 | * | 802.3 Mac Header | IP Header | TCP Packet | FCS | |
| 172 | * | DA | SA | Type | |
| 173 | * |
| 174 | * BUT, packet catched by our HW is in '802.11 frame', begin from LLC |
| 175 | * | 24 or 30 | 6 | 2 | 20 | Variable | 4 | |
| 176 | * |-------------------+--------+------+-----------+------------+-----| |
| 177 | * | 802.11 MAC Header | LLC | IP Header | TCP Packet | FCS | |
| 178 | * | Others | Tpye | |
| 179 | * |
| 180 | * Therefore, we need translate mask_from_OS to mask_to_hw. |
| 181 | * We should left-shift mask by 6 bits, then set the new bit[0~5] = 0, |
| 182 | * because new mask[0~5] means 'SA', but our HW packet begins from LLC, |
| 183 | * bit[0~5] corresponds to first 6 Bytes in LLC, they just don't match. |
| 184 | */ |
| 185 | |
| 186 | /* Shift 6 bits */ |
| 187 | for (i = 0; i < mask_len - 1; i++) { |
| 188 | mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6)); |
| 189 | mask_hw[i] |= u8_get_bits(mask[i + 1], GENMASK(5, 0)) << 2; |
| 190 | } |
| 191 | mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6)); |
| 192 | |
| 193 | /* Set bit 0-5 to zero */ |
| 194 | mask_hw[0] &= (~GENMASK(5, 0)); |
| 195 | |
| 196 | memcpy(rtw_pattern->mask, mask_hw, RTW_MAX_PATTERN_MASK_SIZE); |
| 197 | |
| 198 | /* To get the wake up pattern from the mask. |
| 199 | * We do not count first 12 bits which means |
| 200 | * DA[6] and SA[6] in the pattern to match HW design. |
| 201 | */ |
| 202 | count = 0; |
| 203 | for (i = 12; i < len; i++) { |
| 204 | if ((mask[i / 8] >> (i % 8)) & 0x01) { |
| 205 | content[count] = pattern[i]; |
| 206 | count++; |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | rtw_pattern->crc = rtw_calc_crc(content, count); |
| 211 | } |
| 212 | |
| 213 | static void rtw_wow_pattern_clear_cam(struct rtw_dev *rtwdev) |
| 214 | { |
| 215 | bool ret; |
| 216 | |
| 217 | rtw_write32(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1 | |
| 218 | BIT_WKFCAM_CLR_V1); |
| 219 | |
| 220 | ret = check_hw_ready(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1, 0); |
| 221 | if (!ret) |
| 222 | rtw_err(rtwdev, "failed to clean pattern cam\n"); |
| 223 | } |
| 224 | |
| 225 | static void rtw_wow_pattern_write(struct rtw_dev *rtwdev) |
| 226 | { |
| 227 | struct rtw_wow_param *rtw_wow = &rtwdev->wow; |
| 228 | struct rtw_wow_pattern *rtw_pattern = rtw_wow->patterns; |
| 229 | int i = 0; |
| 230 | |
| 231 | for (i = 0; i < rtw_wow->pattern_cnt; i++) |
| 232 | rtw_wow_pattern_write_cam_ent(rtwdev, i, rtw_pattern + i); |
| 233 | } |
| 234 | |
| 235 | static void rtw_wow_pattern_clear(struct rtw_dev *rtwdev) |
| 236 | { |
| 237 | struct rtw_wow_param *rtw_wow = &rtwdev->wow; |
| 238 | |
| 239 | rtw_wow_pattern_clear_cam(rtwdev); |
| 240 | |
| 241 | rtw_wow->pattern_cnt = 0; |
| 242 | memset(rtw_wow->patterns, 0, sizeof(rtw_wow->patterns)); |
| 243 | } |
| 244 | |
| 245 | static void rtw_wow_bb_stop(struct rtw_dev *rtwdev) |
| 246 | { |
| 247 | struct rtw_wow_param *rtw_wow = &rtwdev->wow; |
| 248 | |
| 249 | /* wait 100ms for firmware to finish TX */ |
| 250 | msleep(100); |
| 251 | |
| 252 | if (!rtw_read32_mask(rtwdev, REG_BCNQ_INFO, BIT_MGQ_CPU_EMPTY)) |
| 253 | rtw_warn(rtwdev, "Wrong status of MGQ_CPU empty!\n"); |
| 254 | |
| 255 | rtw_wow->txpause = rtw_read8(rtwdev, REG_TXPAUSE); |
| 256 | rtw_write8(rtwdev, REG_TXPAUSE, 0xff); |
| 257 | rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB); |
| 258 | } |
| 259 | |
| 260 | static void rtw_wow_bb_start(struct rtw_dev *rtwdev) |
| 261 | { |
| 262 | struct rtw_wow_param *rtw_wow = &rtwdev->wow; |
| 263 | |
| 264 | rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB); |
| 265 | rtw_write8(rtwdev, REG_TXPAUSE, rtw_wow->txpause); |
| 266 | } |
| 267 | |
| 268 | static void rtw_wow_rx_dma_stop(struct rtw_dev *rtwdev) |
| 269 | { |
| 270 | /* wait 100ms for HW to finish rx dma */ |
| 271 | msleep(100); |
| 272 | |
| 273 | rtw_write32_set(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE); |
| 274 | |
| 275 | if (!check_hw_ready(rtwdev, REG_RXPKT_NUM, BIT_RXDMA_IDLE, 1)) |
| 276 | rtw_err(rtwdev, "failed to stop rx dma\n"); |
| 277 | } |
| 278 | |
| 279 | static void rtw_wow_rx_dma_start(struct rtw_dev *rtwdev) |
| 280 | { |
| 281 | rtw_write32_clr(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE); |
| 282 | } |
| 283 | |
| 284 | static int rtw_wow_check_fw_status(struct rtw_dev *rtwdev, bool wow_enable) |
| 285 | { |
| 286 | /* wait 100ms for wow firmware to finish work */ |
| 287 | msleep(100); |
| 288 | |
| 289 | if (wow_enable) { |
| 290 | if (rtw_read8(rtwdev, REG_WOWLAN_WAKE_REASON)) |
| 291 | goto wow_fail; |
| 292 | } else { |
| 293 | if (rtw_read32_mask(rtwdev, REG_FE1IMR, BIT_FS_RXDONE) || |
| 294 | rtw_read32_mask(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE)) |
| 295 | goto wow_fail; |
| 296 | } |
| 297 | |
| 298 | return 0; |
| 299 | |
| 300 | wow_fail: |
| 301 | rtw_err(rtwdev, "failed to check wow status %s\n", |
| 302 | wow_enable ? "enabled" : "disabled"); |
| 303 | return -EBUSY; |
| 304 | } |
| 305 | |
| 306 | static void rtw_wow_fw_security_type_iter(struct ieee80211_hw *hw, |
| 307 | struct ieee80211_vif *vif, |
| 308 | struct ieee80211_sta *sta, |
| 309 | struct ieee80211_key_conf *key, |
| 310 | void *data) |
| 311 | { |
| 312 | struct rtw_fw_key_type_iter_data *iter_data = data; |
| 313 | struct rtw_dev *rtwdev = hw->priv; |
| 314 | u8 hw_key_type; |
| 315 | |
| 316 | if (vif != rtwdev->wow.wow_vif) |
| 317 | return; |
| 318 | |
| 319 | switch (key->cipher) { |
| 320 | case WLAN_CIPHER_SUITE_WEP40: |
| 321 | hw_key_type = RTW_CAM_WEP40; |
| 322 | break; |
| 323 | case WLAN_CIPHER_SUITE_WEP104: |
| 324 | hw_key_type = RTW_CAM_WEP104; |
| 325 | break; |
| 326 | case WLAN_CIPHER_SUITE_TKIP: |
| 327 | hw_key_type = RTW_CAM_TKIP; |
| 328 | key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; |
| 329 | break; |
| 330 | case WLAN_CIPHER_SUITE_CCMP: |
| 331 | hw_key_type = RTW_CAM_AES; |
| 332 | key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; |
| 333 | break; |
| 334 | default: |
| 335 | rtw_err(rtwdev, "Unsupported key type for wowlan mode: %#x\n", |
| 336 | key->cipher); |
| 337 | hw_key_type = 0; |
| 338 | break; |
| 339 | } |
| 340 | |
| 341 | if (sta) |
| 342 | iter_data->pairwise_key_type = hw_key_type; |
| 343 | else |
| 344 | iter_data->group_key_type = hw_key_type; |
| 345 | } |
| 346 | |
| 347 | static void rtw_wow_fw_security_type(struct rtw_dev *rtwdev) |
| 348 | { |
| 349 | struct rtw_fw_key_type_iter_data data = {}; |
| 350 | struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif; |
| 351 | |
| 352 | data.rtwdev = rtwdev; |
| 353 | rtw_iterate_keys(rtwdev, wow_vif, |
| 354 | rtw_wow_fw_security_type_iter, &data); |
| 355 | rtw_fw_set_aoac_global_info_cmd(rtwdev, data.pairwise_key_type, |
| 356 | data.group_key_type); |
| 357 | } |
| 358 | |
| 359 | static int rtw_wow_fw_start(struct rtw_dev *rtwdev) |
| 360 | { |
| 361 | if (rtw_wow_mgd_linked(rtwdev)) { |
| 362 | rtw_send_rsvd_page_h2c(rtwdev); |
| 363 | rtw_wow_pattern_write(rtwdev); |
| 364 | rtw_wow_fw_security_type(rtwdev); |
| 365 | rtw_fw_set_disconnect_decision_cmd(rtwdev, true); |
| 366 | rtw_fw_set_keep_alive_cmd(rtwdev, true); |
| 367 | } else if (rtw_wow_no_link(rtwdev)) { |
| 368 | rtw_fw_set_nlo_info(rtwdev, true); |
| 369 | rtw_fw_update_pkt_probe_req(rtwdev, NULL); |
| 370 | rtw_fw_channel_switch(rtwdev, true); |
| 371 | } |
| 372 | |
| 373 | rtw_fw_set_wowlan_ctrl_cmd(rtwdev, true); |
| 374 | rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, true); |
| 375 | |
| 376 | return rtw_wow_check_fw_status(rtwdev, true); |
| 377 | } |
| 378 | |
| 379 | static int rtw_wow_fw_stop(struct rtw_dev *rtwdev) |
| 380 | { |
| 381 | if (rtw_wow_mgd_linked(rtwdev)) { |
| 382 | rtw_fw_set_disconnect_decision_cmd(rtwdev, false); |
| 383 | rtw_fw_set_keep_alive_cmd(rtwdev, false); |
| 384 | rtw_wow_pattern_clear(rtwdev); |
| 385 | } else if (rtw_wow_no_link(rtwdev)) { |
| 386 | rtw_fw_channel_switch(rtwdev, false); |
| 387 | rtw_fw_set_nlo_info(rtwdev, false); |
| 388 | } |
| 389 | |
| 390 | rtw_fw_set_wowlan_ctrl_cmd(rtwdev, false); |
| 391 | rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, false); |
| 392 | |
| 393 | return rtw_wow_check_fw_status(rtwdev, false); |
| 394 | } |
| 395 | |
| 396 | static void rtw_wow_avoid_reset_mac(struct rtw_dev *rtwdev) |
| 397 | { |
| 398 | /* When resuming from wowlan mode, some hosts issue signal |
| 399 | * (PCIE: PREST, USB: SE0RST) to device, and lead to reset |
| 400 | * mac core. If it happens, the connection to AP will be lost. |
| 401 | * Setting REG_RSV_CTRL Register can avoid this process. |
| 402 | */ |
| 403 | switch (rtw_hci_type(rtwdev)) { |
| 404 | case RTW_HCI_TYPE_PCIE: |
| 405 | case RTW_HCI_TYPE_USB: |
| 406 | rtw_write8(rtwdev, REG_RSV_CTRL, BIT_WLOCK_1C_B6); |
| 407 | rtw_write8(rtwdev, REG_RSV_CTRL, |
| 408 | BIT_WLOCK_1C_B6 | BIT_R_DIS_PRST); |
| 409 | break; |
| 410 | default: |
| 411 | rtw_warn(rtwdev, "Unsupported hci type to disable reset MAC\n"); |
| 412 | break; |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | static void rtw_wow_fw_media_status_iter(void *data, struct ieee80211_sta *sta) |
| 417 | { |
| 418 | struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; |
| 419 | struct rtw_fw_media_status_iter_data *iter_data = data; |
| 420 | struct rtw_dev *rtwdev = iter_data->rtwdev; |
| 421 | |
| 422 | rtw_fw_media_status_report(rtwdev, si->mac_id, iter_data->connect); |
| 423 | } |
| 424 | |
| 425 | static void rtw_wow_fw_media_status(struct rtw_dev *rtwdev, bool connect) |
| 426 | { |
| 427 | struct rtw_fw_media_status_iter_data data; |
| 428 | |
| 429 | data.rtwdev = rtwdev; |
| 430 | data.connect = connect; |
| 431 | |
| 432 | rtw_iterate_stas_atomic(rtwdev, rtw_wow_fw_media_status_iter, &data); |
| 433 | } |
| 434 | |
| 435 | static void rtw_wow_config_pno_rsvd_page(struct rtw_dev *rtwdev, |
| 436 | struct rtw_vif *rtwvif) |
| 437 | { |
| 438 | rtw_add_rsvd_page_pno(rtwdev, rtwvif); |
| 439 | } |
| 440 | |
| 441 | static void rtw_wow_config_linked_rsvd_page(struct rtw_dev *rtwdev, |
| 442 | struct rtw_vif *rtwvif) |
| 443 | { |
| 444 | rtw_add_rsvd_page_sta(rtwdev, rtwvif); |
| 445 | } |
| 446 | |
| 447 | static void rtw_wow_config_rsvd_page(struct rtw_dev *rtwdev, |
| 448 | struct rtw_vif *rtwvif) |
| 449 | { |
| 450 | rtw_remove_rsvd_page(rtwdev, rtwvif); |
| 451 | |
| 452 | if (rtw_wow_mgd_linked(rtwdev)) { |
| 453 | rtw_wow_config_linked_rsvd_page(rtwdev, rtwvif); |
| 454 | } else if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags) && |
| 455 | rtw_wow_no_link(rtwdev)) { |
| 456 | rtw_wow_config_pno_rsvd_page(rtwdev, rtwvif); |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | static int rtw_wow_dl_fw_rsvd_page(struct rtw_dev *rtwdev) |
| 461 | { |
| 462 | struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif; |
| 463 | struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv; |
| 464 | |
| 465 | rtw_wow_config_rsvd_page(rtwdev, rtwvif); |
| 466 | |
| 467 | return rtw_fw_download_rsvd_page(rtwdev); |
| 468 | } |
| 469 | |
| 470 | static int rtw_wow_swap_fw(struct rtw_dev *rtwdev, enum rtw_fw_type type) |
| 471 | { |
| 472 | struct rtw_fw_state *fw; |
| 473 | int ret; |
| 474 | |
| 475 | switch (type) { |
| 476 | case RTW_WOWLAN_FW: |
| 477 | fw = &rtwdev->wow_fw; |
| 478 | break; |
| 479 | |
| 480 | case RTW_NORMAL_FW: |
| 481 | fw = &rtwdev->fw; |
| 482 | break; |
| 483 | |
| 484 | default: |
| 485 | rtw_warn(rtwdev, "unsupported firmware type to swap\n"); |
| 486 | return -ENOENT; |
| 487 | } |
| 488 | |
| 489 | ret = rtw_download_firmware(rtwdev, fw); |
| 490 | if (ret) |
| 491 | goto out; |
| 492 | |
| 493 | rtw_fw_send_general_info(rtwdev); |
| 494 | rtw_fw_send_phydm_info(rtwdev); |
| 495 | rtw_wow_fw_media_status(rtwdev, true); |
| 496 | |
| 497 | out: |
| 498 | return ret; |
| 499 | } |
| 500 | |
| 501 | static void rtw_wow_check_pno(struct rtw_dev *rtwdev, |
| 502 | struct cfg80211_sched_scan_request *nd_config) |
| 503 | { |
| 504 | struct rtw_wow_param *rtw_wow = &rtwdev->wow; |
| 505 | struct rtw_pno_request *pno_req = &rtw_wow->pno_req; |
| 506 | struct ieee80211_channel *channel; |
| 507 | int i, size; |
| 508 | |
| 509 | if (!nd_config->n_match_sets || !nd_config->n_channels) |
| 510 | goto err; |
| 511 | |
| 512 | pno_req->match_set_cnt = nd_config->n_match_sets; |
| 513 | size = sizeof(*pno_req->match_sets) * pno_req->match_set_cnt; |
| 514 | pno_req->match_sets = kmemdup(nd_config->match_sets, size, GFP_KERNEL); |
| 515 | if (!pno_req->match_sets) |
| 516 | goto err; |
| 517 | |
| 518 | pno_req->channel_cnt = nd_config->n_channels; |
| 519 | size = sizeof(*nd_config->channels[0]) * nd_config->n_channels; |
| 520 | pno_req->channels = kmalloc(size, GFP_KERNEL); |
| 521 | if (!pno_req->channels) |
| 522 | goto channel_err; |
| 523 | |
| 524 | for (i = 0 ; i < pno_req->channel_cnt; i++) { |
| 525 | channel = pno_req->channels + i; |
| 526 | memcpy(channel, nd_config->channels[i], sizeof(*channel)); |
| 527 | } |
| 528 | |
| 529 | pno_req->scan_plan = *nd_config->scan_plans; |
| 530 | pno_req->inited = true; |
| 531 | |
| 532 | rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: net-detect is enabled\n"); |
| 533 | |
| 534 | return; |
| 535 | |
| 536 | channel_err: |
| 537 | kfree(pno_req->match_sets); |
| 538 | |
| 539 | err: |
| 540 | rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: net-detect is disabled\n"); |
| 541 | } |
| 542 | |
| 543 | static int rtw_wow_leave_linked_ps(struct rtw_dev *rtwdev) |
| 544 | { |
| 545 | if (!test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) |
| 546 | cancel_delayed_work_sync(&rtwdev->watch_dog_work); |
| 547 | |
| 548 | rtw_leave_lps(rtwdev); |
| 549 | |
| 550 | return 0; |
| 551 | } |
| 552 | |
| 553 | static int rtw_wow_leave_no_link_ps(struct rtw_dev *rtwdev) |
| 554 | { |
| 555 | struct rtw_wow_param *rtw_wow = &rtwdev->wow; |
| 556 | int ret = 0; |
| 557 | |
| 558 | if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) { |
| 559 | if (rtw_get_lps_deep_mode(rtwdev) != LPS_DEEP_MODE_NONE) |
| 560 | rtw_leave_lps_deep(rtwdev); |
| 561 | } else { |
| 562 | if (test_bit(RTW_FLAG_INACTIVE_PS, rtwdev->flags)) { |
| 563 | rtw_wow->ips_enabled = true; |
| 564 | ret = rtw_leave_ips(rtwdev); |
| 565 | if (ret) |
| 566 | return ret; |
| 567 | } |
| 568 | } |
| 569 | |
| 570 | return 0; |
| 571 | } |
| 572 | |
| 573 | static int rtw_wow_leave_ps(struct rtw_dev *rtwdev) |
| 574 | { |
| 575 | int ret = 0; |
| 576 | |
| 577 | if (rtw_wow_mgd_linked(rtwdev)) |
| 578 | ret = rtw_wow_leave_linked_ps(rtwdev); |
| 579 | else if (rtw_wow_no_link(rtwdev)) |
| 580 | ret = rtw_wow_leave_no_link_ps(rtwdev); |
| 581 | |
| 582 | return ret; |
| 583 | } |
| 584 | |
| 585 | static int rtw_wow_restore_ps(struct rtw_dev *rtwdev) |
| 586 | { |
| 587 | int ret = 0; |
| 588 | |
| 589 | if (rtw_wow_no_link(rtwdev) && rtwdev->wow.ips_enabled) |
| 590 | ret = rtw_enter_ips(rtwdev); |
| 591 | |
| 592 | return ret; |
| 593 | } |
| 594 | |
| 595 | static int rtw_wow_enter_linked_ps(struct rtw_dev *rtwdev) |
| 596 | { |
| 597 | struct rtw_wow_param *rtw_wow = &rtwdev->wow; |
| 598 | struct ieee80211_vif *wow_vif = rtw_wow->wow_vif; |
| 599 | struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv; |
| 600 | |
| 601 | rtw_enter_lps(rtwdev, rtwvif->port); |
| 602 | |
| 603 | return 0; |
| 604 | } |
| 605 | |
| 606 | static int rtw_wow_enter_no_link_ps(struct rtw_dev *rtwdev) |
| 607 | { |
| 608 | /* firmware enters deep ps by itself if supported */ |
| 609 | set_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags); |
| 610 | |
| 611 | return 0; |
| 612 | } |
| 613 | |
| 614 | static int rtw_wow_enter_ps(struct rtw_dev *rtwdev) |
| 615 | { |
| 616 | int ret = 0; |
| 617 | |
| 618 | if (rtw_wow_mgd_linked(rtwdev)) |
| 619 | ret = rtw_wow_enter_linked_ps(rtwdev); |
| 620 | else if (rtw_wow_no_link(rtwdev) && |
| 621 | rtw_get_lps_deep_mode(rtwdev) != LPS_DEEP_MODE_NONE) |
| 622 | ret = rtw_wow_enter_no_link_ps(rtwdev); |
| 623 | |
| 624 | return ret; |
| 625 | } |
| 626 | |
| 627 | static void rtw_wow_stop_trx(struct rtw_dev *rtwdev) |
| 628 | { |
| 629 | rtw_wow_bb_stop(rtwdev); |
| 630 | rtw_wow_rx_dma_stop(rtwdev); |
| 631 | } |
| 632 | |
| 633 | static int rtw_wow_start(struct rtw_dev *rtwdev) |
| 634 | { |
| 635 | int ret; |
| 636 | |
| 637 | ret = rtw_wow_fw_start(rtwdev); |
| 638 | if (ret) |
| 639 | goto out; |
| 640 | |
| 641 | rtw_hci_stop(rtwdev); |
| 642 | rtw_wow_bb_start(rtwdev); |
| 643 | rtw_wow_avoid_reset_mac(rtwdev); |
| 644 | |
| 645 | out: |
| 646 | return ret; |
| 647 | } |
| 648 | |
| 649 | static int rtw_wow_enable(struct rtw_dev *rtwdev) |
| 650 | { |
| 651 | int ret = 0; |
| 652 | |
| 653 | rtw_wow_stop_trx(rtwdev); |
| 654 | |
| 655 | ret = rtw_wow_swap_fw(rtwdev, RTW_WOWLAN_FW); |
| 656 | if (ret) { |
| 657 | rtw_err(rtwdev, "failed to swap wow fw\n"); |
| 658 | goto error; |
| 659 | } |
| 660 | |
| 661 | set_bit(RTW_FLAG_WOWLAN, rtwdev->flags); |
| 662 | |
| 663 | ret = rtw_wow_dl_fw_rsvd_page(rtwdev); |
| 664 | if (ret) { |
| 665 | rtw_err(rtwdev, "failed to download wowlan rsvd page\n"); |
| 666 | goto error; |
| 667 | } |
| 668 | |
| 669 | ret = rtw_wow_start(rtwdev); |
| 670 | if (ret) { |
| 671 | rtw_err(rtwdev, "failed to start wow\n"); |
| 672 | goto error; |
| 673 | } |
| 674 | |
| 675 | return ret; |
| 676 | |
| 677 | error: |
| 678 | clear_bit(RTW_FLAG_WOWLAN, rtwdev->flags); |
| 679 | return ret; |
| 680 | } |
| 681 | |
| 682 | static int rtw_wow_stop(struct rtw_dev *rtwdev) |
| 683 | { |
| 684 | int ret; |
| 685 | |
| 686 | /* some HCI related registers will be reset after resume, |
| 687 | * need to set them again. |
| 688 | */ |
| 689 | ret = rtw_hci_setup(rtwdev); |
| 690 | if (ret) { |
| 691 | rtw_err(rtwdev, "failed to setup hci\n"); |
| 692 | return ret; |
| 693 | } |
| 694 | |
| 695 | ret = rtw_hci_start(rtwdev); |
| 696 | if (ret) { |
| 697 | rtw_err(rtwdev, "failed to start hci\n"); |
| 698 | return ret; |
| 699 | } |
| 700 | |
| 701 | ret = rtw_wow_fw_stop(rtwdev); |
| 702 | if (ret) |
| 703 | rtw_err(rtwdev, "failed to stop wowlan fw\n"); |
| 704 | |
| 705 | rtw_wow_bb_stop(rtwdev); |
| 706 | |
| 707 | return ret; |
| 708 | } |
| 709 | |
| 710 | static void rtw_wow_resume_trx(struct rtw_dev *rtwdev) |
| 711 | { |
| 712 | rtw_wow_rx_dma_start(rtwdev); |
| 713 | rtw_wow_bb_start(rtwdev); |
| 714 | ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->watch_dog_work, |
| 715 | RTW_WATCH_DOG_DELAY_TIME); |
| 716 | } |
| 717 | |
| 718 | static int rtw_wow_disable(struct rtw_dev *rtwdev) |
| 719 | { |
| 720 | int ret; |
| 721 | |
| 722 | clear_bit(RTW_FLAG_WOWLAN, rtwdev->flags); |
| 723 | |
| 724 | ret = rtw_wow_stop(rtwdev); |
| 725 | if (ret) { |
| 726 | rtw_err(rtwdev, "failed to stop wow\n"); |
| 727 | goto out; |
| 728 | } |
| 729 | |
| 730 | ret = rtw_wow_swap_fw(rtwdev, RTW_NORMAL_FW); |
| 731 | if (ret) { |
| 732 | rtw_err(rtwdev, "failed to swap normal fw\n"); |
| 733 | goto out; |
| 734 | } |
| 735 | |
| 736 | ret = rtw_wow_dl_fw_rsvd_page(rtwdev); |
| 737 | if (ret) |
| 738 | rtw_err(rtwdev, "failed to download normal rsvd page\n"); |
| 739 | |
| 740 | out: |
| 741 | rtw_wow_resume_trx(rtwdev); |
| 742 | return ret; |
| 743 | } |
| 744 | |
| 745 | static void rtw_wow_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) |
| 746 | { |
| 747 | struct rtw_dev *rtwdev = data; |
| 748 | struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; |
| 749 | struct rtw_wow_param *rtw_wow = &rtwdev->wow; |
| 750 | |
| 751 | /* Current wowlan function support setting of only one STATION vif. |
| 752 | * So when one suitable vif is found, stop the iteration. |
| 753 | */ |
| 754 | if (rtw_wow->wow_vif || vif->type != NL80211_IFTYPE_STATION) |
| 755 | return; |
| 756 | |
| 757 | switch (rtwvif->net_type) { |
| 758 | case RTW_NET_MGD_LINKED: |
| 759 | rtw_wow->wow_vif = vif; |
| 760 | break; |
| 761 | case RTW_NET_NO_LINK: |
| 762 | if (rtw_wow->pno_req.inited) |
| 763 | rtwdev->wow.wow_vif = vif; |
| 764 | break; |
| 765 | default: |
| 766 | break; |
| 767 | } |
| 768 | } |
| 769 | |
| 770 | static int rtw_wow_set_wakeups(struct rtw_dev *rtwdev, |
| 771 | struct cfg80211_wowlan *wowlan) |
| 772 | { |
| 773 | struct rtw_wow_param *rtw_wow = &rtwdev->wow; |
| 774 | struct rtw_wow_pattern *rtw_patterns = rtw_wow->patterns; |
| 775 | struct rtw_vif *rtwvif; |
| 776 | int i; |
| 777 | |
| 778 | if (wowlan->disconnect) |
| 779 | set_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags); |
| 780 | if (wowlan->magic_pkt) |
| 781 | set_bit(RTW_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags); |
| 782 | if (wowlan->gtk_rekey_failure) |
| 783 | set_bit(RTW_WOW_FLAG_EN_REKEY_PKT, rtw_wow->flags); |
| 784 | |
| 785 | if (wowlan->nd_config) |
| 786 | rtw_wow_check_pno(rtwdev, wowlan->nd_config); |
| 787 | |
| 788 | rtw_iterate_vifs_atomic(rtwdev, rtw_wow_vif_iter, rtwdev); |
| 789 | if (!rtw_wow->wow_vif) |
| 790 | return -EPERM; |
| 791 | |
| 792 | rtwvif = (struct rtw_vif *)rtw_wow->wow_vif->drv_priv; |
| 793 | if (wowlan->n_patterns && wowlan->patterns) { |
| 794 | rtw_wow->pattern_cnt = wowlan->n_patterns; |
| 795 | for (i = 0; i < wowlan->n_patterns; i++) |
| 796 | rtw_wow_pattern_generate(rtwdev, rtwvif, |
| 797 | wowlan->patterns + i, |
| 798 | rtw_patterns + i); |
| 799 | } |
| 800 | |
| 801 | return 0; |
| 802 | } |
| 803 | |
| 804 | static void rtw_wow_clear_wakeups(struct rtw_dev *rtwdev) |
| 805 | { |
| 806 | struct rtw_wow_param *rtw_wow = &rtwdev->wow; |
| 807 | struct rtw_pno_request *pno_req = &rtw_wow->pno_req; |
| 808 | |
| 809 | if (pno_req->inited) { |
| 810 | kfree(pno_req->channels); |
| 811 | kfree(pno_req->match_sets); |
| 812 | } |
| 813 | |
| 814 | memset(rtw_wow, 0, sizeof(rtwdev->wow)); |
| 815 | } |
| 816 | |
| 817 | int rtw_wow_suspend(struct rtw_dev *rtwdev, struct cfg80211_wowlan *wowlan) |
| 818 | { |
| 819 | int ret = 0; |
| 820 | |
| 821 | ret = rtw_wow_set_wakeups(rtwdev, wowlan); |
| 822 | if (ret) { |
| 823 | rtw_err(rtwdev, "failed to set wakeup event\n"); |
| 824 | goto out; |
| 825 | } |
| 826 | |
| 827 | ret = rtw_wow_leave_ps(rtwdev); |
| 828 | if (ret) { |
| 829 | rtw_err(rtwdev, "failed to leave ps from normal mode\n"); |
| 830 | goto out; |
| 831 | } |
| 832 | |
| 833 | ret = rtw_wow_enable(rtwdev); |
| 834 | if (ret) { |
| 835 | rtw_err(rtwdev, "failed to enable wow\n"); |
| 836 | rtw_wow_restore_ps(rtwdev); |
| 837 | goto out; |
| 838 | } |
| 839 | |
| 840 | ret = rtw_wow_enter_ps(rtwdev); |
| 841 | if (ret) |
| 842 | rtw_err(rtwdev, "failed to enter ps for wow\n"); |
| 843 | |
| 844 | out: |
| 845 | return ret; |
| 846 | } |
| 847 | |
| 848 | int rtw_wow_resume(struct rtw_dev *rtwdev) |
| 849 | { |
| 850 | int ret; |
| 851 | |
| 852 | /* If wowlan mode is not enabled, do nothing */ |
| 853 | if (!test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) { |
| 854 | rtw_err(rtwdev, "wow is not enabled\n"); |
| 855 | ret = -EPERM; |
| 856 | goto out; |
| 857 | } |
| 858 | |
| 859 | ret = rtw_wow_leave_ps(rtwdev); |
| 860 | if (ret) { |
| 861 | rtw_err(rtwdev, "failed to leave ps from wowlan mode\n"); |
| 862 | goto out; |
| 863 | } |
| 864 | |
| 865 | rtw_wow_show_wakeup_reason(rtwdev); |
| 866 | |
| 867 | ret = rtw_wow_disable(rtwdev); |
| 868 | if (ret) { |
| 869 | rtw_err(rtwdev, "failed to disable wow\n"); |
| 870 | goto out; |
| 871 | } |
| 872 | |
| 873 | ret = rtw_wow_restore_ps(rtwdev); |
| 874 | if (ret) |
| 875 | rtw_err(rtwdev, "failed to restore ps to normal mode\n"); |
| 876 | |
| 877 | out: |
| 878 | rtw_wow_clear_wakeups(rtwdev); |
| 879 | return ret; |
| 880 | } |