Skip to content

Commit

Permalink
ath10k: support NET_DETECT WoWLAN feature
Browse files Browse the repository at this point in the history
For WoWLAN support it is expected to support wake up based on discovery of
one or more known SSIDs. This is the WIPHY_WOWLAN_NET_DETECT feature,
which shows up as an NL80211 feature flag.

This shows up in 'iw phy' as:

WoWLAN support:
* wake up on network detection, up to 16 match sets

And it can be enabled with command:

iw phy0 wowlan enable net-detect interval 5000 delay 30 freqs 2412 matches ssid foo

Firmware will do scan by the configured parameters after suspend and
wakeup if it found matched SSIDs. Tested with QCA6174 hw3.0 with
firmware WLAN.RM.4.4.1-00110-QCARMSWPZ-1.

Signed-off-by: Wen Gong <wgong@codeaurora.org>
[kvalo@codeaurora.org: fix lots of endian bugs, whitespace, commit log and style cleanup]
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
  • Loading branch information
Wen Gong authored and Kalle Valo committed Oct 13, 2018
1 parent f115769 commit ce834e2
Show file tree
Hide file tree
Showing 7 changed files with 700 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/net/wireless/ath/ath10k/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,7 @@ struct ath10k {
/* protected by conf_mutex */
u8 ps_state_enable;

bool nlo_enabled;
bool p2p;

struct {
Expand Down
12 changes: 12 additions & 0 deletions drivers/net/wireless/ath/ath10k/mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -8501,6 +8501,18 @@ int ath10k_mac_register(struct ath10k *ar)
ar->hw->wiphy->max_scan_ssids = WLAN_SCAN_PARAMS_MAX_SSID;
ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN;

if (test_bit(WMI_SERVICE_NLO, ar->wmi.svc_map)) {
ar->hw->wiphy->max_sched_scan_reqs = 1;
ar->hw->wiphy->max_sched_scan_ssids = WMI_PNO_MAX_SUPP_NETWORKS;
ar->hw->wiphy->max_match_sets = WMI_PNO_MAX_SUPP_NETWORKS;
ar->hw->wiphy->max_sched_scan_ie_len = WMI_PNO_MAX_IE_LENGTH;
ar->hw->wiphy->max_sched_scan_plans = WMI_PNO_MAX_SCHED_SCAN_PLANS;
ar->hw->wiphy->max_sched_scan_plan_interval =
WMI_PNO_MAX_SCHED_SCAN_PLAN_INT;
ar->hw->wiphy->max_sched_scan_plan_iterations =
WMI_PNO_MAX_SCHED_SCAN_PLAN_ITRNS;
}

ar->hw->vif_data_size = sizeof(struct ath10k_vif);
ar->hw->sta_data_size = sizeof(struct ath10k_sta);
ar->hw->txq_data_size = sizeof(struct ath10k_txq);
Expand Down
21 changes: 21 additions & 0 deletions drivers/net/wireless/ath/ath10k/wmi-ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@ struct wmi_ops {
u32 fw_feature_bitmap);
int (*get_vdev_subtype)(struct ath10k *ar,
enum wmi_vdev_subtype subtype);
struct sk_buff *(*gen_wow_config_pno)(struct ath10k *ar,
u32 vdev_id,
struct wmi_pno_scan_req *pno_scan);
struct sk_buff *(*gen_pdev_bss_chan_info_req)
(struct ath10k *ar,
enum wmi_bss_survey_req_type type);
Expand Down Expand Up @@ -1360,6 +1363,24 @@ ath10k_wmi_wow_del_pattern(struct ath10k *ar, u32 vdev_id, u32 pattern_id)
return ath10k_wmi_cmd_send(ar, skb, cmd_id);
}

static inline int
ath10k_wmi_wow_config_pno(struct ath10k *ar, u32 vdev_id,
struct wmi_pno_scan_req *pno_scan)
{
struct sk_buff *skb;
u32 cmd_id;

if (!ar->wmi.ops->gen_wow_config_pno)
return -EOPNOTSUPP;

skb = ar->wmi.ops->gen_wow_config_pno(ar, vdev_id, pno_scan);
if (IS_ERR(skb))
return PTR_ERR(skb);

cmd_id = ar->wmi.cmd->network_list_offload_config_cmdid;
return ath10k_wmi_cmd_send(ar, skb, cmd_id);
}

static inline int
ath10k_wmi_update_fw_tdls_state(struct ath10k *ar, u32 vdev_id,
enum wmi_tdls_state state)
Expand Down
187 changes: 187 additions & 0 deletions drivers/net/wireless/ath/ath10k/wmi-tlv.c
Original file line number Diff line number Diff line change
Expand Up @@ -3441,6 +3441,192 @@ ath10k_wmi_tlv_op_gen_wow_del_pattern(struct ath10k *ar, u32 vdev_id,
return skb;
}

/* Request FW to start PNO operation */
static struct sk_buff *
ath10k_wmi_tlv_op_gen_config_pno_start(struct ath10k *ar,
u32 vdev_id,
struct wmi_pno_scan_req *pno)
{
struct nlo_configured_parameters *nlo_list;
struct wmi_tlv_wow_nlo_config_cmd *cmd;
struct wmi_tlv *tlv;
struct sk_buff *skb;
__le32 *channel_list;
u16 tlv_len;
size_t len;
void *ptr;
u32 i;

len = sizeof(*tlv) + sizeof(*cmd) +
sizeof(*tlv) +
/* TLV place holder for array of structures
* nlo_configured_parameters(nlo_list)
*/
sizeof(*tlv);
/* TLV place holder for array of uint32 channel_list */

len += sizeof(u32) * min_t(u8, pno->a_networks[0].channel_count,
WMI_NLO_MAX_CHAN);
len += sizeof(struct nlo_configured_parameters) *
min_t(u8, pno->uc_networks_count, WMI_NLO_MAX_SSIDS);

skb = ath10k_wmi_alloc_skb(ar, len);
if (!skb)
return ERR_PTR(-ENOMEM);

ptr = (void *)skb->data;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_NLO_CONFIG_CMD);
tlv->len = __cpu_to_le16(sizeof(*cmd));
cmd = (void *)tlv->value;

/* wmi_tlv_wow_nlo_config_cmd parameters*/
cmd->vdev_id = __cpu_to_le32(pno->vdev_id);
cmd->flags = __cpu_to_le32(WMI_NLO_CONFIG_START | WMI_NLO_CONFIG_SSID_HIDE_EN);

/* current FW does not support min-max range for dwell time */
cmd->active_dwell_time = __cpu_to_le32(pno->active_max_time);
cmd->passive_dwell_time = __cpu_to_le32(pno->passive_max_time);

if (pno->do_passive_scan)
cmd->flags |= __cpu_to_le32(WMI_NLO_CONFIG_SCAN_PASSIVE);

/* copy scan interval */
cmd->fast_scan_period = __cpu_to_le32(pno->fast_scan_period);
cmd->slow_scan_period = __cpu_to_le32(pno->slow_scan_period);
cmd->fast_scan_max_cycles = __cpu_to_le32(pno->fast_scan_max_cycles);
cmd->delay_start_time = __cpu_to_le32(pno->delay_start_time);

if (pno->enable_pno_scan_randomization) {
cmd->flags |= __cpu_to_le32(WMI_NLO_CONFIG_SPOOFED_MAC_IN_PROBE_REQ |
WMI_NLO_CONFIG_RANDOM_SEQ_NO_IN_PROBE_REQ);
ether_addr_copy(cmd->mac_addr.addr, pno->mac_addr);
ether_addr_copy(cmd->mac_mask.addr, pno->mac_addr_mask);
}

ptr += sizeof(*tlv);
ptr += sizeof(*cmd);

/* nlo_configured_parameters(nlo_list) */
cmd->no_of_ssids = __cpu_to_le32(min_t(u8, pno->uc_networks_count,
WMI_NLO_MAX_SSIDS));
tlv_len = __le32_to_cpu(cmd->no_of_ssids) *
sizeof(struct nlo_configured_parameters);

tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
tlv->len = __cpu_to_le16(len);

ptr += sizeof(*tlv);
nlo_list = ptr;
for (i = 0; i < __le32_to_cpu(cmd->no_of_ssids); i++) {
tlv = (struct wmi_tlv *)(&nlo_list[i].tlv_header);
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
tlv->len = __cpu_to_le16(sizeof(struct nlo_configured_parameters) -
sizeof(*tlv));

/* copy ssid and it's length */
nlo_list[i].ssid.valid = __cpu_to_le32(true);
nlo_list[i].ssid.ssid.ssid_len = pno->a_networks[i].ssid.ssid_len;
memcpy(nlo_list[i].ssid.ssid.ssid,
pno->a_networks[i].ssid.ssid,
__le32_to_cpu(nlo_list[i].ssid.ssid.ssid_len));

/* copy rssi threshold */
if (pno->a_networks[i].rssi_threshold &&
pno->a_networks[i].rssi_threshold > -300) {
nlo_list[i].rssi_cond.valid = __cpu_to_le32(true);
nlo_list[i].rssi_cond.rssi =
__cpu_to_le32(pno->a_networks[i].rssi_threshold);
}

nlo_list[i].bcast_nw_type.valid = __cpu_to_le32(true);
nlo_list[i].bcast_nw_type.bcast_nw_type =
__cpu_to_le32(pno->a_networks[i].bcast_nw_type);
}

ptr += __le32_to_cpu(cmd->no_of_ssids) * sizeof(struct nlo_configured_parameters);

/* copy channel info */
cmd->num_of_channels = __cpu_to_le32(min_t(u8,
pno->a_networks[0].channel_count,
WMI_NLO_MAX_CHAN));

tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_UINT32);
tlv->len = __cpu_to_le16(__le32_to_cpu(cmd->num_of_channels) *
sizeof(u_int32_t));
ptr += sizeof(*tlv);

