Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
This commit is contained in:
commit
c1f3bb6bd3
|
@ -152,8 +152,8 @@
|
|||
!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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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 };
|
||||
|
||||
fw = iwl_get_ucode_image(priv, ucode_type);
|
||||
if (WARN_ON(!fw))
|
||||
return -EINVAL;
|
||||
|
||||
old_type = priv->cur_ucode;
|
||||
priv->cur_ucode = ucode_type;
|
||||
fw = iwl_get_ucode_image(priv, 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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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)++;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
static void hci_conn_auto_accept(struct work_struct *work)
|
||||
{
|
||||
struct hci_conn *conn = (void *) arg;
|
||||
struct hci_conn *conn = container_of(work, struct hci_conn,
|
||||
auto_accept_work.work);
|
||||
|
||||
BT_DBG("hcon %p mode %d", conn, conn->mode);
|
||||
|
||||
hci_conn_enter_sniff_mode(conn);
|
||||
}
|
||||
|
||||
static void hci_conn_auto_accept(unsigned long arg)
|
||||
{
|
||||
struct hci_conn *conn = (void *) arg;
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
||||
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 */
|
||||
|
|
|
@ -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",
|
||||
<k->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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
lock_sock(parent);
|
||||
/* If device is blocked, do not create a channel for it */
|
||||
if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, dst_type))
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
@ -17,134 +19,75 @@
|
|||
#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;
|
||||
struct scatterlist assoc, pt, ct[2];
|
||||
struct {
|
||||
struct aead_request req;
|
||||
u8 priv[crypto_aead_reqsize(tfm)];
|
||||
} aead_req;
|
||||
|
||||
b_0 = scratch + 3 * AES_BLOCK_SIZE;
|
||||
aad = scratch + 4 * AES_BLOCK_SIZE;
|
||||
b = scratch;
|
||||
s_0 = scratch + AES_BLOCK_SIZE;
|
||||
memset(&aead_req, 0, sizeof(aead_req));
|
||||
|
||||
crypto_cipher_encrypt_one(tfm, b, b_0);
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
|
||||
aad += AES_BLOCK_SIZE;
|
||||
|
||||
for (i = 0; i < AES_BLOCK_SIZE; i++)
|
||||
aad[i] ^= b[i];
|
||||
crypto_cipher_encrypt_one(tfm, a, aad);
|
||||
|
||||
/* 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;
|
||||
struct scatterlist assoc, pt, ct[2];
|
||||
struct {
|
||||
struct aead_request req;
|
||||
u8 priv[crypto_aead_reqsize(tfm)];
|
||||
} aead_req;
|
||||
|
||||
b = scratch;
|
||||
s_0 = scratch + AES_BLOCK_SIZE;
|
||||
e = scratch + 2 * AES_BLOCK_SIZE;
|
||||
b_0 = scratch + 3 * AES_BLOCK_SIZE;
|
||||
memset(&aead_req, 0, sizeof(aead_req));
|
||||
|
||||
num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
|
||||
last_len = data_len % AES_BLOCK_SIZE;
|
||||
aes_ccm_prepare(tfm, scratch, b);
|
||||
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);
|
||||
|
||||
/* 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;
|
||||
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);
|
||||
|
||||
/* 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];
|
||||
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;
|
||||
struct crypto_aead *tfm;
|
||||
int err;
|
||||
|
||||
b = scratch;
|
||||
s_0 = scratch + AES_BLOCK_SIZE;
|
||||
a = scratch + 2 * AES_BLOCK_SIZE;
|
||||
b_0 = scratch + 3 * AES_BLOCK_SIZE;
|
||||
tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(tfm))
|
||||
return tfm;
|
||||
|
||||
num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
|
||||
last_len = data_len % AES_BLOCK_SIZE;
|
||||
aes_ccm_prepare(tfm, scratch, a);
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
crypto_free_aead(tfm);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
|
||||
struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[])
|
||||
void ieee80211_aes_key_free(struct crypto_aead *tfm)
|
||||
{
|
||||
struct crypto_cipher *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;
|
||||
}
|
||||
|
||||
|
||||
void ieee80211_aes_key_free(struct crypto_cipher *tfm)
|
||||
{
|
||||
crypto_free_cipher(tfm);
|
||||
crypto_free_aead(tfm);
|
||||
}
|
||||
|
|
|
@ -12,13 +12,11 @@
|
|||
|
||||
#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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,12 @@
|
|||
#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__)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(¶ms, 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,
|
||||
¶ms.count, &mode,
|
||||
¶ms.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, ¶ms.chandef,
|
||||
IEEE80211_CHAN_DISABLED)) {
|
||||
if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, ¶ms.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, ¶ms);
|
||||
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, ¶ms.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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(¶ms, 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, ¶ms.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,
|
||||
¶ms.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, ¶ms, 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, ¶ms.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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -24,27 +24,23 @@
|
|||
#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
|
||||
};
|
||||
|
||||
#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_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;
|
||||
const char *name;
|
||||
enum rfkill_type type;
|
||||
int reset_gpio;
|
||||
int shutdown_gpio;
|
||||
|
||||
struct rfkill *rfkill_dev;
|
||||
char *reset_name;
|
||||
char *shutdown_name;
|
||||
struct clk *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(rfkill->name);
|
||||
rfkill->reset_name = devm_kzalloc(&pdev->dev, len + 7, GFP_KERNEL);
|
||||
if (!rfkill->reset_name)
|
||||
return -ENOMEM;
|
||||
|
||||
len = strlen(pdata->name);
|
||||
rfkill->reset_name = kzalloc(len + 7, GFP_KERNEL);
|
||||
if (!rfkill->reset_name) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_alloc;
|
||||
}
|
||||
rfkill->shutdown_name = devm_kzalloc(&pdev->dev, len + 10, GFP_KERNEL);
|
||||
if (!rfkill->shutdown_name)
|
||||
return -ENOMEM;
|
||||
|
||||
rfkill->shutdown_name = kzalloc(len + 10, GFP_KERNEL);
|
||||
if (!rfkill->shutdown_name) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_reset_name;
|
||||
}
|
||||
snprintf(rfkill->reset_name, len + 6 , "%s_reset", rfkill->name);
|
||||
snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", rfkill->name);
|
||||
|
||||
snprintf(rfkill->reset_name, len + 6 , "%s_reset", pdata->name);
|
||||
snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", pdata->name);
|
||||
rfkill->clk = devm_clk_get(&pdev->dev, clk_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;
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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, ¶ms);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = nl80211_parse_sta_wme(info, ¶ms);
|
||||
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, ¶ms.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,
|
||||
¶ms.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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue