]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - src/ap/wmm.c
Fix wmm compile on fedora-17 (gcc 4.7.2)
[thirdparty/hostap.git] / src / ap / wmm.c
index 403560ee427f0223258f461e3f1f8ac1c74ae436..9ebb01e3d748b6b86a1c516cc80133b255be9201 100644 (file)
@@ -4,29 +4,28 @@
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "hostapd.h"
 #include "ieee802_11.h"
 #include "sta_info.h"
 #include "ap_config.h"
+#include "ap_drv_ops.h"
 #include "wmm.h"
 
-
-/* TODO: maintain separate sequence and fragment numbers for each AC
- * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA
- * if only WMM stations are receiving a certain group */
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
 
 
 static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
@@ -47,6 +46,62 @@ static inline u8 wmm_ecw(int ecwmin, int ecwmax)
 }
 
 
+static void
+wmm_set_regulatory_limit(const struct hostapd_wmm_ac_params *wmm_conf,
+                        struct hostapd_wmm_ac_params *wmm,
+                        const struct hostapd_wmm_rule *wmm_reg)
+{
+       int ac;
+
+       for (ac = 0; ac < WMM_AC_NUM; ac++) {
+               wmm[ac].cwmin = MAX(wmm_conf[ac].cwmin, wmm_reg[ac].min_cwmin);
+               wmm[ac].cwmax = MAX(wmm_conf[ac].cwmax, wmm_reg[ac].min_cwmax);
+               wmm[ac].aifs = MAX(wmm_conf[ac].aifs, wmm_reg[ac].min_aifs);
+               wmm[ac].txop_limit =
+                       MIN(wmm_conf[ac].txop_limit, wmm_reg[ac].max_txop);
+               wmm[ac].admission_control_mandatory =
+                       wmm_conf[ac].admission_control_mandatory;
+       }
+}
+
+
+/*
+ * Calculate WMM regulatory limit if any.
+ */
+static void wmm_calc_regulatory_limit(struct hostapd_data *hapd,
+                                     struct hostapd_wmm_ac_params *acp)
+{
+       struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+       int c;
+
+       os_memcpy(acp, hapd->iconf->wmm_ac_params,
+                 sizeof(hapd->iconf->wmm_ac_params));
+
+       for (c = 0; mode && c < mode->num_channels; c++) {
+               struct hostapd_channel_data *chan = &mode->channels[c];
+
+               if (chan->freq != hapd->iface->freq)
+                       continue;
+
+               if (chan->wmm_rules_valid)
+                       wmm_set_regulatory_limit(hapd->iconf->wmm_ac_params,
+                                                acp, chan->wmm_rules);
+               break;
+       }
+
+       /*
+        * Check if we need to update set count. Since both were initialized to
+        * zero we can compare the whole array in one shot.
+        */
+       if (os_memcmp(acp, hapd->iface->prev_wmm,
+                     sizeof(hapd->iconf->wmm_ac_params)) != 0) {
+               os_memcpy(hapd->iface->prev_wmm, acp,
+                         sizeof(hapd->iconf->wmm_ac_params));
+               hapd->parameter_set_count++;
+       }
+}
+
+
 /*
  * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association
  * Response frames.
@@ -56,10 +111,14 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
        u8 *pos = eid;
        struct wmm_parameter_element *wmm =
                (struct wmm_parameter_element *) (pos + 2);
+       struct hostapd_wmm_ac_params wmmp[WMM_AC_NUM];
        int e;
 
+       os_memset(wmmp, 0, sizeof(wmmp));
+
        if (!hapd->conf->wmm_enabled)
                return eid;
+       wmm_calc_regulatory_limit(hapd, wmmp);
        eid[0] = WLAN_EID_VENDOR_SPECIFIC;
        wmm->oui[0] = 0x00;
        wmm->oui[1] = 0x50;
@@ -69,11 +128,16 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
        wmm->version = WMM_VERSION;
        wmm->qos_info = hapd->parameter_set_count & 0xf;
 
+       if (hapd->conf->wmm_uapsd &&
+           (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+               wmm->qos_info |= 0x80;
+
+       wmm->reserved = 0;
+
        /* fill in a parameter set record for each AC */
        for (e = 0; e < 4; e++) {
                struct wmm_ac_parameter *ac = &wmm->ac[e];
-               struct hostapd_wmm_ac_params *acp =
-                       &hapd->iconf->wmm_ac_params[e];
+               struct hostapd_wmm_ac_params *acp = &wmmp[e];
 
                ac->aci_aifsn = wmm_aci_aifsn(acp->aifs,
                                              acp->admission_control_mandatory,
@@ -89,9 +153,11 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
 }
 
 
-/* This function is called when a station sends an association request with
- * WMM info element. The function returns zero on success or non-zero on any
- * error in WMM element. eid does not include Element ID and Length octets. */
+/*
+ * This function is called when a station sends an association request with
+ * WMM info element. The function returns 1 on success or 0 on any error in WMM
+ * element. eid does not include Element ID and Length octets.
+ */
 int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
 {
        struct wmm_information_element *wmm;
@@ -101,7 +167,7 @@ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
        if (len < sizeof(struct wmm_information_element)) {
                wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
                           (unsigned long) len);
-               return -1;
+               return 0;
        }
 
        wmm = (struct wmm_information_element *) eid;
@@ -112,10 +178,10 @@ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
        if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
            wmm->version != WMM_VERSION) {
                wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
-               return -1;
+               return 0;
        }
 
