linux/drivers/net/wireless/mwifiex/uap_event.c
<<
>>
Prefs
   1/*
   2 * Marvell Wireless LAN device driver: AP event handling
   3 *
   4 * Copyright (C) 2012, Marvell International Ltd.
   5 *
   6 * This software file (the "File") is distributed by Marvell International
   7 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
   8 * (the "License").  You may use, redistribute and/or modify this File in
   9 * accordance with the terms and conditions of the License, a copy of which
  10 * is available by writing to the Free Software Foundation, Inc.,
  11 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
  12 * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
  13 *
  14 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
  15 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
  16 * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
  17 * this warranty disclaimer.
  18 */
  19
  20#include "decl.h"
  21#include "main.h"
  22#include "11n.h"
  23
  24/*
  25 * This function will return the pointer to station entry in station list
  26 * table which matches specified mac address.
  27 * This function should be called after acquiring RA list spinlock.
  28 * NULL is returned if station entry is not found in associated STA list.
  29 */
  30struct mwifiex_sta_node *
  31mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac)
  32{
  33        struct mwifiex_sta_node *node;
  34
  35        if (!mac)
  36                return NULL;
  37
  38        list_for_each_entry(node, &priv->sta_list, list) {
  39                if (!memcmp(node->mac_addr, mac, ETH_ALEN))
  40                        return node;
  41        }
  42
  43        return NULL;
  44}
  45
  46/*
  47 * This function will add a sta_node entry to associated station list
  48 * table with the given mac address.
  49 * If entry exist already, existing entry is returned.
  50 * If received mac address is NULL, NULL is returned.
  51 */
  52static struct mwifiex_sta_node *
  53mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac)
  54{
  55        struct mwifiex_sta_node *node;
  56        unsigned long flags;
  57
  58        if (!mac)
  59                return NULL;
  60
  61        spin_lock_irqsave(&priv->sta_list_spinlock, flags);
  62        node = mwifiex_get_sta_entry(priv, mac);
  63        if (node)
  64                goto done;
  65
  66        node = kzalloc(sizeof(struct mwifiex_sta_node), GFP_ATOMIC);
  67        if (!node)
  68                goto done;
  69
  70        memcpy(node->mac_addr, mac, ETH_ALEN);
  71        list_add_tail(&node->list, &priv->sta_list);
  72
  73done:
  74        spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
  75        return node;
  76}
  77
  78/*
  79 * This function will search for HT IE in association request IEs
  80 * and set station HT parameters accordingly.
  81 */
  82static void
  83mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
  84                       int ies_len, struct mwifiex_sta_node *node)
  85{
  86        const struct ieee80211_ht_cap *ht_cap;
  87
  88        if (!ies)
  89                return;
  90
  91        ht_cap = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len);
  92        if (ht_cap) {
  93                node->is_11n_enabled = 1;
  94                node->max_amsdu = le16_to_cpu(ht_cap->cap_info) &
  95                                  IEEE80211_HT_CAP_MAX_AMSDU ?
  96                                  MWIFIEX_TX_DATA_BUF_SIZE_8K :
  97                                  MWIFIEX_TX_DATA_BUF_SIZE_4K;
  98        } else {
  99                node->is_11n_enabled = 0;
 100        }
 101
 102        return;
 103}
 104
 105/*
 106 * This function will delete a station entry from station list
 107 */
 108static void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac)
 109{
 110        struct mwifiex_sta_node *node, *tmp;
 111        unsigned long flags;
 112
 113        spin_lock_irqsave(&priv->sta_list_spinlock, flags);
 114
 115        node = mwifiex_get_sta_entry(priv, mac);
 116        if (node) {
 117                list_for_each_entry_safe(node, tmp, &priv->sta_list,
 118                                         list) {
 119                        list_del(&node->list);
 120                        kfree(node);
 121                }
 122        }
 123
 124        spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
 125        return;
 126}
 127
 128/*
 129 * This function will delete all stations from associated station list.
 130 */
 131static void mwifiex_del_all_sta_list(struct mwifiex_private *priv)
 132{
 133        struct mwifiex_sta_node *node, *tmp;
 134        unsigned long flags;
 135
 136        spin_lock_irqsave(&priv->sta_list_spinlock, flags);
 137
 138        list_for_each_entry_safe(node, tmp, &priv->sta_list, list) {
 139                list_del(&node->list);
 140                kfree(node);
 141        }
 142
 143        INIT_LIST_HEAD(&priv->sta_list);
 144        spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
 145        return;
 146}
 147
 148/*
 149 * This function handles AP interface specific events generated by firmware.
 150 *
 151 * Event specific routines are called by this function based
 152 * upon the generated event cause.
 153 *
 154 *
 155 * Events supported for AP -
 156 *      - EVENT_UAP_STA_ASSOC
 157 *      - EVENT_UAP_STA_DEAUTH
 158 *      - EVENT_UAP_BSS_ACTIVE
 159 *      - EVENT_UAP_BSS_START
 160 *      - EVENT_UAP_BSS_IDLE
 161 *      - EVENT_UAP_MIC_COUNTERMEASURES:
 162 */
 163int mwifiex_process_uap_event(struct mwifiex_private *priv)
 164{
 165        struct mwifiex_adapter *adapter = priv->adapter;
 166        int len, i;
 167        u32 eventcause = adapter->event_cause;
 168        struct station_info sinfo;
 169        struct mwifiex_assoc_event *event;
 170        struct mwifiex_sta_node *node;
 171        u8 *deauth_mac;
 172        struct host_cmd_ds_11n_batimeout *ba_timeout;
 173        u16 ctrl;
 174
 175        switch (eventcause) {
 176        case EVENT_UAP_STA_ASSOC:
 177                memset(&sinfo, 0, sizeof(sinfo));
 178                event = (struct mwifiex_assoc_event *)
 179                        (adapter->event_body + MWIFIEX_UAP_EVENT_EXTRA_HEADER);
 180                if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) {
 181                        len = -1;
 182
 183                        if (ieee80211_is_assoc_req(event->frame_control))
 184                                len = 0;
 185                        else if (ieee80211_is_reassoc_req(event->frame_control))
 186                                /* There will be ETH_ALEN bytes of
 187                                 * current_ap_addr before the re-assoc ies.
 188                                 */
 189                                len = ETH_ALEN;
 190
 191                        if (len != -1) {
 192                                sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
 193                                sinfo.assoc_req_ies = &event->data[len];
 194                                len = (u8 *)sinfo.assoc_req_ies -
 195                                      (u8 *)&event->frame_control;
 196                                sinfo.assoc_req_ies_len =
 197                                        le16_to_cpu(event->len) - (u16)len;
 198                        }
 199                }
 200                cfg80211_new_sta(priv->netdev, event->sta_addr, &sinfo,
 201                                 GFP_KERNEL);
 202
 203                node = mwifiex_add_sta_entry(priv, event->sta_addr);
 204                if (!node) {
 205                        dev_warn(adapter->dev,
 206                                 "could not create station entry!\n");
 207                        return -1;
 208                }
 209
 210                if (!priv->ap_11n_enabled)
 211                        break;
 212
 213                mwifiex_set_sta_ht_cap(priv, sinfo.assoc_req_ies,
 214                                       sinfo.assoc_req_ies_len, node);
 215
 216                for (i = 0; i < MAX_NUM_TID; i++) {
 217                        if (node->is_11n_enabled)
 218                                node->ampdu_sta[i] =
 219                                              priv->aggr_prio_tbl[i].ampdu_user;
 220                        else
 221                                node->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED;
 222                }
 223                memset(node->rx_seq, 0xff, sizeof(node->rx_seq));
 224                break;
 225        case EVENT_UAP_STA_DEAUTH:
 226                deauth_mac = adapter->event_body +
 227                             MWIFIEX_UAP_EVENT_EXTRA_HEADER;
 228                cfg80211_del_sta(priv->netdev, deauth_mac, GFP_KERNEL);
 229
 230                if (priv->ap_11n_enabled) {
 231                        mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac);
 232                        mwifiex_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac);
 233                }
 234                mwifiex_del_sta_entry(priv, deauth_mac);
 235                break;
 236        case EVENT_UAP_BSS_IDLE:
 237                priv->media_connected = false;
 238                mwifiex_clean_txrx(priv);
 239                mwifiex_del_all_sta_list(priv);
 240                break;
 241        case EVENT_UAP_BSS_ACTIVE:
 242                priv->media_connected = true;
 243                break;
 244        case EVENT_UAP_BSS_START:
 245                dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause);
 246                memcpy(priv->netdev->dev_addr, adapter->event_body + 2,
 247                       ETH_ALEN);
 248                break;
 249        case EVENT_UAP_MIC_COUNTERMEASURES:
 250                /* For future development */
 251                dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause);
 252                break;
 253        case EVENT_AMSDU_AGGR_CTRL:
 254                ctrl = le16_to_cpu(*(__le16 *)adapter->event_body);
 255                dev_dbg(adapter->dev, "event: AMSDU_AGGR_CTRL %d\n", ctrl);
 256
 257                if (priv->media_connected) {
 258                        adapter->tx_buf_size =
 259                                min_t(u16, adapter->curr_tx_buf_size, ctrl);
 260                        dev_dbg(adapter->dev, "event: tx_buf_size %d\n",
 261                                adapter->tx_buf_size);
 262                }
 263                break;
 264        case EVENT_ADDBA:
 265                dev_dbg(adapter->dev, "event: ADDBA Request\n");
 266                if (priv->media_connected)
 267                        mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_ADDBA_RSP,
 268                                               HostCmd_ACT_GEN_SET, 0,
 269                                               adapter->event_body);
 270                break;
 271        case EVENT_DELBA:
 272                dev_dbg(adapter->dev, "event: DELBA Request\n");
 273                if (priv->media_connected)
 274                        mwifiex_11n_delete_ba_stream(priv, adapter->event_body);
 275                break;
 276        case EVENT_BA_STREAM_TIEMOUT:
 277                dev_dbg(adapter->dev, "event:  BA Stream timeout\n");
 278                if (priv->media_connected) {
 279                        ba_timeout = (void *)adapter->event_body;
 280                        mwifiex_11n_ba_stream_timeout(priv, ba_timeout);
 281                }
 282                break;
 283        default:
 284                dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
 285                        eventcause);
 286                break;
 287        }
 288
 289        return 0;
 290}
 291