Commit f59ac048 authored by Luis R. Rodriguez's avatar Luis R. Rodriguez Committed by John W. Linville

cfg80211: keep track of supported interface modes

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: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarStephen Blackheath <tramp.enshrine.stephen@blacksapphire.com>
Signed-off-by: default avatarIvo van Doorn <IvDoorn@gmail.com>
Signed-off-by: default avatarLuis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent c6e387a2
...@@ -1884,6 +1884,7 @@ static int __devinit adm8211_probe(struct pci_dev *pdev, ...@@ -1884,6 +1884,7 @@ static int __devinit adm8211_probe(struct pci_dev *pdev,
dev->extra_tx_headroom = sizeof(struct adm8211_tx_hdr); dev->extra_tx_headroom = sizeof(struct adm8211_tx_hdr);
/* dev->flags = IEEE80211_HW_RX_INCLUDES_FCS in promisc mode */ /* dev->flags = IEEE80211_HW_RX_INCLUDES_FCS in promisc mode */
dev->flags = IEEE80211_HW_SIGNAL_UNSPEC; dev->flags = IEEE80211_HW_SIGNAL_UNSPEC;
dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
dev->channel_change_time = 1000; dev->channel_change_time = 1000;
dev->max_signal = 100; /* FIXME: find better value */ dev->max_signal = 100; /* FIXME: find better value */
......
...@@ -485,6 +485,12 @@ ath5k_pci_probe(struct pci_dev *pdev, ...@@ -485,6 +485,12 @@ ath5k_pci_probe(struct pci_dev *pdev,
hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_NOISE_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->extra_tx_headroom = 2;
hw->channel_change_time = 5000; hw->channel_change_time = 5000;
sc = hw->priv; sc = hw->priv;
......
...@@ -1482,6 +1482,11 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -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_SIGNAL_DBM |
IEEE80211_HW_NOISE_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); SET_IEEE80211_DEV(hw, &pdev->dev);
pci_set_drvdata(pdev, hw); pci_set_drvdata(pdev, hw);
......
...@@ -4569,6 +4569,13 @@ static int b43_wireless_init(struct ssb_device *dev) ...@@ -4569,6 +4569,13 @@ static int b43_wireless_init(struct ssb_device *dev)
IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_NOISE_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; hw->queues = b43_modparam_qos ? 4 : 1;
SET_IEEE80211_DEV(hw, dev->dev); SET_IEEE80211_DEV(hw, dev->dev);
if (is_valid_ether_addr(sprom->et1mac)) if (is_valid_ether_addr(sprom->et1mac))
......
...@@ -3704,6 +3704,11 @@ static int b43legacy_wireless_init(struct ssb_device *dev) ...@@ -3704,6 +3704,11 @@ static int b43legacy_wireless_init(struct ssb_device *dev)
hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_NOISE_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 */ hw->queues = 1; /* FIXME: hardware has more queues */
SET_IEEE80211_DEV(hw, dev->dev); SET_IEEE80211_DEV(hw, dev->dev);
if (is_valid_ether_addr(sprom->et1mac)) if (is_valid_ether_addr(sprom->et1mac))
......
...@@ -819,6 +819,10 @@ int iwl_setup_mac(struct iwl_priv *priv) ...@@ -819,6 +819,10 @@ int iwl_setup_mac(struct iwl_priv *priv)
/* Tell mac80211 our characteristics */ /* Tell mac80211 our characteristics */
hw->flags = IEEE80211_HW_SIGNAL_DBM | hw->flags = IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_NOISE_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 */ /* Default value; 4 EDCA QOS priorities */
hw->queues = 4; hw->queues = 4;
/* queues to support 11n aggregation */ /* queues to support 11n aggregation */
......
...@@ -7888,6 +7888,11 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e ...@@ -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 | hw->flags = IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_NOISE_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 */ /* 4 EDCA QOS priorities */
hw->queues = 4; hw->queues = 4;
......
...@@ -447,6 +447,9 @@ static int __init init_mac80211_hwsim(void) ...@@ -447,6 +447,9 @@ static int __init init_mac80211_hwsim(void)
hw->channel_change_time = 1; hw->channel_change_time = 1;
hw->queues = 4; hw->queues = 4;
hw->wiphy->interface_modes =
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP);
hw->ampdu_queues = 1; hw->ampdu_queues = 1;
memcpy(data->channels, hwsim_channels, sizeof(hwsim_channels)); memcpy(data->channels, hwsim_channels, sizeof(hwsim_channels));
......
...@@ -1072,6 +1072,9 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) ...@@ -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 */ dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | /* not sure */
IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_SIGNAL_UNSPEC; IEEE80211_HW_SIGNAL_UNSPEC;
dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
dev->channel_change_time = 1000; /* TODO: find actual value */ dev->channel_change_time = 1000; /* TODO: find actual value */
dev->max_signal = 127; dev->max_signal = 127;
......
...@@ -1052,6 +1052,11 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) ...@@ -1052,6 +1052,11 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
*/ */
rt2x00dev->hw->vif_data_size = sizeof(struct rt2x00_intf); 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. * Let the driver probe the device to detect the capabilities.
*/ */
......
...@@ -1184,6 +1184,8 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, ...@@ -1184,6 +1184,8 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
dev->max_signal = 65; dev->max_signal = 65;
} }
dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b) if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b)
printk(KERN_INFO "rtl8187: inconsistency between id with OEM" printk(KERN_INFO "rtl8187: inconsistency between id with OEM"
" info!\n"); " info!\n");
......
...@@ -937,6 +937,11 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf) ...@@ -937,6 +937,11 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_SIGNAL_DB; 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->max_signal = 100;
hw->queues = 1; hw->queues = 1;
hw->extra_tx_headroom = sizeof(struct zd_ctrlset); hw->extra_tx_headroom = sizeof(struct zd_ctrlset);
......
...@@ -210,6 +210,10 @@ enum nl80211_commands { ...@@ -210,6 +210,10 @@ enum nl80211_commands {
* @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from
* association request when used with NL80211_CMD_NEW_STATION) * 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_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
*/ */
...@@ -259,6 +263,8 @@ enum nl80211_attrs { ...@@ -259,6 +263,8 @@ enum nl80211_attrs {
NL80211_ATTR_HT_CAPABILITY, NL80211_ATTR_HT_CAPABILITY,
NL80211_ATTR_SUPPORTED_IFTYPES,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
......
...@@ -185,6 +185,9 @@ struct wiphy { ...@@ -185,6 +185,9 @@ struct wiphy {
/* permanent MAC address */ /* permanent MAC address */
u8 perm_addr[ETH_ALEN]; 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. /* If multiple wiphys are registered and you're handed e.g.
* a regular netdev with assigned ieee80211_ptr, you won't * a regular netdev with assigned ieee80211_ptr, you won't
* know whether it points to a wiphy your driver has registered * know whether it points to a wiphy your driver has registered
......
...@@ -1675,6 +1675,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ...@@ -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); result = wiphy_register(local->hw.wiphy);
if (result < 0) if (result < 0)
return result; return result;
......
/* /*
* This is the linux wireless configuration interface. * 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> #include <linux/if.h>
...@@ -259,6 +259,13 @@ int wiphy_register(struct wiphy *wiphy) ...@@ -259,6 +259,13 @@ int wiphy_register(struct wiphy *wiphy)
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
bool have_band = false; bool have_band = false;
int i; 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 */ /* sanity check supported bands/channels */
for (band = 0; band < IEEE80211_NUM_BANDS; band++) { for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
......
...@@ -113,10 +113,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, ...@@ -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_bands, *nl_band;
struct nlattr *nl_freqs, *nl_freq; struct nlattr *nl_freqs, *nl_freq;
struct nlattr *nl_rates, *nl_rate; struct nlattr *nl_rates, *nl_rate;
struct nlattr *nl_modes;
enum ieee80211_band band; enum ieee80211_band band;
struct ieee80211_channel *chan; struct ieee80211_channel *chan;
struct ieee80211_rate *rate; struct ieee80211_rate *rate;
int i; int i;
u16 ifmodes = dev->wiphy.interface_modes;
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY); hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
if (!hdr) if (!hdr)
...@@ -125,6 +127,20 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, ...@@ -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_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); 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); nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
if (!nl_bands) if (!nl_bands)
goto nla_put_failure; goto nla_put_failure;
...@@ -415,7 +431,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) ...@@ -415,7 +431,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
ifindex = dev->ifindex; ifindex = dev->ifindex;
dev_put(dev); dev_put(dev);
if (!drv->ops->change_virtual_intf) { if (!drv->ops->change_virtual_intf ||
!(drv->wiphy.interface_modes & (1 << type))) {
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto unlock; goto unlock;
} }
...@@ -462,7 +479,8 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) ...@@ -462,7 +479,8 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
if (IS_ERR(drv)) if (IS_ERR(drv))
return PTR_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; err = -EOPNOTSUPP;
goto unlock; goto unlock;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment