int iwm_send_pmkid_update(struct iwm_priv *iwm, struct cfg80211_pmksa *pmksa, u32 command) { struct iwm_umac_pmkid_update update; int ret; memset(&update, 0, sizeof(struct iwm_umac_pmkid_update)); update.hdr.oid = UMAC_WIFI_IF_CMD_PMKID_UPDATE; update.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_pmkid_update) - sizeof(struct iwm_umac_wifi_if)); update.command = cpu_to_le32(command); if (pmksa->bssid) memcpy(&update.bssid, pmksa->bssid, ETH_ALEN); if (pmksa->pmkid) memcpy(&update.pmkid, pmksa->pmkid, WLAN_PMKID_LEN); ret = iwm_send_wifi_if_cmd(iwm, &update, sizeof(struct iwm_umac_pmkid_update), 0); if (ret) { IWM_ERR(iwm, "PMKID update command failed\n"); return ret; } return 0; }
int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, int ssid_num) { struct iwm_umac_cmd_scan_request req; int i, ret; memset(&req, 0, sizeof(struct iwm_umac_cmd_scan_request)); req.hdr.oid = UMAC_WIFI_IF_CMD_SCAN_REQUEST; req.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_cmd_scan_request) - sizeof(struct iwm_umac_wifi_if)); req.type = UMAC_WIFI_IF_SCAN_TYPE_USER; req.timeout = 2; req.seq_num = iwm->scan_id; req.ssid_num = min(ssid_num, UMAC_WIFI_IF_PROBE_OPTION_MAX); for (i = 0; i < req.ssid_num; i++) { memcpy(req.ssids[i].ssid, ssids[i].ssid, ssids[i].ssid_len); req.ssids[i].ssid_len = ssids[i].ssid_len; } ret = iwm_send_wifi_if_cmd(iwm, &req, sizeof(req), 0); if (ret < 0) { IWM_ERR(iwm, "Couldn't send scan request\n"); return ret; } iwm->scan_id = iwm->scan_id++ % IWM_SCAN_ID_MAX; return 0; }
int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx) { struct iwm_umac_tx_key_id tx_key_id; int ret; ret = iwm_check_profile(iwm); if (ret < 0) return ret; /* */ if ((iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_40 && iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_104) || iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_8021X || iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_RSNA_PSK) return 0; tx_key_id.hdr.oid = UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID; tx_key_id.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_tx_key_id) - sizeof(struct iwm_umac_wifi_if)); tx_key_id.key_idx = key_idx; return iwm_send_wifi_if_cmd(iwm, &tx_key_id, sizeof(tx_key_id), 1); }
int iwm_tx_power_trigger(struct iwm_priv *iwm) { struct iwm_umac_pwr_trigger pwr_trigger; pwr_trigger.hdr.oid = UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER; pwr_trigger.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_pwr_trigger) - sizeof(struct iwm_umac_wifi_if)); return iwm_send_wifi_if_cmd(iwm, &pwr_trigger, sizeof(pwr_trigger), 1); }
int __iwm_invalidate_mlme_profile(struct iwm_priv *iwm) { struct iwm_umac_invalidate_profile invalid; invalid.hdr.oid = UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE; invalid.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_invalidate_profile) - sizeof(struct iwm_umac_wifi_if)); invalid.reason = WLAN_REASON_UNSPECIFIED; return iwm_send_wifi_if_cmd(iwm, &invalid, sizeof(invalid), 1); }
int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx) { struct iwm_umac_tx_key_id tx_key_id; if (!iwm->default_key || !iwm->default_key->in_use) return -EINVAL; tx_key_id.hdr.oid = UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID; tx_key_id.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_tx_key_id) - sizeof(struct iwm_umac_wifi_if)); tx_key_id.key_idx = key_idx; return iwm_send_wifi_if_cmd(iwm, &tx_key_id, sizeof(tx_key_id), 1); }
int iwm_send_mlme_profile(struct iwm_priv *iwm) { int ret, i; struct iwm_umac_profile profile; memcpy(&profile, iwm->umac_profile, sizeof(profile)); profile.hdr.oid = UMAC_WIFI_IF_CMD_SET_PROFILE; profile.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_profile) - sizeof(struct iwm_umac_wifi_if)); ret = iwm_send_wifi_if_cmd(iwm, &profile, sizeof(profile), 1); if (ret < 0) { IWM_ERR(iwm, "Send profile command failed\n"); return ret; } /* Wait for the profile to be active */ ret = wait_event_interruptible_timeout(iwm->mlme_queue, iwm->umac_profile_active == 1, 3 * HZ); if (!ret) return -EBUSY; for (i = 0; i < IWM_NUM_KEYS; i++) if (iwm->keys[i].in_use) { int default_key = 0; struct iwm_key *key = &iwm->keys[i]; if (key == iwm->default_key) default_key = 1; /* Wait for the profile before sending the keys */ wait_event_interruptible_timeout(iwm->mlme_queue, (test_bit(IWM_STATUS_ASSOCIATING, &iwm->status) || test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)), 3 * HZ); ret = iwm_set_key(iwm, 0, default_key, key); if (ret < 0) return ret; } return 0; }
int iwm_send_mlme_profile(struct iwm_priv *iwm) { int ret; struct iwm_umac_profile profile; memcpy(&profile, iwm->umac_profile, sizeof(profile)); profile.hdr.oid = UMAC_WIFI_IF_CMD_SET_PROFILE; profile.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_profile) - sizeof(struct iwm_umac_wifi_if)); ret = iwm_send_wifi_if_cmd(iwm, &profile, sizeof(profile), 1); if (ret) { IWM_ERR(iwm, "Send profile command failed\n"); return ret; } set_bit(IWM_STATUS_SME_CONNECTING, &iwm->status); return 0; }
int iwm_invalidate_mlme_profile(struct iwm_priv *iwm) { int ret; struct iwm_umac_invalidate_profile invalid; invalid.hdr.oid = UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE; invalid.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_invalidate_profile) - sizeof(struct iwm_umac_wifi_if)); invalid.reason = WLAN_REASON_UNSPECIFIED; ret = iwm_send_wifi_if_cmd(iwm, &invalid, sizeof(invalid), 1); if (ret < 0) return ret; ret = wait_event_interruptible_timeout(iwm->mlme_queue, (iwm->umac_profile_active == 0), 2 * HZ); if (!ret) return -EBUSY; return 0; }
int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key, struct iwm_key *key) { int ret; u8 cmd[64], *sta_addr, *key_data, key_len; s8 key_idx; u16 cmd_size = 0; struct iwm_umac_key_hdr *key_hdr = &key->hdr; struct iwm_umac_key_wep40 *wep40 = (struct iwm_umac_key_wep40 *)cmd; struct iwm_umac_key_wep104 *wep104 = (struct iwm_umac_key_wep104 *)cmd; struct iwm_umac_key_tkip *tkip = (struct iwm_umac_key_tkip *)cmd; struct iwm_umac_key_ccmp *ccmp = (struct iwm_umac_key_ccmp *)cmd; if (set_tx_key) iwm->default_key = key; /* * We check if our current profile is valid. * If not, we dont push the key, we just cache them, * so that with the next siwsessid call, the keys * will be actually pushed. */ if (!remove) { ret = iwm_check_profile(iwm); if (ret < 0) return ret; } sta_addr = key->hdr.mac; key_data = key->key; key_len = key->key_len; key_idx = key->hdr.key_idx; if (!remove) { IWM_DBG_WEXT(iwm, DBG, "key_idx:%d set tx key:%d\n", key_idx, set_tx_key); IWM_DBG_WEXT(iwm, DBG, "key_len:%d\n", key_len); IWM_DBG_WEXT(iwm, DBG, "MAC:%pM, idx:%d, multicast:%d\n", key_hdr->mac, key_hdr->key_idx, key_hdr->multicast); IWM_DBG_WEXT(iwm, DBG, "profile: mcast:0x%x, ucast:0x%x\n", iwm->umac_profile->sec.mcast_cipher, iwm->umac_profile->sec.ucast_cipher); IWM_DBG_WEXT(iwm, DBG, "profile: auth_type:0x%x, flags:0x%x\n", iwm->umac_profile->sec.auth_type, iwm->umac_profile->sec.flags); switch (key->alg) { case UMAC_CIPHER_TYPE_WEP_40: wep40->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP40_KEY; wep40->hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_key_wep40) - sizeof(struct iwm_umac_wifi_if)); memcpy(&wep40->key_hdr, key_hdr, sizeof(struct iwm_umac_key_hdr)); memcpy(wep40->key, key_data, key_len); wep40->static_key = 1; cmd_size = sizeof(struct iwm_umac_key_wep40); break; case UMAC_CIPHER_TYPE_WEP_104: wep104->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP104_KEY; wep104->hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_key_wep104) - sizeof(struct iwm_umac_wifi_if)); memcpy(&wep104->key_hdr, key_hdr, sizeof(struct iwm_umac_key_hdr)); memcpy(wep104->key, key_data, key_len); wep104->static_key = 1; cmd_size = sizeof(struct iwm_umac_key_wep104); break; case UMAC_CIPHER_TYPE_CCMP: key_hdr->key_idx++; ccmp->hdr.oid = UMAC_WIFI_IF_CMD_ADD_CCMP_KEY; ccmp->hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_key_ccmp) - sizeof(struct iwm_umac_wifi_if)); memcpy(&ccmp->key_hdr, key_hdr, sizeof(struct iwm_umac_key_hdr)); memcpy(ccmp->key, key_data, key_len); if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID) memcpy(ccmp->iv_count, key->rx_seq, 6); cmd_size = sizeof(struct iwm_umac_key_ccmp); break; case UMAC_CIPHER_TYPE_TKIP: key_hdr->key_idx++; tkip->hdr.oid = UMAC_WIFI_IF_CMD_ADD_TKIP_KEY; tkip->hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_key_tkip) - sizeof(struct iwm_umac_wifi_if)); memcpy(&tkip->key_hdr, key_hdr, sizeof(struct iwm_umac_key_hdr)); memcpy(tkip->tkip_key, key_data, IWM_TKIP_KEY_SIZE); memcpy(tkip->mic_tx_key, key_data + IWM_TKIP_KEY_SIZE, IWM_TKIP_MIC_SIZE); memcpy(tkip->mic_rx_key, key_data + IWM_TKIP_KEY_SIZE + IWM_TKIP_MIC_SIZE, IWM_TKIP_MIC_SIZE); if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID) memcpy(ccmp->iv_count, key->rx_seq, 6); cmd_size = sizeof(struct iwm_umac_key_tkip); break; default: return -ENOTSUPP; } if ((key->alg == UMAC_CIPHER_TYPE_CCMP) || (key->alg == UMAC_CIPHER_TYPE_TKIP)) /* * UGLY_UGLY_UGLY * Copied HACK from the MWG driver. * Without it, the key is set before the second * EAPOL frame is sent, and the latter is thus * encrypted. */ schedule_timeout_interruptible(usecs_to_jiffies(300)); ret = iwm_send_wifi_if_cmd(iwm, cmd, cmd_size, 1); if (ret < 0) goto err; /* * We need a default key only if it is set and * if we're doing WEP. */ if (iwm->default_key == key && ((key->alg == UMAC_CIPHER_TYPE_WEP_40) || (key->alg == UMAC_CIPHER_TYPE_WEP_104))) { ret = iwm_set_tx_key(iwm, key_idx); if (ret < 0) goto err; } } else { struct iwm_umac_key_remove key_remove; key_remove.hdr.oid = UMAC_WIFI_IF_CMD_REMOVE_KEY; key_remove.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_key_remove) - sizeof(struct iwm_umac_wifi_if)); memcpy(&key_remove.key_hdr, key_hdr, sizeof(struct iwm_umac_key_hdr)); ret = iwm_send_wifi_if_cmd(iwm, &key_remove, sizeof(struct iwm_umac_key_remove), 1); if (ret < 0) return ret; iwm->keys[key_idx].in_use = 0; } return 0; err: kfree(key); return ret; }
int iwm_set_key(struct iwm_priv *iwm, bool remove, struct iwm_key *key) { int ret = 0; u8 cmd[64], *sta_addr, *key_data, key_len; s8 key_idx; u16 cmd_size = 0; struct iwm_umac_key_hdr *key_hdr = &key->hdr; struct iwm_umac_key_wep40 *wep40 = (struct iwm_umac_key_wep40 *)cmd; struct iwm_umac_key_wep104 *wep104 = (struct iwm_umac_key_wep104 *)cmd; struct iwm_umac_key_tkip *tkip = (struct iwm_umac_key_tkip *)cmd; struct iwm_umac_key_ccmp *ccmp = (struct iwm_umac_key_ccmp *)cmd; if (!remove) { ret = iwm_check_profile(iwm); if (ret < 0) return ret; } sta_addr = key->hdr.mac; key_data = key->key; key_len = key->key_len; key_idx = key->hdr.key_idx; if (!remove) { u8 auth_type = iwm->umac_profile->sec.auth_type; IWM_DBG_WEXT(iwm, DBG, "key_idx:%d\n", key_idx); IWM_DBG_WEXT(iwm, DBG, "key_len:%d\n", key_len); IWM_DBG_WEXT(iwm, DBG, "MAC:%pM, idx:%d, multicast:%d\n", key_hdr->mac, key_hdr->key_idx, key_hdr->multicast); IWM_DBG_WEXT(iwm, DBG, "profile: mcast:0x%x, ucast:0x%x\n", iwm->umac_profile->sec.mcast_cipher, iwm->umac_profile->sec.ucast_cipher); IWM_DBG_WEXT(iwm, DBG, "profile: auth_type:0x%x, flags:0x%x\n", iwm->umac_profile->sec.auth_type, iwm->umac_profile->sec.flags); switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: wep40->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP40_KEY; wep40->hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_key_wep40) - sizeof(struct iwm_umac_wifi_if)); memcpy(&wep40->key_hdr, key_hdr, sizeof(struct iwm_umac_key_hdr)); memcpy(wep40->key, key_data, key_len); wep40->static_key = !!((auth_type != UMAC_AUTH_TYPE_8021X) && (auth_type != UMAC_AUTH_TYPE_RSNA_PSK)); cmd_size = sizeof(struct iwm_umac_key_wep40); break; case WLAN_CIPHER_SUITE_WEP104: wep104->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP104_KEY; wep104->hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_key_wep104) - sizeof(struct iwm_umac_wifi_if)); memcpy(&wep104->key_hdr, key_hdr, sizeof(struct iwm_umac_key_hdr)); memcpy(wep104->key, key_data, key_len); wep104->static_key = !!((auth_type != UMAC_AUTH_TYPE_8021X) && (auth_type != UMAC_AUTH_TYPE_RSNA_PSK)); cmd_size = sizeof(struct iwm_umac_key_wep104); break; case WLAN_CIPHER_SUITE_CCMP: key_hdr->key_idx++; ccmp->hdr.oid = UMAC_WIFI_IF_CMD_ADD_CCMP_KEY; ccmp->hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_key_ccmp) - sizeof(struct iwm_umac_wifi_if)); memcpy(&ccmp->key_hdr, key_hdr, sizeof(struct iwm_umac_key_hdr)); memcpy(ccmp->key, key_data, key_len); if (key->seq_len) memcpy(ccmp->iv_count, key->seq, key->seq_len); cmd_size = sizeof(struct iwm_umac_key_ccmp); break; case WLAN_CIPHER_SUITE_TKIP: key_hdr->key_idx++; tkip->hdr.oid = UMAC_WIFI_IF_CMD_ADD_TKIP_KEY; tkip->hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_key_tkip) - sizeof(struct iwm_umac_wifi_if)); memcpy(&tkip->key_hdr, key_hdr, sizeof(struct iwm_umac_key_hdr)); memcpy(tkip->tkip_key, key_data, IWM_TKIP_KEY_SIZE); memcpy(tkip->mic_tx_key, key_data + IWM_TKIP_KEY_SIZE, IWM_TKIP_MIC_SIZE); memcpy(tkip->mic_rx_key, key_data + IWM_TKIP_KEY_SIZE + IWM_TKIP_MIC_SIZE, IWM_TKIP_MIC_SIZE); if (key->seq_len) memcpy(ccmp->iv_count, key->seq, key->seq_len); cmd_size = sizeof(struct iwm_umac_key_tkip); break; default: return -ENOTSUPP; } if ((key->cipher == WLAN_CIPHER_SUITE_TKIP) || (key->cipher == WLAN_CIPHER_SUITE_CCMP)) /* */ schedule_timeout_interruptible(usecs_to_jiffies(300)); ret = iwm_send_wifi_if_cmd(iwm, cmd, cmd_size, 1); } else { struct iwm_umac_key_remove key_remove; IWM_DBG_WEXT(iwm, ERR, "Removing key_idx:%d\n", key_idx); key_remove.hdr.oid = UMAC_WIFI_IF_CMD_REMOVE_KEY; key_remove.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_key_remove) - sizeof(struct iwm_umac_wifi_if)); memcpy(&key_remove.key_hdr, key_hdr, sizeof(struct iwm_umac_key_hdr)); ret = iwm_send_wifi_if_cmd(iwm, &key_remove, sizeof(struct iwm_umac_key_remove), 1); if (ret) return ret; iwm->keys[key_idx].key_len = 0; } return ret; }