diff options
author | Sergey Matyukevich <sergey.matyukevich.os@quantenna.com> | 2018-08-02 06:40:43 -0400 |
---|---|---|
committer | Kalle Valo <kvalo@codeaurora.org> | 2018-08-02 14:55:49 -0400 |
commit | 28b9188483908b2579fc4bbb2ec07e9ffdca69f7 (patch) | |
tree | 053903b6e6515685df4481d767ad50bd05fa17d7 | |
parent | a33ce21e2afcdc29b749f74a5ec489595c417907 (diff) |
qtnfmac: implement basic WoWLAN support
This patch implements basic WoWLAN support in qtnfmac driver, including
processing of WoWLAN features reported by firmware and implementation
of cfg80211 suspend/resume/wakeup callbacks. Currently the following
WoWLAN triggers are supported: disconnect, magic packet,
custom pattern packet.
Signed-off-by: Sergey Matyukevich <sergey.matyukevich.os@quantenna.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
-rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 76 | ||||
-rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/commands.c | 112 | ||||
-rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/commands.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/core.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/core.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/qlink.h | 56 |
6 files changed, 248 insertions, 0 deletions
diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index 0032fa9fdddd..4aa332f4646b 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | |||
@@ -859,6 +859,72 @@ static int qtnf_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, | |||
859 | return ret; | 859 | return ret; |
860 | } | 860 | } |
861 | 861 | ||
862 | #ifdef CONFIG_PM | ||
863 | static int qtnf_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wowlan) | ||
864 | { | ||
865 | struct qtnf_wmac *mac = wiphy_priv(wiphy); | ||
866 | struct qtnf_vif *vif; | ||
867 | int ret = 0; | ||
868 | |||
869 | vif = qtnf_mac_get_base_vif(mac); | ||
870 | if (!vif) { | ||
871 | pr_err("MAC%u: primary VIF is not configured\n", mac->macid); | ||
872 | ret = -EFAULT; | ||
873 | goto exit; | ||
874 | } | ||
875 | |||
876 | if (!wowlan) { | ||
877 | pr_debug("WoWLAN triggers are not enabled\n"); | ||
878 | qtnf_virtual_intf_cleanup(vif->netdev); | ||
879 | goto exit; | ||
880 | } | ||
881 | |||
882 | qtnf_scan_done(vif->mac, true); | ||
883 | |||
884 | ret = qtnf_cmd_send_wowlan_set(vif, wowlan); | ||
885 | if (ret) { | ||
886 | pr_err("MAC%u: failed to set WoWLAN triggers\n", | ||
887 | mac->macid); | ||
888 | goto exit; | ||
889 | } | ||
890 | |||
891 | exit: | ||
892 | return ret; | ||
893 | } | ||
894 | |||
895 | static int qtnf_resume(struct wiphy *wiphy) | ||
896 | { | ||
897 | struct qtnf_wmac *mac = wiphy_priv(wiphy); | ||
898 | struct qtnf_vif *vif; | ||
899 | int ret = 0; | ||
900 | |||
901 | vif = qtnf_mac_get_base_vif(mac); | ||
902 | if (!vif) { | ||
903 | pr_err("MAC%u: primary VIF is not configured\n", mac->macid); | ||
904 | ret = -EFAULT; | ||
905 | goto exit; | ||
906 | } | ||
907 | |||
908 | ret = qtnf_cmd_send_wowlan_set(vif, NULL); | ||
909 | if (ret) { | ||
910 | pr_err("MAC%u: failed to reset WoWLAN triggers\n", | ||
911 | mac->macid); | ||
912 | goto exit; | ||
913 | } | ||
914 | |||
915 | exit: | ||
916 | return ret; | ||
917 | } | ||
918 | |||
919 | static void qtnf_set_wakeup(struct wiphy *wiphy, bool enabled) | ||
920 | { | ||
921 | struct qtnf_wmac *mac = wiphy_priv(wiphy); | ||
922 | struct qtnf_bus *bus = mac->bus; | ||
923 | |||
924 | device_set_wakeup_enable(bus->dev, enabled); | ||
925 | } | ||
926 | #endif | ||
927 | |||
862 | static struct cfg80211_ops qtn_cfg80211_ops = { | 928 | static struct cfg80211_ops qtn_cfg80211_ops = { |
863 | .add_virtual_intf = qtnf_add_virtual_intf, | 929 | .add_virtual_intf = qtnf_add_virtual_intf, |
864 | .change_virtual_intf = qtnf_change_virtual_intf, | 930 | .change_virtual_intf = qtnf_change_virtual_intf, |
@@ -886,6 +952,11 @@ static struct cfg80211_ops qtn_cfg80211_ops = { | |||
886 | .start_radar_detection = qtnf_start_radar_detection, | 952 | .start_radar_detection = qtnf_start_radar_detection, |
887 | .set_mac_acl = qtnf_set_mac_acl, | 953 | .set_mac_acl = qtnf_set_mac_acl, |
888 | .set_power_mgmt = qtnf_set_power_mgmt, | 954 | .set_power_mgmt = qtnf_set_power_mgmt, |
955 | #ifdef CONFIG_PM | ||
956 | .suspend = qtnf_suspend, | ||
957 | .resume = qtnf_resume, | ||
958 | .set_wakeup = qtnf_set_wakeup, | ||
959 | #endif | ||
889 | }; | 960 | }; |
890 | 961 | ||
891 | static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in, | 962 | static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in, |
@@ -1038,6 +1109,11 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) | |||
1038 | if (hw_info->hw_capab & QLINK_HW_CAPAB_SCAN_RANDOM_MAC_ADDR) | 1109 | if (hw_info->hw_capab & QLINK_HW_CAPAB_SCAN_RANDOM_MAC_ADDR) |
1039 | wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; | 1110 | wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; |
1040 | 1111 | ||
1112 | #ifdef CONFIG_PM | ||
1113 | if (macinfo->wowlan) | ||
1114 | wiphy->wowlan = macinfo->wowlan; | ||
1115 | #endif | ||
1116 | |||
1041 | if (hw_info->hw_capab & QLINK_HW_CAPAB_REG_UPDATE) { | 1117 | if (hw_info->hw_capab & QLINK_HW_CAPAB_REG_UPDATE) { |
1042 | wiphy->regulatory_flags |= REGULATORY_STRICT_REG | | 1118 | wiphy->regulatory_flags |= REGULATORY_STRICT_REG | |
1043 | REGULATORY_CUSTOM_REG; | 1119 | REGULATORY_CUSTOM_REG; |
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index 7942261961d6..ae9e77300533 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c | |||
@@ -1138,6 +1138,37 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus, | |||
1138 | return 0; | 1138 | return 0; |
1139 | } | 1139 | } |
1140 | 1140 | ||
1141 | static void | ||
1142 | qtnf_parse_wowlan_info(struct qtnf_wmac *mac, | ||
1143 | const struct qlink_wowlan_capab_data *wowlan) | ||
1144 | { | ||
1145 | struct qtnf_mac_info *mac_info = &mac->macinfo; | ||
1146 | const struct qlink_wowlan_support *data1; | ||
1147 | struct wiphy_wowlan_support *supp; | ||
1148 | |||
1149 | supp = kzalloc(sizeof(*supp), GFP_KERNEL); | ||
1150 | if (!supp) | ||
1151 | return; | ||
1152 | |||
1153 | switch (le16_to_cpu(wowlan->version)) { | ||
1154 | case 0x1: | ||
1155 | data1 = (struct qlink_wowlan_support *)wowlan->data; | ||
1156 | |||
1157 | supp->flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT; | ||
1158 | supp->n_patterns = le32_to_cpu(data1->n_patterns); | ||
1159 | supp->pattern_max_len = le32_to_cpu(data1->pattern_max_len); | ||
1160 | supp->pattern_min_len = le32_to_cpu(data1->pattern_min_len); | ||
1161 | |||
1162 | mac_info->wowlan = supp; | ||
1163 | break; | ||
1164 | default: | ||
1165 | pr_warn("MAC%u: unsupported WoWLAN version 0x%x\n", | ||
1166 | mac->macid, le16_to_cpu(wowlan->version)); | ||
1167 | kfree(supp); | ||
1168 | break; | ||
1169 | } | ||
1170 | } | ||
1171 | |||
1141 | static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, | 1172 | static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, |
1142 | const u8 *tlv_buf, size_t tlv_buf_size) | 1173 | const u8 *tlv_buf, size_t tlv_buf_size) |
1143 | { | 1174 | { |
@@ -1147,6 +1178,7 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, | |||
1147 | const struct qlink_iface_comb_num *comb_num; | 1178 | const struct qlink_iface_comb_num *comb_num; |
1148 | const struct qlink_iface_limit_record *rec; | 1179 | const struct qlink_iface_limit_record *rec; |
1149 | const struct qlink_iface_limit *lim; | 1180 | const struct qlink_iface_limit *lim; |
1181 | const struct qlink_wowlan_capab_data *wowlan; | ||
1150 | u16 rec_len; | 1182 | u16 rec_len; |
1151 | u16 tlv_type; | 1183 | u16 tlv_type; |
1152 | u16 tlv_value_len; | 1184 | u16 tlv_value_len; |
@@ -1255,7 +1287,31 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, | |||
1255 | ext_capa_mask = (u8 *)tlv->val; | 1287 | ext_capa_mask = (u8 *)tlv->val; |
1256 | ext_capa_mask_len = tlv_value_len; | 1288 | ext_capa_mask_len = tlv_value_len; |
1257 | break; | 1289 | break; |
1290 | case QTN_TLV_ID_WOWLAN_CAPAB: | ||
1291 | if (tlv_value_len < sizeof(*wowlan)) | ||
1292 | return -EINVAL; | ||
1293 | |||
1294 | wowlan = (void *)tlv->val; | ||
1295 | if (!le16_to_cpu(wowlan->len)) { | ||
1296 | pr_warn("MAC%u: skip empty WoWLAN data\n", | ||
1297 | mac->macid); | ||
1298 | break; | ||
1299 | } | ||
1300 | |||
1301 | rec_len = sizeof(*wowlan) + le16_to_cpu(wowlan->len); | ||
1302 | if (unlikely(tlv_value_len != rec_len)) { | ||
1303 | pr_warn("MAC%u: WoWLAN data size mismatch\n", | ||
1304 | mac->macid); | ||
1305 | return -EINVAL; | ||
1306 | } | ||
1307 | |||
1308 | kfree(mac->macinfo.wowlan); | ||
1309 | mac->macinfo.wowlan = NULL; | ||
1310 | qtnf_parse_wowlan_info(mac, wowlan); | ||
1311 | break; | ||
1258 | default: | 1312 | default: |
1313 | pr_warn("MAC%u: unknown TLV type %u\n", | ||
1314 | mac->macid, tlv_type); | ||
1259 | break; | 1315 | break; |
1260 | } | 1316 | } |
1261 | 1317 | ||
@@ -2831,3 +2887,59 @@ out: | |||
2831 | qtnf_bus_unlock(bus); | 2887 | qtnf_bus_unlock(bus); |
2832 | return ret; | 2888 | return ret; |
2833 | } | 2889 | } |
2890 | |||
2891 | int qtnf_cmd_send_wowlan_set(const struct qtnf_vif *vif, | ||
2892 | const struct cfg80211_wowlan *wowl) | ||
2893 | { | ||
2894 | struct qtnf_bus *bus = vif->mac->bus; | ||
2895 | struct sk_buff *cmd_skb; | ||
2896 | u16 res_code = QLINK_CMD_RESULT_OK; | ||
2897 | struct qlink_cmd_wowlan_set *cmd; | ||
2898 | u32 triggers = 0; | ||
2899 | int count = 0; | ||
2900 | int ret = 0; | ||
2901 | |||
2902 | cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, | ||
2903 | QLINK_CMD_WOWLAN_SET, sizeof(*cmd)); | ||
2904 | if (!cmd_skb) | ||
2905 | return -ENOMEM; | ||
2906 | |||
2907 | qtnf_bus_lock(bus); | ||
2908 | |||
2909 | cmd = (struct qlink_cmd_wowlan_set *)cmd_skb->data; | ||
2910 | |||
2911 | if (wowl) { | ||
2912 | if (wowl->disconnect) | ||
2913 | triggers |= QLINK_WOWLAN_TRIG_DISCONNECT; | ||
2914 | |||
2915 | if (wowl->magic_pkt) | ||
2916 | triggers |= QLINK_WOWLAN_TRIG_MAGIC_PKT; | ||
2917 | |||
2918 | if (wowl->n_patterns && wowl->patterns) { | ||
2919 | triggers |= QLINK_WOWLAN_TRIG_PATTERN_PKT; | ||
2920 | while (count < wowl->n_patterns) { | ||
2921 | qtnf_cmd_skb_put_tlv_arr(cmd_skb, | ||
2922 | QTN_TLV_ID_WOWLAN_PATTERN, | ||
2923 | wowl->patterns[count].pattern, | ||
2924 | wowl->patterns[count].pattern_len); | ||
2925 | count++; | ||
2926 | } | ||
2927 | } | ||
2928 | } | ||
2929 | |||
2930 | cmd->triggers = cpu_to_le32(triggers); | ||
2931 | |||
2932 | ret = qtnf_cmd_send(bus, cmd_skb, &res_code); | ||
2933 | |||
2934 | if (unlikely(ret)) | ||
2935 | goto out; | ||
2936 | |||
2937 | if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { | ||
2938 | pr_err("cmd exec failed: 0x%.4X\n", res_code); | ||
2939 | ret = -EFAULT; | ||
2940 | } | ||
2941 | |||
2942 | out: | ||
2943 | qtnf_bus_unlock(bus); | ||
2944 | return ret; | ||
2945 | } | ||
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.h b/drivers/net/wireless/quantenna/qtnfmac/commands.h index 03a57e37a665..1ac41156c192 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.h +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.h | |||
@@ -77,5 +77,7 @@ int qtnf_cmd_start_cac(const struct qtnf_vif *vif, | |||
77 | int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif, | 77 | int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif, |
78 | const struct cfg80211_acl_data *params); | 78 | const struct cfg80211_acl_data *params); |
79 | int qtnf_cmd_send_pm_set(const struct qtnf_vif *vif, u8 pm_mode, int timeout); | 79 | int qtnf_cmd_send_pm_set(const struct qtnf_vif *vif, u8 pm_mode, int timeout); |
80 | int qtnf_cmd_send_wowlan_set(const struct qtnf_vif *vif, | ||
81 | const struct cfg80211_wowlan *wowl); | ||
80 | 82 | ||
81 | #endif /* QLINK_COMMANDS_H_ */ | 83 | #endif /* QLINK_COMMANDS_H_ */ |
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c index c318340e1bd5..19abbc4e23e0 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.c +++ b/drivers/net/wireless/quantenna/qtnfmac/core.c | |||
@@ -495,6 +495,7 @@ static void qtnf_core_mac_detach(struct qtnf_bus *bus, unsigned int macid) | |||
495 | qtnf_mac_iface_comb_free(mac); | 495 | qtnf_mac_iface_comb_free(mac); |
496 | kfree(mac->macinfo.extended_capabilities); | 496 | kfree(mac->macinfo.extended_capabilities); |
497 | kfree(mac->macinfo.extended_capabilities_mask); | 497 | kfree(mac->macinfo.extended_capabilities_mask); |
498 | kfree(mac->macinfo.wowlan); | ||
498 | wiphy_free(wiphy); | 499 | wiphy_free(wiphy); |
499 | bus->mac[macid] = NULL; | 500 | bus->mac[macid] = NULL; |
500 | } | 501 | } |
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h index c4808f1ba8b0..a1e338a1f055 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.h +++ b/drivers/net/wireless/quantenna/qtnfmac/core.h | |||
@@ -110,6 +110,7 @@ struct qtnf_mac_info { | |||
110 | u8 *extended_capabilities; | 110 | u8 *extended_capabilities; |
111 | u8 *extended_capabilities_mask; | 111 | u8 *extended_capabilities_mask; |
112 | u8 extended_capabilities_len; | 112 | u8 extended_capabilities_len; |
113 | struct wiphy_wowlan_support *wowlan; | ||
113 | }; | 114 | }; |
114 | 115 | ||
115 | struct qtnf_chan_stats { | 116 | struct qtnf_chan_stats { |
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h index 8fbef67fbbb8..99d37e3efba6 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h | |||
@@ -258,6 +258,7 @@ enum qlink_cmd_type { | |||
258 | QLINK_CMD_CONNECT = 0x0060, | 258 | QLINK_CMD_CONNECT = 0x0060, |
259 | QLINK_CMD_DISCONNECT = 0x0061, | 259 | QLINK_CMD_DISCONNECT = 0x0061, |
260 | QLINK_CMD_PM_SET = 0x0062, | 260 | QLINK_CMD_PM_SET = 0x0062, |
261 | QLINK_CMD_WOWLAN_SET = 0x0063, | ||
261 | }; | 262 | }; |
262 | 263 | ||
263 | /** | 264 | /** |
@@ -694,6 +695,30 @@ struct qlink_cmd_pm_set { | |||
694 | u8 pm_mode; | 695 | u8 pm_mode; |
695 | } __packed; | 696 | } __packed; |
696 | 697 | ||
698 | /** | ||
699 | * enum qlink_wowlan_trigger | ||
700 | * | ||
701 | * @QLINK_WOWLAN_TRIG_DISCONNECT: wakeup on disconnect | ||
702 | * @QLINK_WOWLAN_TRIG_MAGIC_PKT: wakeup on magic packet | ||
703 | * @QLINK_WOWLAN_TRIG_PATTERN_PKT: wakeup on user-defined packet | ||
704 | */ | ||
705 | enum qlink_wowlan_trigger { | ||
706 | QLINK_WOWLAN_TRIG_DISCONNECT = BIT(0), | ||
707 | QLINK_WOWLAN_TRIG_MAGIC_PKT = BIT(1), | ||
708 | QLINK_WOWLAN_TRIG_PATTERN_PKT = BIT(2), | ||
709 | }; | ||
710 | |||
711 | /** | ||
712 | * struct qlink_cmd_wowlan_set - data for QLINK_CMD_WOWLAN_SET command | ||
713 | * | ||
714 | * @triggers: requested bitmask of WoWLAN triggers | ||
715 | */ | ||
716 | struct qlink_cmd_wowlan_set { | ||
717 | struct qlink_cmd chdr; | ||
718 | __le32 triggers; | ||
719 | u8 data[0]; | ||
720 | } __packed; | ||
721 | |||
697 | /* QLINK Command Responses messages related definitions | 722 | /* QLINK Command Responses messages related definitions |
698 | */ | 723 | */ |
699 | 724 | ||
@@ -1122,6 +1147,8 @@ enum qlink_tlv_id { | |||
1122 | QTN_TLV_ID_UBOOT_VER = 0x0407, | 1147 | QTN_TLV_ID_UBOOT_VER = 0x0407, |
1123 | QTN_TLV_ID_RANDOM_MAC_ADDR = 0x0408, | 1148 | QTN_TLV_ID_RANDOM_MAC_ADDR = 0x0408, |
1124 | QTN_TLV_ID_MAX_SCAN_SSIDS = 0x0409, | 1149 | QTN_TLV_ID_MAX_SCAN_SSIDS = 0x0409, |
1150 | QTN_TLV_ID_WOWLAN_CAPAB = 0x0410, | ||
1151 | QTN_TLV_ID_WOWLAN_PATTERN = 0x0411, | ||
1125 | }; | 1152 | }; |
1126 | 1153 | ||
1127 | struct qlink_tlv_hdr { | 1154 | struct qlink_tlv_hdr { |
@@ -1409,4 +1436,33 @@ struct qlink_random_mac_addr { | |||
1409 | u8 mac_addr_mask[ETH_ALEN]; | 1436 | u8 mac_addr_mask[ETH_ALEN]; |
1410 | } __packed; | 1437 | } __packed; |
1411 | 1438 | ||
1439 | /** | ||
1440 | * struct qlink_wowlan_capab_data - data for QTN_TLV_ID_WOWLAN_CAPAB TLV | ||
1441 | * | ||
1442 | * WoWLAN capabilities supported by cards. | ||
1443 | * | ||
1444 | * @version: version of WoWLAN data structure, to ensure backward | ||
1445 | * compatibility for firmwares with limited WoWLAN support | ||
1446 | * @len: Total length of WoWLAN data | ||
1447 | * @data: supported WoWLAN features | ||
1448 | */ | ||
1449 | struct qlink_wowlan_capab_data { | ||
1450 | __le16 version; | ||
1451 | __le16 len; | ||
1452 | u8 data[0]; | ||
1453 | } __packed; | ||
1454 | |||
1455 | /** | ||
1456 | * struct qlink_wowlan_support - supported WoWLAN capabilities | ||
1457 | * | ||
1458 | * @n_patterns: number of supported wakeup patterns | ||
1459 | * @pattern_max_len: maximum length of each pattern | ||
1460 | * @pattern_min_len: minimum length of each pattern | ||
1461 | */ | ||
1462 | struct qlink_wowlan_support { | ||
1463 | __le32 n_patterns; | ||
1464 | __le32 pattern_max_len; | ||
1465 | __le32 pattern_min_len; | ||
1466 | } __packed; | ||
1467 | |||
1412 | #endif /* _QTN_QLINK_H_ */ | 1468 | #endif /* _QTN_QLINK_H_ */ |