]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
authorJohn W. Linville <linville@tuxdriver.com>
Fri, 8 Nov 2013 14:03:10 +0000 (09:03 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 8 Nov 2013 14:03:10 +0000 (09:03 -0500)
89 files changed:
Documentation/DocBook/80211.tmpl
drivers/bluetooth/btusb.c
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/iwlwifi/dvm/ucode.c
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-io.c
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/bt-coex.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wlcore/acx.c
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/cmd.h
drivers/net/wireless/ti/wlcore/conf.h
drivers/net/wireless/ti/wlcore/debugfs.c
drivers/net/wireless/ti/wlcore/event.c
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/init.c
drivers/net/wireless/ti/wlcore/io.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/scan.c
drivers/net/wireless/ti/wlcore/testmode.c
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h
include/linux/ieee80211.h
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/cfg80211.h
include/net/mac80211.h
include/uapi/linux/nl80211.h
net/bluetooth/a2mp.c
net/bluetooth/af_bluetooth.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sock.c
net/bluetooth/hci_sysfs.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/sock.c
net/bluetooth/sco.c
net/bluetooth/smp.c
net/mac80211/Kconfig
net/mac80211/aes_ccm.c
net/mac80211/aes_ccm.h
net/mac80211/cfg.c
net/mac80211/debug.h
net/mac80211/debugfs_netdev.c
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.h
net/mac80211/mesh.c
net/mac80211/mesh_plink.c
net/mac80211/mesh_ps.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/spectmgmt.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/wpa.c
net/rfkill/Kconfig
net/rfkill/rfkill-gpio.c
net/wireless/chan.c
net/wireless/ibss.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/reg.c
net/wireless/reg.h
net/wireless/scan.c
net/wireless/sme.c
net/wireless/util.c

index f403ec3c5c9a4a17cf6c534f2da8590144faf766..46ad6faee9ab73dfae2fa3e2b00bb6fb7f1c63a1 100644 (file)
 !Finclude/net/cfg80211.h cfg80211_scan_request
 !Finclude/net/cfg80211.h cfg80211_scan_done
 !Finclude/net/cfg80211.h cfg80211_bss
-!Finclude/net/cfg80211.h cfg80211_inform_bss_frame
-!Finclude/net/cfg80211.h cfg80211_inform_bss
+!Finclude/net/cfg80211.h cfg80211_inform_bss_width_frame
+!Finclude/net/cfg80211.h cfg80211_inform_bss_width
 !Finclude/net/cfg80211.h cfg80211_unlink_bss
 !Finclude/net/cfg80211.h cfg80211_find_ie
 !Finclude/net/cfg80211.h ieee80211_bss_get_ie
index 30868fa870f6086d614184a389feef3267619eef..c0ff34f2d2df577efffe902562f9578690ea4e39 100644 (file)
@@ -1629,7 +1629,6 @@ static struct usb_driver btusb_driver = {
 #ifdef CONFIG_PM
        .suspend        = btusb_suspend,
        .resume         = btusb_resume,
-       .reset_resume   = btusb_resume,
 #endif
        .id_table       = btusb_table,
        .supports_autosuspend = 1,
index e89db64532f567442ae13106a77a09bb282f2445..d8643ebabd3001b6a00544157a65a9bc9f3265c5 100644 (file)
@@ -845,7 +845,8 @@ static const struct ieee80211_iface_limit if_limits[] = {
 };
 
 static const struct ieee80211_iface_limit if_dfs_limits[] = {
-       { .max = 1,     .types = BIT(NL80211_IFTYPE_AP) },
+       { .max = 1,     .types = BIT(NL80211_IFTYPE_AP) |
+                                BIT(NL80211_IFTYPE_ADHOC) },
 };
 
 static const struct ieee80211_iface_combination if_comb[] = {
index 86270b69cd024c3e9fe88f6361b15acc1f77eb39..63637949a146d65246f9d2da0381ec8697e6e4a9 100644 (file)
@@ -330,15 +330,14 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
        enum iwl_ucode_type old_type;
        static const u8 alive_cmd[] = { REPLY_ALIVE };
 
-       old_type = priv->cur_ucode;
-       priv->cur_ucode = ucode_type;
        fw = iwl_get_ucode_image(priv, ucode_type);
+       if (WARN_ON(!fw))
+               return -EINVAL;
 
+       old_type = priv->cur_ucode;
+       priv->cur_ucode = ucode_type;
        priv->ucode_loaded = false;
 
-       if (!fw)
-               return -EINVAL;
-
        iwl_init_notification_wait(&priv->notif_wait, &alive_wait,
                                   alive_cmd, ARRAY_SIZE(alive_cmd),
                                   iwl_alive_fn, &alive_data);
index 87b66a821ec8983fb79876519364880861b7ae71..75db087120c30fc8f2201f1f4de1be4d3e8e270c 100644 (file)
@@ -100,7 +100,7 @@ enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_P2P                 = BIT(3),
        IWL_UCODE_TLV_FLAGS_DW_BC_TABLE         = BIT(4),
        IWL_UCODE_TLV_FLAGS_NEWBT_COEX          = BIT(5),
-       IWL_UCODE_TLV_FLAGS_UAPSD               = BIT(6),
+       IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT      = BIT(6),
        IWL_UCODE_TLV_FLAGS_SHORT_BL            = BIT(7),
        IWL_UCODE_TLV_FLAGS_RX_ENERGY_API       = BIT(8),
        IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2   = BIT(9),
@@ -113,6 +113,7 @@ enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_SCHED_SCAN          = BIT(17),
        IWL_UCODE_TLV_FLAGS_STA_KEY_CMD         = BIT(19),
        IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD       = BIT(20),
+       IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT       = BIT(24),
 };
 
 /* The default calibrate table size if not specified by firmware file */
index dfa4d2e3aaa28124c6074a497d7e003d72cd86a2..ad8e19a56eca034bf231c001ba16c0775fa047a3 100644 (file)
@@ -34,7 +34,6 @@
 #include "iwl-csr.h"
 #include "iwl-debug.h"
 #include "iwl-fh.h"
-#include "iwl-csr.h"
 
 #define IWL_POLL_INTERVAL 10   /* microseconds */
 
index c6bac7c90b00bf458b17a829ec2895c2acaa065d..143292b4dbbffeac763f86d5c9d9b1badae1aecc 100644 (file)
@@ -344,7 +344,7 @@ struct iwl_trans_config {
        u8 cmd_queue;
        u8 cmd_fifo;
        const u8 *no_reclaim_cmds;
-       int n_no_reclaim_cmds;
+       unsigned int n_no_reclaim_cmds;
 
        bool rx_buf_size_8k;
        bool bc_table_dword;
index 5b630f12bbff774e4108c4df070e96ef9a0e2088..5d066cbc5ac7eda17914e85c2510c53fe4a5be2a 100644 (file)
@@ -505,12 +505,16 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
        struct iwl_mvm_sta *mvmsta;
        int ret;
 
-       /* This can happen if the station has been removed right now */
        if (sta_id == IWL_MVM_STATION_COUNT)
                return 0;
 
        sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
                                        lockdep_is_held(&mvm->mutex));
+
+       /* This can happen if the station has been removed right now */
+       if (IS_ERR_OR_NULL(sta))
+               return 0;
+
        mvmsta = (void *)sta->drv_priv;
 
        /* nothing to do */
@@ -751,7 +755,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
 
                cmd.bt_secondary_ci =
                        iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
-               cmd.secondary_ch_phy_id = *((u16 *)data.primary->drv_priv);
+               cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv);
        }
 
        rcu_read_unlock();
index 0675f0c8ef9388a7d888625077860207aba0a826..9864d713eb2cb54920053fb971a65866f5ec0bf4 100644 (file)
@@ -342,6 +342,7 @@ static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
        case MVM_DEBUGFS_PM_DISABLE_POWER_OFF:
                IWL_DEBUG_POWER(mvm, "disable_power_off=%d\n", val);
                dbgfs_pm->disable_power_off = val;
+               break;
        case MVM_DEBUGFS_PM_LPRX_ENA:
                IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled");
                dbgfs_pm->lprx_ena = val;
index 83fc5ca04433f36b4009404465f8646cafba9cb0..70e5297646b29b9d2410699b53b5daa7ed3fab3a 100644 (file)
@@ -151,13 +151,11 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
        enum iwl_ucode_type old_type = mvm->cur_ucode;
        static const u8 alive_cmd[] = { MVM_ALIVE };
 
-       mvm->cur_ucode = ucode_type;
        fw = iwl_get_ucode_image(mvm, ucode_type);
-
-       mvm->ucode_loaded = false;
-
-       if (!fw)
+       if (WARN_ON(!fw))
                return -EINVAL;
+       mvm->cur_ucode = ucode_type;
+       mvm->ucode_loaded = false;
 
        iwl_init_notification_wait(&mvm->notif_wait, &alive_wait,
                                   alive_cmd, ARRAY_SIZE(alive_cmd),
index ab5a7ac90dcd68948882cc245cd8df04e62df37c..f41f9b079831cfe7722b4d0c23203640993e7249 100644 (file)
@@ -719,7 +719,9 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
        cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC |
                                       MAC_FILTER_IN_CONTROL_AND_MGMT |
                                       MAC_FILTER_IN_BEACON |
-                                      MAC_FILTER_IN_PROBE_REQUEST);
+                                      MAC_FILTER_IN_PROBE_REQUEST |
+                                      MAC_FILTER_IN_CRC32);
+       mvm->hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS;
 
        return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
 }
@@ -1122,6 +1124,10 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        }
 
        mvmvif->uploaded = false;
+
+       if (vif->type == NL80211_IFTYPE_MONITOR)
+               mvm->hw->flags &= ~IEEE80211_HW_RX_INCLUDES_FCS;
+
        return 0;
 }
 
index f40685c3764ea8b211ad9fa7affb8ed675ef1ddd..74bc2c8af06d62fb360de4357621dca50201a8c2 100644 (file)
@@ -164,8 +164,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                    IEEE80211_HW_TIMING_BEACON_ONLY |
                    IEEE80211_HW_CONNECTION_MONITOR |
                    IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
-                   IEEE80211_HW_SUPPORTS_STATIC_SMPS |
-                   IEEE80211_HW_SUPPORTS_UAPSD;
+                   IEEE80211_HW_SUPPORTS_STATIC_SMPS;
 
        hw->queues = mvm->first_agg_queue;
        hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
@@ -180,6 +179,12 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
            !iwlwifi_mod_params.sw_crypto)
                hw->flags |= IEEE80211_HW_MFP_CAPABLE;
 
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT) {
+               hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
+               hw->uapsd_queues = IWL_UAPSD_AC_INFO;
+               hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
+       }
+
        hw->sta_data_size = sizeof(struct iwl_mvm_sta);
        hw->vif_data_size = sizeof(struct iwl_mvm_vif);
        hw->chanctx_data_size = sizeof(u16);
@@ -204,8 +209,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 
        hw->wiphy->max_remain_on_channel_duration = 10000;
        hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
-       hw->uapsd_queues = IWL_UAPSD_AC_INFO;
-       hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
 
        /* Extract MAC address */
        memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
@@ -861,7 +864,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                /* reset rssi values */
                mvmvif->bf_data.ave_beacon_signal = 0;
 
-               if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)) {
+               if (!(mvm->fw->ucode_capa.flags &
+                                       IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
                        /* Workaround for FW bug, otherwise FW disables device
                         * power save upon disassociation
                         */
index 6235cb729f5c0ecd5c40fe90d9e78b8fceb1f1c5..fed21ef4162d231102ab7ae76e3229ac24a86f1f 100644 (file)
@@ -73,7 +73,6 @@
 #include "iwl-trans.h"
 #include "iwl-notif-wait.h"
 #include "iwl-eeprom-parse.h"
-#include "iwl-trans.h"
 #include "sta.h"
 #include "fw-api.h"
 #include "constants.h"
index 59b7cb3c61344939c96f285e46ea9751e9197d35..d86083c6f445ac40cbcf824df4892b597ee11f35 100644 (file)
@@ -459,7 +459,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        if (err)
                goto out_unregister;
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)
                mvm->pm_ops = &pm_mac_ops;
        else
                mvm->pm_ops = &pm_legacy_ops;
index a4af5019a4960bc1afe1e0b9a5c5296381d4f590..3a1f3982109d1dbc02bccba42588545e000b8271 100644 (file)
@@ -300,10 +300,14 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                return 0;
        }
 
+       /*
+        * Keep packets with CRC errors (and with overrun) for monitor mode
+        * (otherwise the firmware discards them) but mark them as bad.
+        */
        if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
            !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
                IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
-               return 0;
+               rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
        }
 
        /* This will be used in several places later */
index f644fcf861a8c9ead756169ce4b6d960a8f51e1b..059c5acad3a0d2e7b9eb73bf6466ef088e54e5c2 100644 (file)
@@ -1499,12 +1499,11 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
        IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n",
                       get_cmd_string(trans_pcie, cmd->id));
 
-       if (WARN_ON(test_and_set_bit(STATUS_HCMD_ACTIVE,
-                                    &trans_pcie->status))) {
-               IWL_ERR(trans, "Command %s: a command is already active!\n",
-                       get_cmd_string(trans_pcie, cmd->id));
+       if (WARN(test_and_set_bit(STATUS_HCMD_ACTIVE,
+                                 &trans_pcie->status),
+                "Command %s: a command is already active!\n",
+                get_cmd_string(trans_pcie, cmd->id)))
                return -EIO;
-       }
 
        IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n",
                       get_cmd_string(trans_pcie, cmd->id));
index 2cd3f54e1efa14652e83691e3353290d17e76939..de0df86704e714778891b7fb1af6b2cfc1340bef 100644 (file)
@@ -167,6 +167,7 @@ struct hwsim_vif_priv {
        u32 magic;
        u8 bssid[ETH_ALEN];
        bool assoc;
+       bool bcn_en;
        u16 aid;
 };
 
@@ -1170,6 +1171,16 @@ static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw,
        *total_flags = data->rx_filter;
 }
 
+static void mac80211_hwsim_bcn_en_iter(void *data, u8 *mac,
+                                      struct ieee80211_vif *vif)
+{
+       unsigned int *count = data;
+       struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
+
+       if (vp->bcn_en)
+               (*count)++;
+}
+
 static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
                                            struct ieee80211_vif *vif,
                                            struct ieee80211_bss_conf *info,
@@ -1180,7 +1191,8 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
 
        hwsim_check_magic(vif);
 
-       wiphy_debug(hw->wiphy, "%s(changed=0x%x)\n", __func__, changed);
+       wiphy_debug(hw->wiphy, "%s(changed=0x%x vif->addr=%pM)\n",
+                   __func__, changed, vif->addr);
 
        if (changed & BSS_CHANGED_BSSID) {
                wiphy_debug(hw->wiphy, "%s: BSSID changed: %pM\n",
@@ -1202,6 +1214,7 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
 
        if (changed & BSS_CHANGED_BEACON_ENABLED) {
                wiphy_debug(hw->wiphy, "  BCN EN: %d\n", info->enable_beacon);
+               vp->bcn_en = info->enable_beacon;
                if (data->started &&
                    !hrtimer_is_queued(&data->beacon_timer.timer) &&
                    info->enable_beacon) {
@@ -1215,8 +1228,16 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
                        tasklet_hrtimer_start(&data->beacon_timer,
                                              ns_to_ktime(until_tbtt * 1000),
                                              HRTIMER_MODE_REL);
-               } else if (!info->enable_beacon)
-                       tasklet_hrtimer_cancel(&data->beacon_timer);
+               } else if (!info->enable_beacon) {
+                       unsigned int count = 0;
+                       ieee80211_iterate_active_interfaces(
+                               data->hw, IEEE80211_IFACE_ITER_NORMAL,
+                               mac80211_hwsim_bcn_en_iter, &count);
+                       wiphy_debug(hw->wiphy, "  beaconing vifs remaining: %u",
+                                   count);
+                       if (count == 0)
+                               tasklet_hrtimer_cancel(&data->beacon_timer);
+               }
        }
 
        if (changed & BSS_CHANGED_ERP_CTS_PROT) {
index 591526b991547281e4e04f341e071d57c84d58e2..be7129ba16ad651524910c1897a33b7d48570007 100644 (file)
@@ -333,11 +333,11 @@ static struct wlcore_conf wl12xx_conf = {
                .always                        = 0,
        },
        .fwlog = {
-               .mode                         = WL12XX_FWLOG_ON_DEMAND,
+               .mode                         = WL12XX_FWLOG_CONTINUOUS,
                .mem_blocks                   = 2,
                .severity                     = 0,
                .timestamp                    = WL12XX_FWLOG_TIMESTAMP_DISABLED,
-               .output                       = WL12XX_FWLOG_OUTPUT_HOST,
+               .output                       = WL12XX_FWLOG_OUTPUT_DBG_PINS,
                .threshold                    = 0,
        },
        .rate = {
@@ -717,6 +717,9 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                goto out;
        }
 
+       wl->fw_mem_block_size = 256;
+       wl->fwlog_end = 0x2000000;
+
        /* common settings */
        wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY;
        wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY;
@@ -1262,9 +1265,10 @@ static int wl12xx_boot(struct wl1271 *wl)
                BA_SESSION_RX_CONSTRAINT_EVENT_ID |
                REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
                INACTIVE_STA_EVENT_ID |
-               MAX_TX_RETRY_EVENT_ID |
                CHANNEL_SWITCH_COMPLETE_EVENT_ID;
 
+       wl->ap_event_mask = MAX_TX_RETRY_EVENT_ID;
+
        ret = wlcore_boot_run_firmware(wl);
        if (ret < 0)
                goto out;
@@ -1648,6 +1652,11 @@ static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
        return true;
 }
 
+static u32 wl12xx_convert_hwaddr(struct wl1271 *wl, u32 hwaddr)
+{
+       return hwaddr << 5;
+}
+
 static int wl12xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl12xx_ops = {
@@ -1684,6 +1693,7 @@ static struct wlcore_ops wl12xx_ops = {
        .channel_switch         = wl12xx_cmd_channel_switch,
        .pre_pkt_send           = NULL,
        .set_peer_cap           = wl12xx_set_peer_cap,
+       .convert_hwaddr         = wl12xx_convert_hwaddr,
        .lnk_high_prio          = wl12xx_lnk_high_prio,
        .lnk_low_prio           = wl12xx_lnk_low_prio,
 };
index d0daca1d23bc55154432d2a031feff4ee51b15e4..ec37b16585df939938fb1a75ef060ef0ea3d73fd 100644 (file)
@@ -456,11 +456,11 @@ static struct wlcore_conf wl18xx_conf = {
                .always                        = 0,
        },
        .fwlog = {
-               .mode                         = WL12XX_FWLOG_ON_DEMAND,
+               .mode                         = WL12XX_FWLOG_CONTINUOUS,
                .mem_blocks                   = 2,
                .severity                     = 0,
                .timestamp                    = WL12XX_FWLOG_TIMESTAMP_DISABLED,
-               .output                       = WL12XX_FWLOG_OUTPUT_HOST,
+               .output                       = WL12XX_FWLOG_OUTPUT_DBG_PINS,
                .threshold                    = 0,
        },
        .rate = {
@@ -505,7 +505,7 @@ static struct wlcore_conf wl18xx_conf = {
 
 static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
        .ht = {
-               .mode                           = HT_MODE_DEFAULT,
+               .mode                           = HT_MODE_WIDE,
        },
        .phy = {
                .phy_standalone                 = 0x00,
@@ -516,7 +516,7 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .auto_detect                    = 0x00,
                .dedicated_fem                  = FEM_NONE,
                .low_band_component             = COMPONENT_3_WAY_SWITCH,
-               .low_band_component_type        = 0x04,
+               .low_band_component_type        = 0x05,
                .high_band_component            = COMPONENT_2_WAY_SWITCH,
                .high_band_component_type       = 0x09,
                .tcxo_ldo_voltage               = 0x00,
@@ -556,15 +556,15 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .per_chan_pwr_limit_arr_11p     = { 0xff, 0xff, 0xff, 0xff,
                                                    0xff, 0xff, 0xff },
                .psat                           = 0,
-               .low_power_val                  = 0x08,
-               .med_power_val                  = 0x12,
-               .high_power_val                 = 0x18,
-               .low_power_val_2nd              = 0x05,
-               .med_power_val_2nd              = 0x0a,
-               .high_power_val_2nd             = 0x14,
                .external_pa_dc2dc              = 0,
                .number_of_assembled_ant2_4     = 2,
                .number_of_assembled_ant5       = 1,
+               .low_power_val                  = 0xff,
+               .med_power_val                  = 0xff,
+               .high_power_val                 = 0xff,
+               .low_power_val_2nd              = 0xff,
+               .med_power_val_2nd              = 0xff,
+               .high_power_val_2nd             = 0xff,
                .tx_rf_margin                   = 1,
        },
 };
@@ -686,6 +686,9 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
                goto out;
        }
 
+       wl->fw_mem_block_size = 272;
+       wl->fwlog_end = 0x40000000;
+
        wl->scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
        wl->scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
        wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC;
@@ -988,10 +991,11 @@ static int wl18xx_boot(struct wl1271 *wl)
                BA_SESSION_RX_CONSTRAINT_EVENT_ID |
                REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
                INACTIVE_STA_EVENT_ID |
-               MAX_TX_FAILURE_EVENT_ID |
                CHANNEL_SWITCH_COMPLETE_EVENT_ID |
                DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
 
+       wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID;
+
        ret = wlcore_boot_run_firmware(wl);
        if (ret < 0)
                goto out;
@@ -1604,6 +1608,11 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
        return lnk->allocated_pkts < thold;
 }
 
+static u32 wl18xx_convert_hwaddr(struct wl1271 *wl, u32 hwaddr)
+{
+       return hwaddr & ~0x80000000;
+}
+
 static int wl18xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl18xx_ops = {
@@ -1641,6 +1650,7 @@ static struct wlcore_ops wl18xx_ops = {
        .pre_pkt_send   = wl18xx_pre_pkt_send,
        .sta_rc_update  = wl18xx_sta_rc_update,
        .set_peer_cap   = wl18xx_set_peer_cap,
+       .convert_hwaddr = wl18xx_convert_hwaddr,
        .lnk_high_prio  = wl18xx_lnk_high_prio,
        .lnk_low_prio   = wl18xx_lnk_low_prio,
 };
index 7a970cd9c5551cc538ec0177758f023589c9e281..ec83675a244697537afd791613cfec9e87d13caa 100644 (file)
@@ -162,7 +162,8 @@ int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map,
 
        wl1271_debug(DEBUG_ACX, "acx mem map");
 
-       ret = wl1271_cmd_interrogate(wl, ACX_MEM_MAP, mem_map, len);
+       ret = wl1271_cmd_interrogate(wl, ACX_MEM_MAP, mem_map,
+                                    sizeof(struct acx_header), len);
        if (ret < 0)
                return ret;
 
@@ -722,6 +723,7 @@ int wl1271_acx_statistics(struct wl1271 *wl, void *stats)
        wl1271_debug(DEBUG_ACX, "acx statistics");
 
        ret = wl1271_cmd_interrogate(wl, ACX_STATISTICS, stats,
+                                    sizeof(struct acx_header),
                                     wl->stats.fw_stats_len);
        if (ret < 0) {
                wl1271_warning("acx statistics failed: %d", ret);
@@ -1470,8 +1472,8 @@ int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
        tsf_info->role_id = wlvif->role_id;
 
-       ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO,
-                                    tsf_info, sizeof(*tsf_info));
+       ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO, tsf_info,
+                               sizeof(struct acx_header), sizeof(*tsf_info));
        if (ret < 0) {
                wl1271_warning("acx tsf info interrogate failed");
                goto out;
@@ -1752,7 +1754,7 @@ int wlcore_acx_average_rssi(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
        acx->role_id = wlvif->role_id;
        ret = wl1271_cmd_interrogate(wl, ACX_ROAMING_STATISTICS_TBL,
-                                    acx, sizeof(*acx));
+                                    acx, sizeof(*acx), sizeof(*acx));
        if (ret < 0) {
                wl1271_warning("acx roaming statistics failed: %d", ret);
                ret = -ENOMEM;
index 9e5416f8764d13899cc7de963a71a58bf7b1b0e4..34d9dfff2ad39ead03d3da275cd3e4443385a220 100644 (file)
@@ -60,7 +60,8 @@ static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf,
        u16 status;
        u16 poll_count = 0;
 
-       if (WARN_ON(unlikely(wl->state == WLCORE_STATE_RESTARTING)))
+       if (WARN_ON(wl->state == WLCORE_STATE_RESTARTING &&
+                   id != CMD_STOP_FWLOGGER))
                return -EIO;
 
        cmd = buf;
@@ -845,7 +846,8 @@ EXPORT_SYMBOL_GPL(wl1271_cmd_test);
  * @buf: buffer for the response, including all headers, must work with dma
  * @len: length of buf
  */
-int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len)
+int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf,
+                          size_t cmd_len, size_t res_len)
 {
        struct acx_header *acx = buf;
        int ret;
@@ -854,10 +856,10 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len)
 
        acx->id = cpu_to_le16(id);
 
-       /* payload length, does not include any headers */
-       acx->len = cpu_to_le16(len - sizeof(*acx));
+       /* response payload length, does not include any headers */
+       acx->len = cpu_to_le16(res_len - sizeof(*acx));
 
-       ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx), len);
+       ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, cmd_len, res_len);
        if (ret < 0)
                wl1271_error("INTERROGATE command failed");
 
index fd34123047cdd0255f5a48b7826ddd117b14814d..323d4a856e4ba80d37f52fadf0832ad09066996e 100644 (file)
@@ -45,7 +45,8 @@ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                     enum ieee80211_band band, int channel);
 int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
-int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len);
+int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf,
+                          size_t cmd_len, size_t res_len);
 int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len);
 int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
                                  size_t len, unsigned long valid_rets);
index 2b96ff821341103ac935ade4765409b21e142409..40995c42bef8882458c331c9c6cc863210958d63 100644 (file)
@@ -1274,6 +1274,9 @@ struct conf_rx_streaming_settings {
        u8 always;
 } __packed;
 
+#define CONF_FWLOG_MIN_MEM_BLOCKS      2
+#define CONF_FWLOG_MAX_MEM_BLOCKS      16
+
 struct conf_fwlog {
        /* Continuous or on-demand */
        u8 mode;
@@ -1281,7 +1284,7 @@ struct conf_fwlog {
        /*
         * Number of memory blocks dedicated for the FW logger
         *
-        * Range: 1-3, or 0 to disable the FW logger
+        * Range: 2-16, or 0 to disable the FW logger
         */
        u8 mem_blocks;
 
index e17630c2a84948d40bb5d0f2c05f4ce1e8ccea2c..89893c7170253c8cc5400bf7941cc9ce4a3d3051 100644 (file)
@@ -437,6 +437,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        int res = 0;
        ssize_t ret;
        char *buf;
+       struct wl12xx_vif *wlvif;
 
 #define DRIVER_STATE_BUF_LEN 1024
 
@@ -450,12 +451,28 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        (res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\
                          #x " = " fmt "\n", wl->x))
 
+#define DRIVER_STATE_PRINT_GENERIC(x, fmt, args...)   \
+       (res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\
+                         #x " = " fmt "\n", args))
+
 #define DRIVER_STATE_PRINT_LONG(x) DRIVER_STATE_PRINT(x, "%ld")
 #define DRIVER_STATE_PRINT_INT(x)  DRIVER_STATE_PRINT(x, "%d")
 #define DRIVER_STATE_PRINT_STR(x)  DRIVER_STATE_PRINT(x, "%s")
 #define DRIVER_STATE_PRINT_LHEX(x) DRIVER_STATE_PRINT(x, "0x%lx")
 #define DRIVER_STATE_PRINT_HEX(x)  DRIVER_STATE_PRINT(x, "0x%x")
 
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+                       continue;
+
+               DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel,
+                                          wlvif->p2p ? "P2P-CL" : "STA");
+       }
+
+       wl12xx_for_each_wlvif_ap(wl, wlvif)
+               DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel,
+                                          wlvif->p2p ? "P2P-GO" : "AP");
+
        DRIVER_STATE_PRINT_INT(tx_blocks_available);
        DRIVER_STATE_PRINT_INT(tx_allocated_blocks);
        DRIVER_STATE_PRINT_INT(tx_allocated_pkts[0]);
@@ -474,7 +491,6 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        DRIVER_STATE_PRINT_INT(tx_blocks_freed);
        DRIVER_STATE_PRINT_INT(rx_counter);
        DRIVER_STATE_PRINT_INT(state);
-       DRIVER_STATE_PRINT_INT(channel);
        DRIVER_STATE_PRINT_INT(band);
        DRIVER_STATE_PRINT_INT(power_level);
        DRIVER_STATE_PRINT_INT(sg_enabled);
index 67f61689b49edcba1e54198db538eb277aa4bda0..8d3b34965db3475f64178eeb6463ec2589434c3f 100644 (file)
@@ -266,6 +266,7 @@ int wl1271_event_unmask(struct wl1271 *wl)
 {
        int ret;
 
+       wl1271_debug(DEBUG_EVENT, "unmasking event_mask 0x%x", wl->event_mask);
        ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask));
        if (ret < 0)
                return ret;
index 7fd260c02a0a15aa2a0b8c92867a09fc9af96739..51f8d634d32f43274d2eaccb679a6dff732468f3 100644 (file)
@@ -222,6 +222,15 @@ wlcore_hw_set_peer_cap(struct wl1271 *wl,
        return 0;
 }
 
+static inline u32
+wlcore_hw_convert_hwaddr(struct wl1271 *wl, u32 hwaddr)
+{
+       if (!wl->ops->convert_hwaddr)
+               BUG_ON(1);
+
+       return wl->ops->convert_hwaddr(wl, hwaddr);
+}
+
 static inline bool
 wlcore_hw_lnk_high_prio(struct wl1271 *wl, u8 hlid,
                        struct wl1271_link *lnk)
index 5c6f11e157d9b0016633dfe932f34984fedeb65c..7699f9d07e2636e3528fa7e3c28f260bfaa63e3f 100644 (file)
@@ -571,6 +571,12 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
                ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
                if (ret < 0)
                        return ret;
