Add preliminary IEEE 802.11n support into hostapd
authorJouni Malinen <jouni.malinen@atheros.com>
Thu, 21 Aug 2008 15:18:38 +0000 (18:18 +0300)
committerJouni Malinen <j@w1.fi>
Thu, 21 Aug 2008 15:18:38 +0000 (18:18 +0300)
This commit brings in cleaned up version of IEEE 802.11n implementation
from Intel (1). The Intel tarball includes number of other changes, too,
and only the changes specific to IEEE 802.11n are brought in here. In
addition, this does not include all the changes (e.g., some of the
configuration parameters are still missing and driver wrapper changes for
mac80211 were not included).

(1)
http://www.kernel.org/pub/linux/kernel/people/chuyee/wireless/iwl4965_ap/hostap_0_6_0_intel_0.0.13.1.tgz

14 files changed:
hostapd/Makefile
hostapd/ap.h
hostapd/ap_list.c
hostapd/ap_list.h
hostapd/beacon.c
hostapd/config.c
hostapd/config.h
hostapd/driver.h
hostapd/hostapd.conf
hostapd/hostapd.h
hostapd/ieee802_11.c
hostapd/ieee802_11.h
hostapd/sta_info.c
src/common/ieee802_11_defs.h

index 1e73906..6ba505d 100644 (file)
@@ -95,6 +95,10 @@ OBJS += wpa_ft.o
 NEED_SHA256=y
 endif
 
+ifdef CONFIG_IEEE80211N
+CFLAGS += -DCONFIG_IEEE80211N
+endif
+
 ifdef CONFIG_DRIVER_HOSTAP
 CFLAGS += -DCONFIG_DRIVER_HOSTAP
 OBJS += driver_hostap.o
index 383ecaf..5db4f25 100644 (file)
 #ifndef AP_H
 #define AP_H
 
+#ifdef CONFIG_IEEE80211N
+#include "ieee802_11_defs.h"
+#endif /* CONFIG_IEEE80211N */
+
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
 #define WLAN_STA_ASSOC BIT(1)
@@ -27,6 +31,7 @@
 #define WLAN_STA_PREAUTH BIT(8)
 #define WLAN_STA_WME BIT(9)
 #define WLAN_STA_MFP BIT(10)
+#define WLAN_STA_HT BIT(11)
 #define WLAN_STA_NONERP BIT(31)
 
 /* Maximum number of supported rates (from both Supported Rates and Extended
@@ -83,6 +88,10 @@ struct sta_info {
        struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */
 
        int vlan_id;
+
+#ifdef CONFIG_IEEE80211N
+       struct ht_cap_ie ht_capabilities; /* IEEE 802.11n capabilities */
+#endif /* CONFIG_IEEE80211N */
 };
 
 
index 5f5b5d4..b7082d5 100644 (file)
@@ -107,6 +107,15 @@ static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
 }
 
 
+#ifdef CONFIG_IEEE80211N
+static int ap_list_beacon_olbc_ht(struct hostapd_iface *iface,
+                                 struct ap_info *ap)
+{
+       return !ap->ht_support;
+}
+#endif /* CONFIG_IEEE80211N */
+
+
 struct ap_info * ap_get_ap(struct hostapd_iface *iface, u8 *ap)
 {
        struct ap_info *s;
@@ -282,6 +291,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
        struct ap_info *ap;
        int new_ap = 0;
        size_t len;
+       int set_beacon = 0;
 
        if (iface->conf->ap_table_max_size < 1)
                return;
@@ -338,6 +348,11 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
        else if (fi)
                ap->channel = fi->channel;
 
+       if (elems->ht_capabilities)
+               ap->ht_support = 1;
+       else
+               ap->ht_support = 0;
+
        ap->num_beacons++;
        time(&ap->last_beacon);
        if (fi) {
@@ -358,12 +373,24 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
 
        if (!iface->olbc &&
            ap_list_beacon_olbc(iface, ap)) {
-               struct hostapd_data *hapd = iface->bss[0];
                iface->olbc = 1;
                wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable "
                           "protection", MAC2STR(ap->addr));
-               ieee802_11_set_beacons(hapd->iface);
+               set_beacon++;
        }
+
+#ifdef CONFIG_IEEE80211N
+       if (!iface->olbc_ht && ap_list_beacon_olbc_ht(iface, ap)) {
+               iface->olbc_ht = 1;
+               hostapd_ht_operation_update(iface);
+               wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
+                          " - enable protection", MAC2STR(ap->addr));
+               set_beacon++;
+       }
+#endif /* CONFIG_IEEE80211N */
+
+       if (set_beacon)
+               ieee802_11_set_beacons(iface);
 }
 
 
@@ -372,6 +399,7 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
        struct hostapd_iface *iface = eloop_ctx;
        time_t now;
        struct ap_info *ap;
+       int set_beacon = 0;
 
        eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
 
@@ -395,23 +423,37 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
                ap_free_ap(iface, ap);
        }
 
