mac80211: use ps-poll when dynamic power save mode is disabled
[linux-2.6-block.git] / net / mac80211 / rx.c
index abc3aa583ca63b9154cea548d6f8541b5e28af31..0e030d3fbdec8655405e506d098aadfebaa1d3c4 100644 (file)
@@ -86,8 +86,7 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local,
 
        if (status->flag & RX_FLAG_TSFT)
                len += 8;
-       if (local->hw.flags & IEEE80211_HW_SIGNAL_DB ||
-           local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
+       if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
                len += 1;
        if (local->hw.flags & IEEE80211_HW_NOISE_DBM)
                len += 1;
@@ -158,7 +157,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                 */
                *pos = 0;
        } else {
-               rthdr->it_present |= (1 << IEEE80211_RADIOTAP_RATE);
+               rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
                *pos = rate->bitrate / 5;
        }
        pos++;
@@ -199,14 +198,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
        *pos = status->antenna;
        pos++;
 
-       /* IEEE80211_RADIOTAP_DB_ANTSIGNAL */
-       if (local->hw.flags & IEEE80211_HW_SIGNAL_DB) {
-               *pos = status->signal;
-               rthdr->it_present |=
-                       cpu_to_le32(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL);
-               pos++;
-       }
-
        /* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */
 
        /* IEEE80211_RADIOTAP_RX_FLAGS */
@@ -740,6 +731,39 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
        return result;
 }
 
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_check_more_data(struct ieee80211_rx_data *rx)
+{
+       struct ieee80211_local *local;
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *skb;
+
+       local = rx->local;
+       skb = rx->skb;
+       hdr = (struct ieee80211_hdr *) skb->data;
+
+       if (!local->pspolling)
+               return RX_CONTINUE;
+
+       if (!ieee80211_has_fromds(hdr->frame_control))
+               /* this is not from AP */
+               return RX_CONTINUE;
+
+       if (!ieee80211_is_data(hdr->frame_control))
+               return RX_CONTINUE;
+
+       if (!ieee80211_has_moredata(hdr->frame_control)) {
+               /* AP has no more frames buffered for us */
+               local->pspolling = false;
+               return RX_CONTINUE;
+       }
+
+       /* more data bit is set, let's request a new frame from the AP */
+       ieee80211_send_pspoll(local, rx->sdata);
+
+       return RX_CONTINUE;
+}
+
 static void ap_sta_ps_start(struct sta_info *sta)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
@@ -1225,12 +1249,12 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
 
        switch (hdr->frame_control &
                cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
-       case __constant_cpu_to_le16(IEEE80211_FCTL_TODS):
+       case cpu_to_le16(IEEE80211_FCTL_TODS):
                if (unlikely(sdata->vif.type != NL80211_IFTYPE_AP &&
                             sdata->vif.type != NL80211_IFTYPE_AP_VLAN))
                        return -1;
                break;
-       case __constant_cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
+       case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
                if (unlikely(sdata->vif.type != NL80211_IFTYPE_WDS &&
                             sdata->vif.type != NL80211_IFTYPE_MESH_POINT))
                        return -1;
@@ -1244,13 +1268,13 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
                        }
                }
                break;
-       case __constant_cpu_to_le16(IEEE80211_FCTL_FROMDS):
+       case cpu_to_le16(IEEE80211_FCTL_FROMDS):
                if (sdata->vif.type != NL80211_IFTYPE_STATION ||
                    (is_multicast_ether_addr(dst) &&
                     !compare_ether_addr(src, dev->dev_addr)))
                        return -1;
                break;
-       case __constant_cpu_to_le16(0):
+       case cpu_to_le16(0):
                if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
                        return -1;
                break;
@@ -1667,6 +1691,57 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
        return RX_CONTINUE;
 }
 