+
+               /* unmask ap events */
+               wl->event_mask |= wl->ap_event_mask;
+               ret = wl1271_event_unmask(wl);
+               if (ret < 0)
+                       return ret;
        /* first STA, no APs */
        } else if (wl->sta_count == 0 && wl->ap_count == 0 && !is_ap) {
                u8 sta_auth = wl->conf.conn.sta_sleep_auth;
index af7d9f9b3b4db2140006fb0639af3ff028af79e5..07e3d6a049adf33d40dc586c27ef66c4428b83d1 100644 (file)
@@ -165,8 +165,8 @@ static inline int __must_check wlcore_read_hwaddr(struct wl1271 *wl, int hwaddr,
        int physical;
        int addr;
 
-       /* Addresses are stored internally as addresses to 32 bytes blocks */
-       addr = hwaddr << 5;
+       /* Convert from FW internal address which is chip arch dependent */
+       addr = wl->ops->convert_hwaddr(wl, hwaddr);
 
        physical = wlcore_translate_addr(wl, addr);
 
index bbdd10632373d0481ae7dd21b331ce566903c77c..0368b9cbfb896d6460da3635b199ef6266805644 100644 (file)
@@ -44,6 +44,7 @@
 #define WL1271_BOOT_RETRIES 3
 
 static char *fwlog_param;
+static int fwlog_mem_blocks = -1;
 static int bug_on_recovery = -1;
 static int no_recovery     = -1;
 
@@ -291,6 +292,18 @@ static void wlcore_adjust_conf(struct wl1271 *wl)
 {
        /* Adjust settings according to optional module parameters */
 
+       /* Firmware Logger params */
+       if (fwlog_mem_blocks != -1) {
+               if (fwlog_mem_blocks >= CONF_FWLOG_MIN_MEM_BLOCKS &&
+                   fwlog_mem_blocks <= CONF_FWLOG_MAX_MEM_BLOCKS) {
+                       wl->conf.fwlog.mem_blocks = fwlog_mem_blocks;
+               } else {
+                       wl1271_error(
+                               "Illegal fwlog_mem_blocks=%d using default %d",
+                               fwlog_mem_blocks, wl->conf.fwlog.mem_blocks);
+               }
+       }
+
        if (fwlog_param) {
                if (!strcmp(fwlog_param, "continuous")) {
                        wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
@@ -780,6 +793,7 @@ void wl12xx_queue_recovery_work(struct wl1271 *wl)
        if (wl->state == WLCORE_STATE_ON) {
                wl->state = WLCORE_STATE_RESTARTING;
                set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
+               wl1271_ps_elp_wakeup(wl);
                wlcore_disable_interrupts_nosync(wl);
                ieee80211_queue_work(wl->hw, &wl->recovery_work);
        }
@@ -787,19 +801,10 @@ void wl12xx_queue_recovery_work(struct wl1271 *wl)
 
 size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
 {
-       size_t len = 0;
-
-       /* The FW log is a length-value list, find where the log end */
-       while (len < maxlen) {
-               if (memblock[len] == 0)
-                       break;
-               if (len + memblock[len] + 1 > maxlen)
-                       break;
-               len += memblock[len] + 1;
-       }
+       size_t len;
 
        /* Make sure we have enough room */
-       len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size));
+       len = min(maxlen, (size_t)(PAGE_SIZE - wl->fwlog_size));
 
        /* Fill the FW log file, consumed by the sysfs fwlog entry */
        memcpy(wl->fwlog + wl->fwlog_size, memblock, len);
@@ -808,10 +813,9 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
        return len;
 }
 
-#define WLCORE_FW_LOG_END 0x2000000
-
 static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 {
+       struct wlcore_partition_set part, old_part;
        u32 addr;
        u32 offset;
        u32 end_of_log;
@@ -824,7 +828,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
        wl1271_info("Reading FW panic log");
 
-       block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL);
+       block = kmalloc(wl->fw_mem_block_size, GFP_KERNEL);
        if (!block)
                return;
 
@@ -850,17 +854,31 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
        if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) {
                offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor);
-               end_of_log = WLCORE_FW_LOG_END;
+               end_of_log = wl->fwlog_end;
        } else {
                offset = sizeof(addr);
                end_of_log = addr;
        }
 
+       old_part = wl->curr_part;
+       memset(&part, 0, sizeof(part));
+
        /* Traverse the memory blocks linked list */
        do {
-               memset(block, 0, WL12XX_HW_BLOCK_SIZE);
-               ret = wlcore_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
-                                        false);
+               part.mem.start = wlcore_hw_convert_hwaddr(wl, addr);
+               part.mem.size  = PAGE_SIZE;
+
+               ret = wlcore_set_partition(wl, &part);
+               if (ret < 0) {
+                       wl1271_error("%s: set_partition start=0x%X size=%d",
+                               __func__, part.mem.start, part.mem.size);
+                       goto out;
+               }
+
+               memset(block, 0, wl->fw_mem_block_size);
+               ret = wlcore_read_hwaddr(wl, addr, block,
+                                       wl->fw_mem_block_size, false);
+
                if (ret < 0)
                        goto out;
 
@@ -871,8 +889,9 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
                 * on demand mode and is equal to 0x2000000 in continuous mode.
                 */
                addr = le32_to_cpup((__le32 *)block);
+
                if (!wl12xx_copy_fwlog(wl, block + offset,
-                                      WL12XX_HW_BLOCK_SIZE - offset))
+                                       wl->fw_mem_block_size - offset))
                        break;
        } while (addr && (addr != end_of_log));
 
@@ -880,6 +899,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
 out:
        kfree(block);
+       wlcore_set_partition(wl, &old_part);
 }
 
 static void wlcore_print_recovery(struct wl1271 *wl)
@@ -924,7 +944,8 @@ static void wl1271_recovery_work(struct work_struct *work)
                goto out_unlock;
 
        if (!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) {
-               wl12xx_read_fwlog_panic(wl);
+               if (wl->conf.fwlog.output == WL12XX_FWLOG_OUTPUT_HOST)
+                       wl12xx_read_fwlog_panic(wl);
                wlcore_print_recovery(wl);
        }
 
@@ -1928,8 +1949,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
 
        /*
         * FW channels must be re-calibrated after recovery,
-        * clear the last Reg-Domain channel configuration.
+        * save current Reg-Domain channel configuration and clear it.
         */
+       memcpy(wl->reg_ch_conf_pending, wl->reg_ch_conf_last,
+              sizeof(wl->reg_ch_conf_pending));
        memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last));
 }
 
@@ -2623,6 +2646,12 @@ deinit:
            !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags))
                goto unlock;
 
+       if (wl->ap_count == 0 && is_ap) {
+               /* mask ap events */
+               wl->event_mask &= ~wl->ap_event_mask;
+               wl1271_event_unmask(wl);
+       }
+
        if (wl->ap_count == 0 && is_ap && wl->sta_count) {
                u8 sta_auth = wl->conf.conn.sta_sleep_auth;
                /* Configure for power according to debugfs */
@@ -6152,6 +6181,9 @@ module_param_named(fwlog, fwlog_param, charp, 0);
 MODULE_PARM_DESC(fwlog,
                 "FW logger options: continuous, ondemand, dbgpins or disable");
 
+module_param(fwlog_mem_blocks, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(fwlog_mem_blocks, "fwlog mem_blocks");
+
 module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR);
 MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
 
index 13e743df2e31d45815f8d8fd916e9f5370977841..7ed86203304b700c87e4b1ca48de127f7466420f 100644 (file)
@@ -92,9 +92,31 @@ out:
 static void wlcore_started_vifs_iter(void *data, u8 *mac,
                                     struct ieee80211_vif *vif)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       bool active = false;
        int *count = (int *)data;
 
-       if (!vif->bss_conf.idle)
+       /*
+        * count active interfaces according to interface type.
+        * checking only bss_conf.idle is bad for some cases, e.g.
+        * we don't want to count sta in p2p_find as active interface.
+        */
+       switch (wlvif->bss_type) {
+       case BSS_TYPE_STA_BSS:
+               if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+                       active = true;
+               break;
+
+       case BSS_TYPE_AP_BSS:
+               if (wlvif->wl->active_sta_count > 0)
+                       active = true;
+               break;
+
+       default:
+               break;
+       }
+
+       if (active)
                (*count)++;
 }
 
index a3b7d950d8e9b0f2a989b6624e130ed7cc081d91..ddad58f614da4fa862a0e3f2b776fe0e994a742d 100644 (file)
@@ -179,7 +179,8 @@ static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[])
                goto out_sleep;
        }
 
-       ret = wl1271_cmd_interrogate(wl, ie_id, cmd, sizeof(*cmd));
+       ret = wl1271_cmd_interrogate(wl, ie_id, cmd,
+                                    sizeof(struct acx_header), sizeof(*cmd));
        if (ret < 0) {
                wl1271_warning("testmode cmd interrogate failed: %d", ret);
                goto out_free;
index 54ce5d5e84db0be55da713ddcdbcd19818d507f3..06efc12a39e5175dfde449843ffbb5a7c50b157d 100644 (file)
@@ -110,6 +110,7 @@ struct wlcore_ops {
                            struct ieee80211_sta_ht_cap *ht_cap,
                            bool allow_ht_operation,
                            u32 rate_set, u8 hlid);
+       u32 (*convert_hwaddr)(struct wl1271 *wl, u32 hwaddr);
        bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid,
                              struct wl1271_link *lnk);
        bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
@@ -290,6 +291,12 @@ struct wl1271 {
        /* Number of valid bytes in the FW log buffer */
        ssize_t fwlog_size;
 
+       /* FW log end marker */
+       u32 fwlog_end;
+
+       /* FW memory block size */
+       u32 fw_mem_block_size;
+
        /* Sysfs FW log entry readers wait queue */
        wait_queue_head_t fwlog_waitq;
 
@@ -307,6 +314,8 @@ struct wl1271 {
 
        /* The mbox event mask */
        u32 event_mask;
+       /* events to unmask only when ap interface is up */
+       u32 ap_event_mask;
 
        /* Mailbox pointers */
        u32 mbox_size;
index 2a50e089b0e755eb9fafa79f1cff00c6e9d2d4a7..ce7261ce8b59a244837618a07ecf174762f04946 100644 (file)
@@ -550,6 +550,4 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
 #define HW_HT_RATES_OFFSET     16
 #define HW_MIMO_RATES_OFFSET   24
 
-#define WL12XX_HW_BLOCK_SIZE   256
-
 #endif /* __WLCORE_I_H__ */
index 7c1e1ebc0e2396cc7697bccc31d18be5566dc944..8c3b26a215745b755a5018764e4c0ca1f1bd52b7 100644 (file)
@@ -696,6 +696,18 @@ struct ieee80211_sec_chan_offs_ie {
        u8 sec_chan_offs;
 } __packed;
 
+/**
+ * struct ieee80211_mesh_chansw_params_ie - mesh channel switch parameters IE
+ *
+ * This structure represents the "Mesh Channel Switch Paramters element"
+ */
+struct ieee80211_mesh_chansw_params_ie {
+       u8 mesh_ttl;
+       u8 mesh_flags;
+       __le16 mesh_reason;
+       __le16 mesh_pre_value;
+} __packed;
+
 /**
  * struct ieee80211_wide_bw_chansw_ie - wide bandwidth channel switch IE
  */
@@ -750,6 +762,14 @@ enum mesh_config_capab_flags {
        IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL       = 0x40,
 };
 
+/**
+ * mesh channel switch parameters element's flag indicator
+ *
+ */
+#define WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT BIT(0)
+#define WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR BIT(1)
+#define WLAN_EID_CHAN_SWITCH_PARAM_REASON BIT(2)
+
 /**
  * struct ieee80211_rann_ie
  *
index 2cc9517fb0d5006f4b5b4f9570cf7a748d3e3518..2a628b28249ffd00f8b4bd36d4e024583599ad67 100644 (file)
@@ -282,6 +282,7 @@ struct bt_skb_cb {
        __u8 incoming;
        __u16 expect;
        __u8 force_active;
+       struct l2cap_chan *chan;
        struct l2cap_ctrl control;
        struct hci_req_ctrl req;
        bdaddr_t bdaddr;
index b096f5f73789ad19c4a9180d976962108fb6753a..1784c48699f04dd425027d2e51d0aabc3fd87f4a 100644 (file)
@@ -115,6 +115,7 @@ enum {
        HCI_PAIRABLE,
        HCI_SERVICE_CACHE,
        HCI_DEBUG_KEYS,
+       HCI_DUT_MODE,
        HCI_UNREGISTER,
        HCI_USER_CHANNEL,
 
@@ -125,6 +126,7 @@ enum {
        HCI_ADVERTISING,
        HCI_CONNECTABLE,
        HCI_DISCOVERABLE,
+       HCI_LIMITED_DISCOVERABLE,
        HCI_LINK_SECURITY,
        HCI_PERIODIC_INQ,
        HCI_FAST_CONNECTABLE,
@@ -823,6 +825,12 @@ struct hci_rp_read_num_supported_iac {
 
 #define HCI_OP_READ_CURRENT_IAC_LAP    0x0c39
 
+#define HCI_OP_WRITE_CURRENT_IAC_LAP   0x0c3a
+struct hci_cp_write_current_iac_lap {
+       __u8    num_iac;
+       __u8    iac_lap[6];
+} __packed;
+
 #define HCI_OP_WRITE_INQUIRY_MODE      0x0c45
 
 #define HCI_MAX_EIR_LENGTH             240
@@ -1036,6 +1044,10 @@ struct hci_rp_write_remote_amp_assoc {
        __u8     phy_handle;
 } __packed;
 
+#define HCI_OP_ENABLE_DUT_MODE         0x1803
+
+#define HCI_OP_WRITE_SSP_DEBUG_MODE    0x1804
+
 #define HCI_OP_LE_SET_EVENT_MASK       0x2001
 struct hci_cp_le_set_event_mask {
        __u8     mask[8];
@@ -1056,11 +1068,6 @@ struct hci_rp_le_read_local_features {
 
 #define HCI_OP_LE_SET_RANDOM_ADDR      0x2005
 
-#define LE_ADV_IND                     0x00
-#define LE_ADV_DIRECT_IND              0x01
-#define LE_ADV_SCAN_IND                        0x02
-#define LE_ADV_NONCONN_IND             0x03
-
 #define HCI_OP_LE_SET_ADV_PARAM                0x2006
 struct hci_cp_le_set_adv_param {
        __le16   min_interval;
@@ -1087,6 +1094,12 @@ struct hci_cp_le_set_adv_data {
        __u8    data[HCI_MAX_AD_LENGTH];
 } __packed;
 
+#define HCI_OP_LE_SET_SCAN_RSP_DATA    0x2009
+struct hci_cp_le_set_scan_rsp_data {
+       __u8    length;
+       __u8    data[HCI_MAX_AD_LENGTH];
+} __packed;
+
 #define HCI_OP_LE_SET_ADV_ENABLE       0x200a
 
 #define LE_SCAN_PASSIVE                        0x00
@@ -1567,11 +1580,11 @@ struct hci_ev_le_ltk_req {
 } __packed;
 
 /* Advertising report event types */
-#define ADV_IND                0x00
-#define ADV_DIRECT_IND 0x01
-#define ADV_SCAN_IND   0x02
-#define ADV_NONCONN_IND        0x03
-#define ADV_SCAN_RSP   0x04
+#define LE_ADV_IND             0x00
+#define LE_ADV_DIRECT_IND      0x01
+#define LE_ADV_SCAN_IND                0x02
+#define LE_ADV_NONCONN_IND     0x03
+#define LE_ADV_SCAN_RSP                0x04
 
 #define ADDR_LE_DEV_PUBLIC     0x00
 #define ADDR_LE_DEV_RANDOM     0x01
@@ -1779,6 +1792,4 @@ struct hci_inquiry_req {
 };
 #define IREQ_CACHE_FLUSH 0x0001
 
-extern bool enable_hs;
-
 #endif /* __HCI_H */
index 2dc467939be7e9a189bef661ae96762a8eb31fe8..f8555ad7b10485925b07393019579c1ee706b2b0 100644 (file)
@@ -81,6 +81,7 @@ struct hci_conn_hash {
 struct bdaddr_list {
        struct list_head list;
        bdaddr_t bdaddr;
+       u8 bdaddr_type;
 };
 
 struct bt_uuid {
@@ -141,6 +142,7 @@ struct hci_dev {
        __u8            dev_type;
        bdaddr_t        bdaddr;
        bdaddr_t        static_addr;
+       __u8            own_addr_type;
        __u8            dev_name[HCI_MAX_NAME_LENGTH];
        __u8            short_name[HCI_MAX_SHORT_NAME_LENGTH];
        __u8            eir[HCI_MAX_EIR_LENGTH];
@@ -167,6 +169,9 @@ struct hci_dev {
        __u8            page_scan_type;
        __u16           le_scan_interval;
        __u16           le_scan_window;
+       __u16           le_conn_min_interval;
+       __u16           le_conn_max_interval;
+       __u8            ssp_debug_mode;
 
        __u16           devid_source;
        __u16           devid_vendor;
@@ -283,6 +288,8 @@ struct hci_dev {
        __s8                    adv_tx_power;
        __u8                    adv_data[HCI_MAX_AD_LENGTH];
        __u8                    adv_data_len;
+       __u8                    scan_rsp_data[HCI_MAX_AD_LENGTH];
+       __u8                    scan_rsp_data_len;
 
        int (*open)(struct hci_dev *hdev);
        int (*close)(struct hci_dev *hdev);
@@ -311,7 +318,6 @@ struct hci_conn {
        __u8            attempt;
        __u8            dev_class[3];
        __u8            features[HCI_MAX_PAGES][8];
-       __u16           interval;
        __u16           pkt_type;
        __u16           link_policy;
        __u32           link_mode;
@@ -339,8 +345,8 @@ struct hci_conn {
        struct list_head chan_list;
 
        struct delayed_work disc_work;
-       struct timer_list idle_timer;
-       struct timer_list auto_accept_timer;
+       struct delayed_work auto_accept_work;
+       struct delayed_work idle_work;
 
        struct device   dev;
 
@@ -648,7 +654,7 @@ static inline void hci_conn_drop(struct hci_conn *conn)
                switch (conn->type) {
                case ACL_LINK:
                case LE_LINK:
-                       del_timer(&conn->idle_timer);
+                       cancel_delayed_work(&conn->idle_work);
                        if (conn->state == BT_CONNECTED) {
                                timeo = conn->disc_timeout;
                                if (!conn->out)
@@ -729,7 +735,7 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg);
 int hci_inquiry(void __user *arg);
 
 struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
-                                        bdaddr_t *bdaddr);
+                                        bdaddr_t *bdaddr, u8 type);
 int hci_blacklist_clear(struct hci_dev *hdev);
 int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
 int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
@@ -764,8 +770,6 @@ int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count);
 int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count);
 
 void hci_init_sysfs(struct hci_dev *hdev);
-int hci_add_sysfs(struct hci_dev *hdev);
-void hci_del_sysfs(struct hci_dev *hdev);
 void hci_conn_init_sysfs(struct hci_conn *conn);
 void hci_conn_add_sysfs(struct hci_conn *conn);
 void hci_conn_del_sysfs(struct hci_conn *conn);
@@ -1008,34 +1012,6 @@ static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
        return false;
 }
 
-static inline size_t eir_get_length(u8 *eir, size_t eir_len)
-{
-       size_t parsed = 0;
-
-       while (parsed < eir_len) {
-               u8 field_len = eir[0];
-
-               if (field_len == 0)
-                       return parsed;
-
-               parsed += field_len + 1;
-               eir += field_len + 1;
-       }
-
-       return eir_len;
-}
-
-static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
-                                 u8 data_len)
-{
-       eir[eir_len++] = sizeof(type) + data_len;
-       eir[eir_len++] = type;
-       memcpy(&eir[eir_len], data, data_len);
-       eir_len += data_len;
-
-       return eir_len;
-}
-
 int hci_register_cb(struct hci_cb *hcb);
 int hci_unregister_cb(struct hci_cb *hcb);
 
@@ -1099,11 +1075,12 @@ void mgmt_index_added(struct hci_dev *hdev);
 void mgmt_index_removed(struct hci_dev *hdev);
 void mgmt_set_powered_failed(struct hci_dev *hdev, int err);
 int mgmt_powered(struct hci_dev *hdev, u8 powered);
-int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable);
-int mgmt_connectable(struct hci_dev *hdev, u8 connectable);
-int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
-int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
-                     bool persistent);
+void mgmt_discoverable_timeout(struct hci_dev *hdev);
+void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable);
+void mgmt_connectable(struct hci_dev *hdev, u8 connectable);
+void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
+void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
+                      bool persistent);
 void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                           u8 addr_type, u32 flags, u8 *name, u8 name_len,
                           u8 *dev_class);
@@ -1113,11 +1090,11 @@ void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
                            u8 link_type, u8 addr_type, u8 status);
 void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                         u8 addr_type, u8 status);
-int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
-int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                u8 status);
-int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                    u8 status);
+void mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
+void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                 u8 status);
+void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                     u8 status);
 int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
                              u8 link_type, u8 addr_type, __le32 value,
                              u8 confirm_hint);
@@ -1134,15 +1111,15 @@ int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
 int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
                             u8 link_type, u8 addr_type, u32 passkey,
                             u8 entered);
-int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                    u8 addr_type, u8 status);
-int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
-int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
-int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
-                                  u8 status);
-int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
-int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
-                                           u8 *randomizer, u8 status);
+void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                     u8 addr_type, u8 status);
+void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
+void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
+void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
+                                   u8 status);
+void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
+void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
+                                            u8 *randomizer, u8 status);
 void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                       u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
                       u8 ssp, u8 *eir, u16 eir_len);
@@ -1151,7 +1128,7 @@ void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 void mgmt_discovering(struct hci_dev *hdev, u8 discovering);
 int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
 int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
-int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent);
+void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent);
 void mgmt_reenable_advertising(struct hci_dev *hdev);
 
 /* HCI info for socket */
@@ -1182,8 +1159,6 @@ struct hci_sec_filter {
 #define hci_req_lock(d)                mutex_lock(&d->req_lock)
 #define hci_req_unlock(d)      mutex_unlock(&d->req_lock)
 
-void hci_update_ad(struct hci_request *req);
-
 void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
                                        u16 latency, u16 to_multiplier);
 void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
index 07757a2af94270444083320c12d981c676d3b01c..51329905bfaafda1c290a26103914d5879144a01 100644 (file)
@@ -435,8 +435,6 @@ struct l2cap_seq_list {
 #define L2CAP_SEQ_LIST_TAIL    0x8000
 
 struct l2cap_chan {
-       struct sock *sk;
-
        struct l2cap_conn       *conn;
        struct hci_conn         *hs_hcon;
        struct hci_chan         *hs_hchan;
@@ -551,10 +549,12 @@ struct l2cap_ops {
        void                    (*teardown) (struct l2cap_chan *chan, int err);
        void                    (*close) (struct l2cap_chan *chan);
        void                    (*state_change) (struct l2cap_chan *chan,
-                                                int state);
+                                                int state, int err);
        void                    (*ready) (struct l2cap_chan *chan);
        void                    (*defer) (struct l2cap_chan *chan);
        void                    (*resume) (struct l2cap_chan *chan);
+       void                    (*set_shutdown) (struct l2cap_chan *chan);
+       long                    (*get_sndtimeo) (struct l2cap_chan *chan);
        struct sk_buff          *(*alloc_skb) (struct l2cap_chan *chan,
                                               unsigned long len, int nb);
 };
@@ -795,6 +795,19 @@ static inline void l2cap_chan_no_defer(struct l2cap_chan *chan)
 {
 }
 
+static inline void l2cap_chan_no_resume(struct l2cap_chan *chan)
+{
+}
+
+static inline void l2cap_chan_no_set_shutdown(struct l2cap_chan *chan)
+{
+}
+
+static inline long l2cap_chan_no_get_sndtimeo(struct l2cap_chan *chan)
+{
+       return 0;
+}
+
 extern bool disable_ertm;
 
 int l2cap_init_sockets(void);
@@ -802,7 +815,6 @@ void l2cap_cleanup_sockets(void);
 bool l2cap_is_socket(struct socket *sock);
 
 void __l2cap_connect_rsp_defer(struct l2cap_chan *chan);
-int __l2cap_wait_ack(struct sock *sk);
 
 int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm);
 int l2cap_add_scid(struct l2cap_chan *chan,  __u16 scid);
