aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorWey-Yi Guy <wey-yi.w.guy@intel.com>2011-11-10 09:55:17 -0500
committerJohn W. Linville <linville@tuxdriver.com>2011-11-11 12:32:54 -0500
commit023ca58f1d025d9c210f8003ca47d6b96cdac167 (patch)
treef8fc3ffed8d1d3a6f68e8555d30071f423619766 /drivers
parent76b2933111afe5a04e342040436a90c31c7661d4 (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.c357
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.h6
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-mac80211.c347
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
989static 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
997struct 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
1006static 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
1133int 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
1175int 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);
119int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control); 119int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control);
120void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control); 120void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control);
121int iwlagn_send_beacon_cmd(struct iwl_priv *priv); 121int iwlagn_send_beacon_cmd(struct iwl_priv *priv);
122#ifdef CONFIG_PM_SLEEP
123int iwlagn_send_patterns(struct iwl_priv *priv,
124 struct cfg80211_wowlan *wowlan);
125int iwlagn_suspend(struct iwl_priv *priv,
126 struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
127#endif
122 128
123/* rx */ 129/* rx */
124int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); 130int 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
371struct 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
379static 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
387static 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
513static 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
555static int iwlagn_mac_suspend(struct ieee80211_hw *hw, 372static 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;