/* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* * Read from the hardware through its diagnostic window. No cooperation * from the firmware is required for this.
*/ int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value)
{ int ret;
ret = ath6kl_hif_diag_read32(ar, address, value); if (ret) {
ath6kl_warn("failed to read32 through diagnose window: %d\n",
ret); return ret;
}
return 0;
}
/* * Write to the ATH6KL through its diagnostic window. No cooperation from * the Target is required for this.
*/ int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value)
{ int ret;
ret = ath6kl_hif_diag_write32(ar, address, value);
if (ret) {
ath6kl_err("failed to write 0x%x during diagnose window to 0x%x\n",
address, value); return ret;
}
return 0;
}
int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length)
{
u32 count, *buf = data; int ret;
if (WARN_ON(length % 4)) return -EINVAL;
for (count = 0; count < length / 4; count++, address += 4) {
ret = ath6kl_diag_read32(ar, address, &buf[count]); if (ret) return ret;
}
return 0;
}
int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length)
{
u32 count;
__le32 *buf = data; int ret;
if (WARN_ON(length % 4)) return -EINVAL;
for (count = 0; count < length / 4; count++, address += 4) {
ret = ath6kl_diag_write32(ar, address, buf[count]); if (ret) return ret;
}
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "AP mode started on %u MHz\n", channel);
switch (vif->auth_mode) { case NONE_AUTH: if (vif->prwise_crypto == WEP_CRYPT)
ath6kl_install_static_wep_keys(vif); if (!ik->valid || ik->key_type != WAPI_CRYPT) break; /* for WAPI, we need to set the delayed group key, continue: */
fallthrough; case WPA_PSK_AUTH: case WPA2_PSK_AUTH: case (WPA_PSK_AUTH | WPA2_PSK_AUTH): if (!ik->valid) break;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed addkey for the initial group key for AP mode\n");
memset(key_rsc, 0, sizeof(key_rsc));
res = ath6kl_wmi_addkey_cmd(
ar->wmi, vif->fw_vif_idx, ik->key_index, ik->key_type,
GROUP_USAGE, ik->key_len, key_rsc, ATH6KL_KEY_SEQ_LEN,
ik->key,
KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG); if (res) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed addkey failed: %d\n", res);
} break;
}
if (ar->last_ch != channel) /* we actually don't know the phymode, default to HT20 */
ath6kl_cfg80211_ch_switch_notify(vif, channel, WMI_11G_HT20);
pos = ies; while (pos && pos + 1 < ies + ies_len) { if (pos + 2 + pos[1] > ies + ies_len) break; if (pos[0] == WLAN_EID_RSN)
wpa_ie = pos; /* RSN IE */ elseif (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
pos[1] >= 4 &&
pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2) { if (pos[5] == 0x01)
wpa_ie = pos; /* WPA IE */ elseif (pos[5] == 0x04) {
wpa_ie = pos; /* WPS IE */ break; /* overrides WPA/RSN IE */
}
} elseif (pos[0] == 0x44 && wpa_ie == NULL) { /* * Note: WAPI Parameter Set IE re-uses Element ID that * was officially allocated for BSS AC Access Delay. As * such, we need to be a bit more careful on when * parsing the frame. However, BSS AC Access Delay * element is not supposed to be included in * (Re)Association Request frames, so this should not * cause problems.
*/
wpa_ie = pos; /* WAPI IE */ break;
}
pos += 2 + pos[1];
}
switch (vif->nw_type) { case AP_NETWORK: /* * reconfigure any saved RSN IE capabilities in the beacon / * probe response to stay in sync with the supplicant.
*/ if (vif->rsn_capab &&
test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
ar->fw_capabilities))
ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx,
WLAN_EID_RSN, WMI_RSN_IE_CAPB,
(const u8 *) &vif->rsn_capab, sizeof(vif->rsn_capab));
/* * For AP case, keyid will have aid of STA which sent pkt with * MIC error. Use this aid to get MAC & send it to hostapd.
*/ if (vif->nw_type == AP_NETWORK) {
sta = ath6kl_find_sta_by_aid(ar, (keyid >> 2)); if (!sta) return;
ath6kl_dbg(ATH6KL_DBG_TRC, "ap tkip mic error received from aid=%d\n", keyid);
if (!conn) return; /* * Send out a packet queued on ps queue. When the ps queue * becomes empty update the PVB for this station.
*/
spin_lock_bh(&conn->psq_lock);
psq_empty = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0);
spin_unlock_bh(&conn->psq_lock);
if (psq_empty) /* TODO: Send out a NULL data frame */ return;
/* * If there are no associated STAs, ignore the DTIM expiry event. * There can be potential race conditions where the last associated * STA may disconnect & before the host could clear the 'Indicate * DTIM' request to the firmware, the firmware would have just * indicated a DTIM expiry event. The race is between 'clear DTIM * expiry cmd' going from the host to the firmware & the DTIM * expiry event happening from the firmware to the host.
*/ if (!ar->sta_list_index) return;
if (vif->nw_type == AP_NETWORK) { /* disconnect due to other STA vif switching channels */ if (reason == BSS_DISCONNECTED &&
prot_reason_status == WMI_AP_REASON_STA_ROAM) {
ar->want_ch_switch |= 1 << vif->fw_vif_idx; /* bail back to this channel if STA vif fails connect */
ar->last_ch = le16_to_cpu(vif->profile.ch);
}
if (prot_reason_status == WMI_AP_REASON_MAX_STA) { /* send max client reached notification to user space */
cfg80211_conn_failed(vif->ndev, bssid,
NL80211_CONN_FAIL_MAX_CLIENTS,
GFP_KERNEL);
}
if (prot_reason_status == WMI_AP_REASON_ACL) { /* send blocked client notification to user space */
cfg80211_conn_failed(vif->ndev, bssid,
NL80211_CONN_FAIL_BLOCKED_CLIENT,
GFP_KERNEL);
}
if (!ath6kl_remove_sta(ar, bssid, prot_reason_status)) return;
/* if no more associated STAs, empty the mcast PS q */ if (ar->sta_list_index == 0) {
spin_lock_bh(&ar->mcastpsq_lock);
skb_queue_purge(&ar->mcastpsq);
spin_unlock_bh(&ar->mcastpsq_lock);
/* clear the LSB of the TIM IE's BitMapCtl field */ if (test_bit(WMI_READY, &ar->flag))
ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
MCAST_AID, 0);
}
if (!is_broadcast_ether_addr(bssid)) { /* send event to application */
cfg80211_del_sta(vif->ndev, bssid, GFP_KERNEL);
}
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "disconnect reason is %d\n", reason);
/* * If the event is due to disconnect cmd from the host, only they * the target would stop trying to connect. Under any other * condition, target would keep trying to connect.
*/ if (reason == DISCONNECT_CMD) { if (!ar->usr_bss_filter && test_bit(WMI_READY, &ar->flag))
ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
NONE_BSS_FILTER, 0);
} else {
set_bit(CONNECT_PEND, &vif->flags); if (((reason == ASSOC_FAILED) &&
(prot_reason_status == 0x11)) ||
((reason == ASSOC_FAILED) && (prot_reason_status == 0x0) &&
(vif->reconnect_flag == 1))) {
set_bit(CONNECTED, &vif->flags); return;
}
}
/* restart disconnected concurrent vifs waiting for new channel */
ath6kl_check_ch_switch(ar, ar->last_ch);
/* update connect & link status atomically */
spin_lock_bh(&vif->if_lock);
clear_bit(CONNECTED, &vif->flags);
netif_carrier_off(vif->ndev);
spin_unlock_bh(&vif->if_lock);
ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx,
mc_all_on); if (ret) {
ath6kl_warn("Failed to %s multicast-all receive\n",
mc_all_on ? "enable" : "disable"); return;
}
if (test_bit(NETDEV_MCAST_ALL_ON, &vif->flags)) return;
/* Keep the driver and firmware mcast list in sync. */
list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
found = false;
netdev_for_each_mc_addr(ha, ndev) { if (memcmp(ha->addr, mc_filter->hw_addr,
ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) {
found = true; break;
}
}
if (!found) { /* * Delete the filter which was previously set * but not in the new request.
*/
ath6kl_dbg(ATH6KL_DBG_TRC, "Removing %pM from multicast filter\n",
mc_filter->hw_addr);
ret = ath6kl_wmi_add_del_mcast_filter_cmd(vif->ar->wmi,
vif->fw_vif_idx, mc_filter->hw_addr, false); if (ret) {
ath6kl_warn("Failed to remove multicast filter:%pM\n",
mc_filter->hw_addr); return;
}
list_del(&mc_filter->list);
kfree(mc_filter);
}
}
INIT_LIST_HEAD(&mc_filter_new);
netdev_for_each_mc_addr(ha, ndev) {
found = false;
list_for_each_entry(mc_filter, &vif->mc_filter, list) { if (memcmp(ha->addr, mc_filter->hw_addr,
ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE) == 0) {
found = true; break;
}
}
if (!found) {
mc_filter = kzalloc(sizeof(struct ath6kl_mc_filter),
GFP_ATOMIC); if (!mc_filter) {
WARN_ON(1); goto out;
}
memcpy(mc_filter->hw_addr, ha->addr,
ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE); /* Set the multicast filter */
ath6kl_dbg(ATH6KL_DBG_TRC, "Adding %pM to multicast filter list\n",
mc_filter->hw_addr);
ret = ath6kl_wmi_add_del_mcast_filter_cmd(vif->ar->wmi,
vif->fw_vif_idx, mc_filter->hw_addr, true); if (ret) {
ath6kl_warn("Failed to add multicast filter :%pM\n",
mc_filter->hw_addr);
kfree(mc_filter); goto out;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.