index 419202ce3f958b4a98b5faf9c3bab145e420dbac..3eae46cb1acfecadbff979ee17e17aeab40971e7 100644 (file)
@@ -744,6 +744,10 @@ enum station_parameters_apply_mask {
  * @capability: station capability
  * @ext_capab: extended capabilities of the station
  * @ext_capab_len: number of extended capabilities
+ * @supported_channels: supported channels in IEEE 802.11 format
+ * @supported_channels_len: number of supported channels
+ * @supported_oper_classes: supported oper classes in IEEE 802.11 format
+ * @supported_oper_classes_len: number of supported operating classes
  */
 struct station_parameters {
        const u8 *supported_rates;
@@ -763,6 +767,10 @@ struct station_parameters {
        u16 capability;
        const u8 *ext_capab;
        u8 ext_capab_len;
+       const u8 *supported_channels;
+       u8 supported_channels_len;
+       const u8 *supported_oper_classes;
+       u8 supported_oper_classes_len;
 };
 
 /**
@@ -1656,6 +1664,9 @@ struct cfg80211_disassoc_request {
  *     sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is
  *     required to assume that the port is unauthorized until authorized by
  *     user space. Otherwise, port is marked authorized by default.
+ * @userspace_handles_dfs: whether user space controls DFS operation, i.e.
+ *     changes the channel when a radar is detected. This is required
+ *     to operate on DFS channels.
  * @basic_rates: bitmap of basic rates to use when creating the IBSS
  * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
  * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
@@ -1673,6 +1684,7 @@ struct cfg80211_ibss_params {
        bool channel_fixed;
        bool privacy;
        bool control_port;
+       bool userspace_handles_dfs;
        int mcast_rate[IEEE80211_NUM_BANDS];
        struct ieee80211_ht_cap ht_capa;
        struct ieee80211_ht_cap ht_capa_mask;
@@ -3053,6 +3065,7 @@ struct cfg80211_cached_keys;
  * @conn: (private) cfg80211 software SME connection state machine data
  * @connect_keys: (private) keys to set after connection is established
  * @ibss_fixed: (private) IBSS is using fixed BSSID
+ * @ibss_dfs_possible: (private) IBSS may change to a DFS channel
  * @event_list: (private) list for internal event processing
  * @event_lock: (private) lock for event list
  */
@@ -3091,6 +3104,7 @@ struct wireless_dev {
        struct ieee80211_channel *channel;
 
        bool ibss_fixed;
+       bool ibss_dfs_possible;
 
        bool ps;
        int ps_timeout;
index f386c480e1341dd70308d02300dc26f60af275f0..7ceed99a05bc79218a841352a454a980df48741b 100644 (file)
@@ -1503,6 +1503,10 @@ struct ieee80211_tx_control {
  * @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames
  *     only, to allow getting TBTT of a DTIM beacon.
  *
+ * @IEEE80211_HW_SUPPORTS_HT_CCK_RATES: Hardware supports mixing HT/CCK rates
+ *     and can cope with CCK rates in an aggregation session (e.g. by not
+ *     using aggregation for such frames.)
+ *
  * @IEEE80211_HW_CHANCTX_STA_CSA: Support 802.11h based channel-switch (CSA)
  *     for a single active channel while using channel contexts. When support
  *     is not enabled the default action is to disconnect when getting the
@@ -4567,4 +4571,18 @@ void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
                                    struct cfg80211_wowlan_wakeup *wakeup,
                                    gfp_t gfp);
 
+/**
+ * ieee80211_tx_prepare_skb - prepare an 802.11 skb for transmission
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @vif: virtual interface
+ * @skb: frame to be sent from within the driver
+ * @band: the band to transmit on
+ * @sta: optional pointer to get the station to send the frame to
+ *
+ * Note: must be called under RCU lock
+ */
+bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif, struct sk_buff *skb,
+                             int band, struct ieee80211_sta **sta);
+
 #endif /* MAC80211_H */
index fde2c021b26dbe3be5e00a9c47de5dbdb7d60b80..f752e9821e717ee68066f4d8467775a8bee2fd78 100644 (file)
@@ -988,7 +988,7 @@ enum nl80211_commands {
  *     to query the CRDA to retrieve one regulatory domain. This attribute can
  *     also be used by userspace to query the kernel for the currently set
  *     regulatory domain. We chose an alpha2 as that is also used by the
- *     IEEE-802.11d country information element to identify a country.
+ *     IEEE-802.11 country information element to identify a country.
  *     Users can also simply ask the wireless core to set regulatory domain
  *     to a specific alpha2.
  * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
@@ -1496,6 +1496,18 @@ enum nl80211_commands {
  * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
  *     As specified in the &enum nl80211_rxmgmt_flags.
  *
+ * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
+ *      supported operating classes.
+ *
+ * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space
+ *     controls DFS operation in IBSS mode. If the flag is included in
+ *     %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS
+ *     channels and reports radar events to userspace. Userspace is required
+ *     to react to radar events, e.g. initiate a channel switch or leave the
+ *     IBSS network.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1806,6 +1818,12 @@ enum nl80211_attrs {
 
        NL80211_ATTR_RXMGMT_FLAGS,
 
+       NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+
+       NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+
+       NL80211_ATTR_HANDLE_DFS,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -3860,13 +3878,12 @@ enum nl80211_radar_event {
  *
  * Channel states used by the DFS code.
  *
- * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
+ * @NL80211_DFS_USABLE: The channel can be used, but channel availability
  *     check (CAC) must be performed before using it for AP or IBSS.
- * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
  *     is therefore marked as not available.
- * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
  */
-
 enum nl80211_dfs_state {
        NL80211_DFS_USABLE,
        NL80211_DFS_UNAVAILABLE,
index 60ca52819247506a33a36f444698295e6f4fc762..efcd108822c43134e3d64755e24c16410f0598d5 100644 (file)
@@ -672,7 +672,8 @@ static void a2mp_chan_close_cb(struct l2cap_chan *chan)
        l2cap_chan_put(chan);
 }
 
-static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state)
+static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state,
+                                     int err)
 {
        struct amp_mgr *mgr = chan->data;
 
@@ -709,6 +710,9 @@ static struct l2cap_ops a2mp_chan_ops = {
        .teardown = l2cap_chan_no_teardown,
        .ready = l2cap_chan_no_ready,
        .defer = l2cap_chan_no_defer,
+       .resume = l2cap_chan_no_resume,
+       .set_shutdown = l2cap_chan_no_set_shutdown,
+       .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
 };
 
 static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
@@ -832,6 +836,9 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
 {
        struct amp_mgr *mgr;
 
+       if (conn->hcon->type != ACL_LINK)
+               return NULL;
+
        mgr = amp_mgr_create(conn, false);
        if (!mgr) {
                BT_ERR("Could not create AMP manager");
index 1f1a1118f4891b950ee8ba6b610ff9a8f4f0c36d..f6a1671ea2ff793bfca0efdd1541765803fb4d03 100644 (file)
@@ -25,6 +25,7 @@
 /* Bluetooth address family and sockets. */
 
 #include <linux/module.h>
+#include <linux/debugfs.h>
 #include <asm/ioctls.h>
 
 #include <net/bluetooth/bluetooth.h>
@@ -708,12 +709,17 @@ static struct net_proto_family bt_sock_family_ops = {
        .create = bt_sock_create,
 };
 
+struct dentry *bt_debugfs;
+EXPORT_SYMBOL_GPL(bt_debugfs);
+
 static int __init bt_init(void)
 {
        int err;
 
        BT_INFO("Core ver %s", VERSION);
 
+       bt_debugfs = debugfs_create_dir("bluetooth", NULL);
+
        err = bt_sysfs_init();
        if (err < 0)
                return err;
@@ -754,7 +760,6 @@ error:
 
 static void __exit bt_exit(void)
 {
-
        sco_exit();
 
        l2cap_exit();
@@ -764,6 +769,8 @@ static void __exit bt_exit(void)
        sock_unregister(PF_BLUETOOTH);
 
        bt_sysfs_cleanup();
+
+       debugfs_remove_recursive(bt_debugfs);
 }
 
 subsys_initcall(bt_init);
index ff04b051792debd389e9935bae11a653c14e72e4..ba5366c320dacc7d4db4659aa144051baf1b035a 100644 (file)
@@ -317,8 +317,10 @@ static void hci_conn_timeout(struct work_struct *work)
 }
 
 /* Enter sniff mode */
-static void hci_conn_enter_sniff_mode(struct hci_conn *conn)
+static void hci_conn_idle(struct work_struct *work)
 {
+       struct hci_conn *conn = container_of(work, struct hci_conn,
+                                            idle_work.work);
        struct hci_dev *hdev = conn->hdev;
 
        BT_DBG("hcon %p mode %d", conn, conn->mode);
@@ -352,21 +354,12 @@ static void hci_conn_enter_sniff_mode(struct hci_conn *conn)
        }
 }
 
-static void hci_conn_idle(unsigned long arg)
-{
-       struct hci_conn *conn = (void *) arg;
-
-       BT_DBG("hcon %p mode %d", conn, conn->mode);
-
-       hci_conn_enter_sniff_mode(conn);
-}
-
-static void hci_conn_auto_accept(unsigned long arg)
+static void hci_conn_auto_accept(struct work_struct *work)
 {
-       struct hci_conn *conn = (void *) arg;
-       struct hci_dev *hdev = conn->hdev;
+       struct hci_conn *conn = container_of(work, struct hci_conn,
+                                            auto_accept_work.work);
 
-       hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
+       hci_send_cmd(conn->hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
                     &conn->dst);
 }
 
@@ -415,9 +408,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
        INIT_LIST_HEAD(&conn->chan_list);
 
        INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
-       setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
-       setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
-                   (unsigned long) conn);
+       INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept);
+       INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
 
        atomic_set(&conn->refcnt, 0);
 
@@ -438,11 +430,9 @@ int hci_conn_del(struct hci_conn *conn)
 
        BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle);
 
-       del_timer(&conn->idle_timer);
-
        cancel_delayed_work_sync(&conn->disc_work);
-
-       del_timer(&conn->auto_accept_timer);
+       cancel_delayed_work_sync(&conn->auto_accept_work);
+       cancel_delayed_work_sync(&conn->idle_work);
 
        if (conn->type == ACL_LINK) {
                struct hci_conn *sco = conn->link;
@@ -568,11 +558,12 @@ static int hci_create_le_conn(struct hci_conn *conn)
        bacpy(&cp.peer_addr, &conn->dst);
        cp.peer_addr_type = conn->dst_type;
        cp.own_address_type = conn->src_type;
-       cp.conn_interval_min = __constant_cpu_to_le16(0x0028);
-       cp.conn_interval_max = __constant_cpu_to_le16(0x0038);
+       cp.conn_interval_min = cpu_to_le16(hdev->le_conn_min_interval);
+       cp.conn_interval_max = cpu_to_le16(hdev->le_conn_max_interval);
        cp.supervision_timeout = __constant_cpu_to_le16(0x002a);
        cp.min_ce_len = __constant_cpu_to_le16(0x0000);
        cp.max_ce_len = __constant_cpu_to_le16(0x0000);
+
        hci_req_add(&req, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
 
        err = hci_req_run(&req, create_le_conn_complete);
@@ -625,12 +616,7 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
        else
                conn->dst_type = ADDR_LE_DEV_RANDOM;
 
-       if (bacmp(&conn->src, BDADDR_ANY)) {
-               conn->src_type = ADDR_LE_DEV_PUBLIC;
-       } else {
-               bacpy(&conn->src, &hdev->static_addr);
-               conn->src_type = ADDR_LE_DEV_RANDOM;
-       }
+       conn->src_type = hdev->own_addr_type;
 
        conn->state = BT_CONNECT;
        conn->out = true;
@@ -922,8 +908,8 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
 
 timer:
        if (hdev->idle_timeout > 0)
-               mod_timer(&conn->idle_timer,
-                         jiffies + msecs_to_jiffies(hdev->idle_timeout));
+               queue_delayed_work(hdev->workqueue, &conn->idle_work,
+                                  msecs_to_jiffies(hdev->idle_timeout));
 }
 
 /* Drop all connection on the device */
index 7add9c96e32ca3bc9fc1134f37301c75ee1084c3..6ccc4eb9e55e4958f3eb070894a7668b05c98b5b 100644 (file)
@@ -27,8 +27,9 @@
 
 #include <linux/export.h>
 #include <linux/idr.h>
-
 #include <linux/rfkill.h>
+#include <linux/debugfs.h>
+#include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -55,6 +56,586 @@ static void hci_notify(struct hci_dev *hdev, int event)
        hci_sock_dev_event(hdev, event);
 }
 
+/* ---- HCI debugfs entries ---- */
+
+static ssize_t dut_mode_read(struct file *file, char __user *user_buf,
+                            size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_DUT_MODE, &hdev->dev_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       struct sk_buff *skb;
+       char buf[32];
+       size_t buf_size = min(count, (sizeof(buf)-1));
+       bool enable;
+       int err;
+
+       if (!test_bit(HCI_UP, &hdev->flags))
+               return -ENETDOWN;
+
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+
+       if (enable == test_bit(HCI_DUT_MODE, &hdev->dev_flags))
+               return -EALREADY;
+
+       hci_req_lock(hdev);
+       if (enable)
+               skb = __hci_cmd_sync(hdev, HCI_OP_ENABLE_DUT_MODE, 0, NULL,
+                                    HCI_CMD_TIMEOUT);
+       else
+               skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL,
+                                    HCI_CMD_TIMEOUT);
+       hci_req_unlock(hdev);
+
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       err = -bt_to_errno(skb->data[0]);
+       kfree_skb(skb);
+
+       if (err < 0)
+               return err;
+
+       change_bit(HCI_DUT_MODE, &hdev->dev_flags);
+
+       return count;
+}
+
+static const struct file_operations dut_mode_fops = {
+       .open           = simple_open,
+       .read           = dut_mode_read,
+       .write          = dut_mode_write,
+       .llseek         = default_llseek,
+};
+
+static int features_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       u8 p;
+
+       hci_dev_lock(hdev);
+       for (p = 0; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) {
+               seq_printf(f, "%2u: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+                          "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", p,
+                          hdev->features[p][0], hdev->features[p][1],
+                          hdev->features[p][2], hdev->features[p][3],
+                          hdev->features[p][4], hdev->features[p][5],
+                          hdev->features[p][6], hdev->features[p][7]);
+       }
+       if (lmp_le_capable(hdev))
+               seq_printf(f, "LE: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+                          "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+                          hdev->le_features[0], hdev->le_features[1],
+                          hdev->le_features[2], hdev->le_features[3],
+                          hdev->le_features[4], hdev->le_features[5],
+                          hdev->le_features[6], hdev->le_features[7]);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int features_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, features_show, inode->i_private);
+}
+
+static const struct file_operations features_fops = {
+       .open           = features_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int blacklist_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct bdaddr_list *b;
+
+       hci_dev_lock(hdev);
+       list_for_each_entry(b, &hdev->blacklist, list)
+               seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int blacklist_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, blacklist_show, inode->i_private);
+}
+
+static const struct file_operations blacklist_fops = {
+       .open           = blacklist_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int uuids_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct bt_uuid *uuid;
+
+       hci_dev_lock(hdev);
+       list_for_each_entry(uuid, &hdev->uuids, list) {
+               u8 i, val[16];
+
+               /* The Bluetooth UUID values are stored in big endian,
+                * but with reversed byte order. So convert them into
+                * the right order for the %pUb modifier.
+                */
+               for (i = 0; i < 16; i++)
+                       val[i] = uuid->uuid[15 - i];
+
+               seq_printf(f, "%pUb\n", val);
+       }
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int uuids_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, uuids_show, inode->i_private);
+}
+
+static const struct file_operations uuids_fops = {
+       .open           = uuids_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int inquiry_cache_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct discovery_state *cache = &hdev->discovery;
+       struct inquiry_entry *e;
+
+       hci_dev_lock(hdev);
+
+       list_for_each_entry(e, &cache->all, all) {
+               struct inquiry_data *data = &e->data;
+               seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
+                          &data->bdaddr,
+                          data->pscan_rep_mode, data->pscan_period_mode,
+                          data->pscan_mode, data->dev_class[2],
+                          data->dev_class[1], data->dev_class[0],
+                          __le16_to_cpu(data->clock_offset),
+                          data->rssi, data->ssp_mode, e->timestamp);
+       }
+
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int inquiry_cache_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, inquiry_cache_show, inode->i_private);
+}
+
+static const struct file_operations inquiry_cache_fops = {
+       .open           = inquiry_cache_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int link_keys_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct list_head *p, *n;
+
+       hci_dev_lock(hdev);
+       list_for_each_safe(p, n, &hdev->link_keys) {
+               struct link_key *key = list_entry(p, struct link_key, list);
+               seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
+                          HCI_LINK_KEY_SIZE, key->val, key->pin_len);
+       }
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int link_keys_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, link_keys_show, inode->i_private);
+}
+
+static const struct file_operations link_keys_fops = {
+       .open           = link_keys_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static ssize_t use_debug_keys_read(struct file *file, char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations use_debug_keys_fops = {
+       .open           = simple_open,
+       .read           = use_debug_keys_read,
+       .llseek         = default_llseek,
+};
+
+static int dev_class_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+
+       hci_dev_lock(hdev);
+       seq_printf(f, "0x%.2x%.2x%.2x\n", hdev->dev_class[2],
+                  hdev->dev_class[1], hdev->dev_class[0]);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int dev_class_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dev_class_show, inode->i_private);
+}
+
+static const struct file_operations dev_class_fops = {
+       .open           = dev_class_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int voice_setting_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->voice_setting;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get,
+                       NULL, "0x%4.4llx\n");
+
+static int auto_accept_delay_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       hdev->auto_accept_delay = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int auto_accept_delay_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->auto_accept_delay;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
+                       auto_accept_delay_set, "%llu\n");
+
+static int ssp_debug_mode_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+       struct sk_buff *skb;
+       __u8 mode;
+       int err;
+
+       if (val != 0 && val != 1)
+               return -EINVAL;
+
+       if (!test_bit(HCI_UP, &hdev->flags))
+               return -ENETDOWN;
+
+       hci_req_lock(hdev);
+       mode = val;
+       skb = __hci_cmd_sync(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, sizeof(mode),
+                            &mode, HCI_CMD_TIMEOUT);
+       hci_req_unlock(hdev);
+
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       err = -bt_to_errno(skb->data[0]);
+       kfree_skb(skb);
+
+       if (err < 0)
+               return err;
+
+       hci_dev_lock(hdev);
+       hdev->ssp_debug_mode = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int ssp_debug_mode_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->ssp_debug_mode;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(ssp_debug_mode_fops, ssp_debug_mode_get,
+                       ssp_debug_mode_set, "%llu\n");
+
+static int idle_timeout_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val != 0 && (val < 500 || val > 3600000))
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->idle_timeout = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int idle_timeout_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->idle_timeout;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get,
+                       idle_timeout_set, "%llu\n");
+
+static int sniff_min_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->sniff_min_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int sniff_min_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->sniff_min_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get,
+                       sniff_min_interval_set, "%llu\n");
+
+static int sniff_max_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->sniff_max_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int sniff_max_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->sniff_max_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
+                       sniff_max_interval_set, "%llu\n");
+
+static int static_address_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+
+       hci_dev_lock(hdev);
+       seq_printf(f, "%pMR\n", &hdev->static_addr);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int static_address_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, static_address_show, inode->i_private);
+}
+
+static const struct file_operations static_address_fops = {
+       .open           = static_address_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int own_address_type_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val != 0 && val != 1)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->own_addr_type = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int own_address_type_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->own_addr_type;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(own_address_type_fops, own_address_type_get,
+                       own_address_type_set, "%llu\n");
+
+static int long_term_keys_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct list_head *p, *n;
+
+       hci_dev_lock(hdev);
+       list_for_each_safe(p, n, &hdev->link_keys) {
+               struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list);
+               seq_printf(f, "%pMR (type %u) %u %u %u %.4x %*phN %*phN\\n",
+                          &ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
+                          ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
+                          8, ltk->rand, 16, ltk->val);
+       }
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int long_term_keys_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, long_term_keys_show, inode->i_private);
+}
+
+static const struct file_operations long_term_keys_fops = {
+       .open           = long_term_keys_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int conn_min_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_conn_min_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_min_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_conn_min_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get,
+                       conn_min_interval_set, "%llu\n");
+
+static int conn_max_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_conn_max_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_max_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_conn_max_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
+                       conn_max_interval_set, "%llu\n");
+
 /* ---- HCI requests ---- */
 
 static void hci_req_sync_complete(struct hci_dev *hdev, u8 result)
@@ -556,6 +1137,14 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
                hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
 
        if (lmp_ssp_capable(hdev)) {
+               /* When SSP is available, then the host features page
+                * should also be available as well. However some
+                * controllers list the max_page as 0 as long as SSP
+                * has not been enabled. To achieve proper debugging
+                * output, force the minimum max_page to 1 at least.
+                */
+               hdev->max_page = 0x01;
+
                if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
                        u8 mode = 0x01;
                        hci_req_add(req, HCI_OP_WRITE_SSP_MODE,
@@ -686,8 +1275,17 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
                hci_setup_link_policy(req);
 
        if (lmp_le_capable(hdev)) {
+               /* If the controller has a public BD_ADDR, then by
+                * default use that one. If this is a LE only
+                * controller without one, default to the random
+                * address.
+                */
+               if (bacmp(&hdev->bdaddr, BDADDR_ANY))
+                       hdev->own_addr_type = ADDR_LE_DEV_PUBLIC;
+               else
+                       hdev->own_addr_type = ADDR_LE_DEV_RANDOM;
+
                hci_set_le_support(req);
-               hci_update_ad(req);
        }
 
        /* Read features beyond page 1 if available */
@@ -721,6 +1319,14 @@ static int __hci_init(struct hci_dev *hdev)
        if (err < 0)
                return err;
 
+       /* The Device Under Test (DUT) mode is special and available for
+        * all controller types. So just create it early on.
+        */
+       if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+               debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev,
+                                   &dut_mode_fops);
+       }
+
        /* HCI_BREDR covers both single-mode LE, BR/EDR and dual-mode
         * BR/EDR/LE type controllers. AMP controllers only need the
         * first stage init.
@@ -736,7 +1342,71 @@ static int __hci_init(struct hci_dev *hdev)
        if (err < 0)
                return err;
 
-       return __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT);
+       err = __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT);
+       if (err < 0)
+               return err;
+
+       /* Only create debugfs entries during the initial setup
+        * phase and not every time the controller gets powered on.
+        */
+       if (!test_bit(HCI_SETUP, &hdev->dev_flags))
+               return 0;
+
+       debugfs_create_file("features", 0444, hdev->debugfs, hdev,
+                           &features_fops);
+       debugfs_create_u16("manufacturer", 0444, hdev->debugfs,
+                          &hdev->manufacturer);
+       debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
+       debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
+       debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
+                           &blacklist_fops);
+       debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
+
+       if (lmp_bredr_capable(hdev)) {
+               debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
+                                   hdev, &inquiry_cache_fops);
+               debugfs_create_file("link_keys", 0400, hdev->debugfs,
+                                   hdev, &link_keys_fops);
+               debugfs_create_file("use_debug_keys", 0444, hdev->debugfs,
+                                   hdev, &use_debug_keys_fops);
+               debugfs_create_file("dev_class", 0444, hdev->debugfs,
+                                   hdev, &dev_class_fops);
+               debugfs_create_file("voice_setting", 0444, hdev->debugfs,
+                                   hdev, &voice_setting_fops);
+       }
+
+       if (lmp_ssp_capable(hdev)) {
+               debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
+                                   hdev, &auto_accept_delay_fops);
+               debugfs_create_file("ssp_debug_mode", 0644, hdev->debugfs,
+                                   hdev, &ssp_debug_mode_fops);
+       }
+
+       if (lmp_sniff_capable(hdev)) {
+               debugfs_create_file("idle_timeout", 0644, hdev->debugfs,
+                                   hdev, &idle_timeout_fops);
+               debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs,
+                                   hdev, &sniff_min_interval_fops);
+               debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs,
+                                   hdev, &sniff_max_interval_fops);
+       }
+
+       if (lmp_le_capable(hdev)) {
+               debugfs_create_u8("white_list_size", 0444, hdev->debugfs,
+                                 &hdev->le_white_list_size);
+               debugfs_create_file("static_address", 0444, hdev->debugfs,
+                                  hdev, &static_address_fops);
+               debugfs_create_file("own_address_type", 0644, hdev->debugfs,
+                                   hdev, &own_address_type_fops);
+               debugfs_create_file("long_term_keys", 0400, hdev->debugfs,
+                                   hdev, &long_term_keys_fops);
+               debugfs_create_file("conn_min_interval", 0644, hdev->debugfs,
+                                   hdev, &conn_min_interval_fops);
+               debugfs_create_file("conn_max_interval", 0644, hdev->debugfs,
+                                   hdev, &conn_max_interval_fops);
+       }
+
+       return 0;
 }
 
 static void hci_scan_req(struct hci_request *req, unsigned long opt)
@@ -1127,89 +1797,6 @@ done:
        return err;
 }
 
-static u8 create_ad(struct hci_dev *hdev, u8 *ptr)
-{
-       u8 ad_len = 0, flags = 0;
-       size_t name_len;
-
-       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
-               flags |= LE_AD_GENERAL;
-
-       if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
-               if (lmp_le_br_capable(hdev))
-                       flags |= LE_AD_SIM_LE_BREDR_CTRL;
-               if (lmp_host_le_br_capable(hdev))
-                       flags |= LE_AD_SIM_LE_BREDR_HOST;
-       } else {
-               flags |= LE_AD_NO_BREDR;
-       }
-
-       if (flags) {
-               BT_DBG("adv flags 0x%02x", flags);
-
-               ptr[0] = 2;
-               ptr[1] = EIR_FLAGS;
-               ptr[2] = flags;
-
-               ad_len += 3;
-               ptr += 3;
-       }
-
-       if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) {
-               ptr[0] = 2;
-               ptr[1] = EIR_TX_POWER;
-               ptr[2] = (u8) hdev->adv_tx_power;
-
-               ad_len += 3;
-               ptr += 3;
-       }
-
-       name_len = strlen(hdev->dev_name);
-       if (name_len > 0) {
-               size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
-
-               if (name_len > max_len) {
-                       name_len = max_len;
-                       ptr[1] = EIR_NAME_SHORT;
-               } else
-                       ptr[1] = EIR_NAME_COMPLETE;
-
-               ptr[0] = name_len + 1;
-
-               memcpy(ptr + 2, hdev->dev_name, name_len);
-
-               ad_len += (name_len + 2);
-               ptr += (name_len + 2);
-       }
-
-       return ad_len;
-}
-
-void hci_update_ad(struct hci_request *req)
-{
-       struct hci_dev *hdev = req->hdev;
-       struct hci_cp_le_set_adv_data cp;
-       u8 len;
-
-       if (!lmp_le_capable(hdev))
-               return;
-
-       memset(&cp, 0, sizeof(cp));
-
-       len = create_ad(hdev, cp.data);
-
-       if (hdev->adv_data_len == len &&
-           memcmp(cp.data, hdev->adv_data, len) == 0)
-               return;
-
-       memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
-       hdev->adv_data_len = len;
-
-       cp.length = len;
-
-       hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
-}
-
 static int hci_dev_do_open(struct hci_dev *hdev)
 {
        int ret = 0;
@@ -1367,6 +1954,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
                cancel_delayed_work(&hdev->discov_off);
                hdev->discov_timeout = 0;
                clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
        }
 
        if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
@@ -1789,19 +2377,12 @@ static void hci_power_off(struct work_struct *work)
 static void hci_discov_off(struct work_struct *work)
 {
        struct hci_dev *hdev;
-       u8 scan = SCAN_PAGE;
 
        hdev = container_of(work, struct hci_dev, discov_off.work);
 
        BT_DBG("%s", hdev->name);
 
-       hci_dev_lock(hdev);
-
-       hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
-
-       hdev->discov_timeout = 0;
-
-       hci_dev_unlock(hdev);
+       mgmt_discoverable_timeout(hdev);
 }
 
 int hci_uuids_clear(struct hci_dev *hdev)
@@ -2124,13 +2705,15 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
        return 0;
 }
 
-struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
+struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
+                                        bdaddr_t *bdaddr, u8 type)
 {
        struct bdaddr_list *b;
 
-       list_for_each_entry(b, &hdev->blacklist, list)
-               if (bacmp(bdaddr, &b->bdaddr) == 0)
+       list_for_each_entry(b, &hdev->blacklist, list) {
+               if (!bacmp(&b->bdaddr, bdaddr) && b->bdaddr_type == type)
                        return b;
+       }
 
        return NULL;
 }
@@ -2140,9 +2723,7 @@ int hci_blacklist_clear(struct hci_dev *hdev)
        struct list_head *p, *n;
 
        list_for_each_safe(p, n, &hdev->blacklist) {
-               struct bdaddr_list *b;
-
-               b = list_entry(p, struct bdaddr_list, list);
+               struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list);
 
                list_del(p);
                kfree(b);
@@ -2155,10 +2736,10 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
 {
        struct bdaddr_list *entry;
 
-       if (bacmp(bdaddr, BDADDR_ANY) == 0)
+       if (!bacmp(bdaddr, BDADDR_ANY))
                return -EBADF;
 
-       if (hci_blacklist_lookup(hdev, bdaddr))
+       if (hci_blacklist_lookup(hdev, bdaddr, type))
                return -EEXIST;
 
        entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
@@ -2166,6 +2747,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
                return -ENOMEM;
 
        bacpy(&entry->bdaddr, bdaddr);
+       entry->bdaddr_type = type;
 
        list_add(&entry->list, &hdev->blacklist);
 
@@ -2176,10 +2758,10 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
 {
        struct bdaddr_list *entry;
 
-       if (bacmp(bdaddr, BDADDR_ANY) == 0)
+       if (!bacmp(bdaddr, BDADDR_ANY))
                return hci_blacklist_clear(hdev);
 
-       entry = hci_blacklist_lookup(hdev, bdaddr);
+       entry = hci_blacklist_lookup(hdev, bdaddr, type);
        if (!entry)
                return -ENOENT;
 
@@ -2287,6 +2869,8 @@ struct hci_dev *hci_alloc_dev(void)
 
        hdev->le_scan_interval = 0x0060;
        hdev->le_scan_window = 0x0030;
+       hdev->le_conn_min_interval = 0x0028;
+       hdev->le_conn_max_interval = 0x0038;
 
        mutex_init(&hdev->lock);
        mutex_init(&hdev->req_lock);
@@ -2376,7 +2960,12 @@ int hci_register_dev(struct hci_dev *hdev)
                goto err;
        }
 
-       error = hci_add_sysfs(hdev);
+       if (!IS_ERR_OR_NULL(bt_debugfs))
+               hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs);
+
+       dev_set_name(&hdev->dev, "%s", hdev->name);
+
+       error = device_add(&hdev->dev);
        if (error < 0)
                goto err_wqueue;
 
@@ -2464,7 +3053,9 @@ void hci_unregister_dev(struct hci_dev *hdev)
                rfkill_destroy(hdev->rfkill);
        }
 
-       hci_del_sysfs(hdev);
+       device_del(&hdev->dev);
+
+       debugfs_remove_recursive(hdev->debugfs);
 
        destroy_workqueue(hdev->workqueue);
        destroy_workqueue(hdev->req_workqueue);
index 5391469ff1a562f40bd70c2e60646b66d7b9c608..5935f748c0f9a6fe71cb3c0fe8baeaedb7019c37 100644 (file)
@@ -195,6 +195,11 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
 
        memset(hdev->adv_data, 0, sizeof(hdev->adv_data));
        hdev->adv_data_len = 0;
+
+       memset(hdev->scan_rsp_data, 0, sizeof(hdev->scan_rsp_data));
+       hdev->scan_rsp_data_len = 0;
+
+       hdev->ssp_debug_mode = 0;
 }
 
 static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -310,11 +315,6 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
                set_bit(HCI_ISCAN, &hdev->flags);
                if (!old_iscan)
                        mgmt_discoverable(hdev, 1);
-               if (hdev->discov_timeout > 0) {
-                       int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
-                       queue_delayed_work(hdev->workqueue, &hdev->discov_off,
-                                          to);
-               }
        } else if (old_iscan)
                mgmt_discoverable(hdev, 0);
 
@@ -470,14 +470,13 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
        if (rp->status)
                return;
 
-       hdev->hci_ver = rp->hci_ver;
-       hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
-       hdev->lmp_ver = rp->lmp_ver;
-       hdev->manufacturer = __le16_to_cpu(rp->manufacturer);
-       hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver);
-
-       BT_DBG("%s manufacturer 0x%4.4x hci ver %d:%d", hdev->name,
-              hdev->manufacturer, hdev->hci_ver, hdev->hci_rev);
+       if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+               hdev->hci_ver = rp->hci_ver;
+               hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
+               hdev->lmp_ver = rp->lmp_ver;
+               hdev->manufacturer = __le16_to_cpu(rp->manufacturer);
+               hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver);
+       }
 }
 
 static void hci_cc_read_local_commands(struct hci_dev *hdev,
@@ -557,7 +556,8 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
        if (rp->status)
                return;
 
-       hdev->max_page = rp->max_page;
+       if (hdev->max_page < rp->max_page)
+               hdev->max_page = rp->max_page;
 
        if (rp->page < HCI_MAX_PAGES)
                memcpy(hdev->features[rp->page], rp->features, 8);
@@ -939,14 +939,6 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
                        clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
        }
 
-       if (*sent && !test_bit(HCI_INIT, &hdev->flags)) {
-               struct hci_request req;
-
-               hci_req_init(&req, hdev);
-               hci_update_ad(&req);
-               hci_req_run(&req, NULL);
-       }
-
        hci_dev_unlock(hdev);
 }
 
@@ -1702,7 +1694,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
                                      &flags);
 
        if ((mask & HCI_LM_ACCEPT) &&
-           !hci_blacklist_lookup(hdev, &ev->bdaddr)) {
+           !hci_blacklist_lookup(hdev, &ev->bdaddr, BDADDR_BREDR)) {
                /* Connection accepted */
                struct inquiry_entry *ie;
                struct hci_conn *conn;
@@ -2559,7 +2551,6 @@ static void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
        conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
        if (conn) {
                conn->mode = ev->mode;
-               conn->interval = __le16_to_cpu(ev->interval);
 
                if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND,
                                        &conn->flags)) {
@@ -2941,6 +2932,23 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
+static inline size_t eir_get_length(u8 *eir, size_t eir_len)
+{
+       size_t parsed = 0;
+
+       while (parsed < eir_len) {
+               u8 field_len = eir[0];
+
+               if (field_len == 0)
+                       return parsed;
+
+               parsed += field_len + 1;
+               eir += field_len + 1;
+       }
+
+       return eir_len;
+}
+
 static void hci_extended_inquiry_result_evt(struct hci_dev *hdev,
                                            struct sk_buff *skb)
 {
@@ -3181,7 +3189,8 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
 
                if (hdev->auto_accept_delay > 0) {
                        int delay = msecs_to_jiffies(hdev->auto_accept_delay);
-                       mod_timer(&conn->auto_accept_timer, jiffies + delay);
+                       queue_delayed_work(conn->hdev->workqueue,
+                                          &conn->auto_accept_work, delay);
                        goto unlock;
                }
 
index 97f96ebdd56d9e9be20455582e76cef5f6b6bb92..71f0be1730801a615191a9badc2bd2f588bd4fbb 100644 (file)
@@ -481,7 +481,7 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
 
        hci_dev_lock(hdev);
 
-       err = hci_blacklist_add(hdev, &bdaddr, 0);
+       err = hci_blacklist_add(hdev, &bdaddr, BDADDR_BREDR);
 
        hci_dev_unlock(hdev);
 
@@ -498,7 +498,7 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
 
        hci_dev_lock(hdev);
 
-       err = hci_blacklist_del(hdev, &bdaddr, 0);
+       err = hci_blacklist_del(hdev, &bdaddr, BDADDR_BREDR);
 
        hci_dev_unlock(hdev);
 
index edf623a29043117c4be9cf255b37af3ddf22d988..0b61250cfdf90c9e3a488c9ca3cce41ac79d6a84 100644 (file)
@@ -1,17 +1,12 @@
 /* Bluetooth HCI driver model support. */
 
-#include <linux/debugfs.h>
 #include <linux/module.h>
-#include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
 static struct class *bt_class;
 
-struct dentry *bt_debugfs;
-EXPORT_SYMBOL_GPL(bt_debugfs);
-
 static inline char *link_typetostr(int type)
 {
        switch (type) {
@@ -42,29 +37,15 @@ static ssize_t show_link_address(struct device *dev,
        return sprintf(buf, "%pMR\n", &conn->dst);
 }
 
-static ssize_t show_link_features(struct device *dev,
-                                 struct device_attribute *attr, char *buf)
-{
-       struct hci_conn *conn = to_hci_conn(dev);
-
-       return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
-                      conn->features[0][0], conn->features[0][1],
-                      conn->features[0][2], conn->features[0][3],
-                      conn->features[0][4], conn->features[0][5],
-                      conn->features[0][6], conn->features[0][7]);
-}
-
 #define LINK_ATTR(_name, _mode, _show, _store) \
 struct device_attribute link_attr_##_name = __ATTR(_name, _mode, _show, _store)
 
 static LINK_ATTR(type, S_IRUGO, show_link_type, NULL);
 static LINK_ATTR(address, S_IRUGO, show_link_address, NULL);
-static LINK_ATTR(features, S_IRUGO, show_link_features, NULL);
 
 static struct attribute *bt_link_attrs[] = {
        &link_attr_type.attr,
        &link_attr_address.attr,
-       &link_attr_features.attr,
        NULL
 };
 
@@ -150,28 +131,6 @@ void hci_conn_del_sysfs(struct hci_conn *conn)
        hci_dev_put(hdev);
 }
 
-static inline char *host_bustostr(int bus)
-{
-       switch (bus) {
-       case HCI_VIRTUAL:
-               return "VIRTUAL";
-       case HCI_USB:
-               return "USB";
-       case HCI_PCCARD:
-               return "PCCARD";
-       case HCI_UART:
-               return "UART";
-       case HCI_RS232:
-               return "RS232";
-       case HCI_PCI:
-               return "PCI";
-       case HCI_SDIO:
-               return "SDIO";
-       default:
-               return "UNKNOWN";
-       }
-}
-
 static inline char *host_typetostr(int type)
 {
        switch (type) {
@@ -184,13 +143,6 @@ static inline char *host_typetostr(int type)
        }
 }
 
-static ssize_t show_bus(struct device *dev,
-                       struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%s\n", host_bustostr(hdev->bus));
-}
-
 static ssize_t show_type(struct device *dev,
                         struct device_attribute *attr, char *buf)
 {
@@ -212,14 +164,6 @@ static ssize_t show_name(struct device *dev,
        return sprintf(buf, "%s\n", name);
 }
 
-static ssize_t show_class(struct device *dev,
-                         struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "0x%.2x%.2x%.2x\n", hdev->dev_class[2],
-                      hdev->dev_class[1], hdev->dev_class[0]);
-}
-
 static ssize_t show_address(struct device *dev,
                            struct device_attribute *attr, char *buf)
 {
@@ -227,150 +171,14 @@ static ssize_t show_address(struct device *dev,
        return sprintf(buf, "%pMR\n", &hdev->bdaddr);
 }
 
-static ssize_t show_features(struct device *dev,
-                            struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-
-       return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
-                      hdev->features[0][0], hdev->features[0][1],
-                      hdev->features[0][2], hdev->features[0][3],
-                      hdev->features[0][4], hdev->features[0][5],
-                      hdev->features[0][6], hdev->features[0][7]);
-}
-
-static ssize_t show_manufacturer(struct device *dev,
-                                struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->manufacturer);
-}
-
-static ssize_t show_hci_version(struct device *dev,
-                               struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->hci_ver);
-}
-
-static ssize_t show_hci_revision(struct device *dev,
-                                struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->hci_rev);
-}
-
-static ssize_t show_idle_timeout(struct device *dev,
-                                struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->idle_timeout);
-}
-
-static ssize_t store_idle_timeout(struct device *dev,
-                                 struct device_attribute *attr,
-                                 const char *buf, size_t count)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       unsigned int val;
-       int rv;
-
-       rv = kstrtouint(buf, 0, &val);
-       if (rv < 0)
-               return rv;
-
-       if (val != 0 && (val < 500 || val > 3600000))
-               return -EINVAL;
-
-       hdev->idle_timeout = val;
-
-       return count;
-}
-
-static ssize_t show_sniff_max_interval(struct device *dev,
-                                      struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->sniff_max_interval);
-}
-
-static ssize_t store_sniff_max_interval(struct device *dev,
-                                       struct device_attribute *attr,
-                                       const char *buf, size_t count)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       u16 val;
-       int rv;
-
-       rv = kstrtou16(buf, 0, &val);
-       if (rv < 0)
-               return rv;
-
-       if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
-               return -EINVAL;
-
-       hdev->sniff_max_interval = val;
-
-       return count;
-}
-
-static ssize_t show_sniff_min_interval(struct device *dev,
-                                      struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->sniff_min_interval);
-}
-
-static ssize_t store_sniff_min_interval(struct device *dev,
-                                       struct device_attribute *attr,
-                                       const char *buf, size_t count)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       u16 val;
-       int rv;
-
-       rv = kstrtou16(buf, 0, &val);
-       if (rv < 0)
-               return rv;
-
-       if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
-               return -EINVAL;
-
-       hdev->sniff_min_interval = val;
-
-       return count;
-}
-
-static DEVICE_ATTR(bus, S_IRUGO, show_bus, NULL);
 static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
 static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