channel_list = (__le32 *)ptr;
for (i = 0; i < __le32_to_cpu(cmd->num_of_channels); i++)
channel_list[i] = __cpu_to_le32(pno->a_networks[0].channels[i]);

ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv start pno config vdev_id %d\n",
vdev_id);

return skb;
}

/* Request FW to stop ongoing PNO operation */
static struct sk_buff *ath10k_wmi_tlv_op_gen_config_pno_stop(struct ath10k *ar,
u32 vdev_id)
{
struct wmi_tlv_wow_nlo_config_cmd *cmd;
struct wmi_tlv *tlv;
struct sk_buff *skb;
void *ptr;
size_t len;

len = sizeof(*tlv) + sizeof(*cmd) +
sizeof(*tlv) +
/* TLV place holder for array of structures
* nlo_configured_parameters(nlo_list)
*/
sizeof(*tlv);
/* TLV place holder for array of uint32 channel_list */
skb = ath10k_wmi_alloc_skb(ar, len);
if (!skb)
return ERR_PTR(-ENOMEM);

ptr = (void *)skb->data;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_NLO_CONFIG_CMD);
tlv->len = __cpu_to_le16(sizeof(*cmd));
cmd = (void *)tlv->value;

cmd->vdev_id = __cpu_to_le32(vdev_id);
cmd->flags = __cpu_to_le32(WMI_NLO_CONFIG_STOP);

