mwifiex: multi-interface support for mwifiex
[deliverable/linux.git] / drivers / net / wireless / mwifiex / cfg80211.c
index 65050384c42b061100fe9bd8ca42ce302b0cc369..453239389d26bc0373930d1d640fe5c459885e88 100644 (file)
 #include "cfg80211.h"
 #include "main.h"
 
+static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = {
+       {
+               .max = 1, .types = BIT(NL80211_IFTYPE_STATION),
+       },
+       {
+               .max = 1, .types = BIT(NL80211_IFTYPE_AP),
+       },
+};
+
+static const struct ieee80211_iface_combination mwifiex_iface_comb_ap_sta = {
+       .limits = mwifiex_ap_sta_limits,
+       .num_different_channels = 1,
+       .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits),
+       .max_interfaces = MWIFIEX_MAX_BSS_NUM,
+       .beacon_int_infra_match = true,
+};
+
 /*
  * This function maps the nl802.11 channel type into driver channel type.
  *
@@ -516,25 +533,23 @@ static int
 mwifiex_dump_station_info(struct mwifiex_private *priv,
                          struct station_info *sinfo)
 {
-       struct mwifiex_ds_get_signal signal;
        struct mwifiex_rate_cfg rate;
-       int ret = 0;
 
        sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES |
-               STATION_INFO_RX_PACKETS |
-               STATION_INFO_TX_PACKETS
-               | STATION_INFO_SIGNAL | STATION_INFO_TX_BITRATE;
+                       STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS |
+                       STATION_INFO_TX_BITRATE |
+                       STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
 
        /* Get signal information from the firmware */
-       memset(&signal, 0, sizeof(struct mwifiex_ds_get_signal));
-       if (mwifiex_get_signal_info(priv, &signal)) {
-               dev_err(priv->adapter->dev, "getting signal information\n");
-               ret = -EFAULT;
+       if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_RSSI_INFO,
+                                 HostCmd_ACT_GEN_GET, 0, NULL)) {
+               dev_err(priv->adapter->dev, "failed to get signal information\n");
+               return -EFAULT;
        }
 
        if (mwifiex_drv_get_data_rate(priv, &rate)) {
                dev_err(priv->adapter->dev, "getting data rate\n");
-               ret = -EFAULT;
+               return -EFAULT;
        }
 
        /* Get DTIM period information from firmware */
@@ -557,11 +572,12 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
                        sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
        }
 
+       sinfo->signal_avg = priv->bcn_rssi_avg;
        sinfo->rx_bytes = priv->stats.rx_bytes;
        sinfo->tx_bytes = priv->stats.tx_bytes;
        sinfo->rx_packets = priv->stats.rx_packets;
        sinfo->tx_packets = priv->stats.tx_packets;
-       sinfo->signal = priv->qual_level;
+       sinfo->signal = priv->bcn_rssi_avg;
        /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */
        sinfo->txrate.legacy = rate.rate * 5;
 
@@ -581,7 +597,7 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
                        priv->curr_bss_params.bss_descriptor.beacon_period;
        }
 
-       return ret;
+       return 0;
 }
 
 /*
@@ -604,6 +620,23 @@ mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
        return mwifiex_dump_station_info(priv, sinfo);
 }
 
+/*
+ * CFG802.11 operation handler to dump station information.
+ */
+static int
+mwifiex_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
+                             int idx, u8 *mac, struct station_info *sinfo)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+       if (!priv->media_connected || idx)
+               return -ENOENT;
+
+       memcpy(mac, priv->cfg_bssid, ETH_ALEN);
+
+       return mwifiex_dump_station_info(priv, sinfo);
+}
+
 /* Supported rates to be advertised to the cfg80211 */
 
 static struct ieee80211_rate mwifiex_rates[] = {
@@ -749,6 +782,45 @@ static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy,
        return 0;
 }
 
