]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/mac80211/iface.c
minstrel_ht: move minstrel_mcs_groups declaration to header file
[mv-sheeva.git] / net / mac80211 / iface.c
index 906fc2be0cfb45bd43f1f5a71f0df53941c828ab..910729fc18cd2489e217b27f6153d55eb265bcbd 100644 (file)
@@ -460,27 +460,14 @@ static int ieee80211_stop(struct net_device *dev)
                 * whether the interface is running, which, at this point,
                 * it no longer is.
                 */
-               cancel_work_sync(&sdata->u.mgd.work);
                cancel_work_sync(&sdata->u.mgd.chswitch_work);
                cancel_work_sync(&sdata->u.mgd.monitor_work);
                cancel_work_sync(&sdata->u.mgd.beacon_connection_loss_work);
 
-               /*
-                * When we get here, the interface is marked down.
-                * Call synchronize_rcu() to wait for the RX path
-                * should it be using the interface and enqueuing
-                * frames at this very time on another CPU.
-                */
-               synchronize_rcu();
-               skb_queue_purge(&sdata->u.mgd.skb_queue);
                /* fall through */
        case NL80211_IFTYPE_ADHOC:
-               if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+               if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
                        del_timer_sync(&sdata->u.ibss.timer);
-                       cancel_work_sync(&sdata->u.ibss.work);
-                       synchronize_rcu();
-                       skb_queue_purge(&sdata->u.ibss.skb_queue);
-               }
                /* fall through */
        case NL80211_IFTYPE_MESH_POINT:
                if (ieee80211_vif_is_mesh(&sdata->vif)) {
@@ -495,6 +482,16 @@ static int ieee80211_stop(struct net_device *dev)
                }
                /* fall through */
        default:
+               flush_work(&sdata->work);
+               /*
+                * When we get here, the interface is marked down.
+                * Call synchronize_rcu() to wait for the RX path
+                * should it be using the interface and enqueuing
+                * frames at this very time on another CPU.
+                */
+               synchronize_rcu();
+               skb_queue_purge(&sdata->skb_queue);
+
                if (local->scan_sdata == sdata)
                        ieee80211_scan_cancel(local);
 
@@ -704,6 +701,136 @@ static void ieee80211_if_setup(struct net_device *dev)
        dev->destructor = free_netdev;
 }
 