-static DEVICE_ATTR(class, S_IRUGO, show_class, NULL);
 static DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
-static DEVICE_ATTR(features, S_IRUGO, show_features, NULL);
-static DEVICE_ATTR(manufacturer, S_IRUGO, show_manufacturer, NULL);
-static DEVICE_ATTR(hci_version, S_IRUGO, show_hci_version, NULL);
-static DEVICE_ATTR(hci_revision, S_IRUGO, show_hci_revision, NULL);
-
-static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR,
-                  show_idle_timeout, store_idle_timeout);
-static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR,
-                  show_sniff_max_interval, store_sniff_max_interval);
-static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR,
-                  show_sniff_min_interval, store_sniff_min_interval);
 
 static struct attribute *bt_host_attrs[] = {
-       &dev_attr_bus.attr,
        &dev_attr_type.attr,
        &dev_attr_name.attr,
-       &dev_attr_class.attr,
        &dev_attr_address.attr,
-       &dev_attr_features.attr,
-       &dev_attr_manufacturer.attr,
-       &dev_attr_hci_version.attr,
-       &dev_attr_hci_revision.attr,
-       &dev_attr_idle_timeout.attr,
-       &dev_attr_sniff_max_interval.attr,
-       &dev_attr_sniff_min_interval.attr,
        NULL
 };
 
@@ -396,141 +204,6 @@ static struct device_type bt_host = {
        .release = bt_host_release,
 };
 
-static int inquiry_cache_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-       struct discovery_state *cache = &hdev->discovery;
-       struct inquiry_entry *e;
-
-       hci_dev_lock(hdev);
-
-       list_for_each_entry(e, &cache->all, all) {
-               struct inquiry_data *data = &e->data;
-               seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
-                          &data->bdaddr,
-                          data->pscan_rep_mode, data->pscan_period_mode,
-                          data->pscan_mode, data->dev_class[2],
-                          data->dev_class[1], data->dev_class[0],
-                          __le16_to_cpu(data->clock_offset),
-                          data->rssi, data->ssp_mode, e->timestamp);
-       }
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int inquiry_cache_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, inquiry_cache_show, inode->i_private);
-}
-
-static const struct file_operations inquiry_cache_fops = {
-       .open           = inquiry_cache_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int blacklist_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-       struct bdaddr_list *b;
-
-       hci_dev_lock(hdev);
-
-       list_for_each_entry(b, &hdev->blacklist, list)
-               seq_printf(f, "%pMR\n", &b->bdaddr);
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int blacklist_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, blacklist_show, inode->i_private);
-}
-
-static const struct file_operations blacklist_fops = {
-       .open           = blacklist_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static void print_bt_uuid(struct seq_file *f, u8 *uuid)
-{
-       u32 data0, data5;
-       u16 data1, data2, data3, data4;
-
-       data5 = get_unaligned_le32(uuid);
-       data4 = get_unaligned_le16(uuid + 4);
-       data3 = get_unaligned_le16(uuid + 6);
-       data2 = get_unaligned_le16(uuid + 8);
-       data1 = get_unaligned_le16(uuid + 10);
-       data0 = get_unaligned_le32(uuid + 12);
-
-       seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.4x%.8x\n",
-                  data0, data1, data2, data3, data4, data5);
-}
-
-static int uuids_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-       struct bt_uuid *uuid;
-
-       hci_dev_lock(hdev);
-
-       list_for_each_entry(uuid, &hdev->uuids, list)
-               print_bt_uuid(f, uuid->uuid);
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int uuids_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, uuids_show, inode->i_private);
-}
-
-static const struct file_operations uuids_fops = {
-       .open           = uuids_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int auto_accept_delay_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-
-       hdev->auto_accept_delay = val;
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int auto_accept_delay_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-
-       *val = hdev->auto_accept_delay;
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
-                       auto_accept_delay_set, "%llu\n");
-
 void hci_init_sysfs(struct hci_dev *hdev)
 {
        struct device *dev = &hdev->dev;
@@ -542,52 +215,8 @@ void hci_init_sysfs(struct hci_dev *hdev)
        device_initialize(dev);
 }
 
-int hci_add_sysfs(struct hci_dev *hdev)
-{
-       struct device *dev = &hdev->dev;
-       int err;
-
-       BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
-
-       dev_set_name(dev, "%s", hdev->name);
-
-       err = device_add(dev);
-       if (err < 0)
-               return err;
-
-       if (!bt_debugfs)
-               return 0;
-
-       hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs);
-       if (!hdev->debugfs)
-               return 0;
-
-       debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
-                           hdev, &inquiry_cache_fops);
-
-       debugfs_create_file("blacklist", 0444, hdev->debugfs,
-                           hdev, &blacklist_fops);
-
-       debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
-
-       debugfs_create_file("auto_accept_delay", 0444, hdev->debugfs, hdev,
-                           &auto_accept_delay_fops);
-       return 0;
-}
-
-void hci_del_sysfs(struct hci_dev *hdev)
-{
-       BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
-
-       debugfs_remove_recursive(hdev->debugfs);
-
-       device_del(&hdev->dev);
-}
-
 int __init bt_sysfs_init(void)
 {
-       bt_debugfs = debugfs_create_dir("bluetooth", NULL);
-
        bt_class = class_create(THIS_MODULE, "bluetooth");
 
        return PTR_ERR_OR_ZERO(bt_class);
@@ -596,6 +225,4 @@ int __init bt_sysfs_init(void)
 void bt_sysfs_cleanup(void)
 {
        class_destroy(bt_class);
-
-       debugfs_remove_recursive(bt_debugfs);
 }
index 0c3446da1ec9d7f6765c7f4523ad08c6bd20dbe3..0cef677078381315c7ce3e58abb6573136bc227b 100644 (file)
@@ -223,38 +223,25 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
        return 0;
 }
 
-static void __l2cap_state_change(struct l2cap_chan *chan, int state)
+static void l2cap_state_change(struct l2cap_chan *chan, int state)
 {
        BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state),
               state_to_string(state));
 
        chan->state = state;
-       chan->ops->state_change(chan, state);
+       chan->ops->state_change(chan, state, 0);
 }
 
-static void l2cap_state_change(struct l2cap_chan *chan, int state)
+static inline void l2cap_state_change_and_error(struct l2cap_chan *chan,
+                                               int state, int err)
 {
-       struct sock *sk = chan->sk;
-
-       lock_sock(sk);
-       __l2cap_state_change(chan, state);
-       release_sock(sk);
-}
-
-static inline void __l2cap_chan_set_err(struct l2cap_chan *chan, int err)
-{
-       struct sock *sk = chan->sk;
-
-       sk->sk_err = err;
+       chan->state = state;
+       chan->ops->state_change(chan, chan->state, err);
 }
 
 static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err)
 {
-       struct sock *sk = chan->sk;
-
-       lock_sock(sk);
-       __l2cap_chan_set_err(chan, err);
-       release_sock(sk);
+       chan->ops->state_change(chan, chan->state, err);
 }
 
 static void __set_retrans_timer(struct l2cap_chan *chan)
@@ -645,8 +632,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
        case BT_CONFIG:
                if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
                    conn->hcon->type == ACL_LINK) {
-                       struct sock *sk = chan->sk;
-                       __set_chan_timer(chan, sk->sk_sndtimeo);
+                       __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
                        l2cap_send_disconn_req(chan, reason);
                } else
                        l2cap_chan_del(chan, reason);
@@ -1230,7 +1216,6 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
 
 static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
 {
-       struct sock *sk = chan->sk;
        struct l2cap_conn *conn = chan->conn;
        struct l2cap_disconn_req req;
 
@@ -1253,10 +1238,7 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
        l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ,
                       sizeof(req), &req);
 
-       lock_sock(sk);
-       __l2cap_state_change(chan, BT_DISCONN);
-       __l2cap_chan_set_err(chan, err);
-       release_sock(sk);
+       l2cap_state_change_and_error(chan, BT_DISCONN, err);
 }
 
 /* ---- L2CAP connections ---- */
@@ -1300,20 +1282,16 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                        rsp.dcid = cpu_to_le16(chan->scid);
 
                        if (l2cap_chan_check_security(chan)) {
-                               struct sock *sk = chan->sk;
-
-                               lock_sock(sk);
                                if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
                                        rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
                                        rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
                                        chan->ops->defer(chan);
 
                                } else {
-                                       __l2cap_state_change(chan, BT_CONFIG);
+                                       l2cap_state_change(chan, BT_CONFIG);
                                        rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
                                        rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
                                }
-                               release_sock(sk);
                        } else {
                                rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
                                rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
@@ -1383,14 +1361,15 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
 
 static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 {
-       struct sock *parent;
+       struct hci_conn *hcon = conn->hcon;
        struct l2cap_chan *chan, *pchan;
+       u8 dst_type;
 
        BT_DBG("");
 
        /* Check if we have socket listening on cid */
        pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
-                                         &conn->hcon->src, &conn->hcon->dst);
+                                         &hcon->src, &hcon->dst);
        if (!pchan)
                return;
 
@@ -1398,9 +1377,13 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
        if (__l2cap_get_chan_by_dcid(conn, L2CAP_CID_ATT))
                return;
 
-       parent = pchan->sk;
+       dst_type = bdaddr_type(hcon, hcon->dst_type);
+
+       /* If device is blocked, do not create a channel for it */
+       if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, dst_type))
+               return;
 
-       lock_sock(parent);
+       l2cap_chan_lock(pchan);
 
        chan = pchan->ops->new_connection(pchan);
        if (!chan)
@@ -1408,15 +1391,15 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 
        chan->dcid = L2CAP_CID_ATT;
 
-       bacpy(&chan->src, &conn->hcon->src);
-       bacpy(&chan->dst, &conn->hcon->dst);
-       chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type);
-       chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type);
+       bacpy(&chan->src, &hcon->src);
+       bacpy(&chan->dst, &hcon->dst);
+       chan->src_type = bdaddr_type(hcon, hcon->src_type);
+       chan->dst_type = dst_type;
 
        __l2cap_chan_add(conn, chan);
 
 clean:
-       release_sock(parent);
+       l2cap_chan_unlock(pchan);
 }
 
 static void l2cap_conn_ready(struct l2cap_conn *conn)
@@ -1451,12 +1434,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
                                l2cap_chan_ready(chan);
 
                } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
-                       struct sock *sk = chan->sk;
-                       __clear_chan_timer(chan);
-                       lock_sock(sk);
-                       __l2cap_state_change(chan, BT_CONNECTED);
-                       sk->sk_state_change(sk);
-                       release_sock(sk);
+                       l2cap_chan_ready(chan);
 
                } else if (chan->state == BT_CONNECT) {
                        l2cap_do_start(chan);
@@ -1764,7 +1742,6 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
 int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
                       bdaddr_t *dst, u8 dst_type)
 {
-       struct sock *sk = chan->sk;
        struct l2cap_conn *conn;
        struct hci_conn *hcon;
        struct hci_dev *hdev;
@@ -1876,7 +1853,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        hci_conn_drop(hcon);
 
        l2cap_state_change(chan, BT_CONNECT);
-       __set_chan_timer(chan, sk->sk_sndtimeo);
+       __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
 
        if (hcon->state == BT_CONNECTED) {
                if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
@@ -1896,38 +1873,6 @@ done:
        return err;
 }
 
-int __l2cap_wait_ack(struct sock *sk)
-{
-       struct l2cap_chan *chan = l2cap_pi(sk)->chan;
-       DECLARE_WAITQUEUE(wait, current);
-       int err = 0;
-       int timeo = HZ/5;
-
-       add_wait_queue(sk_sleep(sk), &wait);
-       set_current_state(TASK_INTERRUPTIBLE);
-       while (chan->unacked_frames > 0 && chan->conn) {
-               if (!timeo)
-                       timeo = HZ/5;
-
-               if (signal_pending(current)) {
-                       err = sock_intr_errno(timeo);
-                       break;
-               }
-
-               release_sock(sk);
-               timeo = schedule_timeout(timeo);
-               lock_sock(sk);
-               set_current_state(TASK_INTERRUPTIBLE);
-
-               err = sock_error(sk);
-               if (err)
-                       break;
-       }
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(sk_sleep(sk), &wait);
-       return err;
-}
-
 static void l2cap_monitor_timeout(struct work_struct *work)
 {
        struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
@@ -2868,17 +2813,16 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
        mutex_lock(&conn->chan_lock);
 
        list_for_each_entry(chan, &conn->chan_l, list) {
-               struct sock *sk = chan->sk;
                if (chan->chan_type != L2CAP_CHAN_RAW)
                        continue;
 
-               /* Don't send frame to the socket it came from */
-               if (skb->sk == sk)
+               /* Don't send frame to the channel it came from */
+               if (bt_cb(skb)->chan == chan)
                        continue;
+
                nskb = skb_clone(skb, GFP_KERNEL);
                if (!nskb)
                        continue;
-
                if (chan->ops->recv(chan, nskb))
                        kfree_skb(nskb);
        }
@@ -3757,7 +3701,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
        struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
        struct l2cap_conn_rsp rsp;
        struct l2cap_chan *chan = NULL, *pchan;
-       struct sock *parent, *sk = NULL;
        int result, status = L2CAP_CS_NO_INFO;
 
        u16 dcid = 0, scid = __le16_to_cpu(req->scid);
@@ -3773,10 +3716,8 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
                goto sendresp;
        }
 
-       parent = pchan->sk;
-
        mutex_lock(&conn->chan_lock);
-       lock_sock(parent);
+       l2cap_chan_lock(pchan);
 
        /* Check if the ACL is secure enough (if not SDP) */
        if (psm != __constant_cpu_to_le16(L2CAP_PSM_SDP) &&
@@ -3796,8 +3737,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
        if (!chan)
                goto response;
 
-       sk = chan->sk;
-
        /* For certain devices (ex: HID mouse), support for authentication,
         * pairing and bonding is optional. For such devices, inorder to avoid
         * the ACL alive for too long after L2CAP disconnection, reset the ACL
@@ -3817,14 +3756,14 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
 
        dcid = chan->scid;
 
-       __set_chan_timer(chan, sk->sk_sndtimeo);
+       __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
 
        chan->ident = cmd->ident;
 
        if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
                if (l2cap_chan_check_security(chan)) {
                        if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
-                               __l2cap_state_change(chan, BT_CONNECT2);
+                               l2cap_state_change(chan, BT_CONNECT2);
                                result = L2CAP_CR_PEND;
                                status = L2CAP_CS_AUTHOR_PEND;
                                chan->ops->defer(chan);
@@ -3834,27 +3773,27 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
                                 * physical link is up.
                                 */
                                if (amp_id == AMP_ID_BREDR) {
-                                       __l2cap_state_change(chan, BT_CONFIG);
+                                       l2cap_state_change(chan, BT_CONFIG);
                                        result = L2CAP_CR_SUCCESS;
                                } else {
-                                       __l2cap_state_change(chan, BT_CONNECT2);
+                                       l2cap_state_change(chan, BT_CONNECT2);
                                        result = L2CAP_CR_PEND;
                                }
                                status = L2CAP_CS_NO_INFO;
                        }
                } else {
-                       __l2cap_state_change(chan, BT_CONNECT2);
+                       l2cap_state_change(chan, BT_CONNECT2);
                        result = L2CAP_CR_PEND;
                        status = L2CAP_CS_AUTHEN_PEND;
                }
        } else {
-               __l2cap_state_change(chan, BT_CONNECT2);
+               l2cap_state_change(chan, BT_CONNECT2);
                result = L2CAP_CR_PEND;
                status = L2CAP_CS_NO_INFO;
        }
 
 response:
-       release_sock(parent);
+       l2cap_chan_unlock(pchan);
        mutex_unlock(&conn->chan_lock);
 
 sendresp:
@@ -4010,6 +3949,18 @@ static void l2cap_send_efs_conf_rsp(struct l2cap_chan *chan, void *data,
                                            L2CAP_CONF_SUCCESS, flags), data);
 }
 
+static void cmd_reject_invalid_cid(struct l2cap_conn *conn, u8 ident,
+                                  u16 scid, u16 dcid)
+{
+       struct l2cap_cmd_rej_cid rej;
+
+       rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
+       rej.scid = __cpu_to_le16(scid);
+       rej.dcid = __cpu_to_le16(dcid);
+
+       l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
+}
+
 static inline int l2cap_config_req(struct l2cap_conn *conn,
                                   struct l2cap_cmd_hdr *cmd, u16 cmd_len,
                                   u8 *data)
@@ -4029,18 +3980,14 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
        BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
 
        chan = l2cap_get_chan_by_scid(conn, dcid);
-       if (!chan)
-               return -EBADSLT;
+       if (!chan) {
+               cmd_reject_invalid_cid(conn, cmd->ident, dcid, 0);
+               return 0;
+       }
 
        if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
-               struct l2cap_cmd_rej_cid rej;
-
-               rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
-               rej.scid = cpu_to_le16(chan->scid);
-               rej.dcid = cpu_to_le16(chan->dcid);
-
-               l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
-                              sizeof(rej), &rej);
+               cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
+                                      chan->dcid);
                goto unlock;
        }
 
@@ -4243,7 +4190,6 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
        struct l2cap_disconn_rsp rsp;
        u16 dcid, scid;
        struct l2cap_chan *chan;
-       struct sock *sk;
 
        if (cmd_len != sizeof(*req))
                return -EPROTO;
@@ -4258,20 +4204,17 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
        chan = __l2cap_get_chan_by_scid(conn, dcid);
        if (!chan) {
                mutex_unlock(&conn->chan_lock);
-               return -EBADSLT;
+               cmd_reject_invalid_cid(conn, cmd->ident, dcid, scid);
+               return 0;
        }
 
        l2cap_chan_lock(chan);
 
-       sk = chan->sk;
-
        rsp.dcid = cpu_to_le16(chan->scid);
        rsp.scid = cpu_to_le16(chan->dcid);
        l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);
 
-       lock_sock(sk);
-       sk->sk_shutdown = SHUTDOWN_MASK;
-       release_sock(sk);
+       chan->ops->set_shutdown(chan);
 
        l2cap_chan_hold(chan);
        l2cap_chan_del(chan, ECONNRESET);
@@ -4491,7 +4434,9 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
                                                  &conn->hcon->dst);
                if (!hs_hcon) {
                        hci_dev_put(hdev);
-                       return -EBADSLT;
+                       cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
+                                              chan->dcid);
+                       return 0;
                }
 
                BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon);
@@ -4769,7 +4714,7 @@ static void l2cap_do_create(struct l2cap_chan *chan, int result,
                               sizeof(rsp), &rsp);
 
                if (result == L2CAP_CR_SUCCESS) {
-                       __l2cap_state_change(chan, BT_CONFIG);
+                       l2cap_state_change(chan, BT_CONFIG);
                        set_bit(CONF_REQ_SENT, &chan->conf_state);
                        l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn),
                                       L2CAP_CONF_REQ,
@@ -5347,20 +5292,6 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
        }
 }
 
-static __le16 l2cap_err_to_reason(int err)
-{
-       switch (err) {
-       case -EBADSLT:
-               return __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
-       case -EMSGSIZE:
-               return __constant_cpu_to_le16(L2CAP_REJ_MTU_EXCEEDED);
-       case -EINVAL:
-       case -EPROTO:
-       default:
-               return __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
-       }
-}
-
 static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
                                        struct sk_buff *skb)
 {
@@ -5393,7 +5324,7 @@ static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
 
                BT_ERR("Wrong link type (%d)", err);
 
-               rej.reason = l2cap_err_to_reason(err);
+               rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
                l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
                               sizeof(rej), &rej);
        }
@@ -5438,7 +5369,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
 
                        BT_ERR("Wrong link type (%d)", err);
 
-                       rej.reason = l2cap_err_to_reason(err);
+                       rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
                        l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
                                       sizeof(rej), &rej);
                }
@@ -6446,8 +6377,7 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
        if (hcon->type != ACL_LINK)
                goto drop;
 
-       chan = l2cap_global_chan_by_psm(0, psm, &conn->hcon->src,
-                                       &conn->hcon->dst);
+       chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst);
        if (!chan)
                goto drop;
 
@@ -6460,7 +6390,7 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
                goto drop;
 
        /* Store remote BD_ADDR and PSM for msg_name */
-       bacpy(&bt_cb(skb)->bdaddr, &conn->hcon->dst);
+       bacpy(&bt_cb(skb)->bdaddr, &hcon->dst);
        bt_cb(skb)->psm = psm;
 
        if (!chan->ops->recv(chan, skb))
@@ -6480,12 +6410,15 @@ static void l2cap_att_channel(struct l2cap_conn *conn,
                goto drop;
 
        chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT,
-                                        &conn->hcon->src, &conn->hcon->dst);
+                                        &hcon->src, &hcon->dst);
        if (!chan)
                goto drop;
 
        BT_DBG("chan %p, len %d", chan, skb->len);
 
+       if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, hcon->dst_type))
+               goto drop;
+
        if (chan->imtu < skb->len)
                goto drop;
 
@@ -6682,31 +6615,26 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
                                __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
                        }
                } else if (chan->state == BT_CONNECT2) {
-                       struct sock *sk = chan->sk;
                        struct l2cap_conn_rsp rsp;
                        __u16 res, stat;
 
-                       lock_sock(sk);
-
                        if (!status) {
                                if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
                                        res = L2CAP_CR_PEND;
                                        stat = L2CAP_CS_AUTHOR_PEND;
                                        chan->ops->defer(chan);
                                } else {
-                                       __l2cap_state_change(chan, BT_CONFIG);
+                                       l2cap_state_change(chan, BT_CONFIG);
                                        res = L2CAP_CR_SUCCESS;
                                        stat = L2CAP_CS_NO_INFO;
                                }
                        } else {
-                               __l2cap_state_change(chan, BT_DISCONN);
+                               l2cap_state_change(chan, BT_DISCONN);
                                __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
                                res = L2CAP_CR_SEC_BLOCK;
                                stat = L2CAP_CS_NO_INFO;
                        }
 
-                       release_sock(sk);
-
                        rsp.scid   = cpu_to_le16(chan->dcid);
                        rsp.dcid   = cpu_to_le16(chan->scid);
                        rsp.result = cpu_to_le16(res);
@@ -6880,12 +6808,11 @@ int __init l2cap_init(void)
        if (err < 0)
                return err;
 
-       if (bt_debugfs) {
-               l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
-                                                   NULL, &l2cap_debugfs_fops);
-               if (!l2cap_debugfs)
-                       BT_ERR("Failed to create L2CAP debug file");
-       }
+       if (IS_ERR_OR_NULL(bt_debugfs))
+               return 0;
+
+       l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
+                                           NULL, &l2cap_debugfs_fops);
 
        return 0;
 }
index 5ffd75e20bde0d30ebc416d99fe46176670264b0..7cc24d263caaab45af9d8bbe5be09ebda20e87a4 100644 (file)
@@ -72,6 +72,15 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
        if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
                return -EINVAL;
 
+       if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
+               /* Connection oriented channels are not supported on LE */
+               if (la.l2_psm)
+                       return -EINVAL;
+               /* We only allow ATT user space socket */
+               if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+                       return -EINVAL;
+       }
+
        lock_sock(sk);
 
        if (sk->sk_state != BT_OPEN) {
@@ -150,12 +159,44 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
        if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
                return -EINVAL;
 
-       if (chan->src_type == BDADDR_BREDR && la.l2_bdaddr_type != BDADDR_BREDR)
-               return -EINVAL;
+       /* Check that the socket wasn't bound to something that
+        * conflicts with the address given to connect(). If chan->src
+        * is BDADDR_ANY it means bind() was never used, in which case
+        * chan->src_type and la.l2_bdaddr_type do not need to match.
+        */
+       if (chan->src_type == BDADDR_BREDR && bacmp(&chan->src, BDADDR_ANY) &&
+           bdaddr_type_is_le(la.l2_bdaddr_type)) {
+               /* Old user space versions will try to incorrectly bind
+                * the ATT socket using BDADDR_BREDR. We need to accept
+                * this and fix up the source address type only when
+                * both the source CID and destination CID indicate
+                * ATT. Anything else is an invalid combination.
+                */
+               if (chan->scid != L2CAP_CID_ATT ||
+                   la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+                       return -EINVAL;
+
+               /* We don't have the hdev available here to make a
+                * better decision on random vs public, but since all
+                * user space versions that exhibit this issue anyway do
+                * not support random local addresses assuming public
+                * here is good enough.
+                */
+               chan->src_type = BDADDR_LE_PUBLIC;
+       }
 
        if (chan->src_type != BDADDR_BREDR && la.l2_bdaddr_type == BDADDR_BREDR)
                return -EINVAL;
 
+       if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
+               /* Connection oriented channels are not supported on LE */
+               if (la.l2_psm)
+                       return -EINVAL;
+               /* We only allow ATT user space socket */
+               if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+                       return -EINVAL;
+       }
+
        err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
                                 &la.l2_bdaddr, la.l2_bdaddr_type);
        if (err)
@@ -879,6 +920,38 @@ static void l2cap_sock_kill(struct sock *sk)
        sock_put(sk);
 }
 
+static int __l2cap_wait_ack(struct sock *sk)
+{
+       struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+       DECLARE_WAITQUEUE(wait, current);
+       int err = 0;
+       int timeo = HZ/5;
+
+       add_wait_queue(sk_sleep(sk), &wait);
+       set_current_state(TASK_INTERRUPTIBLE);
+       while (chan->unacked_frames > 0 && chan->conn) {
+               if (!timeo)
+                       timeo = HZ/5;
+
+               if (signal_pending(current)) {
+                       err = sock_intr_errno(timeo);
+                       break;
+               }
+
+               release_sock(sk);
+               timeo = schedule_timeout(timeo);
+               lock_sock(sk);
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               err = sock_error(sk);
+               if (err)
+                       break;
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(sk_sleep(sk), &wait);
+       return err;
+}
+
 static int l2cap_sock_shutdown(struct socket *sock, int how)
 {
        struct sock *sk = sock->sk;
@@ -969,6 +1042,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
 {
        struct sock *sk, *parent = chan->data;
 
+       lock_sock(parent);
+
        /* Check for backlog size */
        if (sk_acceptq_is_full(parent)) {
                BT_DBG("backlog full %d", parent->sk_ack_backlog);
@@ -986,6 +1061,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
 
        bt_accept_enqueue(parent, sk);
 
+       release_sock(parent);
+
        return l2cap_pi(sk)->chan;
 }
 
@@ -1072,26 +1149,33 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err)
        release_sock(sk);
 }
 
-static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state)
+static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state,
+                                      int err)
 {
        struct sock *sk = chan->data;
 
        sk->sk_state = state;
+
+       if (err)
+               sk->sk_err = err;
 }
 
 static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
                                               unsigned long len, int nb)
 {
+       struct sock *sk = chan->data;
        struct sk_buff *skb;
        int err;
 
        l2cap_chan_unlock(chan);
-       skb = bt_skb_send_alloc(chan->sk, len, nb, &err);
+       skb = bt_skb_send_alloc(sk, len, nb, &err);
        l2cap_chan_lock(chan);
 
        if (!skb)
                return ERR_PTR(err);
 
+       bt_cb(skb)->chan = chan;
+
        return skb;
 }
 
@@ -1117,11 +1201,15 @@ static void l2cap_sock_ready_cb(struct l2cap_chan *chan)
 
 static void l2cap_sock_defer_cb(struct l2cap_chan *chan)
 {
-       struct sock *sk = chan->data;
-       struct sock *parent = bt_sk(sk)->parent;
+       struct sock *parent, *sk = chan->data;
+
+       lock_sock(sk);
 
+       parent = bt_sk(sk)->parent;
        if (parent)
                parent->sk_data_ready(parent, 0);
+
+       release_sock(sk);
 }
 
 static void l2cap_sock_resume_cb(struct l2cap_chan *chan)
@@ -1132,6 +1220,22 @@ static void l2cap_sock_resume_cb(struct l2cap_chan *chan)
        sk->sk_state_change(sk);
 }
 
+static void l2cap_sock_set_shutdown_cb(struct l2cap_chan *chan)
+{
+       struct sock *sk = chan->data;
+
+       lock_sock(sk);
+       sk->sk_shutdown = SHUTDOWN_MASK;
+       release_sock(sk);
+}
+
+static long l2cap_sock_get_sndtimeo_cb(struct l2cap_chan *chan)
+{
+       struct sock *sk = chan->data;
+
+       return sk->sk_sndtimeo;
+}
+
 static struct l2cap_ops l2cap_chan_ops = {
        .name           = "L2CAP Socket Interface",
        .new_connection = l2cap_sock_new_connection_cb,
@@ -1142,6 +1246,8 @@ static struct l2cap_ops l2cap_chan_ops = {
        .ready          = l2cap_sock_ready_cb,
        .defer          = l2cap_sock_defer_cb,
        .resume         = l2cap_sock_resume_cb,
+       .set_shutdown   = l2cap_sock_set_shutdown_cb,
+       .get_sndtimeo   = l2cap_sock_get_sndtimeo_cb,
        .alloc_skb      = l2cap_sock_alloc_skb_cb,
 };
 
@@ -1268,8 +1374,6 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
 
        l2cap_chan_hold(chan);
 
-       chan->sk = sk;
-
        l2cap_pi(sk)->chan = chan;
 
        return sk;
index 861e389f4b4c4282162819a90b90e1346fb7f3ac..074d83690a414c8e668499d7f7e7c86215c563ea 100644 (file)
@@ -536,6 +536,156 @@ static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
        return ptr;
 }
 
+static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
+{
+       struct pending_cmd *cmd;
+
+       list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
+               if (cmd->opcode == opcode)
+                       return cmd;
+       }
+
+       return NULL;
+}
+
+static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
+{
+       u8 ad_len = 0;
+       size_t name_len;
+
+       name_len = strlen(hdev->dev_name);
+       if (name_len > 0) {
+               size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
+
+               if (name_len > max_len) {
+                       name_len = max_len;
+                       ptr[1] = EIR_NAME_SHORT;
+               } else
+                       ptr[1] = EIR_NAME_COMPLETE;
+
+               ptr[0] = name_len + 1;
+
+               memcpy(ptr + 2, hdev->dev_name, name_len);
+
+               ad_len += (name_len + 2);
+               ptr += (name_len + 2);
+       }
+
+       return ad_len;
+}
+
+static void update_scan_rsp_data(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_scan_rsp_data cp;
+       u8 len;
+
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+               return;
+
+       memset(&cp, 0, sizeof(cp));
+
+       len = create_scan_rsp_data(hdev, cp.data);
+
+       if (hdev->scan_rsp_data_len == len &&
+           memcmp(cp.data, hdev->scan_rsp_data, len) == 0)
+               return;
+
+       memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data));
+       hdev->scan_rsp_data_len = len;
+
+       cp.length = len;
+
+       hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp);
+}
+
+static u8 get_adv_discov_flags(struct hci_dev *hdev)
+{
+       struct pending_cmd *cmd;
+
+       /* If there's a pending mgmt command the flags will not yet have
+        * their final values, so check for this first.
+        */
+       cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
+       if (cmd) {
+               struct mgmt_mode *cp = cmd->param;
+               if (cp->val == 0x01)
+                       return LE_AD_GENERAL;
+               else if (cp->val == 0x02)
+                       return LE_AD_LIMITED;
+       } else {
+               if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags))
+                       return LE_AD_LIMITED;
+               else if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+                       return LE_AD_GENERAL;
+       }
+
+       return 0;
+}
+
+static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr)
+{
+       u8 ad_len = 0, flags = 0;
+
+       flags |= get_adv_discov_flags(hdev);
+
+       if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+               if (lmp_le_br_capable(hdev))
+                       flags |= LE_AD_SIM_LE_BREDR_CTRL;
+               if (lmp_host_le_br_capable(hdev))
+                       flags |= LE_AD_SIM_LE_BREDR_HOST;
+       } else {
+               flags |= LE_AD_NO_BREDR;
+       }
+
+       if (flags) {
+               BT_DBG("adv flags 0x%02x", flags);
+
+               ptr[0] = 2;
+               ptr[1] = EIR_FLAGS;
+               ptr[2] = flags;
+
+               ad_len += 3;
+               ptr += 3;
+       }
+
+       if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) {
+               ptr[0] = 2;
+               ptr[1] = EIR_TX_POWER;
+               ptr[2] = (u8) hdev->adv_tx_power;
+
+               ad_len += 3;
+               ptr += 3;
+       }
+
+       return ad_len;
+}
+
+static void update_adv_data(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_adv_data cp;
+       u8 len;
+
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+               return;
+
+       memset(&cp, 0, sizeof(cp));
+
+       len = create_adv_data(hdev, cp.data);
+
+       if (hdev->adv_data_len == len &&
+           memcmp(cp.data, hdev->adv_data, len) == 0)
+               return;
+
+       memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
+       hdev->adv_data_len = len;
+
+       cp.length = len;
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
+}
+
 static void create_eir(struct hci_dev *hdev, u8 *data)
 {
        u8 *ptr = data;
@@ -634,6 +784,9 @@ static void update_class(struct hci_request *req)
        if (!hdev_is_powered(hdev))
                return;
 
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+               return;
+
        if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
                return;
 
@@ -641,6 +794,9 @@ static void update_class(struct hci_request *req)
        cod[1] = hdev->major_class;
        cod[2] = get_service_classes(hdev);
 
+       if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags))
+               cod[1] |= 0x20;
+
        if (memcmp(cod, hdev->dev_class, 3) == 0)
                return;
 
@@ -765,18 +921,6 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
        }
 }
 
-static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
-{
-       struct pending_cmd *cmd;
-
-       list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
-               if (cmd->opcode == opcode)
-                       return cmd;
-       }
-
-       return NULL;
-}
-
 static void mgmt_pending_remove(struct pending_cmd *cmd)
 {
        list_del(&cmd->list);
@@ -939,6 +1083,7 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status)
 {
        struct pending_cmd *cmd;
        struct mgmt_mode *cp;
+       struct hci_request req;
        bool changed;
 
        BT_DBG("status 0x%02x", status);
@@ -952,22 +1097,38 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status)
        if (status) {
                u8 mgmt_err = mgmt_status(status);
                cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
                goto remove_cmd;
        }
 
        cp = cmd->param;
-       if (cp->val)
+       if (cp->val) {
                changed = !test_and_set_bit(HCI_DISCOVERABLE,
                                            &hdev->dev_flags);
-       else
+
+               if (hdev->discov_timeout > 0) {
+                       int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
+                       queue_delayed_work(hdev->workqueue, &hdev->discov_off,
+                                          to);
+               }
+       } else {
                changed = test_and_clear_bit(HCI_DISCOVERABLE,
                                             &hdev->dev_flags);
+       }
 
        send_settings_rsp(cmd->sk, MGMT_OP_SET_DISCOVERABLE, hdev);
 
        if (changed)
                new_settings(hdev, cmd->sk);
 
+       /* When the discoverable mode gets changed, make sure
+        * that class of device has the limited discoverable
+        * bit correctly set.
+        */
+       hci_req_init(&req, hdev);
+       update_class(&req);
+       hci_req_run(&req, NULL);
+
 remove_cmd:
        mgmt_pending_remove(cmd);
 
@@ -982,22 +1143,27 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
        struct pending_cmd *cmd;
        struct hci_request req;
        u16 timeout;
-       u8 scan, status;
+       u8 scan;
        int err;
 
        BT_DBG("request for %s", hdev->name);
 
-       status = mgmt_bredr_support(hdev);
-       if (status)
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
+           !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
-                                 status);
+                                 MGMT_STATUS_REJECTED);
 
-       if (cp->val != 0x00 && cp->val != 0x01)
+       if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
                                  MGMT_STATUS_INVALID_PARAMS);
 
        timeout = __le16_to_cpu(cp->timeout);
-       if (!cp->val && timeout > 0)
+
+       /* Disabling discoverable requires that no timeout is set,
+        * and enabling limited discoverable requires a timeout.
+        */
+       if ((cp->val == 0x00 && timeout > 0) ||
+           (cp->val == 0x02 && timeout == 0))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
                                  MGMT_STATUS_INVALID_PARAMS);
 
@@ -1025,6 +1191,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
        if (!hdev_is_powered(hdev)) {
                bool changed = false;
 
+               /* Setting limited discoverable when powered off is
+                * not a valid operation since it requires a timeout
+                * and so no need to check HCI_LIMITED_DISCOVERABLE.
+                */
                if (!!cp->val != test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
                        change_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
                        changed = true;
@@ -1040,16 +1210,20 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
-       if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
-               if (hdev->discov_timeout > 0) {
-                       cancel_delayed_work(&hdev->discov_off);
-                       hdev->discov_timeout = 0;
-               }
+       /* If the current mode is the same, then just update the timeout
+        * value with the new value. And if only the timeout gets updated,
+        * then no need for any HCI transactions.
+        */
+       if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags) &&
+           (cp->val == 0x02) == test_bit(HCI_LIMITED_DISCOVERABLE,
+                                         &hdev->dev_flags)) {
+               cancel_delayed_work(&hdev->discov_off);
+               hdev->discov_timeout = timeout;
 
-               if (cp->val && timeout > 0) {
-                       hdev->discov_timeout = timeout;
+               if (cp->val && hdev->discov_timeout > 0) {
+                       int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
                        queue_delayed_work(hdev->workqueue, &hdev->discov_off,
-                               msecs_to_jiffies(hdev->discov_timeout * 1000));
+                                          to);
                }
 
                err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
@@ -1062,24 +1236,66 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
+       /* Cancel any potential discoverable timeout that might be
+        * still active and store new timeout value. The arming of
+        * the timeout happens in the complete handler.
+        */
+       cancel_delayed_work(&hdev->discov_off);
+       hdev->discov_timeout = timeout;
+
+       /* Limited discoverable mode */
+       if (cp->val == 0x02)
+               set_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+       else
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+
        hci_req_init(&req, hdev);
 
+       /* The procedure for LE-only controllers is much simpler - just
+        * update the advertising data.
+        */
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+               goto update_ad;
+
        scan = SCAN_PAGE;
 
-       if (cp->val)
+       if (cp->val) {
+               struct hci_cp_write_current_iac_lap hci_cp;
+
+               if (cp->val == 0x02) {
+                       /* Limited discoverable mode */
+                       hci_cp.num_iac = 2;
+                       hci_cp.iac_lap[0] = 0x00;       /* LIAC */
+                       hci_cp.iac_lap[1] = 0x8b;
+                       hci_cp.iac_lap[2] = 0x9e;
+                       hci_cp.iac_lap[3] = 0x33;       /* GIAC */
+                       hci_cp.iac_lap[4] = 0x8b;
+                       hci_cp.iac_lap[5] = 0x9e;
+               } else {
+                       /* General discoverable mode */
+                       hci_cp.num_iac = 1;
+                       hci_cp.iac_lap[0] = 0x33;       /* GIAC */
+                       hci_cp.iac_lap[1] = 0x8b;
+                       hci_cp.iac_lap[2] = 0x9e;
+               }
+
+               hci_req_add(&req, HCI_OP_WRITE_CURRENT_IAC_LAP,
+                           (hci_cp.num_iac * 3) + 1, &hci_cp);
+
                scan |= SCAN_INQUIRY;
-       else
-               cancel_delayed_work(&hdev->discov_off);
+       } else {
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+       }
 
-       hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+       hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
+
+update_ad:
+       update_adv_data(&req);
 
        err = hci_req_run(&req, set_discoverable_complete);
        if (err < 0)
                mgmt_pending_remove(cmd);
 
-       if (cp->val)
-               hdev->discov_timeout = timeout;
-
 failed:
        hci_dev_unlock(hdev);
        return err;
@@ -1091,6 +1307,9 @@ static void write_fast_connectable(struct hci_request *req, bool enable)
        struct hci_cp_write_page_scan_activity acp;
        u8 type;
 
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+               return;
+
        if (hdev->hci_ver < BLUETOOTH_VER_1_2)
                return;
 
@@ -1146,10 +1365,7 @@ static void enable_advertising(struct hci_request *req)
        cp.min_interval = __constant_cpu_to_le16(0x0800);
        cp.max_interval = __constant_cpu_to_le16(0x0800);
        cp.type = get_adv_type(hdev);
-       if (bacmp(&hdev->bdaddr, BDADDR_ANY))
-               cp.own_address_type = ADDR_LE_DEV_PUBLIC;
-       else
-               cp.own_address_type = ADDR_LE_DEV_RANDOM;
+       cp.own_address_type = hdev->own_addr_type;
        cp.channel_map = 0x07;
 
        hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
@@ -1202,6 +1418,32 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
+static int set_connectable_update_settings(struct hci_dev *hdev,
+                                          struct sock *sk, u8 val)
+{
+       bool changed = false;
+       int err;
+
+       if (!!val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+               changed = true;
+
+       if (val) {
+               set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+       } else {
+               clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+               clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+       }
+
+       err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
+       if (err < 0)
+               return err;
+
+       if (changed)
+               return new_settings(hdev, sk);
+
+       return 0;
+}
+
 static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
                           u16 len)
 {
@@ -1225,25 +1467,7 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
        hci_dev_lock(hdev);
 
        if (!hdev_is_powered(hdev)) {
-               bool changed = false;
-
-               if (!!cp->val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
-                       changed = true;
-
-               if (cp->val) {
-                       set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
-               } else {
-                       clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
-                       clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
-               }
-
-               err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
-               if (err < 0)
-                       goto failed;
-
-               if (changed)
-                       err = new_settings(hdev, sk);
-
+               err = set_connectable_update_settings(hdev, sk, cp->val);
                goto failed;
        }
 
@@ -1262,16 +1486,24 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
 
        hci_req_init(&req, hdev);
 
-       if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) &&
-           cp->val != test_bit(HCI_PSCAN, &hdev->flags)) {
-
+       /* If BR/EDR is not enabled and we disable advertising as a
+        * by-product of disabling connectable, we need to update the
+        * advertising flags.
+        */
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+               if (!cp->val) {
+                       clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+                       clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+               }
+               update_adv_data(&req);
+       } else if (cp->val != test_bit(HCI_PSCAN, &hdev->flags)) {
                if (cp->val) {
                        scan = SCAN_PAGE;
                } else {
                        scan = 0;
 
                        if (test_bit(HCI_ISCAN, &hdev->flags) &&
-                                       hdev->discov_timeout > 0)
+                           hdev->discov_timeout > 0)
                                cancel_delayed_work(&hdev->discov_off);
                }
 
@@ -1297,8 +1529,8 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
        if (err < 0) {
                mgmt_pending_remove(cmd);
                if (err == -ENODATA)
-                       err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE,
-                                               hdev);
+                       err = set_connectable_update_settings(hdev, sk,
+                                                             cp->val);
                goto failed;
        }
 
@@ -1556,6 +1788,24 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status)
 
        if (match.sk)
                sock_put(match.sk);
+
+       /* Make sure the controller has a good default for
+        * advertising data. Restrict the update to when LE
+        * has actually been enabled. During power on, the
+        * update in powered_update_hci will take care of it.
+        */
+       if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+               struct hci_request req;
+
+               hci_dev_lock(hdev);
+
+               hci_req_init(&req, hdev);
+               update_adv_data(&req);
+               update_scan_rsp_data(&req);
+               hci_req_run(&req, NULL);
+
+               hci_dev_unlock(hdev);
+       }
 }
 
 static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
@@ -1623,18 +1873,18 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                goto unlock;
        }
 
+       hci_req_init(&req, hdev);
+
        memset(&hci_cp, 0, sizeof(hci_cp));
 
        if (val) {
                hci_cp.le = val;
                hci_cp.simul = lmp_le_br_capable(hdev);
+       } else {
+               if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+                       disable_advertising(&req);
        }
 
-       hci_req_init(&req, hdev);
-
-       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags) && !val)
-               disable_advertising(&req);
-
        hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
                    &hci_cp);
 
@@ -2772,8 +3022,11 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
                update_eir(&req);
        }
 
+       /* The name is stored in the scan response data and so
+        * no need to udpate the advertising data here.
+        */
        if (lmp_le_capable(hdev))
-               hci_update_ad(&req);
+               update_scan_rsp_data(&req);
 
        err = hci_req_run(&req, set_name_complete);
        if (err < 0)
@@ -3038,10 +3291,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                param_cp.type = LE_SCAN_ACTIVE;
                param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
                param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
-               if (bacmp(&hdev->bdaddr, BDADDR_ANY))
-                       param_cp.own_address_type = ADDR_LE_DEV_PUBLIC;
-               else
-                       param_cp.own_address_type = ADDR_LE_DEV_RANDOM;
+               param_cp.own_address_type = hdev->own_addr_type;
                hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
                            &param_cp);
 
@@ -3725,7 +3975,7 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                goto unlock;
        }
 
-       /* We need to flip the bit already here so that hci_update_ad
+       /* We need to flip the bit already here so that update_adv_data
         * generates the correct flags.
         */
        set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
@@ -3735,7 +3985,10 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
        if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
                set_bredr_scan(&req);
 
-       hci_update_ad(&req);
+       /* Since only the advertising data flags will change, there
+        * is no need to update the scan response data.
+        */
+       update_adv_data(&req);
 
        err = hci_req_run(&req, set_bredr_complete);
        if (err < 0)
@@ -4036,9 +4289,6 @@ static int powered_update_hci(struct hci_dev *hdev)
                    cp.simul != lmp_host_le_br_capable(hdev))
                        hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED,
                                    sizeof(cp), &cp);
-
-               /* In case BR/EDR was toggled during the AUTO_OFF phase */
-               hci_update_ad(&req);
        }
 
        if (lmp_le_capable(hdev)) {
@@ -4047,6 +4297,15 @@ static int powered_update_hci(struct hci_dev *hdev)
                        hci_req_add(&req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
                                    &hdev->static_addr);
 
+               /* Make sure the controller has a good default for
+                * advertising data. This also applies to the case
+                * where BR/EDR was toggled during the AUTO_OFF phase.
+                */
+               if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+                       update_adv_data(&req);
+                       update_scan_rsp_data(&req);
+               }
+
                if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
                        enable_advertising(&req);
        }
@@ -4121,59 +4380,91 @@ void mgmt_set_powered_failed(struct hci_dev *hdev, int err)
        mgmt_pending_remove(cmd);
 }
 
-int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
+void mgmt_discoverable_timeout(struct hci_dev *hdev)
 {
-       bool changed = false;
-       int err = 0;
+       struct hci_request req;
+
+       hci_dev_lock(hdev);
+
+       /* When discoverable timeout triggers, then just make sure
+        * the limited discoverable flag is cleared. Even in the case
+        * of a timeout triggered from general discoverable, it is
+        * safe to unconditionally clear the flag.
+        */
+       clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+       clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+
+       hci_req_init(&req, hdev);
+       if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+               u8 scan = SCAN_PAGE;
+               hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE,
+                           sizeof(scan), &scan);
+       }
+       update_class(&req);
+       update_adv_data(&req);
+       hci_req_run(&req, NULL);
+
+       hdev->discov_timeout = 0;
+
+       new_settings(hdev, NULL);
+
+       hci_dev_unlock(hdev);
+}
+
+void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
+{
+       bool changed;
 
        /* Nothing needed here if there's a pending command since that
         * commands request completion callback takes care of everything
         * necessary.
         */
        if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev))
-               return 0;
+               return;
 
        if (discoverable) {
-               if (!test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
-                       changed = true;
+               changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
        } else {
-               if (test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
-                       changed = true;
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+               changed = test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
        }
 
-       if (changed)
-               err = new_settings(hdev, NULL);
+       if (changed) {
+               struct hci_request req;
 
-       return err;
+               /* In case this change in discoverable was triggered by
+                * a disabling of connectable there could be a need to
+                * update the advertising flags.
+                */
+               hci_req_init(&req, hdev);
+               update_adv_data(&req);
+               hci_req_run(&req, NULL);
+
+               new_settings(hdev, NULL);
+       }
 }
 
-int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
+void mgmt_connectable(struct hci_dev *hdev, u8 connectable)
 {
-       bool changed = false;
-       int err = 0;
+       bool changed;
 
        /* Nothing needed here if there's a pending command since that
         * commands request completion callback takes care of everything
         * necessary.
         */
        if (mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev))
-               return 0;
+               return;
 
-       if (connectable) {
-               if (!test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags))
-                       changed = true;
-       } else {
-               if (test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags))
-                       changed = true;
-       }
+       if (connectable)
+               changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+       else
+               changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
 
        if (changed)
-               err = new_settings(hdev, NULL);
-
-       return err;
+               new_settings(hdev, NULL);
 }
 
-int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
+void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
 {
        u8 mgmt_err = mgmt_status(status);
 
@@ -4184,12 +4475,10 @@ int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
        if (scan & SCAN_INQUIRY)
                mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
                                     cmd_status_rsp, &mgmt_err);
-
-       return 0;
 }
 
-int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
-                     bool persistent)
+void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
+                      bool persistent)
 {
        struct mgmt_ev_new_link_key ev;
 
@@ -4202,10 +4491,10 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
        memcpy(ev.key.val, key->val, HCI_LINK_KEY_SIZE);
        ev.key.pin_len = key->pin_len;
 
-       return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
+       mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
+void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
 {
        struct mgmt_ev_new_long_term_key ev;
 
@@ -4224,8 +4513,18 @@ int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
        memcpy(ev.key.rand, key->rand, sizeof(key->rand));
        memcpy(ev.key.val, key->val, sizeof(key->val));
 
-       return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev),
-                         NULL);
+       mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL);
+}
+
+static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
+                                 u8 data_len)
+{
+       eir[eir_len++] = sizeof(type) + data_len;
+       eir[eir_len++] = type;
+       memcpy(&eir[eir_len], data, data_len);
+       eir_len += data_len;
+
+       return eir_len;
 }
 
 void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
@@ -4345,7 +4644,7 @@ void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
        mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
+void mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
 {
        struct mgmt_ev_pin_code_request ev;
 
@@ -4353,52 +4652,45 @@ int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
        ev.addr.type = BDADDR_BREDR;
        ev.secure = secure;
 
-       return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
-                         NULL);
+       mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                u8 status)
+void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                 u8 status)
 {
        struct pending_cmd *cmd;
        struct mgmt_rp_pin_code_reply rp;
-       int err;
 
        cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
        if (!cmd)
-               return -ENOENT;
+               return;
 
        bacpy(&rp.addr.bdaddr, bdaddr);
        rp.addr.type = BDADDR_BREDR;
 
-       err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
-                          mgmt_status(status), &rp, sizeof(rp));
+       cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+                    mgmt_status(status), &rp, sizeof(rp));
 
        mgmt_pending_remove(cmd);
-
-       return err;
 }
 
-int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                    u8 status)
+void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                     u8 status)
 {
        struct pending_cmd *cmd;
        struct mgmt_rp_pin_code_reply rp;
-       int err;
 
        cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
        if (!cmd)
-               return -ENOENT;
+               return;
 
        bacpy(&rp.addr.bdaddr, bdaddr);
        rp.addr.type = BDADDR_BREDR;
 
-       err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
-                          mgmt_status(status), &rp, sizeof(rp));
+       cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
+                    mgmt_status(status), &rp, sizeof(rp));
 
        mgmt_pending_remove(cmd);
-
-       return err;
 }
 
 int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
@@ -4500,8 +4792,8 @@ int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
        return mgmt_event(MGMT_EV_PASSKEY_NOTIFY, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                    u8 addr_type, u8 status)
+void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                     u8 addr_type, u8 status)
 {
        struct mgmt_ev_auth_failed ev;
 
@@ -4509,40 +4801,36 @@ int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
        ev.addr.type = link_to_bdaddr(link_type, addr_type);
        ev.status = mgmt_status(status);
 
-       return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
+       mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
+void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
 {
        struct cmd_lookup match = { NULL, hdev };
-       bool changed = false;
-       int err = 0;
+       bool changed;
 
        if (status) {
                u8 mgmt_err = mgmt_status(status);
                mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev,
                                     cmd_status_rsp, &mgmt_err);
-               return 0;
+               return;
        }
 
-       if (test_bit(HCI_AUTH, &hdev->flags)) {
-               if (!test_and_set_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
-                       changed = true;
-       } else {
-               if (test_and_clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
-                       changed = true;
-       }
+       if (test_bit(HCI_AUTH, &hdev->flags))
+               changed = !test_and_set_bit(HCI_LINK_SECURITY,
+                                           &hdev->dev_flags);
+       else
+               changed = test_and_clear_bit(HCI_LINK_SECURITY,
+                                            &hdev->dev_flags);
 
        mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp,
                             &match);
 
        if (changed)
-               err = new_settings(hdev, match.sk);
+               new_settings(hdev, match.sk);
 
        if (match.sk)
                sock_put(match.sk);
-
-       return err;
 }
 
 static void clear_eir(struct hci_request *req)
@@ -4560,12 +4848,11 @@ static void clear_eir(struct hci_request *req)
        hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
 }
 
-int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
+void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
 {
        struct cmd_lookup match = { NULL, hdev };
        struct hci_request req;
        bool changed = false;
-       int err = 0;
 
        if (status) {
                u8 mgmt_err = mgmt_status(status);
@@ -4573,13 +4860,12 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
                if (enable && test_and_clear_bit(HCI_SSP_ENABLED,
                                                 &hdev->dev_flags)) {
                        clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
-                       err = new_settings(hdev, NULL);
+                       new_settings(hdev, NULL);
                }
 
                mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, cmd_status_rsp,
                                     &mgmt_err);
-
-               return err;
+               return;
        }
 
        if (enable) {
@@ -4596,7 +4882,7 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
        mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match);
 
        if (changed)
-               err = new_settings(hdev, match.sk);
+               new_settings(hdev, match.sk);
 
        if (match.sk)
                sock_put(match.sk);
@@ -4609,8 +4895,6 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
                clear_eir(&req);
 
        hci_req_run(&req, NULL);
-
-       return err;
 }
 
 static void sk_lookup(struct pending_cmd *cmd, void *data)
@@ -4623,33 +4907,30 @@ static void sk_lookup(struct pending_cmd *cmd, void *data)
        }
 }
 
-int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
-                                  u8 status)
+void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
+                                   u8 status)
 {
        struct cmd_lookup match = { NULL, hdev, mgmt_status(status) };
-       int err = 0;
 
        mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, sk_lookup, &match);
        mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, sk_lookup, &match);
        mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match);
 
        if (!status)
-               err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class,
-                                3, NULL);
+               mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 3,
+                          NULL);
 
        if (match.sk)
                sock_put(match.sk);
-
-       return err;
 }
 
-int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
+void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
 {
        struct mgmt_cp_set_local_name ev;
        struct pending_cmd *cmd;
 
        if (status)
-               return 0;
+               return;
 
        memset(&ev, 0, sizeof(ev));
        memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
@@ -4663,42 +4944,38 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
                 * HCI dev don't send any mgmt signals.
                 */
                if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
-                       return 0;
+                       return;
        }
 
-       return mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
-                         cmd ? cmd->sk : NULL);
+       mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
+                  cmd ? cmd->sk : NULL);
 }
 
-int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
-                                           u8 *randomizer, u8 status)
+void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
+                                            u8 *randomizer, u8 status)
 {
        struct pending_cmd *cmd;
-       int err;
 
        BT_DBG("%s status %u", hdev->name, status);
 
        cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
        if (!cmd)
-               return -ENOENT;
+               return;
 
        if (status) {
-               err = cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
-                                mgmt_status(status));
+               cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+                          mgmt_status(status));
        } else {
                struct mgmt_rp_read_local_oob_data rp;
 
                memcpy(rp.hash, hash, sizeof(rp.hash));
                memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
 
-               err = cmd_complete(cmd->sk, hdev->id,
-                                  MGMT_OP_READ_LOCAL_OOB_DATA, 0, &rp,
-                                  sizeof(rp));
+               cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+                            0, &rp, sizeof(rp));
        }
 
        mgmt_pending_remove(cmd);
-
-       return err;
 }
 
 void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
index 27e936a7ddd977254460ea71c8da1674d5bb7a85..94d06cbfbc184a6e827aa5c3cbd15d4693eca49a 100644 (file)
@@ -2154,13 +2154,6 @@ static int __init rfcomm_init(void)
                goto unregister;
        }
 
-       if (bt_debugfs) {
-               rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444,
-                               bt_debugfs, NULL, &rfcomm_dlc_debugfs_fops);
-               if (!rfcomm_dlc_debugfs)
-                       BT_ERR("Failed to create RFCOMM debug file");
-       }
-
        err = rfcomm_init_ttys();
        if (err < 0)
                goto stop;
@@ -2171,6 +2164,13 @@ static int __init rfcomm_init(void)
 
        BT_INFO("RFCOMM ver %s", VERSION);
 
+       if (IS_ERR_OR_NULL(bt_debugfs))
+               return 0;
+
+       rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444,
+                                                bt_debugfs, NULL,
+                                                &rfcomm_dlc_debugfs_fops);
+
        return 0;
 
 cleanup:
index df17276eb32b5f62d5d1f5ce915720276c4a42a6..c4d3d423f89b84d41b9ff0e6919e9f37786f4960 100644 (file)
@@ -1051,15 +1051,15 @@ int __init rfcomm_init_sockets(void)
                goto error;
        }
 
-       if (bt_debugfs) {
-               rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444,
-                               bt_debugfs, NULL, &rfcomm_sock_debugfs_fops);
-               if (!rfcomm_sock_debugfs)
-                       BT_ERR("Failed to create RFCOMM debug file");
-       }
-
        BT_INFO("RFCOMM socket layer initialized");
 
+       if (IS_ERR_OR_NULL(bt_debugfs))
+               return 0;
+
+       rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444,
+                                                 bt_debugfs, NULL,
+                                                 &rfcomm_sock_debugfs_fops);
+
        return 0;
 
 error:
index a92aebac56caf631eb8ebe9ade6dc462c3988a16..12a0e51e21e13631beeec14d97e64aa137fd0e99 100644 (file)
@@ -1177,15 +1177,14 @@ int __init sco_init(void)
                goto error;
        }
 
-       if (bt_debugfs) {
-               sco_debugfs = debugfs_create_file("sco", 0444, bt_debugfs,
-                                                 NULL, &sco_debugfs_fops);
-               if (!sco_debugfs)
-                       BT_ERR("Failed to create SCO debug file");
-       }
-
        BT_INFO("SCO socket layer initialized");
 
+       if (IS_ERR_OR_NULL(bt_debugfs))
+               return 0;
+
+       sco_debugfs = debugfs_create_file("sco", 0444, bt_debugfs,
+                                         NULL, &sco_debugfs_fops);
+
        return 0;
 
 error:
index 463e50c58716359925c975acf6776db91985f397..85a2796cac61bcc423ef724f1911f2a794667aff 100644 (file)
@@ -856,7 +856,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
 
        if (hcon->type != LE_LINK) {
                kfree_skb(skb);
-               return -ENOTSUPP;
+               return 0;
        }
 
        if (skb->len < 1) {
@@ -864,7 +864,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
                return -EILSEQ;
        }
 
