cfg80211: keep track of supported interface modes
authorLuis R. Rodriguez <lrodriguez@atheros.com>
Fri, 29 Aug 2008 23:26:43 +0000 (16:26 -0700)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 5 Sep 2008 20:17:42 +0000 (16:17 -0400)
It is obviously good for userspace to know up front which
interface modes a given piece of hardware might support (even
if adding such an interface might fail later because of
concurrency issues), so let's make cfg80211 aware of that.
For good measure, disallow adding interfaces in all other
modes so drivers don't forget to announce support for one mode
when they add it.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Stephen Blackheath <tramp.enshrine.stephen@blacksapphire.com>
Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
17 files changed:
drivers/net/wireless/adm8211.c
drivers/net/wireless/ath5k/base.c
drivers/net/wireless/ath9k/main.c
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43legacy/main.c
drivers/net/wireless/iwlwifi/iwl-core.c
drivers/net/wireless/iwlwifi/iwl3945-base.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/p54/p54common.c
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rtl8187_dev.c
drivers/net/wireless/zd1211rw/zd_mac.c
include/linux/nl80211.h
include/net/wireless.h
net/mac80211/main.c
net/wireless/core.c
net/wireless/nl80211.c

index 3333d4596b8d1cabcd40dbc8d0ec67c8b588a0fc..c6a55cd12db936c64493a5b172898aae0e90f8be 100644 (file)
@@ -1884,6 +1884,7 @@ static int __devinit adm8211_probe(struct pci_dev *pdev,
        dev->extra_tx_headroom = sizeof(struct adm8211_tx_hdr);
        /* dev->flags = IEEE80211_HW_RX_INCLUDES_FCS in promisc mode */
        dev->flags = IEEE80211_HW_SIGNAL_UNSPEC;
+       dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
 
        dev->channel_change_time = 1000;
        dev->max_signal = 100;    /* FIXME: find better value */
index 7989ab5c2bbae8850e7a9f8f4dfc27211a8088f4..85260c39aa2b80dce9b463d75042a94ec67c234a 100644 (file)
@@ -485,6 +485,12 @@ ath5k_pci_probe(struct pci_dev *pdev,
        hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
                    IEEE80211_HW_SIGNAL_DBM |
                    IEEE80211_HW_NOISE_DBM;
+
+       hw->wiphy->interface_modes =
+               BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_ADHOC) |
+               BIT(NL80211_IFTYPE_MESH_POINT);
+
        hw->extra_tx_headroom = 2;
        hw->channel_change_time = 5000;
        sc = hw->priv;
index dc45eef3289a010d137e82530a40598f04c9fa33..39a4a70d01305f239d66cc48e01b846a9ff79eb0 100644 (file)
@@ -1482,6 +1482,11 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                IEEE80211_HW_SIGNAL_DBM |
                IEEE80211_HW_NOISE_DBM;
 
+       hw->wiphy->interface_modes =
+               BIT(NL80211_IFTYPE_AP) |
+               BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_ADHOC);
+
        SET_IEEE80211_DEV(hw, &pdev->dev);
        pci_set_drvdata(pdev, hw);
 
index 63bafc2f3f0a29e42807bb348f302f09063db56e..2d915c1a82a18f17edbe7609e5152db662168761 100644 (file)
@@ -4569,6 +4569,13 @@ static int b43_wireless_init(struct ssb_device *dev)
                    IEEE80211_HW_SIGNAL_DBM |
                    IEEE80211_HW_NOISE_DBM;
 
+       hw->wiphy->interface_modes =
+               BIT(NL80211_IFTYPE_AP) |
+               BIT(NL80211_IFTYPE_MESH_POINT) |
+               BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_WDS) |
+               BIT(NL80211_IFTYPE_ADHOC);
+
        hw->queues = b43_modparam_qos ? 4 : 1;
        SET_IEEE80211_DEV(hw, dev->dev);
        if (is_valid_ether_addr(sprom->et1mac))
index 1cb77db5c29265d55d059b398f0c3dcb04c3b47a..68f63f5093afeda892451ac5cb4acc93e17c7952 100644 (file)
@@ -3704,6 +3704,11 @@ static int b43legacy_wireless_init(struct ssb_device *dev)
        hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
                    IEEE80211_HW_SIGNAL_DBM |
                    IEEE80211_HW_NOISE_DBM;
+       hw->wiphy->interface_modes =
+               BIT(NL80211_IFTYPE_AP) |
+               BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_WDS) |
+               BIT(NL80211_IFTYPE_ADHOC);
        hw->queues = 1; /* FIXME: hardware has more queues */
        SET_IEEE80211_DEV(hw, dev->dev);
        if (is_valid_ether_addr(sprom->et1mac))
index fbf75a62958dd85f1d493ca8978219fa5f3df691..0a511ef8e354c562d65732dd8a9a729ec554301e 100644 (file)
@@ -819,6 +819,10 @@ int iwl_setup_mac(struct iwl_priv *priv)
        /* Tell mac80211 our characteristics */
        hw->flags = IEEE80211_HW_SIGNAL_DBM |
                    IEEE80211_HW_NOISE_DBM;
+       hw->wiphy->interface_modes =
+               BIT(NL80211_IFTYPE_AP) |
+               BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_ADHOC);
        /* Default value; 4 EDCA QOS priorities */
        hw->queues = 4;
        /* queues to support 11n aggregation */
index a622fc33590a3517aacb86afc729cb397c19baad..cee3045f1606ebe905a9aa4578189b871093e8e8 100644 (file)
@@ -7888,6 +7888,11 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
        hw->flags = IEEE80211_HW_SIGNAL_DBM |
                    IEEE80211_HW_NOISE_DBM;
 
+       hw->wiphy->interface_modes =
+               BIT(NL80211_IFTYPE_AP) |
+               BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_ADHOC);
+
        /* 4 EDCA QOS priorities */
        hw->queues = 4;
 
index 732429d491222a7567a3dc4fc15d96926887c0d5..6ba50f087f7bfe31a2ec2bd7f03b390b932e862e 100644 (file)
@@ -447,6 +447,9 @@ static int __init init_mac80211_hwsim(void)
 
                hw->channel_change_time = 1;
                hw->queues = 4;
+               hw->wiphy->interface_modes =
+                       BIT(NL80211_IFTYPE_STATION) |
+                       BIT(NL80211_IFTYPE_AP);
                hw->ampdu_queues = 1;
 
                memcpy(data->channels, hwsim_channels, sizeof(hwsim_channels));
index 17e06bbc996a1b5a4eefa5abe142c0099d919733..6da98e6e6a9a61acfcff290301a52a6943442488 100644 (file)
@@ -1072,6 +1072,9 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
        dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | /* not sure */
                     IEEE80211_HW_RX_INCLUDES_FCS |
                     IEEE80211_HW_SIGNAL_UNSPEC;
+
+       dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+
        dev->channel_change_time = 1000;        /* TODO: find actual value */
        dev->max_signal = 127;
 
index 369b0b2d8643ae8a8d583e4ed806024d8e113b37..2f3bfc60688090e494cb47e52a308e51966597aa 100644 (file)
@@ -1052,6 +1052,11 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
         */
        rt2x00dev->hw->vif_data_size = sizeof(struct rt2x00_intf);
 
+       rt2x00dev->hw->wiphy->interface_modes =
+           BIT(NL80211_IFTYPE_AP) |
+           BIT(NL80211_IFTYPE_STATION) |
+           BIT(NL80211_IFTYPE_ADHOC);
+
        /*
         * Let the driver probe the device to detect the capabilities.
         */
index 060a265053583f867f4b6eae86fcb518932927bc..8a42bfa6d4f0f4b2f1aa7df61c86231307960161 100644 (file)
@@ -1184,6 +1184,8 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
                dev->max_signal = 65;
        }
 
+       dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+
        if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b)
                printk(KERN_INFO "rtl8187: inconsistency between id with OEM"
                       " info!\n");
index 4d7b98b050304684d6b9d09d4b3dbc34209348fb..e019102b2285ec153eaa18f4b0a8387def9ab4f4 100644 (file)
@@ -937,6 +937,11 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
        hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
                    IEEE80211_HW_SIGNAL_DB;
 
+       hw->wiphy->interface_modes =
+               BIT(NL80211_IFTYPE_MESH_POINT) |
+               BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_ADHOC);
+
        hw->max_signal = 100;
        hw->queues = 1;
        hw->extra_tx_headroom = sizeof(struct zd_ctrlset);
