diff options
author | Wey-Yi Guy <wey-yi.w.guy@intel.com> | 2011-11-10 09:55:17 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-11-11 12:32:54 -0500 |
commit | 023ca58f1d025d9c210f8003ca47d6b96cdac167 (patch) | |
tree | f8fc3ffed8d1d3a6f68e8555d30071f423619766 /drivers | |
parent | 76b2933111afe5a04e342040436a90c31c7661d4 (diff) |
iwlwifi: Move the core suspend function to iwl-agn-lib
The core suspend function is part of agn, iwl_mac80211 should only
handle mac80211 I/F operations.
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 357 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-mac80211.c | 347 |
3 files changed, 365 insertions, 345 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 6465983fef34..0bc962217351 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c | |||
@@ -984,3 +984,360 @@ void iwlagn_remove_notification(struct iwl_priv *priv, | |||
984 | list_del(&wait_entry->list); | 984 | list_del(&wait_entry->list); |
985 | spin_unlock_bh(&priv->notif_wait_lock); | 985 | spin_unlock_bh(&priv->notif_wait_lock); |
986 | } | 986 | } |
987 | |||
988 | #ifdef CONFIG_PM_SLEEP | ||
989 | static void iwlagn_convert_p1k(u16 *p1k, __le16 *out) | ||
990 | { | ||
991 | int i; | ||
992 | |||
993 | for (i = 0; i < IWLAGN_P1K_SIZE; i++) | ||
994 | out[i] = cpu_to_le16(p1k[i]); | ||
995 | } | ||
996 | |||
997 | struct wowlan_key_data { | ||
998 | struct iwl_rxon_context *ctx; | ||
999 | struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc; | ||
1000 | struct iwlagn_wowlan_tkip_params_cmd *tkip; | ||
1001 | const u8 *bssid; | ||
1002 | bool error, use_rsc_tsc, use_tkip; | ||
1003 | }; | ||
1004 | |||
1005 | |||
1006 | static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, | ||
1007 | struct ieee80211_vif *vif, | ||
1008 | struct ieee80211_sta *sta, | ||
1009 | struct ieee80211_key_conf *key, | ||
1010 | void *_data) | ||
1011 | { | ||
1012 | struct iwl_priv *priv = hw->priv; | ||
1013 | struct wowlan_key_data *data = _data; | ||
1014 | struct iwl_rxon_context *ctx = data->ctx; | ||
1015 | struct aes_sc *aes_sc, *aes_tx_sc = NULL; | ||
1016 | struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; | ||
1017 | struct iwlagn_p1k_cache *rx_p1ks; | ||
1018 | u8 *rx_mic_key; | ||
1019 | struct ieee80211_key_seq seq; | ||
1020 | u32 cur_rx_iv32 = 0; | ||
1021 | u16 p1k[IWLAGN_P1K_SIZE]; | ||
1022 | int ret, i; | ||
1023 | |||
1024 | mutex_lock(&priv->shrd->mutex); | ||
1025 | |||
1026 | if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || | ||
1027 | key->cipher == WLAN_CIPHER_SUITE_WEP104) && | ||
1028 | !sta && !ctx->key_mapping_keys) | ||
1029 | ret = iwl_set_default_wep_key(priv, ctx, key); | ||
1030 | else | ||
1031 | ret = iwl_set_dynamic_key(priv, ctx, key, sta); | ||
1032 | |||
1033 | if (ret) { | ||
1034 | IWL_ERR(priv, "Error setting key during suspend!\n"); | ||
1035 | data->error = true; | ||
1036 | } | ||
1037 | |||
1038 | switch (key->cipher) { | ||
1039 | case WLAN_CIPHER_SUITE_TKIP: | ||
1040 | if (sta) { | ||
1041 | tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; | ||
1042 | tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; | ||
1043 | |||
1044 | rx_p1ks = data->tkip->rx_uni; | ||
1045 | |||
1046 | ieee80211_get_key_tx_seq(key, &seq); | ||
1047 | tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); | ||
1048 | tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); | ||
1049 | |||
1050 | ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); | ||
1051 | iwlagn_convert_p1k(p1k, data->tkip->tx.p1k); | ||
1052 | |||
1053 | memcpy(data->tkip->mic_keys.tx, | ||
1054 | &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], | ||
1055 | IWLAGN_MIC_KEY_SIZE); | ||
1056 | |||
1057 | rx_mic_key = data->tkip->mic_keys.rx_unicast; | ||
1058 | } else { | ||
1059 | tkip_sc = | ||
1060 | data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; | ||
1061 | rx_p1ks = data->tkip->rx_multi; | ||
1062 | rx_mic_key = data->tkip->mic_keys.rx_mcast; | ||
1063 | } | ||
1064 | |||
1065 | /* | ||
1066 | * For non-QoS this relies on the fact that both the uCode and | ||
1067 | * mac80211 use TID 0 (as they need to to avoid replay attacks) | ||
1068 | * for checking the IV in the frames. | ||
1069 | */ | ||
1070 | for (i = 0; i < IWLAGN_NUM_RSC; i++) { | ||
1071 | ieee80211_get_key_rx_seq(key, i, &seq); | ||
1072 | tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); | ||
1073 | tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); | ||
1074 | /* wrapping isn't allowed, AP must rekey */ | ||
1075 | if (seq.tkip.iv32 > cur_rx_iv32) | ||
1076 | cur_rx_iv32 = seq.tkip.iv32; | ||
1077 | } | ||
1078 | |||
1079 | ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k); | ||
1080 | iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k); | ||
1081 | ieee80211_get_tkip_rx_p1k(key, data->bssid, | ||
1082 | cur_rx_iv32 + 1, p1k); | ||
1083 | iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k); | ||
1084 | |||
1085 | memcpy(rx_mic_key, | ||
1086 | &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], | ||
1087 | IWLAGN_MIC_KEY_SIZE); | ||
1088 | |||
1089 | data->use_tkip = true; | ||
1090 | data->use_rsc_tsc = true; | ||
1091 | break; | ||
1092 | case WLAN_CIPHER_SUITE_CCMP: | ||
1093 | if (sta) { | ||
1094 | u8 *pn = seq.ccmp.pn; | ||
1095 | |||
1096 | aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; | ||
1097 | aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; | ||
1098 | |||
1099 | ieee80211_get_key_tx_seq(key, &seq); | ||
1100 | aes_tx_sc->pn = cpu_to_le64( | ||
1101 | (u64)pn[5] | | ||
1102 | ((u64)pn[4] << 8) | | ||
1103 | ((u64)pn[3] << 16) | | ||
1104 | ((u64)pn[2] << 24) | | ||
1105 | ((u64)pn[1] << 32) | | ||
1106 | ((u64)pn[0] << 40)); | ||
1107 | } else | ||
1108 | aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; | ||
1109 | |||
1110 | /* | ||
1111 | * For non-QoS this relies on the fact that both the uCode and | ||
1112 | * mac80211 use TID 0 for checking the IV in the frames. | ||
1113 | */ | ||
1114 | for (i = 0; i < IWLAGN_NUM_RSC; i++) { | ||
1115 | u8 *pn = seq.ccmp.pn; | ||
1116 | |||
1117 | ieee80211_get_key_rx_seq(key, i, &seq); | ||
1118 | aes_sc->pn = cpu_to_le64( | ||
1119 | (u64)pn[5] | | ||
1120 | ((u64)pn[4] << 8) | | ||
1121 | ((u64)pn[3] << 16) | | ||
1122 | ((u64)pn[2] << 24) | | ||
1123 | ((u64)pn[1] << 32) | | ||
1124 | ((u64)pn[0] << 40)); | ||
1125 | } | ||
1126 | data->use_rsc_tsc = true; | ||
1127 | break; | ||
1128 | } | ||
1129 | |||
1130 | mutex_unlock(&priv->shrd->mutex); | ||
1131 | } | ||
1132 | |||
1133 | int iwlagn_send_patterns(struct iwl_priv *priv, | ||
1134 | struct cfg80211_wowlan *wowlan) | ||
1135 | { | ||
1136 | struct iwlagn_wowlan_patterns_cmd *pattern_cmd; | ||
1137 | struct iwl_host_cmd cmd = { | ||
1138 | .id = REPLY_WOWLAN_PATTERNS, | ||
1139 | .dataflags[0] = IWL_HCMD_DFL_NOCOPY, | ||
1140 | .flags = CMD_SYNC, | ||
1141 | }; | ||
1142 | int i, err; | ||
1143 | |||
1144 | if (!wowlan->n_patterns) | ||
1145 | return 0; | ||
1146 | |||
1147 | cmd.len[0] = sizeof(*pattern_cmd) + | ||
1148 | wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern); | ||
1149 | |||
1150 | pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); | ||
1151 | if (!pattern_cmd) | ||
1152 | return -ENOMEM; | ||
1153 | |||
1154 | pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); | ||
1155 | |||
1156 | for (i = 0; i < wowlan->n_patterns; i++) { | ||
1157 | int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); | ||
1158 | |||
1159 | memcpy(&pattern_cmd->patterns[i].mask, | ||
1160 | wowlan->patterns[i].mask, mask_len); | ||
1161 | memcpy(&pattern_cmd->patterns[i].pattern, | ||
1162 | wowlan->patterns[i].pattern, | ||
1163 | wowlan->patterns[i].pattern_len); | ||
1164 | pattern_cmd->patterns[i].mask_size = mask_len; | ||
1165 | pattern_cmd->patterns[i].pattern_size = | ||
1166 | wowlan->patterns[i].pattern_len; | ||
1167 | } | ||
1168 | |||
1169 | cmd.data[0] = pattern_cmd; | ||
1170 | err = iwl_trans_send_cmd(trans(priv), &cmd); | ||
1171 | kfree(pattern_cmd); | ||
1172 | return err; | ||
1173 | } | ||
1174 | |||
1175 | int iwlagn_suspend(struct iwl_priv *priv, | ||
1176 | struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) | ||
1177 | { | ||
1178 | struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd; | ||
1179 | struct iwl_rxon_cmd rxon; | ||
1180 | struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; | ||
1181 | struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd; | ||
1182 | struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {}; | ||
1183 | struct iwlagn_d3_config_cmd d3_cfg_cmd = {}; | ||
1184 | struct wowlan_key_data key_data = { | ||
1185 | .ctx = ctx, | ||
1186 | .bssid = ctx->active.bssid_addr, | ||
1187 | .use_rsc_tsc = false, | ||
1188 | .tkip = &tkip_cmd, | ||
1189 | .use_tkip = false, | ||
1190 | }; | ||
1191 | int ret, i; | ||
1192 | u16 seq; | ||
1193 | |||
1194 | key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); | ||
1195 | if (!key_data.rsc_tsc) | ||
1196 | return -ENOMEM; | ||
1197 | |||
1198 | memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd)); | ||
1199 | |||
1200 | /* | ||
1201 | * We know the last used seqno, and the uCode expects to know that | ||
1202 | * one, it will increment before TX. | ||
1203 | */ | ||
1204 | seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ; | ||
1205 | wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq); | ||
1206 | |||
1207 | /* | ||
1208 | * For QoS counters, we store the one to use next, so subtract 0x10 | ||
1209 | * since the uCode will add 0x10 before using the value. | ||
1210 | */ | ||
1211 | for (i = 0; i < 8; i++) { | ||
1212 | seq = priv->shrd->tid_data[IWL_AP_ID][i].seq_number; | ||
1213 | seq -= 0x10; | ||
1214 | wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq); | ||
1215 | } | ||
1216 | |||
1217 | if (wowlan->disconnect) | ||
1218 | wakeup_filter_cmd.enabled |= | ||
1219 | cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | | ||
1220 | IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE); | ||
1221 | if (wowlan->magic_pkt) | ||
1222 | wakeup_filter_cmd.enabled |= | ||
1223 | cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET); | ||
1224 | if (wowlan->gtk_rekey_failure) | ||
1225 | wakeup_filter_cmd.enabled |= | ||
1226 | cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL); | ||
1227 | if (wowlan->eap_identity_req) | ||
1228 | wakeup_filter_cmd.enabled |= | ||
1229 | cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ); | ||
1230 | if (wowlan->four_way_handshake) | ||
1231 | wakeup_filter_cmd.enabled |= | ||
1232 | cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE); | ||
1233 | if (wowlan->n_patterns) | ||
1234 | wakeup_filter_cmd.enabled |= | ||
1235 | cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH); | ||
1236 | |||
1237 | if (wowlan->rfkill_release) | ||
1238 | d3_cfg_cmd.wakeup_flags |= | ||
1239 | cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL); | ||
1240 | |||
1241 | iwl_scan_cancel_timeout(priv, 200); | ||
1242 | |||
1243 | memcpy(&rxon, &ctx->active, sizeof(rxon)); | ||
1244 | |||
1245 | iwl_trans_stop_device(trans(priv)); | ||
1246 | |||
1247 | priv->shrd->wowlan = true; | ||
1248 | |||
1249 | ret = iwlagn_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN); | ||
1250 | if (ret) | ||
1251 | goto out; | ||
1252 | |||
1253 | /* now configure WoWLAN ucode */ | ||
1254 | ret = iwl_alive_start(priv); | ||
1255 | if (ret) | ||
1256 | goto out; | ||
1257 | |||
1258 | memcpy(&ctx->staging, &rxon, sizeof(rxon)); | ||
1259 | ret = iwlagn_commit_rxon(priv, ctx); | ||
1260 | if (ret) | ||
1261 | goto out; | ||
1262 | |||
1263 | ret = iwl_power_update_mode(priv, true); | ||
1264 | if (ret) | ||
1265 | goto out; | ||
1266 | |||
1267 | if (!iwlagn_mod_params.sw_crypto) { | ||
1268 | /* mark all keys clear */ | ||
1269 | priv->ucode_key_table = 0; | ||
1270 | ctx->key_mapping_keys = 0; | ||
1271 | |||
1272 | /* | ||
1273 | * This needs to be unlocked due to lock ordering | ||
1274 | * constraints. Since we're in the suspend path | ||
1275 | * that isn't really a problem though. | ||
1276 | */ | ||
1277 | mutex_unlock(&priv->shrd->mutex); | ||
1278 | ieee80211_iter_keys(priv->hw, ctx->vif, | ||
1279 | iwlagn_wowlan_program_keys, | ||
1280 | &key_data); | ||
1281 | mutex_lock(&priv->shrd->mutex); | ||
1282 | if (key_data.error) { | ||
1283 | ret = -EIO; | ||
1284 | goto out; | ||
1285 | } | ||
1286 | |||
1287 | if (key_data.use_rsc_tsc) { | ||
1288 | struct iwl_host_cmd rsc_tsc_cmd = { | ||
1289 | .id = REPLY_WOWLAN_TSC_RSC_PARAMS, | ||
1290 | .flags = CMD_SYNC, | ||
1291 | .data[0] = key_data.rsc_tsc, | ||
1292 | .dataflags[0] = IWL_HCMD_DFL_NOCOPY, | ||
1293 | .len[0] = sizeof(key_data.rsc_tsc), | ||
1294 | }; | ||
1295 | |||
1296 | ret = iwl_trans_send_cmd(trans(priv), &rsc_tsc_cmd); | ||
1297 | if (ret) | ||
1298 | goto out; | ||
1299 | } | ||
1300 | |||
1301 | if (key_data.use_tkip) { | ||
1302 | ret = iwl_trans_send_cmd_pdu(trans(priv), | ||
1303 | REPLY_WOWLAN_TKIP_PARAMS, | ||
1304 | CMD_SYNC, sizeof(tkip_cmd), | ||
1305 | &tkip_cmd); | ||
1306 | if (ret) | ||
1307 | goto out; | ||
1308 | } | ||
1309 | |||
1310 | if (priv->have_rekey_data) { | ||
1311 | memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); | ||
1312 | memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN); | ||
1313 | kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); | ||
1314 | memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN); | ||
1315 | kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); | ||
1316 | kek_kck_cmd.replay_ctr = priv->replay_ctr; | ||
1317 | |||
1318 | ret = iwl_trans_send_cmd_pdu(trans(priv), | ||
1319 | REPLY_WOWLAN_KEK_KCK_MATERIAL, | ||
1320 | CMD_SYNC, sizeof(kek_kck_cmd), | ||
1321 | &kek_kck_cmd); | ||
1322 | if (ret) | ||
1323 | goto out; | ||
1324 | } | ||
1325 | } | ||
1326 | |||
1327 | ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_D3_CONFIG, CMD_SYNC, | ||
1328 | sizeof(d3_cfg_cmd), &d3_cfg_cmd); | ||
1329 | if (ret) | ||
1330 | goto out; | ||
1331 | |||
1332 | ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_WOWLAN_WAKEUP_FILTER, | ||
1333 | CMD_SYNC, sizeof(wakeup_filter_cmd), | ||
1334 | &wakeup_filter_cmd); | ||
1335 | if (ret) | ||
1336 | goto out; | ||
1337 | |||
1338 | ret = iwlagn_send_patterns(priv, wowlan); | ||
1339 | out: | ||
1340 | kfree(key_data.rsc_tsc); | ||
1341 | return ret; | ||
1342 | } | ||
1343 | #endif | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h index d325132849f7..5d8d2f445923 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn.h | |||
@@ -119,6 +119,12 @@ u16 iwlagn_eeprom_calib_version(struct iwl_priv *priv); | |||
119 | int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control); | 119 | int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control); |
120 | void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control); | 120 | void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control); |
121 | int iwlagn_send_beacon_cmd(struct iwl_priv *priv); | 121 | int iwlagn_send_beacon_cmd(struct iwl_priv *priv); |
122 | #ifdef CONFIG_PM_SLEEP | ||
123 | int iwlagn_send_patterns(struct iwl_priv *priv, | ||
124 | struct cfg80211_wowlan *wowlan); | ||
125 | int iwlagn_suspend(struct iwl_priv *priv, | ||
126 | struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); | ||
127 | #endif | ||
122 | 128 | ||
123 | /* rx */ | 129 | /* rx */ |
124 | int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); | 130 | int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); |
diff --git a/drivers/net/wireless/iwlwifi/iwl-mac80211.c b/drivers/net/wireless/iwlwifi/iwl-mac80211.c index b46702c34715..073e827c462b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-mac80211.c +++ b/drivers/net/wireless/iwlwifi/iwl-mac80211.c | |||
@@ -368,209 +368,13 @@ static void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw, | |||
368 | } | 368 | } |
369 | 369 | ||
370 | #ifdef CONFIG_PM_SLEEP | 370 | #ifdef CONFIG_PM_SLEEP |
371 | struct wowlan_key_data { | ||
372 | struct iwl_rxon_context *ctx; | ||
373 | struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc; | ||
374 | struct iwlagn_wowlan_tkip_params_cmd *tkip; | ||
375 | const u8 *bssid; | ||
376 | bool error, use_rsc_tsc, use_tkip; | ||
377 | }; | ||
378 | |||
379 | static void iwlagn_convert_p1k(u16 *p1k, __le16 *out) | ||
380 | { | ||
381 | int i; | ||
382 | |||
383 | for (i = 0; i < IWLAGN_P1K_SIZE; i++) | ||
384 | out[i] = cpu_to_le16(p1k[i]); | ||
385 | } | ||
386 | |||
387 | static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, | ||
388 | struct ieee80211_vif *vif, | ||
389 | struct ieee80211_sta *sta, | ||
390 | struct ieee80211_key_conf *key, | ||
391 | void *_data) | ||
392 | { | ||
393 | struct iwl_priv *priv = hw->priv; | ||
394 | struct wowlan_key_data *data = _data; | ||
395 | struct iwl_rxon_context *ctx = data->ctx; | ||
396 | struct aes_sc *aes_sc, *aes_tx_sc = NULL; | ||
397 | struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; | ||
398 | struct iwlagn_p1k_cache *rx_p1ks; | ||
399 | u8 *rx_mic_key; | ||
400 | struct ieee80211_key_seq seq; | ||
401 | u32 cur_rx_iv32 = 0; | ||
402 | u16 p1k[IWLAGN_P1K_SIZE]; | ||
403 | int ret, i; | ||
404 | |||
405 | mutex_lock(&priv->shrd->mutex); | ||
406 | |||
407 | if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || | ||
408 | key->cipher == WLAN_CIPHER_SUITE_WEP104) && | ||
409 | !sta && !ctx->key_mapping_keys) | ||
410 | ret = iwl_set_default_wep_key(priv, ctx, key); | ||
411 | else | ||
412 | ret = iwl_set_dynamic_key(priv, ctx, key, sta); | ||
413 | |||
414 | if (ret) { | ||
415 | IWL_ERR(priv, "Error setting key during suspend!\n"); | ||
416 | data->error = true; | ||
417 | } | ||
418 | |||
419 | switch (key->cipher) { | ||
420 | case WLAN_CIPHER_SUITE_TKIP: | ||
421 | if (sta) { | ||
422 | tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; | ||
423 | tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; | ||
424 | |||
425 | rx_p1ks = data->tkip->rx_uni; | ||
426 | |||
427 | ieee80211_get_key_tx_seq(key, &seq); | ||
428 | tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); | ||
429 | tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); | ||
430 | |||
431 | ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); | ||
432 | iwlagn_convert_p1k(p1k, data->tkip->tx.p1k); | ||
433 | |||
434 | memcpy(data->tkip->mic_keys.tx, | ||
435 | &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], | ||
436 | IWLAGN_MIC_KEY_SIZE); | ||
437 | |||
438 | rx_mic_key = data->tkip->mic_keys.rx_unicast; | ||
439 | } else { | ||
440 | tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; | ||
441 | rx_p1ks = data->tkip->rx_multi; | ||
442 | rx_mic_key = data->tkip->mic_keys.rx_mcast; | ||
443 | } | ||
444 | |||
445 | /* | ||
446 | * For non-QoS this relies on the fact that both the uCode and | ||
447 | * mac80211 use TID 0 (as they need to to avoid replay attacks) | ||
448 | * for checking the IV in the frames. | ||
449 | */ | ||
450 | for (i = 0; i < IWLAGN_NUM_RSC; i++) { | ||
451 | ieee80211_get_key_rx_seq(key, i, &seq); | ||
452 | tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); | ||
453 | tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); | ||
454 | /* wrapping isn't allowed, AP must rekey */ | ||
455 | if (seq.tkip.iv32 > cur_rx_iv32) | ||
456 | cur_rx_iv32 = seq.tkip.iv32; | ||
457 | } | ||
458 | |||
459 | ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k); | ||
460 | iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k); | ||
461 | ieee80211_get_tkip_rx_p1k(key, data->bssid, | ||
462 | cur_rx_iv32 + 1, p1k); | ||
463 | iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k); | ||
464 | |||
465 | memcpy(rx_mic_key, | ||
466 | &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], | ||
467 | IWLAGN_MIC_KEY_SIZE); | ||
468 | |||
469 | data->use_tkip = true; | ||
470 | data->use_rsc_tsc = true; | ||
471 | break; | ||
472 | case WLAN_CIPHER_SUITE_CCMP: | ||
473 | if (sta) { | ||
474 | u8 *pn = seq.ccmp.pn; | ||
475 | |||
476 | aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; | ||
477 | aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; | ||
478 | |||
479 | ieee80211_get_key_tx_seq(key, &seq); | ||
480 | aes_tx_sc->pn = cpu_to_le64( | ||
481 | (u64)pn[5] | | ||
482 | ((u64)pn[4] << 8) | | ||
483 | ((u64)pn[3] << 16) | | ||
484 | ((u64)pn[2] << 24) | | ||
485 | ((u64)pn[1] << 32) | | ||
486 | ((u64)pn[0] << 40)); | ||
487 | } else | ||
488 | aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; | ||
489 | |||
490 | /* | ||
491 | * For non-QoS this relies on the fact that both the uCode and | ||
492 | * mac80211 use TID 0 for checking the IV in the frames. | ||
493 | */ | ||
494 | for (i = 0; i < IWLAGN_NUM_RSC; i++) { | ||
495 | u8 *pn = seq.ccmp.pn; | ||
496 | |||
497 | ieee80211_get_key_rx_seq(key, i, &seq); | ||
498 | aes_sc->pn = cpu_to_le64( | ||
499 | (u64)pn[5] | | ||
500 | ((u64)pn[4] << 8) | | ||
501 | ((u64)pn[3] << 16) | | ||
502 | ((u64)pn[2] << 24) | | ||
503 | ((u64)pn[1] << 32) | | ||
504 | ((u64)pn[0] << 40)); | ||
505 | } | ||
506 | data->use_rsc_tsc = true; | ||
507 | break; | ||
508 | } | ||
509 | |||
510 | mutex_unlock(&priv->shrd->mutex); | ||
511 | } | ||
512 | |||
513 | static int iwlagn_send_patterns(struct iwl_priv *priv, | ||
514 | struct cfg80211_wowlan *wowlan) | ||
515 | { | ||
516 | struct iwlagn_wowlan_patterns_cmd *pattern_cmd; | ||
517 | struct iwl_host_cmd cmd = { | ||
518 | .id = REPLY_WOWLAN_PATTERNS, | ||
519 | .dataflags[0] = IWL_HCMD_DFL_NOCOPY, | ||
520 | .flags = CMD_SYNC, | ||
521 | }; | ||
522 | int i, err; | ||
523 | |||
524 | if (!wowlan->n_patterns) | ||
525 | return 0; | ||
526 | |||
527 | cmd.len[0] = sizeof(*pattern_cmd) + | ||
528 | wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern); | ||
529 | |||
530 | pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); | ||
531 | if (!pattern_cmd) | ||
532 | return -ENOMEM; | ||
533 | |||
534 | pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); | ||
535 | |||
536 | for (i = 0; i < wowlan->n_patterns; i++) { | ||
537 | int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); | ||
538 | |||
539 | memcpy(&pattern_cmd->patterns[i].mask, | ||
540 | wowlan->patterns[i].mask, mask_len); | ||
541 | memcpy(&pattern_cmd->patterns[i].pattern, | ||
542 | wowlan->patterns[i].pattern, | ||
543 | wowlan->patterns[i].pattern_len); | ||
544 | pattern_cmd->patterns[i].mask_size = mask_len; | ||
545 | pattern_cmd->patterns[i].pattern_size = | ||
546 | wowlan->patterns[i].pattern_len; | ||
547 | } | ||
548 | |||
549 | cmd.data[0] = pattern_cmd; | ||
550 | err = iwl_trans_send_cmd(trans(priv), &cmd); | ||
551 | kfree(pattern_cmd); | ||
552 | return err; | ||
553 | } | ||
554 | 371 | ||
555 | static int iwlagn_mac_suspend(struct ieee80211_hw *hw, | 372 | static int iwlagn_mac_suspend(struct ieee80211_hw *hw, |
556 | struct cfg80211_wowlan *wowlan) | 373 | struct cfg80211_wowlan *wowlan) |
557 | { | 374 | { |
558 | struct iwl_priv *priv = hw->priv; | 375 | struct iwl_priv *priv = hw->priv; |
559 | struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd; | ||
560 | struct iwl_rxon_cmd rxon; | ||
561 | struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; | 376 | struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; |
562 | struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd; | 377 | int ret; |
563 | struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {}; | ||
564 | struct wowlan_key_data key_data = { | ||
565 | .ctx = ctx, | ||
566 | .bssid = ctx->active.bssid_addr, | ||
567 | .use_rsc_tsc = false, | ||
568 | .tkip = &tkip_cmd, | ||
569 | .use_tkip = false, | ||
570 | }; | ||
571 | struct iwlagn_d3_config_cmd d3_cfg_cmd = {}; | ||
572 | int ret, i; | ||
573 | u16 seq; | ||
574 | 378 | ||
575 | if (WARN_ON(!wowlan)) | 379 | if (WARN_ON(!wowlan)) |
576 | return -EINVAL; | 380 | return -EINVAL; |
@@ -585,153 +389,7 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw, | |||
585 | goto out; | 389 | goto out; |
586 | } | 390 | } |
587 | 391 | ||
588 | key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); | 392 | ret = iwlagn_suspend(priv, hw, wowlan); |
589 | if (!key_data.rsc_tsc) { | ||
590 | ret = -ENOMEM; | ||
591 | goto out; | ||
592 | } | ||
593 | |||
594 | memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd)); | ||
595 | |||
596 | /* | ||
597 | * We know the last used seqno, and the uCode expects to know that | ||
598 | * one, it will increment before TX. | ||
599 | */ | ||
600 | seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ; | ||
601 | wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq); | ||
602 | |||
603 | /* | ||
604 | * For QoS counters, we store the one to use next, so subtract 0x10 | ||
605 | * since the uCode will add 0x10 before using the value. | ||
606 | */ | ||
607 | for (i = 0; i < 8; i++) { | ||
608 | seq = priv->shrd->tid_data[IWL_AP_ID][i].seq_number; | ||
609 | seq -= 0x10; | ||
610 | wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq); | ||
611 | } | ||
612 | |||
613 | if (wowlan->disconnect) | ||
614 | wakeup_filter_cmd.enabled |= | ||
615 | cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | | ||
616 | IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE); | ||
617 | if (wowlan->magic_pkt) | ||
618 | wakeup_filter_cmd.enabled |= | ||
619 | cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET); | ||
620 | if (wowlan->gtk_rekey_failure) | ||
621 | wakeup_filter_cmd.enabled |= | ||
622 | cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL); | ||
623 | if (wowlan->eap_identity_req) | ||
624 | wakeup_filter_cmd.enabled |= | ||
625 | cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ); | ||
626 | if (wowlan->four_way_handshake) | ||
627 | wakeup_filter_cmd.enabled |= | ||
628 | cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE); | ||
629 | if (wowlan->n_patterns) | ||
630 | wakeup_filter_cmd.enabled |= | ||
631 | cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH); | ||
632 | |||
633 | if (wowlan->rfkill_release) | ||
634 | d3_cfg_cmd.wakeup_flags |= | ||
635 | cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL); | ||
636 | |||
637 | iwl_scan_cancel_timeout(priv, 200); | ||
638 | |||
639 | memcpy(&rxon, &ctx->active, sizeof(rxon)); | ||
640 | |||
641 | iwl_trans_stop_device(trans(priv)); | ||
642 | |||
643 | priv->shrd->wowlan = true; | ||
644 | |||
645 | ret = iwlagn_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN); | ||
646 | if (ret) | ||
647 | goto error; | ||
648 | |||
649 | /* now configure WoWLAN ucode */ | ||
650 | ret = iwl_alive_start(priv); | ||
651 | if (ret) | ||
652 | goto error; | ||
653 | |||
654 | memcpy(&ctx->staging, &rxon, sizeof(rxon)); | ||
655 | ret = iwlagn_commit_rxon(priv, ctx); | ||
656 | if (ret) | ||
657 | goto error; | ||
658 | |||
659 | ret = iwl_power_update_mode(priv, true); | ||
660 | if (ret) | ||
661 | goto error; | ||
662 | |||
663 | if (!iwlagn_mod_params.sw_crypto) { | ||
664 | /* mark all keys clear */ | ||
665 | priv->ucode_key_table = 0; | ||
666 | ctx->key_mapping_keys = 0; | ||
667 | |||
668 | /* | ||
669 | * This needs to be unlocked due to lock ordering | ||
670 | * constraints. Since we're in the suspend path | ||
671 | * that isn't really a problem though. | ||
672 | */ | ||
673 | mutex_unlock(&priv->shrd->mutex); | ||
674 | ieee80211_iter_keys(priv->hw, ctx->vif, | ||
675 | iwlagn_wowlan_program_keys, | ||
676 | &key_data); | ||
677 | mutex_lock(&priv->shrd->mutex); | ||
678 | if (key_data.error) { | ||
679 | ret = -EIO; | ||
680 | goto error; | ||
681 | } | ||
682 | |||
683 | if (key_data.use_rsc_tsc) { | ||
684 | struct iwl_host_cmd rsc_tsc_cmd = { | ||
685 | .id = REPLY_WOWLAN_TSC_RSC_PARAMS, | ||
686 | .flags = CMD_SYNC, | ||
687 | .data[0] = key_data.rsc_tsc, | ||
688 | .dataflags[0] = IWL_HCMD_DFL_NOCOPY, | ||
689 | .len[0] = sizeof(*key_data.rsc_tsc), | ||
690 | }; | ||
691 | |||
692 | ret = iwl_trans_send_cmd(trans(priv), &rsc_tsc_cmd); | ||
693 | if (ret) | ||
694 | goto error; | ||
695 | } | ||
696 | |||
697 | if (key_data.use_tkip) { | ||
698 | ret = iwl_trans_send_cmd_pdu(trans(priv), | ||
699 | REPLY_WOWLAN_TKIP_PARAMS, | ||
700 | CMD_SYNC, sizeof(tkip_cmd), | ||
701 | &tkip_cmd); | ||
702 | if (ret) | ||
703 | goto error; | ||
704 | } | ||
705 | |||
706 | if (priv->have_rekey_data) { | ||
707 | memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); | ||
708 | memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN); | ||
709 | kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); | ||
710 | memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN); | ||
711 | kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); | ||
712 | kek_kck_cmd.replay_ctr = priv->replay_ctr; | ||
713 | |||
714 | ret = iwl_trans_send_cmd_pdu(trans(priv), | ||
715 | REPLY_WOWLAN_KEK_KCK_MATERIAL, | ||
716 | CMD_SYNC, sizeof(kek_kck_cmd), | ||
717 | &kek_kck_cmd); | ||
718 | if (ret) | ||
719 | goto error; | ||
720 | } | ||
721 | } | ||
722 | |||
723 | ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_D3_CONFIG, CMD_SYNC, | ||
724 | sizeof(d3_cfg_cmd), &d3_cfg_cmd); | ||
725 | if (ret) | ||
726 | goto error; | ||
727 | |||
728 | ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_WOWLAN_WAKEUP_FILTER, | ||
729 | CMD_SYNC, sizeof(wakeup_filter_cmd), | ||
730 | &wakeup_filter_cmd); | ||
731 | if (ret) | ||
732 | goto error; | ||
733 | |||
734 | ret = iwlagn_send_patterns(priv, wowlan); | ||
735 | if (ret) | 393 | if (ret) |
736 | goto error; | 394 | goto error; |
737 | 395 | ||
@@ -749,7 +407,6 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw, | |||
749 | ieee80211_restart_hw(priv->hw); | 407 | ieee80211_restart_hw(priv->hw); |
750 | out: | 408 | out: |
751 | mutex_unlock(&priv->shrd->mutex); | 409 | mutex_unlock(&priv->shrd->mutex); |
752 | kfree(key_data.rsc_tsc); | ||
753 | IWL_DEBUG_MAC80211(priv, "leave\n"); | 410 | IWL_DEBUG_MAC80211(priv, "leave\n"); |
754 | 411 | ||
755 | return ret; | 412 | return ret; |