+static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata,
+                                          struct ieee80211_mgmt *mgmt,
+                                          size_t len)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *resp;
+
+       if (compare_ether_addr(mgmt->da, sdata->dev->dev_addr) != 0) {
+               /* Not to own unicast address */
+               return;
+       }
+
+       if (compare_ether_addr(mgmt->sa, sdata->u.sta.bssid) != 0 ||
+           compare_ether_addr(mgmt->bssid, sdata->u.sta.bssid) != 0) {
+               /* Not from the current AP. */
+               return;
+       }
+
+       if (sdata->u.sta.state == IEEE80211_STA_MLME_ASSOCIATE) {
+               /* Association in progress; ignore SA Query */
+               return;
+       }
+
+       if (len < 24 + 1 + sizeof(resp->u.action.u.sa_query)) {
+               /* Too short SA Query request frame */
+               return;
+       }
+
+       skb = dev_alloc_skb(sizeof(*resp) + local->hw.extra_tx_headroom);
+       if (skb == NULL)
+               return;
+
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+       resp = (struct ieee80211_mgmt *) skb_put(skb, 24);
+       memset(resp, 0, 24);
+       memcpy(resp->da, mgmt->sa, ETH_ALEN);
+       memcpy(resp->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(resp->bssid, sdata->u.sta.bssid, ETH_ALEN);
+       resp->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_ACTION);
+       skb_put(skb, 1 + sizeof(resp->u.action.u.sa_query));
+       resp->u.action.category = WLAN_CATEGORY_SA_QUERY;
+       resp->u.action.u.sa_query.action = WLAN_ACTION_SA_QUERY_RESPONSE;
+       memcpy(resp->u.action.u.sa_query.trans_id,
+              mgmt->u.action.u.sa_query.trans_id,
+              WLAN_SA_QUERY_TR_ID_LEN);
+
+       ieee80211_tx_skb(sdata, skb, 1);
+}
+
 static ieee80211_rx_result debug_noinline
 ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
 {
@@ -1686,6 +1761,9 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
        if (!(rx->flags & IEEE80211_RX_RA_MATCH))
                return RX_DROP_MONITOR;
 
+       if (ieee80211_drop_unencrypted(rx, mgmt->frame_control))
+               return RX_DROP_MONITOR;
+
        /* all categories we currently handle have action_code */
        if (len < IEEE80211_MIN_ACTION_SIZE + 1)
                return RX_DROP_MONITOR;
@@ -1743,6 +1821,24 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                        break;
                }
                break;
+       case WLAN_CATEGORY_SA_QUERY:
+               if (len < (IEEE80211_MIN_ACTION_SIZE +
+                          sizeof(mgmt->u.action.u.sa_query)))
+                       return RX_DROP_MONITOR;
+               switch (mgmt->u.action.u.sa_query.action) {
+               case WLAN_ACTION_SA_QUERY_REQUEST:
+                       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+                               return RX_DROP_MONITOR;
+                       ieee80211_process_sa_query_req(sdata, mgmt, len);
+                       break;
+               case WLAN_ACTION_SA_QUERY_RESPONSE:
+                       /*
+                        * SA Query response is currently only used in AP mode
+                        * and it is processed in user space.
+                        */
+                       return RX_CONTINUE;
+               }
+               break;
        default:
                return RX_CONTINUE;
        }
@@ -1756,10 +1852,14 @@ static ieee80211_rx_result debug_noinline
 ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
 
        if (!(rx->flags & IEEE80211_RX_RA_MATCH))
                return RX_DROP_MONITOR;
 
+       if (ieee80211_drop_unencrypted(rx, mgmt->frame_control))
+               return RX_DROP_MONITOR;
+
        if (ieee80211_vif_is_mesh(&sdata->vif))
                return ieee80211_mesh_rx_mgmt(sdata, rx->skb, rx->status);
 
@@ -1920,6 +2020,7 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata,
        CALL_RXH(ieee80211_rx_h_passive_scan)
        CALL_RXH(ieee80211_rx_h_check)
        CALL_RXH(ieee80211_rx_h_decrypt)
+       CALL_RXH(ieee80211_rx_h_check_more_data)
        CALL_RXH(ieee80211_rx_h_sta_process)
        CALL_RXH(ieee80211_rx_h_defragment)
        CALL_RXH(ieee80211_rx_h_ps_poll)