+static void ieee80211_iface_work(struct work_struct *work)
+{
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data, work);
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb;
+       struct sta_info *sta;
+       struct ieee80211_ra_tid *ra_tid;
+
+       if (!ieee80211_sdata_running(sdata))
+               return;
+
+       if (local->scanning)
+               return;
+
+       /*
+        * ieee80211_queue_work() should have picked up most cases,
+        * here we'll pick the rest.
+        */
+       if (WARN(local->suspended,
+                "interface work scheduled while going to suspend\n"))
+               return;
+
+       /* first process frames */
+       while ((skb = skb_dequeue(&sdata->skb_queue))) {
+               struct ieee80211_mgmt *mgmt = (void *)skb->data;
+
+               if (skb->pkt_type == IEEE80211_SDATA_QUEUE_AGG_START) {
+                       ra_tid = (void *)&skb->cb;
+                       ieee80211_start_tx_ba_cb(&sdata->vif, ra_tid->ra,
+                                                ra_tid->tid);
+               } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_AGG_STOP) {
+                       ra_tid = (void *)&skb->cb;
+                       ieee80211_stop_tx_ba_cb(&sdata->vif, ra_tid->ra,
+                                               ra_tid->tid);
+               } else if (ieee80211_is_action(mgmt->frame_control) &&
+                          mgmt->u.action.category == WLAN_CATEGORY_BACK) {
+                       int len = skb->len;
+
+                       mutex_lock(&local->sta_mtx);
+                       sta = sta_info_get(sdata, mgmt->sa);
+                       if (sta) {
+                               switch (mgmt->u.action.u.addba_req.action_code) {
+                               case WLAN_ACTION_ADDBA_REQ:
+                                       ieee80211_process_addba_request(
+                                                       local, sta, mgmt, len);
+                                       break;
+                               case WLAN_ACTION_ADDBA_RESP:
+                                       ieee80211_process_addba_resp(local, sta,
+                                                                    mgmt, len);
+                                       break;
+                               case WLAN_ACTION_DELBA:
+                                       ieee80211_process_delba(sdata, sta,
+                                                               mgmt, len);
+                                       break;
+                               default:
+                                       WARN_ON(1);
+                                       break;
+                               }
+                       }
+                       mutex_unlock(&local->sta_mtx);
+               } else if (ieee80211_is_data_qos(mgmt->frame_control)) {
+                       struct ieee80211_hdr *hdr = (void *)mgmt;
+                       /*
+                        * So the frame isn't mgmt, but frame_control
+                        * is at the right place anyway, of course, so
+                        * the if statement is correct.
+                        *
+                        * Warn if we have other data frame types here,
+                        * they must not get here.
+                        */
+                       WARN_ON(hdr->frame_control &
+                                       cpu_to_le16(IEEE80211_STYPE_NULLFUNC));
+                       WARN_ON(!(hdr->seq_ctrl &
+                                       cpu_to_le16(IEEE80211_SCTL_FRAG)));
+                       /*
+                        * This was a fragment of a frame, received while
+                        * a block-ack session was active. That cannot be
+                        * right, so terminate the session.
+                        */
+                       mutex_lock(&local->sta_mtx);
+                       sta = sta_info_get(sdata, mgmt->sa);
+                       if (sta) {
+                               u16 tid = *ieee80211_get_qos_ctl(hdr) &
+                                               IEEE80211_QOS_CTL_TID_MASK;
+
+                               __ieee80211_stop_rx_ba_session(
+                                       sta, tid, WLAN_BACK_RECIPIENT,
+                                       WLAN_REASON_QSTA_REQUIRE_SETUP);
+                       }
+                       mutex_unlock(&local->sta_mtx);
+               } else switch (sdata->vif.type) {
+               case NL80211_IFTYPE_STATION:
+                       ieee80211_sta_rx_queued_mgmt(sdata, skb);
+                       break;
+               case NL80211_IFTYPE_ADHOC:
+                       ieee80211_ibss_rx_queued_mgmt(sdata, skb);
+                       break;
+               case NL80211_IFTYPE_MESH_POINT:
+                       if (!ieee80211_vif_is_mesh(&sdata->vif))
+                               break;
+                       ieee80211_mesh_rx_queued_mgmt(sdata, skb);
+                       break;
+               default:
+                       WARN(1, "frame for unexpected interface type");
+                       break;
+               }
+
+               kfree_skb(skb);
+       }
+
+       /* then other type-dependent work */
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_STATION:
+               ieee80211_sta_work(sdata);
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               ieee80211_ibss_work(sdata);
+               break;
+       case NL80211_IFTYPE_MESH_POINT:
+               if (!ieee80211_vif_is_mesh(&sdata->vif))
+                       break;
+               ieee80211_mesh_work(sdata);
+               break;
+       default:
+               break;
+       }
+}
+
+
 /*
  * Helper function to initialise an interface to a specific type.
  */
@@ -721,6 +848,9 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
        /* only monitor differs */
        sdata->dev->type = ARPHRD_ETHER;
 
+       skb_queue_head_init(&sdata->skb_queue);
+       INIT_WORK(&sdata->work, ieee80211_iface_work);
+
        switch (type) {
        case NL80211_IFTYPE_AP:
                skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
@@ -946,6 +1076,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
        sdata->wdev.wiphy = local->hw.wiphy;
        sdata->local = local;
        sdata->dev = ndev;
+#ifdef CONFIG_INET
+       sdata->arp_filter_state = true;
+#endif
 
        for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
                skb_queue_head_init(&sdata->fragments[i].skb_list);