-       return 0;
+       return 1;
 }
 
 
@@ -145,15 +211,16 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
        os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
        len = ((u8 *) (t + 1)) - buf;
 
-       if (hapd->drv.send_mgmt_frame(hapd, m, len) < 0)
-               perror("wmm_send_action: send");
+       if (hostapd_drv_send_mlme(hapd, m, len, 0, NULL, 0, 0) < 0)
+               wpa_printf(MSG_INFO, "wmm_send_action: send failed");
 }
 
 
 int wmm_process_tspec(struct wmm_tspec_element *tspec)
 {
-       int medium_time, pps, duration;
-       int up, psb, dir, tid;
+       u64 medium_time;
+       unsigned int pps, duration;
+       unsigned int up, psb, dir, tid;
        u16 val, surplus;
 
        up = (tspec->ts_info[1] >> 3) & 0x07;
@@ -201,8 +268,9 @@ int wmm_process_tspec(struct wmm_tspec_element *tspec)
                return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
        }
 
-       medium_time = surplus * pps * duration / 0x2000;
-       wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time);
+       medium_time = (u64) surplus * pps * duration / 0x2000;
+       wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %lu",
+                  (unsigned long) medium_time);
 
        /*
         * TODO: store list of granted (and still active) TSPECs and check
@@ -225,10 +293,11 @@ int wmm_process_tspec(struct wmm_tspec_element *tspec)
 
 static void wmm_addts_req(struct hostapd_data *hapd,
                          const struct ieee80211_mgmt *mgmt,
-                         struct wmm_tspec_element *tspec, size_t len)
+                         const struct wmm_tspec_element *tspec, size_t len)
 {
        const u8 *end = ((const u8 *) mgmt) + len;
        int res;
+       struct wmm_tspec_element tspec_resp;
 
        if ((const u8 *) (tspec + 1) > end) {
                wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request");
@@ -240,10 +309,11 @@ static void wmm_addts_req(struct hostapd_data *hapd,
                   mgmt->u.action.u.wmm_action.dialog_token,
                   MAC2STR(mgmt->sa));
 
-       res = wmm_process_tspec(tspec);
+       os_memcpy(&tspec_resp, tspec, sizeof(struct wmm_tspec_element));
+       res = wmm_process_tspec(&tspec_resp);
        wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res);
 
-       wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP,
+       wmm_send_action(hapd, mgmt->sa, &tspec_resp, WMM_ACTION_CODE_ADDTS_RESP,
                        mgmt->u.action.u.wmm_action.dialog_token, res);
 }
 
@@ -269,6 +339,9 @@ void hostapd_wmm_action(struct hostapd_data *hapd,
                return;
        }
 
+       if (left < 0)
+               return; /* not a valid WMM Action frame */
+
        /* extract the tspec info element */
        if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) {
                hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,