diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-agn-lib.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 1a52ed29f2d6..0bc962217351 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c | |||
@@ -827,6 +827,7 @@ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) | |||
827 | case IEEE80211_SMPS_STATIC: | 827 | case IEEE80211_SMPS_STATIC: |
828 | case IEEE80211_SMPS_DYNAMIC: | 828 | case IEEE80211_SMPS_DYNAMIC: |
829 | return IWL_NUM_IDLE_CHAINS_SINGLE; | 829 | return IWL_NUM_IDLE_CHAINS_SINGLE; |
830 | case IEEE80211_SMPS_AUTOMATIC: | ||
830 | case IEEE80211_SMPS_OFF: | 831 | case IEEE80211_SMPS_OFF: |
831 | return active_cnt; | 832 | return active_cnt; |
832 | default: | 833 | default: |
@@ -983,3 +984,360 @@ void iwlagn_remove_notification(struct iwl_priv *priv, | |||
983 | list_del(&wait_entry->list); | 984 | list_del(&wait_entry->list); |
984 | spin_unlock_bh(&priv->notif_wait_lock); | 985 | spin_unlock_bh(&priv->notif_wait_lock); |
985 | } | 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 | ||