ptr += sizeof(*tlv);
ptr += sizeof(*cmd);

/* nlo_configured_parameters(nlo_list) */
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
tlv->len = __cpu_to_le16(0);

ptr += sizeof(*tlv);

/* channel list */
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_UINT32);
tlv->len = __cpu_to_le16(0);

ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv stop pno config vdev_id %d\n", vdev_id);
return skb;
}

static struct sk_buff *
ath10k_wmi_tlv_op_gen_config_pno(struct ath10k *ar, u32 vdev_id,
struct wmi_pno_scan_req *pno_scan)
{
if (pno_scan->enable)
return ath10k_wmi_tlv_op_gen_config_pno_start(ar, vdev_id, pno_scan);
else
return ath10k_wmi_tlv_op_gen_config_pno_stop(ar, vdev_id);
}

static struct sk_buff *
ath10k_wmi_tlv_op_gen_adaptive_qcs(struct ath10k *ar, bool enable)
{
Expand Down Expand Up @@ -3973,6 +4159,7 @@ static const struct wmi_ops wmi_tlv_ops = {
.gen_wow_host_wakeup_ind = ath10k_wmi_tlv_gen_wow_host_wakeup_ind,
.gen_wow_add_pattern = ath10k_wmi_tlv_op_gen_wow_add_pattern,
.gen_wow_del_pattern = ath10k_wmi_tlv_op_gen_wow_del_pattern,
.gen_wow_config_pno = ath10k_wmi_tlv_op_gen_config_pno,
.gen_update_fw_tdls_state = ath10k_wmi_tlv_op_gen_update_fw_tdls_state,
.gen_tdls_peer_update = ath10k_wmi_tlv_op_gen_tdls_peer_update,
.gen_adaptive_qcs = ath10k_wmi_tlv_op_gen_adaptive_qcs,
Expand Down
Loading

0 comments on commit ce834e2

Please sign in to comment.