-       if (!test_bit(HCI_LE_ENABLED, &conn->hcon->hdev->dev_flags)) {
+       if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) {
                err = -ENOTSUPP;
                reason = SMP_PAIRING_NOTSUPP;
                goto done;
index 62535fe9f570273d83597567e8fb360262f7f208..97b5dcad50250d22bb5c5ad7ac41c18558d967ce 100644 (file)
@@ -4,6 +4,7 @@ config MAC80211
        select CRYPTO
        select CRYPTO_ARC4
        select CRYPTO_AES
+       select CRYPTO_CCM
        select CRC32
        select AVERAGE
        ---help---
@@ -258,6 +259,17 @@ config MAC80211_MESH_SYNC_DEBUG
 
          Do not select this option.
 
+config MAC80211_MESH_CSA_DEBUG
+       bool "Verbose mesh channel switch debugging"
+       depends on MAC80211_DEBUG_MENU
+       depends on MAC80211_MESH
+       ---help---
+         Selecting this option causes mac80211 to print out very verbose mesh
+         channel switch debugging messages (when mac80211 is taking part in a
+         mesh network).
+
+         Do not select this option.
+
 config MAC80211_MESH_PS_DEBUG
        bool "Verbose mesh powersave debugging"
        depends on MAC80211_DEBUG_MENU
index be7614b9ed27b7827cbae7a423e5957ec1302c25..7c7df475a401693dd44cb1a6b9d68dd255b2acd5 100644 (file)
@@ -2,6 +2,8 @@
  * Copyright 2003-2004, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
  *
+ * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
+ *
  * 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.
 #include "key.h"
 #include "aes_ccm.h"
 
-static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a)
+void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+                              u8 *data, size_t data_len, u8 *mic)
 {
-       int i;
-       u8 *b_0, *aad, *b, *s_0;
-
-       b_0 = scratch + 3 * AES_BLOCK_SIZE;
-       aad = scratch + 4 * AES_BLOCK_SIZE;
-       b = scratch;
-       s_0 = scratch + AES_BLOCK_SIZE;
-
-       crypto_cipher_encrypt_one(tfm, b, b_0);
+       struct scatterlist assoc, pt, ct[2];
+       struct {
+               struct aead_request     req;
+               u8                      priv[crypto_aead_reqsize(tfm)];
+       } aead_req;
 
-       /* Extra Authenticate-only data (always two AES blocks) */
-       for (i = 0; i < AES_BLOCK_SIZE; i++)
-               aad[i] ^= b[i];
-       crypto_cipher_encrypt_one(tfm, b, aad);
+       memset(&aead_req, 0, sizeof(aead_req));
 
-       aad += AES_BLOCK_SIZE;
+       sg_init_one(&pt, data, data_len);
+       sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
+       sg_init_table(ct, 2);
+       sg_set_buf(&ct[0], data, data_len);
+       sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
 
-       for (i = 0; i < AES_BLOCK_SIZE; i++)
-               aad[i] ^= b[i];
-       crypto_cipher_encrypt_one(tfm, a, aad);
+       aead_request_set_tfm(&aead_req.req, tfm);
+       aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
+       aead_request_set_crypt(&aead_req.req, &pt, ct, data_len, b_0);
 
-       /* Mask out bits from auth-only-b_0 */
-       b_0[0] &= 0x07;
-
-       /* S_0 is used to encrypt T (= MIC) */
-       b_0[14] = 0;
-       b_0[15] = 0;
-       crypto_cipher_encrypt_one(tfm, s_0, b_0);
+       crypto_aead_encrypt(&aead_req.req);
 }
 
-
-void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
-                              u8 *data, size_t data_len,
-                              u8 *cdata, u8 *mic)
+int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+                             u8 *data, size_t data_len, u8 *mic)
 {
-       int i, j, last_len, num_blocks;
-       u8 *pos, *cpos, *b, *s_0, *e, *b_0;
-
-       b = scratch;
-       s_0 = scratch + AES_BLOCK_SIZE;
-       e = scratch + 2 * AES_BLOCK_SIZE;
-       b_0 = scratch + 3 * AES_BLOCK_SIZE;
-
-       num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
-       last_len = data_len % AES_BLOCK_SIZE;
-       aes_ccm_prepare(tfm, scratch, b);
-
-       /* Process payload blocks */
-       pos = data;
-       cpos = cdata;
-       for (j = 1; j <= num_blocks; j++) {
-               int blen = (j == num_blocks && last_len) ?
-                       last_len : AES_BLOCK_SIZE;
-
-               /* Authentication followed by encryption */
-               for (i = 0; i < blen; i++)
-                       b[i] ^= pos[i];
-               crypto_cipher_encrypt_one(tfm, b, b);
-
-               b_0[14] = (j >> 8) & 0xff;
-               b_0[15] = j & 0xff;
-               crypto_cipher_encrypt_one(tfm, e, b_0);
-               for (i = 0; i < blen; i++)
-                       *cpos++ = *pos++ ^ e[i];
-       }
-
-       for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++)
-               mic[i] = b[i] ^ s_0[i];
+       struct scatterlist assoc, pt, ct[2];
+       struct {
+               struct aead_request     req;
+               u8                      priv[crypto_aead_reqsize(tfm)];
+       } aead_req;
+
+       memset(&aead_req, 0, sizeof(aead_req));
+
+       sg_init_one(&pt, data, data_len);
+       sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
+       sg_init_table(ct, 2);
+       sg_set_buf(&ct[0], data, data_len);
+       sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
+
+       aead_request_set_tfm(&aead_req.req, tfm);
+       aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
+       aead_request_set_crypt(&aead_req.req, ct, &pt,
+                              data_len + IEEE80211_CCMP_MIC_LEN, b_0);
+
+       return crypto_aead_decrypt(&aead_req.req);
 }
 
-
-int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
-                             u8 *cdata, size_t data_len, u8 *mic, u8 *data)
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])
 {
-       int i, j, last_len, num_blocks;
-       u8 *pos, *cpos, *b, *s_0, *a, *b_0;
-
-       b = scratch;
-       s_0 = scratch + AES_BLOCK_SIZE;
-       a = scratch + 2 * AES_BLOCK_SIZE;
-       b_0 = scratch + 3 * AES_BLOCK_SIZE;
-
-       num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
-       last_len = data_len % AES_BLOCK_SIZE;
-       aes_ccm_prepare(tfm, scratch, a);
-
-       /* Process payload blocks */
-       cpos = cdata;
-       pos = data;
-       for (j = 1; j <= num_blocks; j++) {
-               int blen = (j == num_blocks && last_len) ?
-                       last_len : AES_BLOCK_SIZE;
-
-               /* Decryption followed by authentication */
-               b_0[14] = (j >> 8) & 0xff;
-               b_0[15] = j & 0xff;
-               crypto_cipher_encrypt_one(tfm, b, b_0);
-               for (i = 0; i < blen; i++) {
-                       *pos = *cpos++ ^ b[i];
-                       a[i] ^= *pos++;
-               }
-               crypto_cipher_encrypt_one(tfm, a, a);
-       }
-
-       for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) {
-               if ((mic[i] ^ s_0[i]) != a[i])
-                       return -1;
-       }
-
-       return 0;
-}
+       struct crypto_aead *tfm;
+       int err;
 
+       tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(tfm))
+               return tfm;
 
-struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[])
-{
-       struct crypto_cipher *tfm;
+       err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
+       if (!err)
+               err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN);
+       if (!err)
+               return tfm;
 
-       tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
-       if (!IS_ERR(tfm))
-               crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
-
-       return tfm;
+       crypto_free_aead(tfm);
+       return ERR_PTR(err);
 }
 
-
-void ieee80211_aes_key_free(struct crypto_cipher *tfm)
+void ieee80211_aes_key_free(struct crypto_aead *tfm)
 {
-       crypto_free_cipher(tfm);
+       crypto_free_aead(tfm);
 }
index 5b7d744e237032e7326bcd792bc2a17423e20f1f..2c7ab1948a2edba3964a5c0edfb7e941752719f6 100644 (file)
 
 #include <linux/crypto.h>
 
-struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]);
-void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
-                              u8 *data, size_t data_len,
-                              u8 *cdata, u8 *mic);
-int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
-                             u8 *cdata, size_t data_len,
-                             u8 *mic, u8 *data);
-void ieee80211_aes_key_free(struct crypto_cipher *tfm);
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]);
+void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+                              u8 *data, size_t data_len, u8 *mic);
+int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+                             u8 *data, size_t data_len, u8 *mic);
+void ieee80211_aes_key_free(struct crypto_aead *tfm);
 
 #endif /* AES_CCM_H */
index b0a651cc389fdab807b2a8b800f63144d0eaeb2f..95667b088c5b73cd0e95e8c1753ed76acec9dca0 100644 (file)
@@ -1059,6 +1059,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        /* abort any running channel switch */
        sdata->vif.csa_active = false;
        cancel_work_sync(&sdata->csa_finalize_work);
+       cancel_work_sync(&sdata->u.ap.request_smps_work);
 
        /* turn off carrier for this interface and dependent VLANs */
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
@@ -1342,8 +1343,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                                sta->plink_state = params->plink_state;
 
                                ieee80211_mps_sta_status_update(sta);
-                               changed |=
-                                     ieee80211_mps_local_status_update(sdata);
+                               changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                               NL80211_MESH_POWER_UNKNOWN);
                                break;
                        default:
                                /*  nothing  */
@@ -1553,6 +1554,20 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
        mutex_unlock(&local->sta_mtx);
 
+       if ((sdata->vif.type == NL80211_IFTYPE_AP ||
+            sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
+           sta->known_smps_mode != sta->sdata->bss->req_smps &&
+           test_sta_flag(sta, WLAN_STA_AUTHORIZED) &&
+           sta_info_tx_streams(sta) != 1) {
+               ht_dbg(sta->sdata,
+                      "%pM just authorized and MIMO capable - update SMPS\n",
+                      sta->sta.addr);
+               ieee80211_send_smps_action(sta->sdata,
+                       sta->sdata->bss->req_smps,
+                       sta->sta.addr,
+                       sta->sdata->vif.bss_conf.bssid);
+       }
+
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
            params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
                ieee80211_recalc_ps(local, -1);
@@ -2337,8 +2352,92 @@ static int ieee80211_testmode_dump(struct wiphy *wiphy,
 }
 #endif
 
-int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
-                            enum ieee80211_smps_mode smps_mode)
+int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
+                               enum ieee80211_smps_mode smps_mode)
+{
+       struct sta_info *sta;
+       enum ieee80211_smps_mode old_req;
+       int i;
+
+       if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP))
+               return -EINVAL;
+
+       if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
+               return 0;
+
+       old_req = sdata->u.ap.req_smps;
+       sdata->u.ap.req_smps = smps_mode;
+
+       /* AUTOMATIC doesn't mean much for AP - don't allow it */
+       if (old_req == smps_mode ||
+           smps_mode == IEEE80211_SMPS_AUTOMATIC)
+               return 0;
+
+        /* If no associated stations, there's no need to do anything */
+       if (!atomic_read(&sdata->u.ap.num_mcast_sta)) {
+               sdata->smps_mode = smps_mode;
+               ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
+               return 0;
+       }
+
+       ht_dbg(sdata,
+              "SMSP %d requested in AP mode, sending Action frame to %d stations\n",
+              smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta));
+
+       mutex_lock(&sdata->local->sta_mtx);
+       for (i = 0; i < STA_HASH_SIZE; i++) {
+               for (sta = rcu_dereference_protected(sdata->local->sta_hash[i],
+                               lockdep_is_held(&sdata->local->sta_mtx));
+                    sta;
+                    sta = rcu_dereference_protected(sta->hnext,
+                               lockdep_is_held(&sdata->local->sta_mtx))) {
+                       /*
+                        * Only stations associated to our AP and
+                        * associated VLANs
+                        */
+                       if (sta->sdata->bss != &sdata->u.ap)
+                               continue;
+
+                       /* This station doesn't support MIMO - skip it */
+                       if (sta_info_tx_streams(sta) == 1)
+                               continue;
+
+                       /*
+                        * Don't wake up a STA just to send the action frame
+                        * unless we are getting more restrictive.
+                        */
+                       if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
+                           !ieee80211_smps_is_restrictive(sta->known_smps_mode,
+                                                          smps_mode)) {
+                               ht_dbg(sdata,
+                                      "Won't send SMPS to sleeping STA %pM\n",
+                                      sta->sta.addr);
+                               continue;
+                       }
+
+                       /*
+                        * If the STA is not authorized, wait until it gets
+                        * authorized and the action frame will be sent then.
+                        */
+                       if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                               continue;
+
+                       ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
+                       ieee80211_send_smps_action(sdata, smps_mode,
+                                                  sta->sta.addr,
+                                                  sdata->vif.bss_conf.bssid);
+               }
+       }
+       mutex_unlock(&sdata->local->sta_mtx);
+
+       sdata->smps_mode = smps_mode;
+       ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
+
+       return 0;
+}
+
+int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
+                                enum ieee80211_smps_mode smps_mode)
 {
        const u8 *ap;
        enum ieee80211_smps_mode old_req;
@@ -2346,6 +2445,9 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
 
        lockdep_assert_held(&sdata->wdev.mtx);
 
+       if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
+               return -EINVAL;
+
        old_req = sdata->u.mgd.req_smps;
        sdata->u.mgd.req_smps = smps_mode;
 
@@ -2402,7 +2504,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
 
        /* no change, but if automatic follow powersave */
        sdata_lock(sdata);
-       __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
+       __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps);
        sdata_unlock(sdata);
 
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
@@ -2860,7 +2962,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
                container_of(work, struct ieee80211_sub_if_data,
                             csa_finalize_work);
        struct ieee80211_local *local = sdata->local;
-       int err, changed;
+       int err, changed = 0;
 
        if (!ieee80211_sdata_running(sdata))
                return;
@@ -2892,6 +2994,13 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
        case NL80211_IFTYPE_ADHOC:
                ieee80211_ibss_finish_csa(sdata);
                break;
+#ifdef CONFIG_MAC80211_MESH
+       case NL80211_IFTYPE_MESH_POINT:
+               err = ieee80211_mesh_finish_csa(sdata);
+               if (err < 0)
+                       return;
+               break;
+#endif
        default:
                WARN_ON(1);
                return;
@@ -2912,6 +3021,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_chanctx *chanctx;
+       struct ieee80211_if_mesh __maybe_unused *ifmsh;
        int err, num_chanctx;
 
        if (!list_empty(&local->roc_list) || local->scanning)
@@ -2995,6 +3105,26 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                if (err < 0)
                        return err;
                break;
+#ifdef CONFIG_MAC80211_MESH
+       case NL80211_IFTYPE_MESH_POINT:
+               ifmsh = &sdata->u.mesh;
+
+               if (!ifmsh->mesh_id)
+                       return -EINVAL;
+
+               if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
+                       return -EINVAL;
+
+               /* changes into another band are not supported */
+               if (sdata->vif.bss_conf.chandef.chan->band !=
+                   params->chandef.chan->band)
+                       return -EINVAL;
+
+               err = ieee80211_mesh_csa_beacon(sdata, params, true);
+               if (err < 0)
+                       return err;
+               break;
+#endif
        default:
                return -EOPNOTSUPP;
        }
index 4ccc5ed6237dd00e5701c19908e55d413e83d506..493d68061f0c2717d8828809e8ec27d87d6d08b6 100644 (file)
 #define MAC80211_MESH_SYNC_DEBUG 0
 #endif
 
+#ifdef CONFIG_MAC80211_MESH_CSA_DEBUG
+#define MAC80211_MESH_CSA_DEBUG 1
+#else
+#define MAC80211_MESH_CSA_DEBUG 0
+#endif
+
 #ifdef CONFIG_MAC80211_MESH_PS_DEBUG
 #define MAC80211_MESH_PS_DEBUG 1
 #else
@@ -157,6 +163,10 @@ do {                                                                       \
        _sdata_dbg(MAC80211_MESH_SYNC_DEBUG,                            \
                   sdata, fmt, ##__VA_ARGS__)
 
+#define mcsa_dbg(sdata, fmt, ...)                                      \
+       _sdata_dbg(MAC80211_MESH_CSA_DEBUG,                             \
+                  sdata, fmt, ##__VA_ARGS__)
+
 #define mps_dbg(sdata, fmt, ...)                                       \
        _sdata_dbg(MAC80211_MESH_PS_DEBUG,                              \
                   sdata, fmt, ##__VA_ARGS__)
index cafe614ef93d672087fb66a1d82aaff4110cc22c..04b5a14c8a054e33227b893de78912e5ee4a6f80 100644 (file)
@@ -224,12 +224,15 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
             smps_mode == IEEE80211_SMPS_AUTOMATIC))
                return -EINVAL;
 
-       /* supported only on managed interfaces for now */
-       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+       if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+           sdata->vif.type != NL80211_IFTYPE_AP)
                return -EOPNOTSUPP;
 
        sdata_lock(sdata);
-       err = __ieee80211_request_smps(sdata, smps_mode);
+       if (sdata->vif.type == NL80211_IFTYPE_STATION)
+               err = __ieee80211_request_smps_mgd(sdata, smps_mode);
+       else
+               err = __ieee80211_request_smps_ap(sdata, smps_mode);
        sdata_unlock(sdata);
 
        return err;
@@ -245,12 +248,15 @@ static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
 static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,
                                     char *buf, int buflen)
 {
-       if (sdata->vif.type != NL80211_IFTYPE_STATION)
-               return -EOPNOTSUPP;
-
-       return snprintf(buf, buflen, "request: %s\nused: %s\n",
-                       smps_modes[sdata->u.mgd.req_smps],
-                       smps_modes[sdata->smps_mode]);
+       if (sdata->vif.type == NL80211_IFTYPE_STATION)
+               return snprintf(buf, buflen, "request: %s\nused: %s\n",
+                               smps_modes[sdata->u.mgd.req_smps],
+                               smps_modes[sdata->smps_mode]);
+       if (sdata->vif.type == NL80211_IFTYPE_AP)
+               return snprintf(buf, buflen, "request: %s\nused: %s\n",
+                               smps_modes[sdata->u.ap.req_smps],
+                               smps_modes[sdata->smps_mode]);
+       return -EINVAL;
 }
 
 static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,
@@ -563,6 +569,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
 static void add_ap_files(struct ieee80211_sub_if_data *sdata)
 {
        DEBUGFS_ADD(num_mcast_sta);
+       DEBUGFS_ADD_MODE(smps, 0600);
        DEBUGFS_ADD(num_sta_ps);
        DEBUGFS_ADD(dtim_count);
        DEBUGFS_ADD(num_buffered_multicast);
index 529bf58bc14511beae95c4ff250ec54eff5c9710..9a8be8f69224d0be11fe9b10a0176a2f5a0002f1 100644 (file)
@@ -448,14 +448,25 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
-void ieee80211_request_smps_work(struct work_struct *work)
+void ieee80211_request_smps_mgd_work(struct work_struct *work)
 {
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data,
                             u.mgd.request_smps_work);
 
        sdata_lock(sdata);
-       __ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode);
+       __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode);
+       sdata_unlock(sdata);
+}
+
+void ieee80211_request_smps_ap_work(struct work_struct *work)
+{
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data,
+                            u.ap.request_smps_work);
+
+       sdata_lock(sdata);
+       __ieee80211_request_smps_ap(sdata, sdata->u.ap.driver_smps_mode);
        sdata_unlock(sdata);
 }
 
@@ -464,19 +475,29 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 
-       if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
+       if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION &&
+                        vif->type != NL80211_IFTYPE_AP))
                return;
 
        if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
                smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
-       if (sdata->u.mgd.driver_smps_mode == smps_mode)
-               return;
-
-       sdata->u.mgd.driver_smps_mode = smps_mode;
-
-       ieee80211_queue_work(&sdata->local->hw,
-                            &sdata->u.mgd.request_smps_work);
+       if (vif->type == NL80211_IFTYPE_STATION) {
+               if (sdata->u.mgd.driver_smps_mode == smps_mode)
+                       return;
+               sdata->u.mgd.driver_smps_mode = smps_mode;
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &sdata->u.mgd.request_smps_work);
+       } else {
+               /* AUTOMATIC is meaningless in AP mode */
+               if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC))
+                       return;
+               if (sdata->u.ap.driver_smps_mode == smps_mode)
+                       return;
+               sdata->u.ap.driver_smps_mode = smps_mode;
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &sdata->u.ap.request_smps_work);
+       }
 }
 /* this might change ... don't want non-open drivers using it */
 EXPORT_SYMBOL_GPL(ieee80211_request_smps);
index 21a0b8835cb31d4a27dda9b2243038c2280b8f6a..531be040b9ae85972d61bf7df248ae28308d811b 100644 (file)
@@ -229,6 +229,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        struct beacon_data *presp;
        enum nl80211_bss_scan_width scan_width;
        bool have_higher_than_11mbit;
+       bool radar_required = false;
        int err;
 
        sdata_assert_lock(sdata);
@@ -273,6 +274,23 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                }
                chandef.width = NL80211_CHAN_WIDTH_20;
                chandef.center_freq1 = chan->center_freq;
+               /* check again for downgraded chandef */
+               if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+                       sdata_info(sdata,
+                                  "Failed to join IBSS, beacons forbidden\n");
+                       return;
+               }
+       }
+
+       err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
+                                           &chandef);
+       if (err > 0) {
+               if (!ifibss->userspace_handles_dfs) {
+                       sdata_info(sdata,
+                                  "Failed to join IBSS, DFS channel without control program\n");
+                       return;
+               }
+               radar_required = true;
        }
 
        ieee80211_vif_release_channel(sdata);
@@ -297,6 +315,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        rcu_assign_pointer(ifibss->presp, presp);
        mgmt = (void *)presp->head;
 
+       sdata->radar_required = radar_required;
        sdata->vif.bss_conf.enable_beacon = true;
        sdata->vif.bss_conf.beacon_int = beacon_int;
        sdata->vif.bss_conf.basic_rates = basic_rates;
@@ -445,60 +464,6 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                                  tsf, false);
 }
 
-static int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
-                                    struct cfg80211_csa_settings *csa_settings)
-{
-       struct sk_buff *skb;
-       struct ieee80211_mgmt *mgmt;
-       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
-       struct ieee80211_local *local = sdata->local;
-       int freq;
-       int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
-                     sizeof(mgmt->u.action.u.chan_switch);
-       u8 *pos;
-
-       skb = dev_alloc_skb(local->tx_headroom + hdr_len +
-                           5 + /* channel switch announcement element */
-                           3); /* secondary channel offset element */
-       if (!skb)
-               return -1;
-
-       skb_reserve(skb, local->tx_headroom);
-       mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
-       memset(mgmt, 0, hdr_len);
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                         IEEE80211_STYPE_ACTION);
-
-       eth_broadcast_addr(mgmt->da);
-       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
-       memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
-       mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
-       mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
-       pos = skb_put(skb, 5);
-       *pos++ = WLAN_EID_CHANNEL_SWITCH;                       /* EID */
-       *pos++ = 3;                                             /* IE length */
-       *pos++ = csa_settings->block_tx ? 1 : 0;                /* CSA mode */
-       freq = csa_settings->chandef.chan->center_freq;
-       *pos++ = ieee80211_frequency_to_channel(freq);          /* channel */
-       *pos++ = csa_settings->count;                           /* count */
-
-       if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
-               enum nl80211_channel_type ch_type;
-
-               skb_put(skb, 3);
-               *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;     /* EID */
-               *pos++ = 1;                                     /* IE length */
-               ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
-               if (ch_type == NL80211_CHAN_HT40PLUS)
-                       *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
-               else
-                       *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
-       }
-
-       ieee80211_tx_skb(sdata, skb);
-       return 0;
-}
-
 int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
                              struct cfg80211_csa_settings *csa_settings)
 {
@@ -796,19 +761,34 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work)
        ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 }
 
+static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+       int err;
+
+       /* if the current channel is a DFS channel, mark the channel as
+        * unavailable.
+        */
+       err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
+                                           &ifibss->chandef);
+       if (err > 0)
+               cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef,
+                                    GFP_ATOMIC);
+}
+
 static bool
 ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                                  struct ieee802_11_elems *elems,
                                  bool beacon)
 {
        struct cfg80211_csa_settings params;
+       struct ieee80211_csa_ie csa_ie;
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_chanctx *chanctx;
        enum nl80211_channel_type ch_type;
        int err, num_chanctx;
        u32 sta_flags;
-       u8 mode;
 
        if (sdata->vif.csa_active)
                return true;
@@ -831,12 +811,10 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
 
        memset(&params, 0, sizeof(params));
+       memset(&csa_ie, 0, sizeof(csa_ie));
        err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
                                           ifibss->chandef.chan->band,
-                                          sta_flags, ifibss->bssid,
-                                          &params.count, &mode,
-                                          &params.chandef);
-
+                                          sta_flags, ifibss->bssid, &csa_ie);
        /* can't switch to destination channel, fail */
        if (err < 0)
                goto disconnect;
@@ -845,6 +823,9 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        if (err)
                return false;
 
+       params.count = csa_ie.count;
+       params.chandef = csa_ie.chandef;
+
        if (ifibss->chandef.chan->band != params.chandef.chan->band)
                goto disconnect;
 
@@ -880,8 +861,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                goto disconnect;
        }
 
-       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, &params.chandef,
-                                    IEEE80211_CHAN_DISABLED)) {
+       if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, &params.chandef)) {
                sdata_info(sdata,
                           "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
                           ifibss->bssid,
@@ -897,10 +877,11 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        if (err < 0)
                goto disconnect;
        if (err) {
-               params.radar_required = true;
+               /* IBSS-DFS only allowed with a control program */
+               if (!ifibss->userspace_handles_dfs)
+                       goto disconnect;
 
-               /* TODO: IBSS-DFS not (yet) supported, disconnect. */
-               goto disconnect;
+               params.radar_required = true;
        }
 
        rcu_read_lock();
@@ -931,7 +912,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                 "received channel switch announcement to go to channel %d MHz\n",
                 params.chandef.chan->center_freq);
 
-       params.block_tx = !!mode;
+       params.block_tx = !!csa_ie.mode;
 
        ieee80211_ibss_csa_beacon(sdata, &params);
        sdata->csa_radar_required = params.radar_required;
@@ -947,12 +928,16 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        ieee80211_bss_info_change_notify(sdata, err);
        drv_channel_switch_beacon(sdata, &params.chandef);
 
+       ieee80211_ibss_csa_mark_radar(sdata);
+
        return true;
 disconnect:
        ibss_dbg(sdata, "Can't handle channel switch, disconnect\n");
        ieee80211_queue_work(&sdata->local->hw,
                             &ifibss->csa_connection_drop_work);
 
+       ieee80211_ibss_csa_mark_radar(sdata);
+
        return true;
 }
 
@@ -1688,6 +1673,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
 
        sdata->u.ibss.privacy = params->privacy;
        sdata->u.ibss.control_port = params->control_port;
+       sdata->u.ibss.userspace_handles_dfs = params->userspace_handles_dfs;
        sdata->u.ibss.basic_rates = params->basic_rates;
 
        /* fix basic_rates if channel does not support these rates */
index fe48b093d4dc1cafb420f6e75526f4bf851fa4fe..29dc505be125c3c19737f8f6cee911492c52727c 100644 (file)
@@ -262,6 +262,10 @@ struct ieee80211_if_ap {
 
        struct ps_data ps;
        atomic_t num_mcast_sta; /* number of stations receiving multicast */
+       enum ieee80211_smps_mode req_smps, /* requested smps mode */
+                        driver_smps_mode; /* smps mode request */
+
+       struct work_struct request_smps_work;
 };
 
 struct ieee80211_if_wds {
@@ -498,6 +502,7 @@ struct ieee80211_if_ibss {
        bool privacy;
 
        bool control_port;
+       bool userspace_handles_dfs;
 
        u8 bssid[ETH_ALEN] __aligned(2);
        u8 ssid[IEEE80211_MAX_SSID_LEN];
@@ -539,6 +544,11 @@ struct ieee80211_mesh_sync_ops {
        /* add other framework functions here */
 };
 
+struct mesh_csa_settings {
+       struct rcu_head rcu_head;
+       struct cfg80211_csa_settings settings;
+};
+
 struct ieee80211_if_mesh {
        struct timer_list housekeeping_timer;
        struct timer_list mesh_path_timer;
@@ -599,6 +609,11 @@ struct ieee80211_if_mesh {
        int ps_peers_light_sleep;
        int ps_peers_deep_sleep;
        struct ps_data ps;
+       /* Channel Switching Support */
+       struct mesh_csa_settings __rcu *csa;
+       bool chsw_init;
+       u8 chsw_ttl;
+       u16 pre_value;
 };
 
 #ifdef CONFIG_MAC80211_MESH
@@ -1207,6 +1222,14 @@ struct ieee80211_ra_tid {
        u16 tid;
 };
 
+/* this struct holds the value parsing from channel switch IE  */
+struct ieee80211_csa_ie {
+       struct cfg80211_chan_def chandef;
+       u8 mode;
+       u8 count;
+       u8 ttl;
+};
+
 /* Parsed Information Elements */
 struct ieee802_11_elems {
        const u8 *ie_start;
@@ -1243,6 +1266,7 @@ struct ieee802_11_elems {
        const struct ieee80211_timeout_interval_ie *timeout_int;
        const u8 *opmode_notif;
        const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
+       const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;
 
        /* length of them, respectively */
        u8 ssid_len;
@@ -1343,6 +1367,10 @@ void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
 void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
 void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                   struct sk_buff *skb);
+int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings,
+                             bool csa_action);
+int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
 
 /* scan/BSS handling */
 void ieee80211_scan_work(struct work_struct *work);
@@ -1439,7 +1467,10 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
 int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
                               enum ieee80211_smps_mode smps, const u8 *da,
                               const u8 *bssid);
-void ieee80211_request_smps_work(struct work_struct *work);
+void ieee80211_request_smps_ap_work(struct work_struct *work);
+void ieee80211_request_smps_mgd_work(struct work_struct *work);
+bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
+                                  enum ieee80211_smps_mode smps_mode_new);
 
 void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                     u16 initiator, u16 reason, bool stop);
@@ -1501,17 +1532,16 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
  *     %IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT,
  *     %IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ,
  *     %IEEE80211_STA_DISABLE_160MHZ.
- * @count: to be filled with the counter until the switch (on success only)
  * @bssid: the currently connected bssid (for reporting)
- * @mode: to be filled with CSA mode (on success only)
- * @new_chandef: to be filled with destination chandef (on success only)
+ * @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl.
+       All of them will be filled with if success only.
  * Return: 0 on success, <0 on error and >0 if there is nothing to parse.
  */
 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
                                 struct ieee802_11_elems *elems, bool beacon,
                                 enum ieee80211_band current_band,
-                                u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
-                                struct cfg80211_chan_def *new_chandef);
+                                u32 sta_flags, u8 *bssid,
+                                struct ieee80211_csa_ie *csa_ie);
 
 /* Suspend/resume and hw reconfiguration */
 int ieee80211_reconfig(struct ieee80211_local *local);