+/*
+ * CFG802.11 operation handler for connection quality monitoring.
+ *
+ * This function subscribes/unsubscribes HIGH_RSSI and LOW_RSSI
+ * events to FW.
+ */
+static int mwifiex_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
+                                               struct net_device *dev,
+                                               s32 rssi_thold, u32 rssi_hyst)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       struct mwifiex_ds_misc_subsc_evt subsc_evt;
+
+       priv->cqm_rssi_thold = rssi_thold;
+       priv->cqm_rssi_hyst = rssi_hyst;
+
+       memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt));
+       subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH;
+
+       /* Subscribe/unsubscribe low and high rssi events */
+       if (rssi_thold && rssi_hyst) {
+               subsc_evt.action = HostCmd_ACT_BITWISE_SET;
+               subsc_evt.bcn_l_rssi_cfg.abs_value = abs(rssi_thold);
+               subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold);
+               subsc_evt.bcn_l_rssi_cfg.evt_freq = 1;
+               subsc_evt.bcn_h_rssi_cfg.evt_freq = 1;
+               return mwifiex_send_cmd_sync(priv,
+                                            HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
+                                            0, 0, &subsc_evt);
+       } else {
+               subsc_evt.action = HostCmd_ACT_BITWISE_CLR;
+               return mwifiex_send_cmd_sync(priv,
+                                            HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
+                                            0, 0, &subsc_evt);
+       }
+
+       return 0;
+}
+
 /*
  * CFG802.11 operation handler for disconnection request.
  *
@@ -1107,6 +1179,17 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev,
        priv->user_scan_cfg->num_ssids = request->n_ssids;
        priv->user_scan_cfg->ssid_list = request->ssids;
 
+       if (request->ie && request->ie_len) {
+               for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) {
+                       if (priv->vs_ie[i].mask != MWIFIEX_VSIE_MASK_CLEAR)
+                               continue;
+                       priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_SCAN;
+                       memcpy(&priv->vs_ie[i].ie, request->ie,
+                              request->ie_len);
+                       break;
+               }
+       }
+
        for (i = 0; i < request->n_channels; i++) {
                chan = request->channels[i];
                priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
@@ -1124,6 +1207,15 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev,
        if (mwifiex_set_user_scan_ioctl(priv, priv->user_scan_cfg))
                return -EFAULT;
 
+       if (request->ie && request->ie_len) {
+               for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) {
+                       if (priv->vs_ie[i].mask == MWIFIEX_VSIE_MASK_SCAN) {
+                               priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_CLEAR;
+                               memset(&priv->vs_ie[i].ie, 0,
+                                      MWIFIEX_MAX_VSIE_LEN);
+                       }
+               }
+       }
        return 0;
 }
 
@@ -1212,6 +1304,7 @@ struct net_device *mwifiex_add_virtual_intf(struct wiphy *wiphy,
        struct mwifiex_adapter *adapter;
        struct net_device *dev;
        void *mdev_priv;
+       struct wireless_dev *wdev;
 
        if (!priv)
                return NULL;
@@ -1224,12 +1317,21 @@ struct net_device *mwifiex_add_virtual_intf(struct wiphy *wiphy,
        case NL80211_IFTYPE_UNSPECIFIED:
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
+               priv = adapter->priv[MWIFIEX_BSS_TYPE_STA];
                if (priv->bss_mode) {
-                       wiphy_err(wiphy, "cannot create multiple"
-                                       " station/adhoc interfaces\n");
+                       wiphy_err(wiphy,
+                                 "cannot create multiple sta/adhoc ifaces\n");
                        return NULL;
                }
 
+               wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+               if (!wdev)
+                       return NULL;
+
+               wdev->wiphy = wiphy;
+               priv->wdev = wdev;
+               wdev->iftype = NL80211_IFTYPE_STATION;
+
                if (type == NL80211_IFTYPE_UNSPECIFIED)
                        priv->bss_mode = NL80211_IFTYPE_STATION;
                else
@@ -1237,10 +1339,35 @@ struct net_device *mwifiex_add_virtual_intf(struct wiphy *wiphy,
 
                priv->bss_type = MWIFIEX_BSS_TYPE_STA;
                priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
-               priv->bss_priority = 0;
+               priv->bss_priority = MWIFIEX_BSS_ROLE_STA;
                priv->bss_role = MWIFIEX_BSS_ROLE_STA;
                priv->bss_num = 0;
 
+               break;
+       case NL80211_IFTYPE_AP:
+               priv = adapter->priv[MWIFIEX_BSS_TYPE_UAP];
+
+               if (priv->bss_mode) {
+                       wiphy_err(wiphy, "Can't create multiple AP interfaces");
+                       return NULL;
+               }
+
+               wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+               if (!wdev)
+                       return NULL;
+
+               priv->wdev = wdev;
+               wdev->wiphy = wiphy;
+               wdev->iftype = NL80211_IFTYPE_AP;
+
+               priv->bss_type = MWIFIEX_BSS_TYPE_UAP;
+               priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
+               priv->bss_priority = MWIFIEX_BSS_ROLE_UAP;
+               priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
+               priv->bss_started = 0;
+               priv->bss_num = 0;
+               priv->bss_mode = type;
+
                break;
        default:
                wiphy_err(wiphy, "type not supported\n");
@@ -1254,6 +1381,15 @@ struct net_device *mwifiex_add_virtual_intf(struct wiphy *wiphy,
                goto error;
        }
 
+       mwifiex_init_priv_params(priv, dev);
+       priv->netdev = dev;
+
+       mwifiex_setup_ht_caps(&wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, priv);
+
+       if (adapter->config_bands & BAND_A)
+               mwifiex_setup_ht_caps(
+                       &wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, priv);
+
        dev_net_set(dev, wiphy_net(wiphy));
        dev->ieee80211_ptr = priv->wdev;
        dev->ieee80211_ptr->iftype = priv->bss_mode;
@@ -1268,9 +1404,6 @@ struct net_device *mwifiex_add_virtual_intf(struct wiphy *wiphy,
        mdev_priv = netdev_priv(dev);
        *((unsigned long *) mdev_priv) = (unsigned long) priv;
 
-       priv->netdev = dev;
-       mwifiex_init_priv_params(priv, dev);
-
        SET_NETDEV_DEV(dev, adapter->dev);
 
        /* Register network device */
@@ -1340,6 +1473,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
        .connect = mwifiex_cfg80211_connect,
        .disconnect = mwifiex_cfg80211_disconnect,
        .get_station = mwifiex_cfg80211_get_station,
+       .dump_station = mwifiex_cfg80211_dump_station,
        .set_wiphy_params = mwifiex_cfg80211_set_wiphy_params,
        .set_channel = mwifiex_cfg80211_set_channel,
        .join_ibss = mwifiex_cfg80211_join_ibss,
@@ -1350,6 +1484,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
        .set_power_mgmt = mwifiex_cfg80211_set_power_mgmt,
        .set_tx_power = mwifiex_cfg80211_set_tx_power,
        .set_bitrate_mask = mwifiex_cfg80211_set_bitrate_mask,
+       .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config,
 };
 
 /*
@@ -1359,75 +1494,68 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
  * default parameters and handler function pointers, and finally
  * registers the device.
  */
-int mwifiex_register_cfg80211(struct mwifiex_private *priv)
+
+int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
 {
        int ret;
        void *wdev_priv;
-       struct wireless_dev *wdev;
-       struct ieee80211_sta_ht_cap *ht_info;
-
-       wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
-       if (!wdev) {
-               dev_err(priv->adapter->dev, "%s: allocating wireless device\n",
-                       __func__);
+       struct wiphy *wiphy;
+       struct mwifiex_private *priv = adapter->priv[MWIFIEX_BSS_TYPE_STA];
+       u8 *country_code;
+
+       /* create a new wiphy for use with cfg80211 */
+       wiphy = wiphy_new(&mwifiex_cfg80211_ops,
+                         sizeof(struct mwifiex_adapter *));
+       if (!wiphy) {
+               dev_err(adapter->dev, "%s: creating new wiphy\n", __func__);
                return -ENOMEM;
        }
-       wdev->wiphy =
-               wiphy_new(&mwifiex_cfg80211_ops,
-                         sizeof(struct mwifiex_private *));
-       if (!wdev->wiphy) {
-               kfree(wdev);
-               return -ENOMEM;
-       }
-       wdev->iftype = NL80211_IFTYPE_STATION;
-       wdev->wiphy->max_scan_ssids = 10;
-       wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
-                                      BIT(NL80211_IFTYPE_ADHOC);
-
-       wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz;
-       ht_info = &wdev->wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap;
-       mwifiex_setup_ht_caps(ht_info, priv);
-
-       if (priv->adapter->config_bands & BAND_A) {
-               wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz;
-               ht_info = &wdev->wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap;
-               mwifiex_setup_ht_caps(ht_info, priv);
-       } else {
-               wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
-       }
+       wiphy->max_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH;
+       wiphy->max_scan_ie_len = MWIFIEX_MAX_VSIE_LEN;
+       wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+                                BIT(NL80211_IFTYPE_ADHOC) |
+                                BIT(NL80211_IFTYPE_AP);
+
+       wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz;
+       if (adapter->config_bands & BAND_A)
+               wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz;
+       else
+               wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
+
+       wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta;
+       wiphy->n_iface_combinations = 1;
 
        /* Initialize cipher suits */
-       wdev->wiphy->cipher_suites = mwifiex_cipher_suites;
-       wdev->wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites);
+       wiphy->cipher_suites = mwifiex_cipher_suites;
+       wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites);
 
-       memcpy(wdev->wiphy->perm_addr, priv->curr_addr, ETH_ALEN);
-       wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+       memcpy(wiphy->perm_addr, priv->curr_addr, ETH_ALEN);
+       wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+       wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_CUSTOM_REGULATORY;
 
-       /* Reserve space for bss band information */
-       wdev->wiphy->bss_priv_size = sizeof(u8);
+       /* Reserve space for mwifiex specific private data for BSS */
+       wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv);
 
-       wdev->wiphy->reg_notifier = mwifiex_reg_notifier;
+       wiphy->reg_notifier = mwifiex_reg_notifier;
 
        /* Set struct mwifiex_private pointer in wiphy_priv */
-       wdev_priv = wiphy_priv(wdev->wiphy);
+       wdev_priv = wiphy_priv(wiphy);
 
        *(unsigned long *) wdev_priv = (unsigned long) priv;
 
-       set_wiphy_dev(wdev->wiphy, (struct device *) priv->adapter->dev);
+       set_wiphy_dev(wiphy, (struct device *)priv->adapter->dev);
 
-       ret = wiphy_register(wdev->wiphy);
+       ret = wiphy_register(wiphy);
        if (ret < 0) {
-               dev_err(priv->adapter->dev, "%s: registering cfg80211 device\n",
-                       __func__);
-               wiphy_free(wdev->wiphy);
-               kfree(wdev);
+               dev_err(adapter->dev,
+                       "%s: wiphy_register failed: %d\n", __func__, ret);
+               wiphy_free(wiphy);
                return ret;
-       } else {
-               dev_dbg(priv->adapter->dev,
-                       "info: successfully registered wiphy device\n");
        }
+       country_code = mwifiex_11d_code_2_region(priv->adapter->region_code);
+       if (country_code && regulatory_hint(wiphy, country_code))
+               dev_err(adapter->dev, "regulatory_hint() failed\n");
 
-       priv->wdev = wdev;
-
+       adapter->wiphy = wiphy;
        return ret;
 }
This page took 0.031703 seconds and 5 git commands to generate.