index 0c1147de3ec792f866bb1152ae50c4fe24de38d7..5e51f4e7600b8e25bed549ce92764be7e089608c 100644 (file)
@@ -210,6 +210,10 @@ enum nl80211_commands {
  * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from
  *     association request when used with NL80211_CMD_NEW_STATION)
  *
+ * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all
+ *     supported interface types, each a flag attribute with the number
+ *     of the interface mode.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -259,6 +263,8 @@ enum nl80211_attrs {
 
        NL80211_ATTR_HT_CAPABILITY,
 
+       NL80211_ATTR_SUPPORTED_IFTYPES,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
index 9324f8dd183eac07d299269fdf0f836bd6c8f193..1dc8ec3daa2ff6fcf0fad9857d40354d6cccf6f9 100644 (file)
@@ -185,6 +185,9 @@ struct wiphy {
        /* permanent MAC address */
        u8 perm_addr[ETH_ALEN];
 
+       /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */
+       u16 interface_modes;
+
        /* If multiple wiphys are registered and you're handed e.g.
         * a regular netdev with assigned ieee80211_ptr, you won't
         * know whether it points to a wiphy your driver has registered
index 638b75f36e23aa8f4462744b1f48afc6e06249dd..396cfb2d0f4646fdd93623131309e9b385f45e44 100644 (file)
@@ -1675,6 +1675,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                }
        }
 
+       /* if low-level driver supports AP, we also support VLAN */
+       if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP))
+               local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN);
+
+       /* mac80211 always supports monitor */
+       local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
+
        result = wiphy_register(local->hw.wiphy);
        if (result < 0)
                return result;
index f1da0b93bc56d177cae4d350ce470c29c1e4da4c..7e995ac06a0c70cffdd42ce233545da4249da458 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This is the linux wireless configuration interface.
  *
- * Copyright 2006, 2007                Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2006-2008         Johannes Berg <johannes@sipsolutions.net>
  */
 
 #include <linux/if.h>
@@ -259,6 +259,13 @@ int wiphy_register(struct wiphy *wiphy)
        struct ieee80211_supported_band *sband;
        bool have_band = false;
        int i;
+       u16 ifmodes = wiphy->interface_modes;
+
+       /* sanity check ifmodes */
+       WARN_ON(!ifmodes);
+       ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1;
+       if (WARN_ON(ifmodes != wiphy->interface_modes))
+               wiphy->interface_modes = ifmodes;
 
        /* sanity check supported bands/channels */
        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
index 4d6c02afd6f5474744b27fd151757b6c5bc6125d..77880ba8b619c806195142baf78d603e93d5fcfe 100644 (file)
@@ -113,10 +113,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        struct nlattr *nl_bands, *nl_band;
        struct nlattr *nl_freqs, *nl_freq;
        struct nlattr *nl_rates, *nl_rate;
+       struct nlattr *nl_modes;
        enum ieee80211_band band;
        struct ieee80211_channel *chan;
        struct ieee80211_rate *rate;
        int i;
+       u16 ifmodes = dev->wiphy.interface_modes;
 
        hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
        if (!hdr)
@@ -125,6 +127,20 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
        NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
 
+       nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
+       if (!nl_modes)
+               goto nla_put_failure;
+
+       i = 0;
+       while (ifmodes) {
+               if (ifmodes & 1)
+                       NLA_PUT_FLAG(msg, i);
+               ifmodes >>= 1;
+               i++;
+       }
+
+       nla_nest_end(msg, nl_modes);
+
        nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
        if (!nl_bands)
                goto nla_put_failure;
@@ -415,7 +431,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
        ifindex = dev->ifindex;
        dev_put(dev);
 
-       if (!drv->ops->change_virtual_intf) {
+       if (!drv->ops->change_virtual_intf ||
+           !(drv->wiphy.interface_modes & (1 << type))) {
                err = -EOPNOTSUPP;
                goto unlock;
        }
@@ -462,7 +479,8 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
        if (IS_ERR(drv))
                return PTR_ERR(drv);
 
-       if (!drv->ops->add_virtual_intf) {
+       if (!drv->ops->add_virtual_intf ||
+           !(drv->wiphy.interface_modes & (1 << type))) {
                err = -EOPNOTSUPP;
                goto unlock;
        }