ath6kl: logical continuations should be on the previous line
[cascardo/linux.git] / drivers / net / wireless / ath / ath6kl / main.c
index eea3c74..229e192 100644 (file)
@@ -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;
 }