summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath6kl/main.c
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2012-04-14 13:18:27 +0200
committerIngo Molnar <mingo@kernel.org>2012-04-14 13:19:04 +0200
commit6ac1ef482d7ae0c690f1640bf6eb818ff9a2d91e (patch)
tree021cc9f6b477146fcebe6f3be4752abfa2ba18a9 /drivers/net/wireless/ath/ath6kl/main.c
parent682968e0c425c60f0dde37977e5beb2b12ddc4cc (diff)
parenta385ec4f11bdcf81af094c03e2444ee9b7fad2e5 (diff)
Merge branch 'perf/core' into perf/uprobes
Merge in latest upstream (and the latest perf development tree), to prepare for tooling changes, and also to pick up v3.4 MM changes that the uprobes code needs to take care of. Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'drivers/net/wireless/ath/ath6kl/main.c')
-rw-r--r--drivers/net/wireless/ath/ath6kl/main.c234
1 files changed, 204 insertions, 30 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
index eea3c747653..229e1922ebe 100644
--- a/drivers/net/wireless/ath/ath6kl/main.c
+++ b/drivers/net/wireless/ath/ath6kl/main.c
@@ -1,5 +1,6 @@
/*
* 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
@@ -52,9 +53,11 @@ struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid)
return conn;
}
-static void ath6kl_add_new_sta(struct ath6kl *ar, u8 *mac, u16 aid, u8 *wpaie,
- u8 ielen, u8 keymgmt, u8 ucipher, u8 auth)
+static void ath6kl_add_new_sta(struct ath6kl_vif *vif, u8 *mac, u16 aid,
+ u8 *wpaie, size_t ielen, u8 keymgmt,
+ u8 ucipher, u8 auth, u8 apsd_info)
{
+ struct ath6kl *ar = vif->ar;
struct ath6kl_sta *sta;
u8 free_slot;
@@ -68,18 +71,31 @@ static void ath6kl_add_new_sta(struct ath6kl *ar, u8 *mac, u16 aid, u8 *wpaie,
sta->keymgmt = keymgmt;
sta->ucipher = ucipher;
sta->auth = auth;
+ sta->apsd_info = apsd_info;
ar->sta_list_index = ar->sta_list_index | (1 << free_slot);
ar->ap_stats.sta[free_slot].aid = cpu_to_le32(aid);
+ aggr_conn_init(vif, vif->aggr_cntxt, sta->aggr_conn);
}
static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i)
{
struct ath6kl_sta *sta = &ar->sta_list[i];
+ struct ath6kl_mgmt_buff *entry, *tmp;
/* empty the queued pkts in the PS queue if any */
spin_lock_bh(&sta->psq_lock);
skb_queue_purge(&sta->psq);
+ skb_queue_purge(&sta->apsdq);
+
+ if (sta->mgmt_psq_len != 0) {
+ list_for_each_entry_safe(entry, tmp, &sta->mgmt_psq, list) {
+ kfree(entry);
+ }
+ INIT_LIST_HEAD(&sta->mgmt_psq);
+ sta->mgmt_psq_len = 0;
+ }
+
spin_unlock_bh(&sta->psq_lock);
memset(&ar->ap_stats.sta[sta->aid - 1], 0,
@@ -90,7 +106,7 @@ static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i)
sta->sta_flags = 0;
ar->sta_list_index = ar->sta_list_index & ~(1 << i);
-
+ aggr_reset_state(sta->aggr_conn);
}
static u8 ath6kl_remove_sta(struct ath6kl *ar, u8 *mac, u16 reason)
@@ -252,7 +268,7 @@ int ath6kl_read_fwlogs(struct ath6kl *ar)
struct ath6kl_dbglog_hdr debug_hdr;
struct ath6kl_dbglog_buf debug_buf;
u32 address, length, dropped, firstbuf, debug_hdr_addr;
- int ret = 0, loop;
+ int ret, loop;
u8 *buf;
buf = kmalloc(ATH6KL_FWLOG_PAYLOAD_SIZE, GFP_KERNEL);
@@ -334,7 +350,7 @@ void ath6kl_reset_device(struct ath6kl *ar, u32 target_type,
__le32 data;
if (target_type != TARGET_TYPE_AR6003 &&
- target_type != TARGET_TYPE_AR6004)
+ target_type != TARGET_TYPE_AR6004)
return;
data = cold_reset ? cpu_to_le32(RESET_CONTROL_COLD_RST) :
@@ -347,9 +363,6 @@ void ath6kl_reset_device(struct ath6kl *ar, u32 target_type,
case TARGET_TYPE_AR6004:
address = AR6004_RESET_CONTROL_ADDRESS;
break;
- default:
- address = AR6003_RESET_CONTROL_ADDRESS;
- break;
}
status = ath6kl_diag_write32(ar, address, data);
@@ -363,7 +376,7 @@ static void ath6kl_install_static_wep_keys(struct ath6kl_vif *vif)
u8 index;
u8 keyusage;
- for (index = WMI_MIN_KEY_INDEX; index <= WMI_MAX_KEY_INDEX; index++) {
+ for (index = 0; index <= WMI_MAX_KEY_INDEX; index++) {
if (vif->wep_key_list[index].key_len) {
keyusage = GROUP_USAGE;
if (index == vif->def_txkey_index)
@@ -428,9 +441,8 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel)
void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr,
u8 keymgmt, u8 ucipher, u8 auth,
- u8 assoc_req_len, u8 *assoc_info)
+ u8 assoc_req_len, u8 *assoc_info, u8 apsd_info)
{
- struct ath6kl *ar = vif->ar;
u8 *ies = NULL, *wpa_ie = NULL, *pos;
size_t ies_len = 0;
struct station_info sinfo;
@@ -484,9 +496,9 @@ void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr,
pos += 2 + pos[1];
}
- ath6kl_add_new_sta(ar, mac_addr, aid, wpa_ie,
+ ath6kl_add_new_sta(vif, mac_addr, aid, wpa_ie,
wpa_ie ? 2 + wpa_ie[1] : 0,
- keymgmt, ucipher, auth);
+ keymgmt, ucipher, auth, apsd_info);
/* send event to application */
memset(&sinfo, 0, sizeof(sinfo));
@@ -589,8 +601,7 @@ void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid,
if ((vif->nw_type == INFRA_NETWORK))
ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
- ar->listen_intvl_t,
- ar->listen_intvl_b);
+ vif->listen_intvl_t, 0);
netif_wake_queue(vif->ndev);
@@ -601,7 +612,7 @@ void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid,
netif_carrier_on(vif->ndev);
spin_unlock_bh(&vif->if_lock);
- aggr_reset_state(vif->aggr_cntxt);
+ aggr_reset_state(vif->aggr_cntxt->aggr_conn);
vif->reconnect_flag = 0;
if ((vif->nw_type == ADHOC_NETWORK) && ar->ibss_ps_enable) {
@@ -808,6 +819,7 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
struct sk_buff *skb;
bool psq_empty = false;
struct ath6kl *ar = vif->ar;
+ struct ath6kl_mgmt_buff *mgmt_buf;
conn = ath6kl_find_sta_by_aid(ar, aid);
@@ -818,7 +830,7 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
* becomes empty update the PVB for this station.
*/
spin_lock_bh(&conn->psq_lock);
- psq_empty = skb_queue_empty(&conn->psq);
+ psq_empty = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0);
spin_unlock_bh(&conn->psq_lock);
if (psq_empty)
@@ -826,15 +838,31 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
return;
spin_lock_bh(&conn->psq_lock);
- skb = skb_dequeue(&conn->psq);
- spin_unlock_bh(&conn->psq_lock);
+ if (conn->mgmt_psq_len > 0) {
+ mgmt_buf = list_first_entry(&conn->mgmt_psq,
+ struct ath6kl_mgmt_buff, list);
+ list_del(&mgmt_buf->list);
+ conn->mgmt_psq_len--;
+ spin_unlock_bh(&conn->psq_lock);
+
+ conn->sta_flags |= STA_PS_POLLED;
+ ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx,
+ mgmt_buf->id, mgmt_buf->freq,
+ mgmt_buf->wait, mgmt_buf->buf,
+ mgmt_buf->len, mgmt_buf->no_cck);
+ conn->sta_flags &= ~STA_PS_POLLED;
+ kfree(mgmt_buf);
+ } else {
+ skb = skb_dequeue(&conn->psq);
+ spin_unlock_bh(&conn->psq_lock);
- conn->sta_flags |= STA_PS_POLLED;
- ath6kl_data_tx(skb, vif->ndev);
- conn->sta_flags &= ~STA_PS_POLLED;
+ conn->sta_flags |= STA_PS_POLLED;
+ ath6kl_data_tx(skb, vif->ndev);
+ conn->sta_flags &= ~STA_PS_POLLED;
+ }
spin_lock_bh(&conn->psq_lock);
- psq_empty = skb_queue_empty(&conn->psq);
+ psq_empty = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0);
spin_unlock_bh(&conn->psq_lock);
if (psq_empty)
@@ -920,10 +948,10 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid,
}
ath6kl_cfg80211_disconnect_event(vif, reason, bssid,
- assoc_resp_len, assoc_info,
- prot_reason_status);
+ assoc_resp_len, assoc_info,
+ prot_reason_status);
- aggr_reset_state(vif->aggr_cntxt);
+ aggr_reset_state(vif->aggr_cntxt->aggr_conn);
del_timer(&vif->disconnect_timer);
@@ -941,9 +969,9 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid,
} 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))) {
+ (prot_reason_status == 0x11)) ||
+ ((reason == ASSOC_FAILED) && (prot_reason_status == 0x0) &&
+ (vif->reconnect_flag == 1))) {
set_bit(CONNECTED, &vif->flags);
return;
}
@@ -1020,11 +1048,155 @@ static struct net_device_stats *ath6kl_get_stats(struct net_device *dev)
return &vif->net_stats;
}
-static struct net_device_ops ath6kl_netdev_ops = {
+static int ath6kl_set_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct ath6kl_vif *vif = netdev_priv(dev);
+ struct ath6kl *ar = vif->ar;
+ int err = 0;
+
+ if ((features & NETIF_F_RXCSUM) &&
+ (ar->rx_meta_ver != WMI_META_VERSION_2)) {
+ ar->rx_meta_ver = WMI_META_VERSION_2;
+ err = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi,
+ vif->fw_vif_idx,
+ ar->rx_meta_ver, 0, 0);
+ if (err) {
+ dev->features = features & ~NETIF_F_RXCSUM;
+ return err;
+ }
+ } else if (!(features & NETIF_F_RXCSUM) &&
+ (ar->rx_meta_ver == WMI_META_VERSION_2)) {
+ ar->rx_meta_ver = 0;
+ err = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi,
+ vif->fw_vif_idx,
+ ar->rx_meta_ver, 0, 0);
+ if (err) {
+ dev->features = features | NETIF_F_RXCSUM;
+ return err;
+ }
+
+ }
+
+ return err;
+}
+
+static void ath6kl_set_multicast_list(struct net_device *ndev)
+{
+ struct ath6kl_vif *vif = netdev_priv(ndev);
+ bool mc_all_on = false, mc_all_off = false;
+ int mc_count = netdev_mc_count(ndev);
+ struct netdev_hw_addr *ha;
+ bool found;
+ struct ath6kl_mc_filter *mc_filter, *tmp;
+ struct list_head mc_filter_new;
+ int ret;
+
+ if (!test_bit(WMI_READY, &vif->ar->flag) ||
+ !test_bit(WLAN_ENABLED, &vif->flags))
+ return;
+
+ mc_all_on = !!(ndev->flags & IFF_PROMISC) ||
+ !!(ndev->flags & IFF_ALLMULTI) ||
+ !!(mc_count > ATH6K_MAX_MC_FILTERS_PER_LIST);
+
+ mc_all_off = !(ndev->flags & IFF_MULTICAST) || mc_count == 0;
+
+ if (mc_all_on || mc_all_off) {
+ /* Enable/disable all multicast */
+ ath6kl_dbg(ATH6KL_DBG_TRC, "%s multicast filter\n",
+ mc_all_on ? "enabling" : "disabling");
+ ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx,
+ mc_all_on);
+ if (ret)
+ ath6kl_warn("Failed to %s multicast receive\n",
+ mc_all_on ? "enable" : "disable");
+ return;
+ }
+
+ 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;
+ }
+
+ list_add_tail(&mc_filter->list, &mc_filter_new);
+ }
+ }
+
+out:
+ list_splice_tail(&mc_filter_new, &vif->mc_filter);
+}
+
+static const struct net_device_ops ath6kl_netdev_ops = {
.ndo_open = ath6kl_open,
.ndo_stop = ath6kl_close,
.ndo_start_xmit = ath6kl_data_tx,
.ndo_get_stats = ath6kl_get_stats,
+ .ndo_set_features = ath6kl_set_features,
+ .ndo_set_rx_mode = ath6kl_set_multicast_list,
};
void init_netdev(struct net_device *dev)
@@ -1038,5 +1210,7 @@ void init_netdev(struct net_device *dev)
sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH
+ WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES;
+ dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
+
return;
}