-       if (iface->olbc) {
+       if (iface->olbc || iface->olbc_ht) {
                int olbc = 0;
+               int olbc_ht = 0;
+
                ap = iface->ap_list;
-               while (ap) {
-                       if (ap_list_beacon_olbc(iface, ap)) {
+               while (ap && (olbc == 0 || olbc_ht == 0)) {
+                       if (ap_list_beacon_olbc(iface, ap))
                                olbc = 1;
-                               break;
-                       }
+#ifdef CONFIG_IEEE80211N
+                       if (ap_list_beacon_olbc_ht(iface, ap))
+                               olbc_ht = 1;
+#endif /* CONFIG_IEEE80211N */
                        ap = ap->next;
                }
-               if (!olbc) {
-                       struct hostapd_data *hapd = iface->bss[0];
+               if (!olbc && iface->olbc) {
                        wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
                        iface->olbc = 0;
-                       ieee802_11_set_beacons(hapd->iface);
+                       set_beacon++;
+               }
+#ifdef CONFIG_IEEE80211N
+               if (!olbc_ht && iface->olbc_ht) {
+                       wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
+                       iface->olbc_ht = 0;
+                       hostapd_ht_operation_update(iface);
+                       set_beacon++;
                }
+#endif /* CONFIG_IEEE80211N */
        }
+
+       if (set_beacon)
+               ieee802_11_set_beacons(iface);
 }
 
 
index 668d909..c5db8b8 100644 (file)
@@ -43,6 +43,8 @@ struct ap_info {
        int datarate; /* in 100 kbps */
        int ssi_signal;
 
+       int ht_support;
+
        unsigned int num_beacons; /* number of beacon frames received */
        time_t last_beacon;
 
index d005559..1a221d7 100644 (file)
@@ -285,6 +285,13 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt,
        if (hapd->conf->wme_enabled)
                pos = hostapd_eid_wme(hapd, pos);
 
+#ifdef CONFIG_IEEE80211N
+       if (hapd->conf->ieee80211n) {
+               pos = hostapd_eid_ht_capabilities_info(hapd, pos);
+               pos = hostapd_eid_ht_operation(hapd, pos);
+       }
+#endif /* CONFIG_IEEE80211N */
+
        if (hostapd_send_mgmt_frame(hapd, resp, pos - (u8 *) resp, 0) < 0)
                perror("handle_probe_req: send");
 
@@ -376,6 +383,25 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
        if (hapd->conf->wme_enabled)
                tailpos = hostapd_eid_wme(hapd, tailpos);
 
+#ifdef CONFIG_IEEE80211N
+       if (hapd->conf->ieee80211n) {
+               tailpos = hostapd_eid_ht_capabilities_info(hapd, tailpos);
+               tailpos = hostapd_eid_ht_operation(hapd, tailpos);
+
+               if (hostapd_set_ht_capability(
+                           hapd->conf->iface, hapd,
+                           &hapd->conf->ht_capabilities.data)) {
+                       wpa_printf(MSG_ERROR, "Could not set HT capabilities "
+                                  "for kernel driver");
+               }
+               if (hostapd_set_ht_operation(
+                           hapd->conf->iface, hapd,
+                           &hapd->conf->ht_operation.data))
+                       wpa_printf(MSG_ERROR, "Could not set HT operation for "
+                                  "kernel driver");
+       }
+#endif /* CONFIG_IEEE80211N */
+
        tail_len = tailpos > tail ? tailpos - tail : 0;
 
        if (hostapd_set_beacon(hapd->conf->iface, hapd, (u8 *) head, head_len,
index 58fdaef..72461c0 100644 (file)
@@ -183,6 +183,54 @@ static void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 }
 
 
+#ifdef CONFIG_IEEE80211N
+static int hostapd_config_defaults_bss_80211n(struct hostapd_bss_config *bss)
+{
+       u16 capabilities_info = 0;
+       u16 operation_mode = 0;
+
+       if (bss == NULL)
+               return -1;
+
+       /* add default values to HT capabilities parameters */
+       os_memset(&bss->ht_capabilities, 0, sizeof(struct ht_cap_ie));
+       bss->ht_capabilities.id = WLAN_EID_HT_CAP;
+       bss->ht_capabilities.length = HT_CAPABILITIES_LEN;
+
+#if 0 /* FIX: remove? was commented out */
+       bss->ht_capabilities.mac_ht_param_info.max_rx_ampdu_factor =
+               MAX_RX_AMPDU_FACTOR_64KB;
+#endif
+       SET_2BIT_U8(&bss->ht_capabilities.data.mac_ht_params_info,
+                   MAC_HT_PARAM_INFO_MAX_RX_AMPDU_FACTOR_OFFSET,
+                   MAX_RX_AMPDU_FACTOR_64KB);
+
+       SET_2BIT_LE16(&capabilities_info,
+                     HT_CAP_INFO_MIMO_PWR_SAVE_OFFSET,
+                     MIMO_PWR_NO_LIMIT_ON_MIMO_SEQS);
+
+       capabilities_info |= HT_CAP_INFO_GREEN_FIELD;
+
+       bss->ht_capabilities.data.capabilities_info =
+               host_to_le16(capabilities_info);
+
+       bss->ht_capabilities.data.supported_mcs_set[0] = 0xff;
+       bss->ht_capabilities.data.supported_mcs_set[1] = 0xff;
+
+       /* add default values to HT operation parameters */
+       os_memset(&bss->ht_operation, 0, sizeof(struct ht_operation_ie));
+       bss->ht_operation.id = WLAN_EID_HT_OPERATION;
+       bss->ht_operation.length = HT_OPERATION_LEN;
+       SET_2BIT_LE16(&operation_mode,
+                     HT_INFO_OPERATION_MODE_OP_MODE_OFFSET,
+                     OP_MODE_PURE);
+       bss->ht_operation.data.operation_mode = host_to_le16(operation_mode);
+
+       return 0;
+}
+#endif /* CONFIG_IEEE80211N */
+
+
 static struct hostapd_config * hostapd_config_defaults(void)
 {
        struct hostapd_config *conf;
@@ -244,6 +292,10 @@ static struct hostapd_config * hostapd_config_defaults(void)
        conf->wme_ac_params[2] = ac_vi;
        conf->wme_ac_params[3] = ac_vo;
 
+#ifdef CONFIG_IEEE80211N
+       hostapd_config_defaults_bss_80211n(bss);
+#endif /* CONFIG_IEEE80211N */
+
        return conf;
 }
 
@@ -1933,6 +1985,10 @@ struct hostapd_config * hostapd_config_read(const char *fname)
                } else if (os_strcmp(buf, "ieee80211w") == 0) {
                        bss->ieee80211w = atoi(pos);
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211N
+               } else if (os_strcmp(buf, "ieee80211n") == 0) {
+                       bss->ieee80211n = atoi(pos);
+#endif /* CONFIG_IEEE80211N */
                } else if (os_strcmp(buf, "max_listen_interval") == 0) {
                        bss->max_listen_interval = atoi(pos);
                } else if (os_strcmp(buf, "okc") == 0) {
index 3f740bd..d1d15df 100644 (file)
@@ -18,6 +18,9 @@
 #include "defs.h"
 #include "ip_addr.h"
 #include "wpa_common.h"
+#ifdef CONFIG_IEEE80211N
+#include "ieee802_11_defs.h"
+#endif /* CONFIG_IEEE80211N */
 
 #ifndef IFNAMSIZ
 #define IFNAMSIZ 16
@@ -272,6 +275,16 @@ struct hostapd_bss_config {
        u16 max_listen_interval;
 
        int okc; /* Opportunistic Key Caching */
+
+#ifdef CONFIG_IEEE80211N
+       int ieee80211n;
+       /* TODO: these structures should not really be used here; move to
+        * struct hostapd_data or struct hostapd_iface and just include the
+        * needed values here for generating IEs elsewhere */
+       struct ht_cap_ie ht_capabilities;
+       struct ht_operation_ie ht_operation;
+       int ht_op_mode_fixed;
+#endif /* CONFIG_IEEE80211N */
 };
 
 
index f76d2d4..2b323a1 100644 (file)
 #ifndef DRIVER_H
 #define DRIVER_H
 
+struct hostapd_sta_add_params {
+       const u8 *addr;
+       u16 aid;
+       u16 capability;
+       const u8 *supp_rates;
+       size_t supp_rates_len;
+       int flags;
+       u16 listen_interval;
+       const struct ht_cap_ie *ht_capabilities;
+};
+
 enum hostapd_driver_if_type {
        HOSTAPD_IF_VLAN, HOSTAPD_IF_WDS
 };
@@ -78,9 +89,12 @@ struct wpa_driver_ops {
        int (*send_mgmt_frame)(void *priv, const void *msg, size_t len,
                               int flags);
        int (*set_assoc_ap)(void *priv, const u8 *addr);
+       /* note: sta_add() is deprecated; use sta_add2() instead */
        int (*sta_add)(const char *ifname, void *priv, const u8 *addr, u16 aid,
                       u16 capability, u8 *supp_rates, size_t supp_rates_len,
                       int flags, u16 listen_interval);
+       int (*sta_add2)(const char *ifname, void *priv,
+                       struct hostapd_sta_add_params *params);
        int (*get_inact_sec)(void *priv, const u8 *addr);
        int (*sta_clear_stats)(void *priv, const u8 *addr);
 
@@ -163,6 +177,11 @@ struct wpa_driver_ops {
        int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, 
                                   u32 session_timeout);
        int (*set_radius_acl_expire)(void *priv, const u8 *mac);
+
+       int (*set_ht_capability)(const char *ifname, void *priv,
+                                const u8 *data, size_t data_len);
+       int (*set_ht_operation)(const char *ifname, void *priv,
+                               const u8 *data, size_t data_len);
 };
 
 static inline void *
@@ -362,13 +381,32 @@ hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled)
 
 static inline int
 hostapd_sta_add(const char *ifname, struct hostapd_data *hapd, const u8 *addr,
-               u16 aid, u16 capability, u8 *supp_rates, size_t supp_rates_len,
-               int flags, u16 listen_interval)
+               u16 aid, u16 capability, const u8 *supp_rates,
+               size_t supp_rates_len, int flags, u16 listen_interval,
+               const struct ht_cap_ie *ht_capabilities)
 {
-       if (hapd->driver == NULL || hapd->driver->sta_add == NULL)
+       if (hapd->driver == NULL)
+               return 0;
+
+       if (hapd->driver->sta_add2) {
+               struct hostapd_sta_add_params params;
+               os_memset(&params, 0, sizeof(params));
+               params.addr = addr;
+               params.aid = aid;
+               params.capability = capability;
+               params.supp_rates = supp_rates;
+               params.supp_rates_len = supp_rates_len;
+               params.flags = flags;
+               params.listen_interval = listen_interval;
+               params.ht_capabilities = ht_capabilities;
+               return hapd->driver->sta_add2(ifname, hapd->drv_priv, &params);
+       }
+
+       if (hapd->driver->sta_add == NULL)
                return 0;
        return hapd->driver->sta_add(ifname, hapd->drv_priv, addr, aid,
-                                    capability, supp_rates, supp_rates_len,
+                                    capability, (u8 *) supp_rates,
+                                    supp_rates_len,
                                     flags, listen_interval);
 }
 
@@ -701,4 +739,30 @@ hostapd_set_radius_acl_expire(struct hostapd_data *hapd, const u8 *mac)
        return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
 }
 
+#ifdef CONFIG_IEEE80211N
+static inline int
+hostapd_set_ht_capability(const char *ifname, struct hostapd_data *hapd,
+                         const struct ieee80211_ht_capability *ht_cap)
+{
+       if (hapd->driver == NULL || hapd->driver->set_ht_capability == NULL ||
+           ht_cap == NULL)
+               return 0;
+       return hapd->driver->set_ht_capability(
+               ifname, hapd->drv_priv, (const u8 *) ht_cap,
+               sizeof(struct ieee80211_ht_capability));
+}
+
+static inline int
+hostapd_set_ht_operation(const char *ifname, struct hostapd_data *hapd,
+                        const struct ieee80211_ht_operation *ht_operation)
+{
+       if (hapd->driver == NULL || hapd->driver->set_ht_operation == NULL ||
+           ht_operation == NULL)
+               return 0;
+       return hapd->driver->set_ht_operation(
+               ifname, hapd->drv_priv, (const u8 *) ht_operation,
+               sizeof(struct ieee80211_ht_operation));
+}
+#endif /* CONFIG_IEEE80211N */
+
 #endif /* DRIVER_H */
index 755d190..55987dc 100644 (file)
@@ -377,6 +377,13 @@ wme_ac_vo_acm=0
 # remain asleep). Default: 65535 (no limit apart from field size)
 #max_listen_interval=100
 
+##### IEEE 802.11n related configuration ######################################
+
+# ieee80211n: Whether IEEE 802.11n (HT) is enabled
+# 0 = disabled (default)
+# 1 = enabled
+#ieee80211n=1
+
 ##### IEEE 802.1X-2004 related configuration ##################################
 
 # Require IEEE 802.1X authorization
index 10afd5e..d989820 100644 (file)
@@ -221,6 +221,18 @@ struct hostapd_iface {
 
        int olbc; /* Overlapping Legacy BSS Condition */
 
+       /* Number of HT associated stations that do not support greenfield */
+       int num_sta_ht_no_gf;
+
+       /* Number of associated non-HT stations */
+       int num_sta_no_ht;
+
+       /* Number of HT associated stations 20 MHz */
+       int num_sta_ht_20mhz;
+
+       /* Overlapping BSS information */
+       int olbc_ht;
+
        int dfs_enable;
        u8 pwr_const;
        unsigned int tx_power;
index 1ebf985..b3a9f3d 100644 (file)
@@ -100,6 +100,113 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
 }
 
 
+#ifdef CONFIG_IEEE80211N
+
+u8 * hostapd_eid_ht_capabilities_info(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 *pos = eid;
+       os_memcpy(pos, &hapd->conf->ht_capabilities, sizeof(struct ht_cap_ie));
+       pos += sizeof(struct ht_cap_ie);
+       return pos;
+}
+
+
+u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 *pos = eid;
+       os_memcpy(pos, &hapd->conf->ht_operation,
+                 sizeof(struct ht_operation_ie));
+       pos += sizeof(struct ht_operation_ie);
+       return pos;
+}
+
+
+/*
+op_mode
+Set to 0 (HT pure) under the followign conditions
+       - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or
+       - all STAs in the BSS are 20 MHz HT in 20 MHz BSS
+Set to 1 (HT non-member protection) if there may be non-HT STAs
+       in both the primary and the secondary channel
+Set to 2 if only HT STAs are associated in BSS,
+       however and at least one 20 MHz HT STA is associated
+Set to 3 (HT mixed mode) when one or more non-HT STAs are associated
+       (currently non-GF HT station is considered as non-HT STA also)
+*/
+int hostapd_ht_operation_update(struct hostapd_iface *iface)
+{
+       struct ht_operation_ie *ht_operation;
+       u16 operation_mode = 0;
+       u16 cur_op_mode, new_op_mode;
+       int op_mode_changes = 0;
+       struct hostapd_data *hapd = iface->bss[0];
+
+       /* TODO: should hapd pointer really be used here? This should most
+        * likely be per radio, not per BSS.. */
+
+       if (!hapd->conf->ieee80211n || hapd->conf->ht_op_mode_fixed)
+               return 0;
+
+       ht_operation = &hapd->conf->ht_operation;
+       operation_mode = le_to_host16(ht_operation->data.operation_mode);
+       wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X",
+                  __func__, operation_mode);
+
+       if ((operation_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) == 0
+           && iface->num_sta_ht_no_gf) {
+               operation_mode |= HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+               op_mode_changes++;
+       } else if ((operation_mode &
+                   HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) &&
+                  iface->num_sta_ht_no_gf == 0) {
+               operation_mode &= ~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+               op_mode_changes++;
+       }
+
+       if ((operation_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) == 0
+           && (iface->num_sta_no_ht || iface->olbc_ht)) {
+               operation_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+               op_mode_changes++;
+       } else if ((operation_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT)
+                  && (iface->num_sta_no_ht == 0 && iface->olbc_ht == 0)) {
+               operation_mode &= ~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+               op_mode_changes++;
+       }
+
+       /* Note: currently we switch to the MIXED op mode if HT non-greenfield
+        * station is associated. Probably it's a theoretical case, since
+        * it looks like all known HT STAs support greenfield.
+        */
+       new_op_mode = 0;
+       if (iface->num_sta_no_ht ||
+           (operation_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT))
+               new_op_mode = OP_MODE_MIXED;
+       else if ((hapd->conf->ht_capabilities.data.capabilities_info &
+                 HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+                 iface->num_sta_ht_20mhz)
+               new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED;
+       else if (iface->olbc_ht)
+               new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS;
+       else
+               new_op_mode = OP_MODE_PURE;
+
+       cur_op_mode = operation_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+       if (cur_op_mode != new_op_mode) {
+               operation_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+               operation_mode |= new_op_mode;
+               op_mode_changes++;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d",
+                  __func__, operation_mode, op_mode_changes);
+       ht_operation->data.operation_mode = host_to_le16(operation_mode);
+
+       return op_mode_changes;
+}
+
+#endif /* CONFIG_IEEE80211N */
+
+
 u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
                           int probe)
 {
@@ -326,6 +433,14 @@ ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start,
                        elems->ftie = pos;
                        elems->ftie_len = elen;
                        break;
+               case WLAN_EID_HT_CAP:
+                       elems->ht_capabilities = pos;
+                       elems->ht_capabilities_len = elen;
+                       break;
+               case WLAN_EID_HT_OPERATION:
+                       elems->ht_operation = pos;
+                       elems->ht_operation_len = elen;
+                       break;
                default:
                        unknown++;
                        if (!show_errors)
@@ -871,7 +986,8 @@ static void handle_assoc(struct hostapd_data *hapd,
 
        sta->capability = capab_info;
 
-       /* followed by SSID and Supported rates */
+       /* followed by SSID and Supported rates; and HT capabilities if 802.11n
+        * is used */
        if (ieee802_11_parse_elems(hapd, pos, left, &elems, 1) == ParseFailed
            || !elems.ssid) {
                printf("STA " MACSTR " sent invalid association request\n",
@@ -943,6 +1059,24 @@ static void handle_assoc(struct hostapd_data *hapd,
                sta->supported_rates_len += elems.ext_supp_rates_len;
        }
 
+#ifdef CONFIG_IEEE80211N
+       /* save HT capabilities in the sta object */
+       os_memset(&sta->ht_capabilities, 0, sizeof(sta->ht_capabilities));
+       if (elems.ht_capabilities &&
+           elems.ht_capabilities_len >= sizeof(struct ieee80211_ht_capability)
+           && (sta->flags & WLAN_STA_WME)) {
+               /* note: without WMM capability, treat the sta as non-HT */
+               sta->flags |= WLAN_STA_HT;
+               sta->ht_capabilities.id = WLAN_EID_HT_CAP;
+               sta->ht_capabilities.length =
+                       sizeof(struct ieee80211_ht_capability);
+               os_memcpy(&sta->ht_capabilities.data,
+                         elems.ht_capabilities,
+                         sizeof(struct ieee80211_ht_capability));
+       } else
+               sta->flags &= ~WLAN_STA_HT;
+#endif /* CONFIG_IEEE80211N */
+
        if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
                wpa_ie = elems.rsn_ie;
                wpa_ie_len = elems.rsn_ie_len;
@@ -1074,6 +1208,38 @@ static void handle_assoc(struct hostapd_data *hapd,
                        ieee802_11_set_beacons(hapd->iface);
        }
 
+#ifdef CONFIG_IEEE80211N
+       if (sta->flags & WLAN_STA_HT) {
+               if ((sta->ht_capabilities.data.capabilities_info &
+                    HT_CAP_INFO_GREEN_FIELD) == 0) {
+                       hapd->iface->num_sta_ht_no_gf++;
+                       wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no "
+                                  "greenfield, num of non-gf stations %d",
+                                  __func__, MAC2STR(sta->addr),
+                                  hapd->iface->num_sta_ht_no_gf);
+               }
+               if ((sta->ht_capabilities.data.capabilities_info &
+                    HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) {
+                       hapd->iface->num_sta_ht_20mhz++;
+                       wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, "
+                                  "num of 20MHz HT STAs %d",
+                                  __func__, MAC2STR(sta->addr),
+                                  hapd->iface->num_sta_ht_20mhz);
+               }
+       } else {
+               hapd->iface->num_sta_no_ht++;
+               if (hapd->conf->ieee80211n) {
+                       wpa_printf(MSG_DEBUG, "%s STA " MACSTR
+                                  " - no HT, num of non-HT stations %d",
+                                  __func__, MAC2STR(sta->addr),
+                                  hapd->iface->num_sta_no_ht);
+               }
+       }
+
+       if (hostapd_ht_operation_update(hapd->iface) > 0)
+               ieee802_11_set_beacons(hapd->iface);
+#endif /* CONFIG_IEEE80211N */
+
        /* get a unique AID */
        if (sta->aid > 0) {
                wpa_printf(MSG_DEBUG, "  old AID %d", sta->aid);
@@ -1139,6 +1305,13 @@ static void handle_assoc(struct hostapd_data *hapd,
                if (sta->flags & WLAN_STA_WME)
                        p = hostapd_eid_wme(hapd, p);
 
+#ifdef CONFIG_IEEE80211N
+               if (hapd->conf->ieee80211n) {
+                       p = hostapd_eid_ht_capabilities_info(hapd, p);
+                       p = hostapd_eid_ht_operation(hapd, p);
+               }
+#endif /* CONFIG_IEEE80211N */
+
 #ifdef CONFIG_IEEE80211R
                if (resp == WLAN_STATUS_SUCCESS) {
                        /* IEEE 802.11r: Mobility Domain Information, Fast BSS
@@ -1552,6 +1725,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
        u16 status;
        struct sta_info *sta;
        int new_assoc = 1;
+       struct ht_cap_ie *ht_cap = NULL;
 
        if (!ok) {
                hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
@@ -1602,9 +1776,15 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
        else
                mlme_associate_indication(hapd, sta);
 
+#ifdef CONFIG_IEEE80211N
+       if (sta->flags & WLAN_STA_HT)
+               ht_cap = &sta->ht_capabilities;
+#endif /* CONFIG_IEEE80211N */
+
        if (hostapd_sta_add(hapd->conf->iface, hapd, sta->addr, sta->aid,
                            sta->capability, sta->supported_rates,
-                           sta->supported_rates_len, 0, sta->listen_interval))
+                           sta->supported_rates_len, 0, sta->listen_interval,
+                           ht_cap))
        {
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_NOTICE,
index 9f2a4d8..186c0d3 100644 (file)
@@ -55,6 +55,10 @@ struct ieee802_11_elems {
        u8 mdie_len;
        u8 *ftie;
        u8 ftie_len;
+       u8 *ht_capabilities;
+       u8 ht_capabilities_len;
+       u8 *ht_operation;
+       u8 ht_operation_len;
 };
 
 typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -69,6 +73,7 @@ struct hostapd_frame_info {
        unsigned int passive_scan:1;
 };
 
+struct hostapd_iface;
 struct hostapd_data;
 struct sta_info;
 
@@ -91,5 +96,8 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
                           int probe);
 u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ht_capabilities_info(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
+int hostapd_ht_operation_update(struct hostapd_iface *iface);
 
 #endif /* IEEE802_11_H */
index 635e018..7bc7fae 100644 (file)
@@ -150,6 +150,21 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
                        set_beacon++;
        }
 
+#ifdef CONFIG_IEEE80211N
+       if (sta->flags & WLAN_STA_HT) {
+               if ((sta->ht_capabilities.data.capabilities_info &
+                    HT_CAP_INFO_GREEN_FIELD) == 0)
+                       hapd->iface->num_sta_ht_no_gf--;
+               if ((sta->ht_capabilities.data.capabilities_info &
+                    HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0)
+                       hapd->iface->num_sta_ht_20mhz--;
+       } else
+               hapd->iface->num_sta_no_ht--;
+
+       if (hostapd_ht_operation_update(hapd->iface) > 0)
+               set_beacon++;
+#endif /* CONFIG_IEEE80211N */
+
        if (set_beacon)
                ieee802_11_set_beacons(hapd->iface);
 
index f7c47d9..47fe0fb 100644 (file)
 #define WLAN_EID_IBSS_DFS 41
 /* EIDs defined by IEEE 802.11h - END */
 #define WLAN_EID_ERP_INFO 42
+#define WLAN_EID_HT_CAP 45
 #define WLAN_EID_RSN 48
 #define WLAN_EID_EXT_SUPP_RATES 50
 #define WLAN_EID_MOBILITY_DOMAIN 54
 #define WLAN_EID_FAST_BSS_TRANSITION 55
 #define WLAN_EID_TIMEOUT_INTERVAL 56
 #define WLAN_EID_RIC_DATA 57
+#define WLAN_EID_HT_OPERATION 61
+#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
+#define WLAN_EID_20_40_BSS_COEXISTENCE 72
+#define WLAN_EID_20_40_BSS_INTOLERANT 73
+#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
 #define WLAN_EID_VENDOR_SPECIFIC 221
 
 
@@ -312,4 +318,227 @@ struct ieee80211_mgmt {
 #define ERP_INFO_USE_PROTECTION BIT(1)
 #define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2)
 
+
+/* HT Capability element */
+
+#define MIMO_PWR_DONT_SEND_MIMO_SEQS            0
+#define MIMO_PWR_NEED2PRECEDE_MIMO_SEQS_BY_RTS  1
+#define MIMO_PWR_NO_LIMIT_ON_MIMO_SEQS          3
+
+enum {
+       MAX_RX_AMPDU_FACTOR_8KB = 0,
+       MAX_RX_AMPDU_FACTOR_16KB,
+       MAX_RX_AMPDU_FACTOR_32KB,
+       MAX_RX_AMPDU_FACTOR_64KB
+};
+
+enum {
+       CALIBRATION_NOT_SUPPORTED = 0,
+       CALIBRATION_CANNOT_INIT,
+       CALIBRATION_CAN_INIT,
+       CALIBRATION_FULL_SUPPORT
+};
+
+enum {
+       MCS_FEEDBACK_NOT_PROVIDED = 0,
+       MCS_FEEDBACK_UNSOLICITED,
+       MCS_FEEDBACK_MRQ_RESPONSE
+};
+
+
+struct ieee80211_ht_capability {
+       le16 capabilities_info;
+       u8 mac_ht_params_info;
+       u8 supported_mcs_set[16];
+       le16 extended_ht_capability_info;
+       le32 tx_BF_capability_info;
+       u8 antenna_selection_info;
+} STRUCT_PACKED;
+
+
+struct ieee80211_ht_operation {
+       u8 control_chan;
+       u8 ht_param;
+       le16 operation_mode;
+       le16 stbc_param;
+       u8 basic_set[16];
+} STRUCT_PACKED;
+
+/* auxiliary bit manipulation macros FIXME: move it to common later... */
+#define SET_2BIT_U8(_ptr_, _shift_, _val_)                             \
+       ((*(_ptr_) &= ~(3 << (_shift_))),                               \
+        (*(_ptr_) |= (*(_ptr_) & (((u8)3) << (_shift_))) |             \
+                     (((u8)(_val_) & 3) << _shift_)))
+
+#define GET_2BIT_U8(_var_, _shift_)    \
+       (((_var_) & (((u8)3) << (_shift_))) >> (_shift_))
+
+#define SET_2BIT_LE16(_u16ptr_, _shift_, _val_)                                \
+       ((*(_u16ptr_) &= ~(3 << (_shift_))),                            \
+        (*(_u16ptr_) |=                                                \
+               (((*(_u16ptr_)) & (((u16)3) << ((u16)_shift_))) |       \
+               (((u16)(_val_) & (u16)3) << (u16)(_shift_)))))
+
+#define GET_2BIT_LE16(_var_, _shift_)  \
+       (((_var_) & (((u16)3) << (_shift_))) >> (_shift_))
+
+#define SET_2BIT_LE32(_u32ptr_, _shift_, _val_)                                \
+       ((*(_u32ptr_) &= ~(3 << (_shift_))),                            \
+        (*(_u32ptr_) |= (((*(_u32ptr_)) & (((u32)3) << (_shift_))) |   \
+                       (((u32)(_val_) & 3) << _shift_))))
+
+#define GET_2BIT_LE32(_var_, _shift_)  \
+       (((_var_) & (((u32)3) << (_shift_))) >> (_shift_))
+
+#define SET_3BIT_LE16(_u16ptr_, _shift_, _val_)                                \
+       ((*(_u16ptr_) &= ~(7 << (_shift_))),                            \
+       (*(_u16ptr_) |= (((*(_u16ptr_)) & (((u16)7) << (_shift_))) |    \
+                       (((u16)(_val_) & 7) << _shift_))))
+
+#define GET_3BIT_LE16(_var_, _shift_)  \
+       (((_var_) & (((u16)7) << (_shift_))) >> (_shift_))
+
+#define SET_3BIT_LE32(_u32ptr_, _shift_, _val_)                                \
+       ((*(_u32ptr_) &= ~(7 << (_shift_))),                            \
+        (*(_u32ptr_) |= (((*(_u32ptr_)) & (((u32)7) << (_shift_))) |   \
+                       (((u32)(_val_) & 7) << _shift_))))
+
+#define GET_3BIT_LE32(_var_, _shift_)  \
+       (((_var_) & (((u32)7) << (_shift_))) >> (_shift_))
+
+
+#define HT_CAP_INFO_ADVANCED_CODDING_CAP       ((u16) BIT(0))
+#define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET     ((u16) BIT(1))
+#define HT_CAP_INFO_MIMO_PWR_SAVE_OFFSET       2
+#define HT_CAP_INFO_GREEN_FIELD                        ((u16) BIT(4))
+#define HT_CAP_INFO_SHORT_GI20MHZ              ((u16) BIT(5))
+#define HT_CAP_INFO_SHORT_GI40MHZ              ((u16) BIT(6))
+#define HT_CAP_INFO_TX_STBC                    ((u16) BIT(7))
+#define HT_CAP_INFO_RX_STBC_OFFSET             8
+#define HT_CAP_INFO_DELAYED_BA                 ((u16) BIT(10))
+#define HT_CAP_INFO_MAX_AMSDU_SIZE             ((u16) BIT(11))
+#define HT_CAP_INFO_DSSS_CCK40MHZ              ((u16) BIT(12))
+#define HT_CAP_INFO_PSMP_SUPP                  ((u16) BIT(13))
+#define HT_CAP_INFO_STBC_CTRL_FRAME_SUPP       ((u16) BIT(14))
+#define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT  ((u16) BIT(15))
+
+
+#define MAC_HT_PARAM_INFO_MAX_RX_AMPDU_FACTOR_OFFSET   0
+#define MAC_HT_PARAM_INFO_MAX_MPDU_DENSITY_OFFSET      2
+
+#define EXT_HT_CAP_INFO_PCO                    ((u16) BIT(0))
+#define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET      1
+#define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET    8
+#define EXT_HT_CAP_INFO_HTC_SUPPORTED          ((u16) BIT(10))
+#define EXT_HT_CAP_INFO_RD_RESPONDER           ((u16) BIT(11))
+
+
+#define TX_BEAMFORM_CAP_TXBF_CAP ((u32) BIT(0))
+#define TX_BEAMFORM_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1))
+#define TX_BEAMFORM_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2))
+#define TX_BEAMFORM_CAP_RX_ZLF_CAP ((u32) BIT(3))
+#define TX_BEAMFORM_CAP_TX_ZLF_CAP ((u32) BIT(4))
+#define TX_BEAMFORM_CAP_IMPLICIT_ZLF_CAP ((u32) BIT(5))
+#define TX_BEAMFORM_CAP_CALIB_OFFSET 6
+#define TX_BEAMFORM_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8))
+#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_CAP ((u32) BIT(9))
+#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_CAP ((u32) BIT(10))
+#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11
+#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13
+#define TX_BEAMFORM_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15
+#define TX_BEAMFORM_CAP_MINIMAL_GROUPING_OFFSET 17
+#define TX_BEAMFORM_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19
+#define TX_BEAMFORM_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21
+#define TX_BEAMFORM_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23
+#define TX_BEAMFORM_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25
+
+
+#define ASEL_CAPABILITY_ASEL_CAPABLE ((u8) BIT(0))
+#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1))
+#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2))
+#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3))
+#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4))
+#define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5))
+#define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6))
+
+
+struct ht_cap_ie {
+       u8 id;
+       u8 length;
+       struct ieee80211_ht_capability data;
+} STRUCT_PACKED;
+
+
+#define EXT_CHNL_OFF_NONE   0
+#define EXT_CHNL_OFF_ABOVE  1
+#define EXT_CHNL_OFF_BELOW  3
+
+#define REC_TRANS_CHNL_WIDTH_20     0
+#define REC_TRANS_CHNL_WIDTH_ANY    1
+
+#define OP_MODE_PURE                    0
+#define OP_MODE_MAY_BE_LEGACY_STAS      1
+#define OP_MODE_20MHZ_HT_STA_ASSOCED    2
+#define OP_MODE_MIXED                   3
+
+#define HT_INFO_HT_PARAM_EXT_CHNL_OFF_OFFSET           0
+#define HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH          ((u8) BIT(2))
+#define HT_INFO_HT_PARAM_RIFS_MODE                     ((u8) BIT(3))
+#define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY              ((u8) BIT(4))
+#define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY      ((u8) BIT(5))
+
+#define HT_INFO_OPERATION_MODE_OP_MODE_MASK    \
+               ((le16) (0x0001 | 0x0002))
+#define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET          0
+#define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT     ((u8) BIT(2))
+#define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT    ((u8) BIT(3))
+#define HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT      ((u8) BIT(4))
+
+#define HT_INFO_STBC_PARAM_DUAL_BEACON                 ((u16) BIT(6))
+#define HT_INFO_STBC_PARAM_DUAL_STBC_PROTECT           ((u16) BIT(7))
+#define HT_INFO_STBC_PARAM_SECONDARY_BCN               ((u16) BIT(8))
+#define HT_INFO_STBC_PARAM_LSIG_TXOP_PROTECT_ALLOWED   ((u16) BIT(9))
+#define HT_INFO_STBC_PARAM_PCO_ACTIVE                  ((u16) BIT(10))
+#define HT_INFO_STBC_PARAM_PCO_PHASE                   ((u16) BIT(11))
+
+struct ht_operation_ie {
+       u8 id;
+       u8 length;
+       struct ieee80211_ht_operation data;
+} STRUCT_PACKED;
+
+
+#define HT_CAPABILITIES_LEN (sizeof(struct ht_cap_ie) - 2)
+#define HT_OPERATION_LEN (sizeof(struct ht_operation_ie) - 2)
+
+/* Secondary channel offset element */
+#define SECONDARY_CHANNEL_OFFSET_NONE  0
+#define SECONDARY_CHANNEL_OFFSET_ABOVE 1
+#define SECONDARY_CHANNEL_OFFSET_BELOW 3
+struct secondary_channel_offset_ie {
+       u8 id;
+       u8 length;
+       u8 secondary_offset_offset;
+} STRUCT_PACKED;
+
+
+/* body of Recommended Transmit Channel Width action frame */
+#define CHANNEL_WIDTH_20       0
+#define CHANNEL_WIDTH_ANY      1
+struct recommended_tx_channel_width_action {
+       u8 category;
+       u8 action;
+       u8 channel_width;
+} STRUCT_PACKED;
+
+/* body of MIMO Power Save action frame */
+#define PWR_SAVE_MODE_STATIC   0
+#define PWR_SAVE_MODE_DYNAMIC  1
+struct mimo_pwr_save_action {
+       u8 category;
+       u8 action;
+       u8 enable;
+       u8 mode;
+} STRUCT_PACKED;
+
 #endif /* IEEE802_11_DEFS_H */