@@ -1657,8 +1687,10 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
 u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
                            struct ieee802_11_elems *elems,
                            enum ieee80211_band band, u32 *basic_rates);
-int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
-                            enum ieee80211_smps_mode smps_mode);
+int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
+                                enum ieee80211_smps_mode smps_mode);
+int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
+                               enum ieee80211_smps_mode smps_mode);
 void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
 
 size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
@@ -1714,6 +1746,8 @@ void ieee80211_dfs_cac_timer(unsigned long data);
 void ieee80211_dfs_cac_timer_work(struct work_struct *work);
 void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
 void ieee80211_dfs_radar_detected_work(struct work_struct *work);
+int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings);
 
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
index e48f103b9adeb5fd773f68c6d12cb633cea33fa5..ff101ea1d9ae1e208deb5d6fe1d67c1677b398c2 100644 (file)
@@ -1293,7 +1293,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
        case NL80211_IFTYPE_AP:
                skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
                INIT_LIST_HEAD(&sdata->u.ap.vlans);
+               INIT_WORK(&sdata->u.ap.request_smps_work,
+                         ieee80211_request_smps_ap_work);
                sdata->vif.bss_conf.bssid = sdata->vif.addr;
+               sdata->u.ap.req_smps = IEEE80211_SMPS_OFF;
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
                type = NL80211_IFTYPE_STATION;
index 036d57e76a5e37d4156d5abedcd479e0aa0ffb6e..aaae0ed3700402433ae56244eb97baa2804aba5b 100644 (file)
@@ -83,7 +83,7 @@ struct ieee80211_key {
                         * Management frames.
                         */
                        u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN];
-                       struct crypto_cipher *tfm;
+                       struct crypto_aead *tfm;
                        u32 replays; /* dot11RSNAStatsCCMPReplays */
                } ccmp;
                struct {
index 707ac61d63e51a8a1528c8c65204c92c94400768..896fe3bd599e9bedd5db13dbc8ed04ab76e5bd88 100644 (file)
@@ -12,6 +12,7 @@
 #include <asm/unaligned.h>
 #include "ieee80211_i.h"
 #include "mesh.h"
+#include "driver-ops.h"
 
 static int mesh_allocated;
 static struct kmem_cache *rm_cache;
@@ -610,6 +611,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
        struct ieee80211_chanctx_conf *chanctx_conf;
+       struct mesh_csa_settings *csa;
        enum ieee80211_band band;
        u8 *pos;
        struct ieee80211_sub_if_data *sdata;
@@ -624,6 +626,10 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
 
        head_len = hdr_len +
                   2 + /* NULL SSID */
+                  /* Channel Switch Announcement */
+                  2 + sizeof(struct ieee80211_channel_sw_ie) +
+                  /* Mesh Channel Swith Parameters */
+                  2 + sizeof(struct ieee80211_mesh_chansw_params_ie) +
                   2 + 8 + /* supported rates */
                   2 + 3; /* DS params */
        tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
@@ -665,6 +671,38 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
        *pos++ = WLAN_EID_SSID;
        *pos++ = 0x0;
 
+       rcu_read_lock();
+       csa = rcu_dereference(ifmsh->csa);
+       if (csa) {
+               __le16 pre_value;
+
+               pos = skb_put(skb, 13);
+               memset(pos, 0, 13);
+               *pos++ = WLAN_EID_CHANNEL_SWITCH;
+               *pos++ = 3;
+               *pos++ = 0x0;
+               *pos++ = ieee80211_frequency_to_channel(
+                               csa->settings.chandef.chan->center_freq);
+               sdata->csa_counter_offset_beacon = hdr_len + 6;
+               *pos++ = csa->settings.count;
+               *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
+               *pos++ = 6;
+               if (ifmsh->chsw_init) {
+                       *pos++ = ifmsh->mshcfg.dot11MeshTTL;
+                       *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
+               } else {
+                       *pos++ = ifmsh->chsw_ttl;
+               }
+               *pos++ |= csa->settings.block_tx ?
+                         WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
+               put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos);
+               pos += 2;
+               pre_value = cpu_to_le16(ifmsh->pre_value);
+               memcpy(pos, &pre_value, 2);
+               pos += 2;
+       }
+       rcu_read_unlock();
+
        if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
            mesh_add_ds_params_ie(sdata, skb))
                goto out_free;
@@ -812,6 +850,127 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        ieee80211_configure_filter(local);
 }
 
+static bool
+ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
+                                struct ieee802_11_elems *elems, bool beacon)
+{
+       struct cfg80211_csa_settings params;
+       struct ieee80211_csa_ie csa_ie;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_chanctx *chanctx;
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       int err, num_chanctx;
+       u32 sta_flags;
+
+       if (sdata->vif.csa_active)
+               return true;
+
+       if (!ifmsh->mesh_id)
+               return false;
+
+       sta_flags = IEEE80211_STA_DISABLE_VHT;
+       switch (sdata->vif.bss_conf.chandef.width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               sta_flags |= IEEE80211_STA_DISABLE_HT;
+       case NL80211_CHAN_WIDTH_20:
+               sta_flags |= IEEE80211_STA_DISABLE_40MHZ;
+               break;
+       default:
+               break;
+       }
+
+       memset(&params, 0, sizeof(params));
+       memset(&csa_ie, 0, sizeof(csa_ie));
+       err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
+                                          sta_flags, sdata->vif.addr,
+                                          &csa_ie);
+       if (err < 0)
+               return false;
+       if (err)
+               return false;
+
+       params.chandef = csa_ie.chandef;
+       params.count = csa_ie.count;
+
+       if (sdata->vif.bss_conf.chandef.chan->band !=
+           params.chandef.chan->band)
+               return false;
+
+       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, &params.chandef,
+                                    IEEE80211_CHAN_DISABLED)) {
+               sdata_info(sdata,
+                          "mesh STA %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), aborting\n",
+                          sdata->vif.addr,
+                          params.chandef.chan->center_freq,
+                          params.chandef.width,
+                          params.chandef.center_freq1,
+                          params.chandef.center_freq2);
+               return false;
+       }
+
+       err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
+                                           &params.chandef);
+       if (err < 0)
+               return false;
+       if (err) {
+               params.radar_required = true;
+               /* TODO: DFS not (yet) supported */
+               return false;
+       }
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+       if (!chanctx_conf)
+               goto failed_chswitch;
+
+       /* don't handle for multi-VIF cases */
+       chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+       if (chanctx->refcount > 1)
+               goto failed_chswitch;
+
+       num_chanctx = 0;
+       list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list)
+               num_chanctx++;
+
+       if (num_chanctx > 1)
+               goto failed_chswitch;
+
+       rcu_read_unlock();
+
+       mcsa_dbg(sdata,
+                "received channel switch announcement to go to channel %d MHz\n",
+                params.chandef.chan->center_freq);
+
+       params.block_tx = csa_ie.mode & WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT;
+       if (beacon)
+               ifmsh->chsw_ttl = csa_ie.ttl - 1;
+       else
+               ifmsh->chsw_ttl = 0;
+
+       if (ifmsh->chsw_ttl > 0)
+               if (ieee80211_mesh_csa_beacon(sdata, &params, false) < 0)
+                       return false;
+
+       sdata->csa_radar_required = params.radar_required;
+
+       if (params.block_tx)
+               ieee80211_stop_queues_by_reason(&sdata->local->hw,
+                               IEEE80211_MAX_QUEUE_MAP,
+                               IEEE80211_QUEUE_STOP_REASON_CSA);
+
+       sdata->local->csa_chandef = params.chandef;
+       sdata->vif.csa_active = true;
+
+       ieee80211_bss_info_change_notify(sdata, err);
+       drv_channel_switch_beacon(sdata, &params.chandef);
+
+       return true;
+failed_chswitch:
+       rcu_read_unlock();
+       return false;
+}
+
 static void
 ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
                            struct ieee80211_mgmt *mgmt, size_t len)
@@ -918,6 +1077,142 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
        if (ifmsh->sync_ops)
                ifmsh->sync_ops->rx_bcn_presp(sdata,
                        stype, mgmt, &elems, rx_status);
+
+       if (!ifmsh->chsw_init)
+               ieee80211_mesh_process_chnswitch(sdata, &elems, true);
+}
+
+int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       struct mesh_csa_settings *tmp_csa_settings;
+       int ret = 0;
+
+       /* Reset the TTL value and Initiator flag */
+       ifmsh->chsw_init = false;
+       ifmsh->chsw_ttl = 0;
+
+       /* Remove the CSA and MCSP elements from the beacon */
+       tmp_csa_settings = rcu_dereference(ifmsh->csa);
+       rcu_assign_pointer(ifmsh->csa, NULL);
+       kfree_rcu(tmp_csa_settings, rcu_head);
+       ret = ieee80211_mesh_rebuild_beacon(sdata);
+       if (ret)
+               return -EINVAL;
+
+       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
+
+       mcsa_dbg(sdata, "complete switching to center freq %d MHz",
+                sdata->vif.bss_conf.chandef.chan->center_freq);
+       return 0;
+}
+
+int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings,
+                             bool csa_action)
+{
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       struct mesh_csa_settings *tmp_csa_settings;
+       int ret = 0;
+
+       tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings),
+                                  GFP_ATOMIC);
+       if (!tmp_csa_settings)
+               return -ENOMEM;
+
+       memcpy(&tmp_csa_settings->settings, csa_settings,
+              sizeof(struct cfg80211_csa_settings));
+
+       rcu_assign_pointer(ifmsh->csa, tmp_csa_settings);
+
+       ret = ieee80211_mesh_rebuild_beacon(sdata);
+       if (ret) {
+               tmp_csa_settings = rcu_dereference(ifmsh->csa);
+               rcu_assign_pointer(ifmsh->csa, NULL);
+               kfree_rcu(tmp_csa_settings, rcu_head);
+               return ret;
+       }
+
+       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
+
+       if (csa_action)
+               ieee80211_send_action_csa(sdata, csa_settings);
+
+       return 0;
+}
+
+static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
+                              struct ieee80211_mgmt *mgmt, size_t len)
+{
+       struct ieee80211_mgmt *mgmt_fwd;
+       struct sk_buff *skb;
+       struct ieee80211_local *local = sdata->local;
+       u8 *pos = mgmt->u.action.u.chan_switch.variable;
+       size_t offset_ttl;
+
+       skb = dev_alloc_skb(local->tx_headroom + len);
+       if (!skb)
+               return -ENOMEM;
+       skb_reserve(skb, local->tx_headroom);
+       mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len);
+
+       /* offset_ttl is based on whether the secondary channel
+        * offset is available or not. Substract 1 from the mesh TTL
+        * and disable the initiator flag before forwarding.
+        */
+       offset_ttl = (len < 42) ? 7 : 10;
+       *(pos + offset_ttl) -= 1;
+       *(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
+       sdata->u.mesh.chsw_ttl = *(pos + offset_ttl);
+
+       memcpy(mgmt_fwd, mgmt, len);
+       eth_broadcast_addr(mgmt_fwd->da);
+       memcpy(mgmt_fwd->sa, sdata->vif.addr, ETH_ALEN);
+       memcpy(mgmt_fwd->bssid, sdata->vif.addr, ETH_ALEN);
+
+       ieee80211_tx_skb(sdata, skb);
+       return 0;
+}
+
+static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
+                             struct ieee80211_mgmt *mgmt, size_t len)
+{
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       struct ieee802_11_elems elems;
+       u16 pre_value;
+       bool fwd_csa = true;
+       size_t baselen;
+       u8 *pos, ttl;
+
+       if (mgmt->u.action.u.measurement.action_code !=
+           WLAN_ACTION_SPCT_CHL_SWITCH)
+               return;
+
+       pos = mgmt->u.action.u.chan_switch.variable;
+       baselen = offsetof(struct ieee80211_mgmt,
+                          u.action.u.chan_switch.variable);
+       ieee802_11_parse_elems(pos, len - baselen, false, &elems);
+
+       ttl = elems.mesh_chansw_params_ie->mesh_ttl;
+       if (!--ttl)
+               fwd_csa = false;
+
+       pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value);
+       if (ifmsh->pre_value >= pre_value)
+               return;
+
+       ifmsh->pre_value = pre_value;
+
+       if (!ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
+               mcsa_dbg(sdata, "Failed to process CSA action frame");
+               return;
+       }
+
+       /* forward or re-broadcast the CSA frame */
+       if (fwd_csa) {
+               if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0)
+                       mcsa_dbg(sdata, "Failed to forward the CSA frame");
+       }
 }
 
 static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
@@ -939,6 +1234,9 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
                if (mesh_action_is_path_sel(mgmt))
                        mesh_rx_path_sel_frame(sdata, mgmt, len);
                break;
+       case WLAN_CATEGORY_SPECTRUM_MGMT:
+               mesh_rx_csa_frame(sdata, mgmt, len);
+               break;
        }
 }
 
@@ -1056,13 +1354,11 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
                    (unsigned long) sdata);
 
        ifmsh->accepting_plinks = true;
-       ifmsh->preq_id = 0;
-       ifmsh->sn = 0;
-       ifmsh->num_gates = 0;
        atomic_set(&ifmsh->mpaths, 0);
        mesh_rmc_init(sdata);
        ifmsh->last_preq = jiffies;
        ifmsh->next_perr = jiffies;
+       ifmsh->chsw_init = false;
        /* Allocate all mesh structures when creating the first mesh interface. */
        if (!mesh_allocated)
                ieee80211s_init();
index 6b65d5055f5bf7572d29712c6345c88dd11b33a5..4301aa5aa227c3d539cd0bf89bdf5b004679976a 100644 (file)
@@ -222,7 +222,8 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
        mesh_path_flush_by_nexthop(sta);
 
        ieee80211_mps_sta_status_update(sta);
-       changed |= ieee80211_mps_local_status_update(sdata);
+       changed |= ieee80211_mps_set_sta_local_pm(sta,
+                       NL80211_MESH_POWER_UNKNOWN);
 
        return changed;
 }
index 22290a929b94595445865baecdb692aff5e0be9c..0f79b78b5e86b45a6d7059de497b68b44944da09 100644 (file)
@@ -152,6 +152,9 @@ u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
 
+       if (sta->local_pm == pm)
+               return 0;
+
        mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
                pm, sta->sta.addr);
 
@@ -245,6 +248,14 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)
 
        do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
 
+       /* clear the MPSP flags for non-peers or active STA */
+       if (sta->plink_state != NL80211_PLINK_ESTAB) {
+               clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
+               clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
+       } else if (!do_buffer) {
+               clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
+       }
+
        /* Don't let the same PS state be set twice */
        if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer)
                return;
@@ -257,14 +268,6 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)
        } else {
                ieee80211_sta_ps_deliver_wakeup(sta);
        }
-
-       /* clear the MPSP flags for non-peers or active STA */
-       if (sta->plink_state != NL80211_PLINK_ESTAB) {
-               clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
-               clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
-       } else if (!do_buffer) {
-               clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
-       }
 }
 
 static void mps_set_sta_peer_pm(struct sta_info *sta,
@@ -444,8 +447,7 @@ static void mpsp_qos_null_append(struct sta_info *sta,
  */
 static void mps_frame_deliver(struct sta_info *sta, int n_frames)
 {
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
-       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_local *local = sta->sdata->local;
        int ac;
        struct sk_buff_head frames;
        struct sk_buff *skb;
@@ -558,10 +560,10 @@ void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
 }
 
 /**
- * ieee80211_mps_frame_release - release buffered frames in response to beacon
+ * ieee80211_mps_frame_release - release frames buffered due to mesh power save
  *
  * @sta: mesh STA
- * @elems: beacon IEs
+ * @elems: IEs of beacon or probe response
  *
  * For peers if we have individually-addressed frames buffered or the peer
  * indicates buffered frames, send a corresponding MPSP trigger frame. Since
@@ -588,9 +590,10 @@ void ieee80211_mps_frame_release(struct sta_info *sta,
            (!elems->awake_window || !le16_to_cpu(*elems->awake_window)))
                return;
 
-       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
-               buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
-                               skb_queue_len(&sta->tx_filtered[ac]);
+       if (!test_sta_flag(sta, WLAN_STA_MPSP_OWNER))
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
+                                       skb_queue_len(&sta->tx_filtered[ac]);
 
        if (!has_buffered && !buffer_local)
                return;
index d7bdc4b97dde2d57d79e2131333ff55f4878c9cb..d7504ab61a34c7ef6a51f8adce10e58c021408d0 100644 (file)
@@ -958,9 +958,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        struct cfg80211_bss *cbss = ifmgd->associated;
        struct ieee80211_chanctx *chanctx;
        enum ieee80211_band current_band;
-       u8 count;
-       u8 mode;
-       struct cfg80211_chan_def new_chandef = {};
+       struct ieee80211_csa_ie csa_ie;
        int res;
 
        sdata_assert_lock(sdata);
@@ -976,24 +974,24 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                return;
 
        current_band = cbss->channel->band;
+       memset(&csa_ie, 0, sizeof(csa_ie));
        res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
                                           ifmgd->flags,
-                                          ifmgd->associated->bssid, &count,
-                                          &mode, &new_chandef);
+                                          ifmgd->associated->bssid, &csa_ie);
        if (res < 0)
                ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
        if (res)
                return;
 
-       if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
+       if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef,
                                     IEEE80211_CHAN_DISABLED)) {
                sdata_info(sdata,
                           "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
                           ifmgd->associated->bssid,
-                          new_chandef.chan->center_freq,
-                          new_chandef.width, new_chandef.center_freq1,
-                          new_chandef.center_freq2);
+                          csa_ie.chandef.chan->center_freq,
+                          csa_ie.chandef.width, csa_ie.chandef.center_freq1,
+                          csa_ie.chandef.center_freq2);
                ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
                return;
@@ -1037,9 +1035,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
        mutex_unlock(&local->chanctx_mtx);
 
-       local->csa_chandef = new_chandef;
+       local->csa_chandef = csa_ie.chandef;
 
-       if (mode)
+       if (csa_ie.mode)
                ieee80211_stop_queues_by_reason(&local->hw,
                                IEEE80211_MAX_QUEUE_MAP,
                                IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -1048,9 +1046,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                /* use driver's channel switch callback */
                struct ieee80211_channel_switch ch_switch = {
                        .timestamp = timestamp,
-                       .block_tx = mode,
-                       .chandef = new_chandef,
-                       .count = count,
+                       .block_tx = csa_ie.mode,
+                       .chandef = csa_ie.chandef,
+                       .count = csa_ie.count,
                };
 
                drv_channel_switch(local, &ch_switch);
@@ -1058,11 +1056,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
 
        /* channel switch handled in software */
-       if (count <= 1)
+       if (csa_ie.count <= 1)
                ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
        else
                mod_timer(&ifmgd->chswitch_timer,
-                         TU_TO_EXP_TIME(count * cbss->beacon_interval));
+                         TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
 }
 
 static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
@@ -3500,7 +3498,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
                  ieee80211_beacon_connection_loss_work);
        INIT_WORK(&ifmgd->csa_connection_drop_work,
                  ieee80211_csa_connection_drop_work);
-       INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
+       INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
        setup_timer(&ifmgd->timer, ieee80211_sta_timer,
                    (unsigned long) sdata);
        setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
index 0011ac8150972470110b004c4780a8dbc8acd406..caecef870c0e44e3562cdc0a874bcfd233fc77a1 100644 (file)
@@ -2593,13 +2593,16 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                                break;
 
                        if (sdata->vif.type != NL80211_IFTYPE_STATION &&
-                           sdata->vif.type != NL80211_IFTYPE_ADHOC)
+                           sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+                           sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
                                break;
 
                        if (sdata->vif.type == NL80211_IFTYPE_STATION)
                                bssid = sdata->u.mgd.bssid;
                        else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
                                bssid = sdata->u.ibss.bssid;
+                       else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+                               bssid = mgmt->sa;
                        else
                                break;
 
index 921597e279a307857cad5974a434fdd9252ec225..a40da20b32e074a3923f844edf104eca33a6134c 100644 (file)
@@ -24,8 +24,8 @@
 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
                                 struct ieee802_11_elems *elems, bool beacon,
                                 enum ieee80211_band current_band,
-                                u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
-                                struct cfg80211_chan_def *new_chandef)
+                                u32 sta_flags, u8 *bssid,
+                                struct ieee80211_csa_ie *csa_ie)
 {
        enum ieee80211_band new_band;
        int new_freq;
@@ -62,18 +62,24 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
                        return -EINVAL;
                }
                new_chan_no = elems->ext_chansw_ie->new_ch_num;
-               *count = elems->ext_chansw_ie->count;
-               *mode = elems->ext_chansw_ie->mode;
+               csa_ie->count = elems->ext_chansw_ie->count;
+               csa_ie->mode = elems->ext_chansw_ie->mode;
        } else if (elems->ch_switch_ie) {
                new_band = current_band;
                new_chan_no = elems->ch_switch_ie->new_ch_num;
-               *count = elems->ch_switch_ie->count;
-               *mode = elems->ch_switch_ie->mode;
+               csa_ie->count = elems->ch_switch_ie->count;
+               csa_ie->mode = elems->ch_switch_ie->mode;
        } else {
                /* nothing here we understand */
                return 1;
        }
 
+       /* Mesh Channel Switch Parameters Element */
+       if (elems->mesh_chansw_params_ie) {
+               csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl;
+               csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags;
+       }
+
        new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
        new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
        if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
@@ -103,25 +109,26 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
        default:
                /* secondary_channel_offset was present but is invalid */
        case IEEE80211_HT_PARAM_CHA_SEC_NONE:
-               cfg80211_chandef_create(new_chandef, new_chan,
+               cfg80211_chandef_create(&csa_ie->chandef, new_chan,
                                        NL80211_CHAN_HT20);
                break;
        case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-               cfg80211_chandef_create(new_chandef, new_chan,
+               cfg80211_chandef_create(&csa_ie->chandef, new_chan,
                                        NL80211_CHAN_HT40PLUS);
                break;
        case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-               cfg80211_chandef_create(new_chandef, new_chan,
+               cfg80211_chandef_create(&csa_ie->chandef, new_chan,
                                        NL80211_CHAN_HT40MINUS);
                break;
        case -1:
-               cfg80211_chandef_create(new_chandef, new_chan,
+               cfg80211_chandef_create(&csa_ie->chandef, new_chan,
                                        NL80211_CHAN_NO_HT);
                /* keep width for 5/10 MHz channels */
                switch (sdata->vif.bss_conf.chandef.width) {
                case NL80211_CHAN_WIDTH_5:
                case NL80211_CHAN_WIDTH_10:
-                       new_chandef->width = sdata->vif.bss_conf.chandef.width;
+                       csa_ie->chandef.width =
+                               sdata->vif.bss_conf.chandef.width;
                        break;
                default:
                        break;
@@ -171,13 +178,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
        /* if VHT data is there validate & use it */
        if (new_vht_chandef.chan) {
                if (!cfg80211_chandef_compatible(&new_vht_chandef,
-                                                new_chandef)) {
+                                                &csa_ie->chandef)) {
                        sdata_info(sdata,
                                   "BSS %pM: CSA has inconsistent channel data, disconnecting\n",
                                   bssid);
                        return -EINVAL;
                }
-               *new_chandef = new_vht_chandef;
+               csa_ie->chandef = new_vht_chandef;
        }
 
        return 0;
index aeb967a0aeed5ccc49b6c45c5ac13d0d97f97130..1eb66e26e49d0a02869ec5e5ff4789583e9760d1 100644 (file)
@@ -385,6 +385,30 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
                sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
 
        sta->sta.smps_mode = IEEE80211_SMPS_OFF;
+       if (sdata->vif.type == NL80211_IFTYPE_AP ||
+           sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+               struct ieee80211_supported_band *sband =
+                       local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
+               u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
+                               IEEE80211_HT_CAP_SM_PS_SHIFT;
+               /*
+                * Assume that hostapd advertises our caps in the beacon and
+                * this is the known_smps_mode for a station that just assciated
+                */
+               switch (smps) {
+               case WLAN_HT_SMPS_CONTROL_DISABLED:
+                       sta->known_smps_mode = IEEE80211_SMPS_OFF;
+                       break;
+               case WLAN_HT_SMPS_CONTROL_STATIC:
+                       sta->known_smps_mode = IEEE80211_SMPS_STATIC;
+                       break;
+               case WLAN_HT_SMPS_CONTROL_DYNAMIC:
+                       sta->known_smps_mode = IEEE80211_SMPS_DYNAMIC;
+                       break;
+               default:
+                       WARN_ON(1);
+               }
+       }
 
        sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
 
@@ -1069,6 +1093,19 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 
        ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta);
 
+       /* This station just woke up and isn't aware of our SMPS state */
+       if (!ieee80211_smps_is_restrictive(sta->known_smps_mode,
+                                          sdata->smps_mode) &&
+           sta->known_smps_mode != sdata->bss->req_smps &&
+           sta_info_tx_streams(sta) != 1) {
+               ht_dbg(sdata,
+                      "%pM just woke up and MIMO capable - update SMPS\n",
+                      sta->sta.addr);
+               ieee80211_send_smps_action(sdata, sdata->bss->req_smps,
+                                          sta->sta.addr,
+                                          sdata->vif.bss_conf.bssid);
+       }
+
        local->total_ps_buffered -= buffered;
 
        sta_info_recalc_tim(sta);
@@ -1520,3 +1557,38 @@ int sta_info_move_state(struct sta_info *sta,
 
        return 0;
 }
+
+u8 sta_info_tx_streams(struct sta_info *sta)
+{
+       struct ieee80211_sta_ht_cap *ht_cap = &sta->sta.ht_cap;
+       u8 rx_streams;
+
+       if (!sta->sta.ht_cap.ht_supported)
+               return 1;
+
+       if (sta->sta.vht_cap.vht_supported) {
+               int i;
+               u16 tx_mcs_map =
+                       le16_to_cpu(sta->sta.vht_cap.vht_mcs.tx_mcs_map);
+
+               for (i = 7; i >= 0; i--)
+                       if ((tx_mcs_map & (0x3 << (i * 2))) !=
+                           IEEE80211_VHT_MCS_NOT_SUPPORTED)
+                               return i + 1;
+       }
+
+       if (ht_cap->mcs.rx_mask[3])
+               rx_streams = 4;
+       else if (ht_cap->mcs.rx_mask[2])
+               rx_streams = 3;
+       else if (ht_cap->mcs.rx_mask[1])
+               rx_streams = 2;
+       else
+               rx_streams = 1;
+
+       if (!(ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF))
+               return rx_streams;
+
+       return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
+                       >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
+}
index 4208dbd5861f4d87cc8fa94aefc7c00f51e72eed..3ef06a26b9cb9feaddfd3d752dd9bd8ac971a11c 100644 (file)
@@ -301,6 +301,8 @@ struct sta_ampdu_mlme {
  * @chains: chains ever used for RX from this station
  * @chain_signal_last: last signal (per chain)
  * @chain_signal_avg: signal average (per chain)
+ * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
+ *     AP only.
  */
 struct sta_info {
        /* General information, mostly static */
@@ -411,6 +413,8 @@ struct sta_info {
        unsigned int lost_packets;
        unsigned int beacon_loss_count;
 
+       enum ieee80211_smps_mode known_smps_mode;
+
        /* keep last! */
        struct ieee80211_sta sta;
 };
@@ -613,6 +617,7 @@ void sta_set_rate_info_rx(struct sta_info *sta,
                          struct rate_info *rinfo);
 void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
                          unsigned long exp_time);
+u8 sta_info_tx_streams(struct sta_info *sta);
 
 void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
 void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
index 78dc2e99027e06b8a9fc37f5bdf7f06483476f2b..52a152b01b063b226fb5a523b19a4e9863eeffaf 100644 (file)
@@ -194,29 +194,36 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
        if (ieee80211_is_action(mgmt->frame_control) &&
            mgmt->u.action.category == WLAN_CATEGORY_HT &&
            mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS &&
-           sdata->vif.type == NL80211_IFTYPE_STATION &&
            ieee80211_sdata_running(sdata)) {
-               /*
-                * This update looks racy, but isn't -- if we come
-                * here we've definitely got a station that we're
-                * talking to, and on a managed interface that can
-                * only be the AP. And the only other place updating
-                * this variable in managed mode is before association.
-                */
+               enum ieee80211_smps_mode smps_mode;
+
                switch (mgmt->u.action.u.ht_smps.smps_control) {
                case WLAN_HT_SMPS_CONTROL_DYNAMIC:
-                       sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
+                       smps_mode = IEEE80211_SMPS_DYNAMIC;
                        break;
                case WLAN_HT_SMPS_CONTROL_STATIC:
-                       sdata->smps_mode = IEEE80211_SMPS_STATIC;
+                       smps_mode = IEEE80211_SMPS_STATIC;
                        break;
                case WLAN_HT_SMPS_CONTROL_DISABLED:
                default: /* shouldn't happen since we don't send that */
-                       sdata->smps_mode = IEEE80211_SMPS_OFF;
+                       smps_mode = IEEE80211_SMPS_OFF;
                        break;
                }
 
-               ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
+               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+                       /*
+                        * This update looks racy, but isn't -- if we come
+                        * here we've definitely got a station that we're
+                        * talking to, and on a managed interface that can
+                        * only be the AP. And the only other place updating
+                        * this variable in managed mode is before association.
+                        */
+                       sdata->smps_mode = smps_mode;
+                       ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
+               } else if (sdata->vif.type == NL80211_IFTYPE_AP ||
+                          sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+                       sta->known_smps_mode = smps_mode;
+               }
        }
 }
 
index 9993fcb19ecdd8b97ea0c1fe1221f916dfd24e7f..c558b246ef0036c38e6c8a00b338b43c46913f78 100644 (file)
@@ -1367,6 +1367,35 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
        return 0;
 }
 
+bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif, struct sk_buff *skb,
+                             int band, struct ieee80211_sta **sta)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_tx_data tx;
+
+       if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP)
+               return false;
+
+       info->band = band;
+       info->control.vif = vif;
+       info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)];
+
+       if (invoke_tx_handlers(&tx))
+               return false;
+
+       if (sta) {
+               if (tx.sta)
+                       *sta = &tx.sta->sta;
+               else
+                       *sta = NULL;
+       }
+
+       return true;
+}
+EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
+
 /*
  * Returns false if the frame couldn't be transmitted but was queued instead.
  */
@@ -2370,6 +2399,10 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
                beacon_data = beacon->head;
                beacon_data_len = beacon->head_len;
                break;
+       case NL80211_IFTYPE_MESH_POINT:
+               beacon_data = beacon->head;
+               beacon_data_len = beacon->head_len;
+               break;
        default:
                return;
        }
@@ -2424,6 +2457,15 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
                if (!beacon)
                        goto out;
 
+               beacon_data = beacon->head;
+               beacon_data_len = beacon->head_len;
+       } else if (vif->type == NL80211_IFTYPE_MESH_POINT) {
+               struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+               beacon = rcu_dereference(ifmsh->beacon);
+               if (!beacon)
+                       goto out;
+
                beacon_data = beacon->head;
                beacon_data_len = beacon->head_len;
        } else {
@@ -2531,6 +2573,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                if (!bcn)
                        goto out;
 
+               if (sdata->vif.csa_active)
+                       ieee80211_update_csa(sdata, bcn);
+
                if (ifmsh->sync_ops)
                        ifmsh->sync_ops->adjust_tbtt(
                                                sdata);
index aefb9d5b962023eb59b750f867de6733eb1c142b..592a18171f95e9ec5273b03307235dff2bd1c946 100644 (file)
@@ -300,9 +300,6 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
                if (!sdata->dev)
                        continue;
 
-               if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
-                       continue;
-
                if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&
                    local->queue_stop_reasons[sdata->vif.cab_queue] != 0)
                        continue;
@@ -743,6 +740,7 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                case WLAN_EID_TIMEOUT_INTERVAL:
                case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
                case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
+               case WLAN_EID_CHAN_SWITCH_PARAM:
                /*
                 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
                 * that if the content gets bigger it might be needed more than once
@@ -908,6 +906,14 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                        }
                        elems->sec_chan_offs = (void *)pos;
                        break;
+               case WLAN_EID_CHAN_SWITCH_PARAM:
+                       if (elen !=
+                           sizeof(*elems->mesh_chansw_params_ie)) {
+                               elem_parse_failed = true;
+                               break;
+                       }
+                       elems->mesh_chansw_params_ie = (void *)pos;
+                       break;
                case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
                        if (!action ||
                            elen != sizeof(*elems->wide_bw_chansw_ie)) {
@@ -2354,3 +2360,115 @@ u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
 
        return ret;
 }
+
+/*
+ * Returns true if smps_mode_new is strictly more restrictive than
+ * smps_mode_old.
+ */
+bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
+                                  enum ieee80211_smps_mode smps_mode_new)
+{
+       if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC ||
+                        smps_mode_new == IEEE80211_SMPS_AUTOMATIC))
+               return false;
+
+       switch (smps_mode_old) {
+       case IEEE80211_SMPS_STATIC:
+               return false;
+       case IEEE80211_SMPS_DYNAMIC:
+               return smps_mode_new == IEEE80211_SMPS_STATIC;
+       case IEEE80211_SMPS_OFF:
+               return smps_mode_new != IEEE80211_SMPS_OFF;
+       default:
+               WARN_ON(1);
+       }
+
+       return false;
+}
+
+int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings)
+{
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
+       struct ieee80211_local *local = sdata->local;
+       int freq;
+       int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
+                              sizeof(mgmt->u.action.u.chan_switch);
+       u8 *pos;
+
+       if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+           sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+               return -EOPNOTSUPP;
+
+       skb = dev_alloc_skb(local->tx_headroom + hdr_len +
+                           5 + /* channel switch announcement element */
+                           3 + /* secondary channel offset element */
+                           8); /* mesh channel switch parameters element */
+       if (!skb)
+               return -ENOMEM;
+
+       skb_reserve(skb, local->tx_headroom);
+       mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
+       memset(mgmt, 0, hdr_len);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_ACTION);
+
+       eth_broadcast_addr(mgmt->da);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
+       } else {
+               struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+               memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
+       }
+       mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
+       mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
+       pos = skb_put(skb, 5);
+       *pos++ = WLAN_EID_CHANNEL_SWITCH;                       /* EID */
+       *pos++ = 3;                                             /* IE length */
+       *pos++ = csa_settings->block_tx ? 1 : 0;                /* CSA mode */
+       freq = csa_settings->chandef.chan->center_freq;
+       *pos++ = ieee80211_frequency_to_channel(freq);          /* channel */
+       *pos++ = csa_settings->count;                           /* count */
+
+       if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
+               enum nl80211_channel_type ch_type;
+
+               skb_put(skb, 3);
+               *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;     /* EID */
+               *pos++ = 1;                                     /* IE length */
+               ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
+               if (ch_type == NL80211_CHAN_HT40PLUS)
+                       *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+               else
+                       *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+       }
+
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+               __le16 pre_value;
+
+               skb_put(skb, 8);
+               *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;            /* EID */
+               *pos++ = 6;                                     /* IE length */
+               *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL;     /* Mesh TTL */
+               *pos = 0x00;    /* Mesh Flag: Tx Restrict, Initiator, Reason */
+               *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
+               *pos++ |= csa_settings->block_tx ?
+                         WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
+               put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */
+               pos += 2;
+               if (!ifmsh->pre_value)
+                       ifmsh->pre_value = 1;
+               else
+                       ifmsh->pre_value++;
+               pre_value = cpu_to_le16(ifmsh->pre_value);
+               memcpy(pos, &pre_value, 2);             /* Precedence Value */
+               pos += 2;
+               ifmsh->chsw_init = true;
+       }
+
+       ieee80211_tx_skb(sdata, skb);
+       return 0;
+}
index c9edfcb7a13b65ad6d766b182e54cf6cfcc83c0b..d6572822076367cdf203207892a2880c7e4487c2 100644 (file)
@@ -301,22 +301,16 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
 }
 
 
-static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
+static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
                                int encrypted)
 {
        __le16 mask_fc;
        int a4_included, mgmt;
        u8 qos_tid;
-       u8 *b_0, *aad;
-       u16 data_len, len_a;
+       u16 len_a;
        unsigned int hdrlen;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 
-       memset(scratch, 0, 6 * AES_BLOCK_SIZE);
-
-       b_0 = scratch + 3 * AES_BLOCK_SIZE;
-       aad = scratch + 4 * AES_BLOCK_SIZE;
-
        /*
         * Mask FC: zero subtype b4 b5 b6 (if not mgmt)
         * Retry, PwrMgt, MoreData; set Protected
@@ -338,20 +332,21 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
        else
                qos_tid = 0;
 
-       data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN;
-       if (encrypted)
-               data_len -= IEEE80211_CCMP_MIC_LEN;
+       /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
+        * mode authentication are not allowed to collide, yet both are derived
+        * from this vector b_0. We only set L := 1 here to indicate that the
+        * data size can be represented in (L+1) bytes. The CCM layer will take
+        * care of storing the data length in the top (L+1) bytes and setting
+        * and clearing the other bits as is required to derive the two IVs.
+        */
+       b_0[0] = 0x1;
 
-       /* First block, b_0 */
-       b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */
        /* Nonce: Nonce Flags | A2 | PN
         * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)
         */
        b_0[1] = qos_tid | (mgmt << 4);
        memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
        memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
-       /* l(m) */
-       put_unaligned_be16(data_len, &b_0[14]);
 
        /* AAD (extra authenticate-only data) / masked 802.11 header
         * FC | A1 | A2 | A3 | SC | [A4] | [QC] */
@@ -407,7 +402,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
        u8 *pos;
        u8 pn[6];
        u64 pn64;
-       u8 scratch[6 * AES_BLOCK_SIZE];
+       u8 aad[2 * AES_BLOCK_SIZE];
+       u8 b_0[AES_BLOCK_SIZE];
 
        if (info->control.hw_key &&
            !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
@@ -460,9 +456,9 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
                return 0;
 
        pos += IEEE80211_CCMP_HDR_LEN;
-       ccmp_special_blocks(skb, pn, scratch, 0);
-       ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len,
-                                 pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN));
+       ccmp_special_blocks(skb, pn, b_0, aad, 0);
+       ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
+                                 skb_put(skb, IEEE80211_CCMP_MIC_LEN));
 
        return 0;
 }
@@ -525,16 +521,16 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
        }
 
        if (!(status->flag & RX_FLAG_DECRYPTED)) {
-               u8 scratch[6 * AES_BLOCK_SIZE];
+               u8 aad[2 * AES_BLOCK_SIZE];
+               u8 b_0[AES_BLOCK_SIZE];
                /* hardware didn't decrypt/verify MIC */
-               ccmp_special_blocks(skb, pn, scratch, 1);
+               ccmp_special_blocks(skb, pn, b_0, aad, 1);
 
                if (ieee80211_aes_ccm_decrypt(
-                           key->u.ccmp.tfm, scratch,
+                           key->u.ccmp.tfm, b_0, aad,
                            skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
                            data_len,
-                           skb->data + skb->len - IEEE80211_CCMP_MIC_LEN,
-                           skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN))
+                           skb->data + skb->len - IEEE80211_CCMP_MIC_LEN))
                        return RX_DROP_UNUSABLE;
        }
 
index 78efe895b6636c4a57af6d72f156501e2c778992..4c10e7e6c9f6ae53291d3f72d82b6c9b0539b552 100644 (file)
@@ -36,7 +36,7 @@ config RFKILL_REGULATOR
 
 config RFKILL_GPIO
        tristate "GPIO RFKILL driver"
-       depends on RFKILL && GPIOLIB && HAVE_CLK
+       depends on RFKILL && GPIOLIB
        default n
        help
          If you say yes here you get support of a generic gpio RFKILL
index fb076cd6f808ad9b409a9670bffa699a7435de34..5620d3c07479654e64fd0b7d1b325562b880b174 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/acpi_gpio.h>
 
 #include <linux/rfkill-gpio.h>
 
-enum rfkill_gpio_clk_state {
-       UNSPECIFIED = 0,
-       PWR_ENABLED,
-       PWR_DISABLED
-};
+struct rfkill_gpio_data {
+       const char              *name;
+       enum rfkill_type        type;
+       int                     reset_gpio;
+       int                     shutdown_gpio;
 
-#define PWR_CLK_SET(_RF, _EN) \
-       ((_RF)->pwr_clk_enabled = (!(_EN) ? PWR_ENABLED : PWR_DISABLED))
-#define PWR_CLK_ENABLED(_RF) ((_RF)->pwr_clk_enabled == PWR_ENABLED)
-#define PWR_CLK_DISABLED(_RF) ((_RF)->pwr_clk_enabled != PWR_ENABLED)
+       struct rfkill           *rfkill_dev;
+       char                    *reset_name;
+       char                    *shutdown_name;
+       struct clk              *clk;
 
-struct rfkill_gpio_data {
-       struct rfkill_gpio_platform_data        *pdata;
-       struct rfkill                           *rfkill_dev;
-       char                                    *reset_name;
-       char                                    *shutdown_name;
-       enum rfkill_gpio_clk_state              pwr_clk_enabled;
-       struct clk                              *pwr_clk;
+       bool                    clk_enabled;
 };
 
 static int rfkill_gpio_set_power(void *data, bool blocked)
@@ -52,23 +48,22 @@ static int rfkill_gpio_set_power(void *data, bool blocked)
        struct rfkill_gpio_data *rfkill = data;
 
        if (blocked) {
-               if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
-                       gpio_direction_output(rfkill->pdata->shutdown_gpio, 0);
-               if (gpio_is_valid(rfkill->pdata->reset_gpio))
-                       gpio_direction_output(rfkill->pdata->reset_gpio, 0);
-               if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill))
-                       clk_disable(rfkill->pwr_clk);
+               if (gpio_is_valid(rfkill->shutdown_gpio))
+                       gpio_set_value(rfkill->shutdown_gpio, 0);
+               if (gpio_is_valid(rfkill->reset_gpio))
+                       gpio_set_value(rfkill->reset_gpio, 0);
+               if (!IS_ERR(rfkill->clk) && rfkill->clk_enabled)
+                       clk_disable(rfkill->clk);
        } else {
-               if (rfkill->pwr_clk && PWR_CLK_DISABLED(rfkill))
-                       clk_enable(rfkill->pwr_clk);
-               if (gpio_is_valid(rfkill->pdata->reset_gpio))
-                       gpio_direction_output(rfkill->pdata->reset_gpio, 1);
-               if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
-                       gpio_direction_output(rfkill->pdata->shutdown_gpio, 1);
+               if (!IS_ERR(rfkill->clk) && !rfkill->clk_enabled)
+                       clk_enable(rfkill->clk);
+               if (gpio_is_valid(rfkill->reset_gpio))
+                       gpio_set_value(rfkill->reset_gpio, 1);
+               if (gpio_is_valid(rfkill->shutdown_gpio))
+                       gpio_set_value(rfkill->shutdown_gpio, 1);
        }
 
-       if (rfkill->pwr_clk)
-               PWR_CLK_SET(rfkill, blocked);
+       rfkill->clk_enabled = blocked;
 
        return 0;
 }
@@ -77,117 +72,112 @@ static const struct rfkill_ops rfkill_gpio_ops = {
        .set_block = rfkill_gpio_set_power,
 };
 
+static int rfkill_gpio_acpi_probe(struct device *dev,
+                                 struct rfkill_gpio_data *rfkill)
+{
+       const struct acpi_device_id *id;
+
+       id = acpi_match_device(dev->driver->acpi_match_table, dev);
+       if (!id)
+               return -ENODEV;
+
+       rfkill->name = dev_name(dev);
+       rfkill->type = (unsigned)id->driver_data;
+       rfkill->reset_gpio = acpi_get_gpio_by_index(dev, 0, NULL);
+       rfkill->shutdown_gpio = acpi_get_gpio_by_index(dev, 1, NULL);
+
+       return 0;
+}
+
 static int rfkill_gpio_probe(struct platform_device *pdev)
 {
-       struct rfkill_gpio_data *rfkill;
        struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
+       struct rfkill_gpio_data *rfkill;
+       const char *clk_name = NULL;
        int ret = 0;
        int len = 0;
 
-       if (!pdata) {
-               pr_warn("%s: No platform data specified\n", __func__);
-               return -EINVAL;
+       rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL);
+       if (!rfkill)
+               return -ENOMEM;
+
+       if (ACPI_HANDLE(&pdev->dev)) {
+               ret = rfkill_gpio_acpi_probe(&pdev->dev, rfkill);
+               if (ret)
+                       return ret;
+       } else if (pdata) {
+               clk_name = pdata->power_clk_name;
+               rfkill->name = pdata->name;
+               rfkill->type = pdata->type;
+               rfkill->reset_gpio = pdata->reset_gpio;
+               rfkill->shutdown_gpio = pdata->shutdown_gpio;
+       } else {
+               return -ENODEV;
        }
 
        /* make sure at-least one of the GPIO is defined and that
         * a name is specified for this instance */
-       if (!pdata->name || (!gpio_is_valid(pdata->reset_gpio) &&
-               !gpio_is_valid(pdata->shutdown_gpio))) {
+       if ((!gpio_is_valid(rfkill->reset_gpio) &&
+            !gpio_is_valid(rfkill->shutdown_gpio)) || !rfkill->name) {
                pr_warn("%s: invalid platform data\n", __func__);
                return -EINVAL;
        }
 
-       rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL);
-       if (!rfkill)
-               return -ENOMEM;
-
-       if (pdata->gpio_runtime_setup) {
+       if (pdata && pdata->gpio_runtime_setup) {
                ret = pdata->gpio_runtime_setup(pdev);
                if (ret) {
                        pr_warn("%s: can't set up gpio\n", __func__);
-                       goto fail_alloc;
+                       return ret;
                }
        }
 
-       rfkill->pdata = pdata;
-
-       len = strlen(pdata->name);
-       rfkill->reset_name = kzalloc(len + 7, GFP_KERNEL);
-       if (!rfkill->reset_name) {
-               ret = -ENOMEM;
-               goto fail_alloc;
-       }
+       len = strlen(rfkill->name);
+       rfkill->reset_name = devm_kzalloc(&pdev->dev, len + 7, GFP_KERNEL);
+       if (!rfkill->reset_name)
+               return -ENOMEM;
 
-       rfkill->shutdown_name = kzalloc(len + 10, GFP_KERNEL);
-       if (!rfkill->shutdown_name) {
-               ret = -ENOMEM;
-               goto fail_reset_name;
-       }
+       rfkill->shutdown_name = devm_kzalloc(&pdev->dev, len + 10, GFP_KERNEL);
+       if (!rfkill->shutdown_name)
+               return -ENOMEM;
 
-       snprintf(rfkill->reset_name, len + 6 , "%s_reset", pdata->name);
-       snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", pdata->name);
+       snprintf(rfkill->reset_name, len + 6 , "%s_reset", rfkill->name);
+       snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", rfkill->name);
 
-       if (pdata->power_clk_name) {
-               rfkill->pwr_clk = clk_get(&pdev->dev, pdata->power_clk_name);
-               if (IS_ERR(rfkill->pwr_clk)) {
-                       pr_warn("%s: can't find pwr_clk.\n", __func__);
-                       ret = PTR_ERR(rfkill->pwr_clk);
-                       goto fail_shutdown_name;
-               }
-       }
+       rfkill->clk = devm_clk_get(&pdev->dev, clk_name);
 
-       if (gpio_is_valid(pdata->reset_gpio)) {
-               ret = gpio_request(pdata->reset_gpio, rfkill->reset_name);
+       if (gpio_is_valid(rfkill->reset_gpio)) {
+               ret = devm_gpio_request_one(&pdev->dev, rfkill->reset_gpio,
+                                           0, rfkill->reset_name);
                if (ret) {
                        pr_warn("%s: failed to get reset gpio.\n", __func__);
-                       goto fail_clock;
+                       return ret;
                }
        }
 
-       if (gpio_is_valid(pdata->shutdown_gpio)) {
-               ret = gpio_request(pdata->shutdown_gpio, rfkill->shutdown_name);
+       if (gpio_is_valid(rfkill->shutdown_gpio)) {
+               ret = devm_gpio_request_one(&pdev->dev, rfkill->shutdown_gpio,
+                                           0, rfkill->shutdown_name);
                if (ret) {
                        pr_warn("%s: failed to get shutdown gpio.\n", __func__);
-                       goto fail_reset;
+                       return ret;
                }
        }
 
-       rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type,
-                                         &rfkill_gpio_ops, rfkill);
-       if (!rfkill->rfkill_dev) {
-               ret = -ENOMEM;
-               goto fail_shutdown;
-       }
+       rfkill->rfkill_dev = rfkill_alloc(rfkill->name, &pdev->dev,
+                                         rfkill->type, &rfkill_gpio_ops,
+                                         rfkill);
+       if (!rfkill->rfkill_dev)
+               return -ENOMEM;
 
        ret = rfkill_register(rfkill->rfkill_dev);
        if (ret < 0)
-               goto fail_rfkill;
+               return ret;
 
        platform_set_drvdata(pdev, rfkill);
 
-       dev_info(&pdev->dev, "%s device registered.\n", pdata->name);
+       dev_info(&pdev->dev, "%s device registered.\n", rfkill->name);
 
        return 0;
-
-fail_rfkill:
-       rfkill_destroy(rfkill->rfkill_dev);
-fail_shutdown:
-       if (gpio_is_valid(pdata->shutdown_gpio))
-               gpio_free(pdata->shutdown_gpio);
-fail_reset:
-       if (gpio_is_valid(pdata->reset_gpio))
-               gpio_free(pdata->reset_gpio);
-fail_clock:
-       if (rfkill->pwr_clk)
-               clk_put(rfkill->pwr_clk);
-fail_shutdown_name:
-       kfree(rfkill->shutdown_name);
-fail_reset_name:
-       kfree(rfkill->reset_name);
-fail_alloc:
-       kfree(rfkill);
-
-       return ret;
 }
 
 static int rfkill_gpio_remove(struct platform_device *pdev)
@@ -195,31 +185,26 @@ static int rfkill_gpio_remove(struct platform_device *pdev)
        struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev);
        struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
 
-       if (pdata->gpio_runtime_close)
+       if (pdata && pdata->gpio_runtime_close)
                pdata->gpio_runtime_close(pdev);
        rfkill_unregister(rfkill->rfkill_dev);
        rfkill_destroy(rfkill->rfkill_dev);
-       if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
-               gpio_free(rfkill->pdata->shutdown_gpio);
-       if (gpio_is_valid(rfkill->pdata->reset_gpio))
-               gpio_free(rfkill->pdata->reset_gpio);
-       if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill))
-               clk_disable(rfkill->pwr_clk);
-       if (rfkill->pwr_clk)
-               clk_put(rfkill->pwr_clk);
-       kfree(rfkill->shutdown_name);
-       kfree(rfkill->reset_name);
-       kfree(rfkill);
 
        return 0;
 }
 
+static const struct acpi_device_id rfkill_acpi_match[] = {
+       { "BCM4752", RFKILL_TYPE_GPS },
+       { },
+};
+
 static struct platform_driver rfkill_gpio_driver = {
        .probe = rfkill_gpio_probe,
        .remove = rfkill_gpio_remove,
        .driver = {
-                  .name = "rfkill_gpio",
-                  .owner = THIS_MODULE,
+               .name = "rfkill_gpio",
+               .owner = THIS_MODULE,
+               .acpi_match_table = ACPI_PTR(rfkill_acpi_match),
        },
 };
 
index 16f3c3a7b2c17747230f62d1318ed152490eac34..9b8cc877eb191a2a62370c9f2617bed513200d24 100644 (file)
@@ -504,7 +504,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
        case NL80211_IFTYPE_ADHOC:
                if (wdev->current_bss) {
                        *chan = wdev->current_bss->pub.channel;
-                       *chanmode = wdev->ibss_fixed
+                       *chanmode = (wdev->ibss_fixed &&
+                                    !wdev->ibss_dfs_possible)
                                  ? CHAN_MODE_SHARED
                                  : CHAN_MODE_EXCLUSIVE;
                        return;
index 403fe29c024db86f732b0c0d74b8d97d4cfc84bf..9d797df56649c5a47fdf1f61e665ee31e4b77e7c 100644 (file)
@@ -83,6 +83,8 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
                         struct cfg80211_cached_keys *connkeys)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct ieee80211_channel *check_chan;
+       u8 radar_detect_width = 0;
        int err;
 
        ASSERT_WDEV_LOCK(wdev);
@@ -114,14 +116,28 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
        wdev->connect_keys = connkeys;
 
        wdev->ibss_fixed = params->channel_fixed;
+       wdev->ibss_dfs_possible = params->userspace_handles_dfs;
 #ifdef CONFIG_CFG80211_WEXT
        wdev->wext.ibss.chandef = params->chandef;
 #endif
+       check_chan = params->chandef.chan;
+       if (params->userspace_handles_dfs) {
+               /* use channel NULL to check for radar even if the current
+                * channel is not a radar channel - it might decide to change
+                * to DFS channel later.
+                */
+               radar_detect_width = BIT(params->chandef.width);
+               check_chan = NULL;
+       }
+
+       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+                                          check_chan,
+                                          (params->channel_fixed &&
+                                           !radar_detect_width)
+                                          ? CHAN_MODE_SHARED
+                                          : CHAN_MODE_EXCLUSIVE,
+                                          radar_detect_width);
 
-       err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
-                                   params->channel_fixed
-                                   ? CHAN_MODE_SHARED
-                                   : CHAN_MODE_EXCLUSIVE);
        if (err) {
                wdev->connect_keys = NULL;
                return err;
index 8d49c1ce3deacbbb7ec9fe534e081ef7953607f5..6a6b1c8e907df93e73b44a8912a3ae1fda5f80a5 100644 (file)
@@ -707,11 +707,13 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
                        if (c->dfs_state != NL80211_DFS_UNAVAILABLE)
                                continue;
 
-                       timeout = c->dfs_state_entered +
-                                 IEEE80211_DFS_MIN_NOP_TIME_MS;
+                       timeout = c->dfs_state_entered + msecs_to_jiffies(
+                                       IEEE80211_DFS_MIN_NOP_TIME_MS);
 
                        if (time_after_eq(jiffies, timeout)) {
                                c->dfs_state = NL80211_DFS_USABLE;
+                               c->dfs_state_entered = jiffies;
+
                                cfg80211_chandef_create(&chandef, c,
                                                        NL80211_CHAN_NO_HT);
 
index cbbef88a8ebd125fc4797e4493968e5c67108771..a7f4e7902104907adf86c9fffcfc0ab956d36095 100644 (file)
@@ -354,6 +354,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
        [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
        [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
+       [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
+       [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
+       [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -3896,9 +3899,45 @@ static int nl80211_parse_sta_wme(struct genl_info *info,
        return 0;
 }
 
+static int nl80211_parse_sta_channel_info(struct genl_info *info,
+                                     struct station_parameters *params)
+{
+       if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) {
+               params->supported_channels =
+                    nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+               params->supported_channels_len =
+                    nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+               /*
+                * Need to include at least one (first channel, number of
+                * channels) tuple for each subband, and must have proper
+                * tuples for the rest of the data as well.
+                */
+               if (params->supported_channels_len < 2)
+                       return -EINVAL;
+               if (params->supported_channels_len % 2)
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) {
+               params->supported_oper_classes =
+                nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+               params->supported_oper_classes_len =
+                 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+               /*
+                * The value of the Length field of the Supported Operating
+                * Classes element is between 2 and 253.
+                */
+               if (params->supported_oper_classes_len < 2 ||
+                   params->supported_oper_classes_len > 253)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
 static int nl80211_set_station_tdls(struct genl_info *info,
                                    struct station_parameters *params)
 {
+       int err;
        /* Dummy STA entry gets updated once the peer capabilities are known */
        if (info->attrs[NL80211_ATTR_PEER_AID])
                params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
@@ -3909,6 +3948,10 @@ static int nl80211_set_station_tdls(struct genl_info *info,
                params->vht_capa =
                        nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
 
+       err = nl80211_parse_sta_channel_info(info, params);
+       if (err)
+               return err;
+
        return nl80211_parse_sta_wme(info, params);
 }
 
@@ -4089,6 +4132,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
        }
 
+       err = nl80211_parse_sta_channel_info(info, &params);
+       if (err)
+               return err;
+
        err = nl80211_parse_sta_wme(info, &params);
        if (err)
                return err;
@@ -5653,6 +5700,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
                break;
        case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
                break;
        default:
                return -EOPNOTSUPP;
@@ -5665,9 +5713,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
 
        /* only important for AP, IBSS and mesh create IEs internally */
-       if (need_new_beacon &&
-           (!info->attrs[NL80211_ATTR_CSA_IES] ||
-            !info->attrs[NL80211_ATTR_CSA_C_OFF_BEACON]))
+       if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
                return -EINVAL;
 
        params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
@@ -5722,9 +5768,9 @@ skip_beacons:
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
                return -EINVAL;
 
-       /* DFS channels are only supported for AP/P2P GO ... for now. */
        if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
-           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
+           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO ||
+           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC) {
                err = cfg80211_chandef_dfs_required(wdev->wiphy,
                                                    &params.chandef);
                if (err < 0) {
@@ -6556,6 +6602,9 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        ibss.control_port =
                nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
 
+       ibss.userspace_handles_dfs =
+               nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
+
        err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
        if (err)
                kfree(connkeys);
@@ -10762,7 +10811,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
 
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
                    wdev->iftype != NL80211_IFTYPE_P2P_GO &&
-                   wdev->iftype != NL80211_IFTYPE_ADHOC))
+                   wdev->iftype != NL80211_IFTYPE_ADHOC &&
+                   wdev->iftype != NL80211_IFTYPE_MESH_POINT))
                goto out;
 
        wdev->channel = chandef->chan;
index a0ec143ba3dc3593f94fb71a25d649321af9aced..7da67fd0b4188a7cf3e3ded0ae2a64d565274947 100644 (file)
@@ -787,7 +787,6 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
 EXPORT_SYMBOL(reg_initiator_name);
 
 #ifdef CONFIG_CFG80211_REG_DEBUG
-
 static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
                                    const struct ieee80211_reg_rule *reg_rule)
 {
@@ -974,6 +973,13 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
 }
 #endif
 
+static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy)
+{
+       if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY &&
+           !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY))
+               return true;
+       return false;
+}
 
 static bool ignore_reg_update(struct wiphy *wiphy,
                              enum nl80211_reg_initiator initiator)
@@ -1000,7 +1006,7 @@ static bool ignore_reg_update(struct wiphy *wiphy,
         * wiphy->regd will be set once the device has its own
         * desired regulatory domain set
         */
-       if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd &&
+       if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd &&
            initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
            !is_world_regdom(lr->alpha2)) {
                REG_DBG_PRINT("Ignoring regulatory request set by %s "
@@ -1706,8 +1712,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
 }
 EXPORT_SYMBOL(regulatory_hint);
 
-void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
-                        const u8 *country_ie, u8 country_ie_len)
+void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band,
+                               const u8 *country_ie, u8 country_ie_len)
 {
        char alpha2[2];
        enum environment_cap env = ENVIRON_ANY;
index af2d5f8a5d828481e4036cba15d7577674b0e303..9677e3c13da98da5b00f34e112b0562512b6fc65 100644 (file)
@@ -58,7 +58,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
                                 gfp_t gfp);
 
 /**
- * regulatory_hint_11d - hints a country IE as a regulatory domain
+ * regulatory_hint_country_ie - hints a country IE as a regulatory domain
  * @wiphy: the wireless device giving the hint (used only for reporting
  *     conflicts)
  * @band: the band on which the country IE was received on. This determines
@@ -78,7 +78,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
  * not observed. For this reason if a triplet is seen with channel
  * information for a band the BSS is not present in it will be ignored.
  */
-void regulatory_hint_11d(struct wiphy *wiphy,
+void regulatory_hint_country_ie(struct wiphy *wiphy,
                         enum ieee80211_band band,
                         const u8 *country_ie,
                         u8 country_ie_len);
index eeb71480f1af2cecb575780c29dff0c15de3f197..d4397eba5408ea4325ac3404a251b8617d878309 100644 (file)
@@ -254,10 +254,10 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
        rdev = container_of(wk, struct cfg80211_registered_device,
                            sched_scan_results_wk);
 
-       request = rdev->sched_scan_req;
-
        rtnl_lock();
 
+       request = rdev->sched_scan_req;
+
        /* we don't have sched_scan_req anymore if the scan is stopping */
        if (request) {
                if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
index 20e86a95dc4e0ed358485f04208c670297ee6517..65f800890d70d857c9b42d379caad887f5a98c74 100644 (file)
@@ -682,8 +682,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
         * - country_ie + 2, the start of the country ie data, and
         * - and country_ie[1] which is the IE length
         */
-       regulatory_hint_11d(wdev->wiphy, bss->channel->band,
-                           country_ie + 2, country_ie[1]);
+       regulatory_hint_country_ie(wdev->wiphy, bss->channel->band,
+                                  country_ie + 2, country_ie[1]);
        kfree(country_ie);
 }
 
index 3c8be6104ba407a9d0b5d997d8c2fe4d659a63e4..935dea9485da01f7a0fb82cacc6e187058ff1ce0 100644 (file)
@@ -1249,7 +1249,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        enum cfg80211_chan_mode chmode;
        int num_different_channels = 0;
        int total = 1;
-       bool radar_required;
+       bool radar_required = false;
        int i, j;
 
        ASSERT_RTNL();
@@ -1264,14 +1264,20 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_WDS:
-               radar_required = !!(chan &&
-                                   (chan->flags & IEEE80211_CHAN_RADAR));
+               /* if the interface could potentially choose a DFS channel,
+                * then mark DFS as required.
+                */
+               if (!chan) {
+                       if (chanmode != CHAN_MODE_UNDEFINED && radar_detect)
+                               radar_required = true;
+                       break;
+               }
+               radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR);
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_DEVICE:
        case NL80211_IFTYPE_MONITOR:
-               radar_required = false;
                break;
        case NUM_NL80211_IFTYPES:
        case NL80211_IFTYPE_UNSPECIFIED: