Googler | b48fa91 | 2023-03-17 12:40:29 +0530 | [diff] [blame^] | 1 | // SPDX-License-Identifier: ISC |
| 2 | /* |
| 3 | * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. |
| 4 | */ |
| 5 | |
| 6 | #include <net/netlink.h> |
| 7 | #include <net/mac80211.h> |
| 8 | #include "core.h" |
| 9 | #include "debug.h" |
| 10 | |
| 11 | static const struct nla_policy |
| 12 | ath11k_vendor_set_wifi_config_policy[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1] = { |
| 13 | [QCA_WLAN_VENDOR_ATTR_CONFIG_GTX] = {.type = NLA_FLAG} |
| 14 | }; |
| 15 | |
| 16 | static const struct nla_policy |
| 17 | ath11k_cfg80211_afc_event_policy[QCA_WLAN_VENDOR_ATTR_AFC_EVENT_MAX] = { |
| 18 | [QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE] = { .type = NLA_U8 }, |
| 19 | [QCA_WLAN_VENDOR_ATTR_AFC_EVENT_DATA] = |
| 20 | { .type = NLA_BINARY, |
| 21 | .len = QCA_NL80211_AFC_REQ_RESP_BUF_MAX_SIZE }, |
| 22 | }; |
| 23 | |
| 24 | static const struct nla_policy |
| 25 | ath11k_cfg80211_afc_response_policy[QCA_WLAN_VENDOR_ATTR_AFC_RESPONSE_MAX] = { |
| 26 | [QCA_WLAN_VENDOR_ATTR_AFC_RESPONSE_DATA_TYPE] = { .type = NLA_U8 }, |
| 27 | [QCA_WLAN_VENDOR_ATTR_AFC_RESPONSE_DATA] = |
| 28 | { .type = NLA_BINARY, |
| 29 | .len = QCA_NL80211_AFC_REQ_RESP_BUF_MAX_SIZE }, |
| 30 | }; |
| 31 | |
| 32 | static int ath11k_vendor_set_wifi_config(struct wiphy *wihpy, |
| 33 | struct wireless_dev *wdev, |
| 34 | const void *data, |
| 35 | int data_len) |
| 36 | { |
| 37 | struct ieee80211_vif *vif; |
| 38 | struct ath11k_vif *arvif; |
| 39 | struct ath11k *ar; |
| 40 | struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1]; |
| 41 | int ret = 0; |
| 42 | |
| 43 | if (!wdev) |
| 44 | return -EINVAL; |
| 45 | |
| 46 | vif = wdev_to_ieee80211_vif(wdev); |
| 47 | if (!vif) |
| 48 | return -EINVAL; |
| 49 | |
| 50 | arvif = (struct ath11k_vif*)vif->drv_priv; |
| 51 | if (!arvif) |
| 52 | return -EINVAL; |
| 53 | |
| 54 | ar = arvif->ar; |
| 55 | |
| 56 | mutex_lock(&ar->conf_mutex); |
| 57 | |
| 58 | ret = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX, data, data_len, |
| 59 | ath11k_vendor_set_wifi_config_policy, NULL); |
| 60 | if (ret) { |
| 61 | ath11k_warn(ar->ab, "invalid set wifi config policy attribute\n"); |
| 62 | goto exit; |
| 63 | } |
| 64 | |
| 65 | ar->ap_ps_enabled = nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_GTX]); |
| 66 | ret = ath11k_mac_ap_ps_recalc(ar); |
| 67 | if (ret) { |
| 68 | ath11k_warn(ar->ab, "failed to send ap ps ret %d\n", ret); |
| 69 | goto exit; |
| 70 | } |
| 71 | |
| 72 | exit: |
| 73 | mutex_unlock(&ar->conf_mutex); |
| 74 | return ret; |
| 75 | } |
| 76 | |
| 77 | static void ath11k_afc_resp_ntoh_conv(struct ath11k_base *ab, u32 *data, int data_len) |
| 78 | { |
| 79 | int iter = 0; |
| 80 | |
| 81 | if (!data || !data_len) { |
| 82 | ath11k_warn(ab, "Invalid AFC response\n"); |
| 83 | return; |
| 84 | } |
| 85 | |
| 86 | for (iter = 0; iter < data_len; iter++) { |
| 87 | /* Skip endian conversion for description field |
| 88 | * in the AFC response as it is a char array. |
| 89 | */ |
| 90 | if (iter < QCA_WLAN_AFC_RESP_DESC_FIELD_START_OCTET || |
| 91 | iter > QCA_WLAN_AFC_RESP_DESC_FIELD_END_OCTET) { |
| 92 | data[iter] = ntohl(data[iter]); |
| 93 | } |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | static int ath11k_vendor_receive_afc_response(struct wiphy *wihpy, |
| 98 | struct wireless_dev *wdev, |
| 99 | const void *data, |
| 100 | int data_len) |
| 101 | { |
| 102 | struct ieee80211_vif *vif; |
| 103 | struct ath11k_vif *arvif; |
| 104 | struct ath11k *ar; |
| 105 | struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_AFC_RESPONSE_MAX + 1]; |
| 106 | int afc_resp_len = 0, afc_resp_len_32 = 0; |
| 107 | u8 data_type = 0; |
| 108 | struct ath11k_afc_host_resp *afc_rsp = NULL; |
| 109 | int ret = 0; |
| 110 | |
| 111 | if (!wdev) |
| 112 | return -EINVAL; |
| 113 | |
| 114 | vif = wdev_to_ieee80211_vif(wdev); |
| 115 | if (!vif) |
| 116 | return -EINVAL; |
| 117 | |
| 118 | arvif = (struct ath11k_vif *)vif->drv_priv; |
| 119 | if (!arvif) |
| 120 | return -EINVAL; |
| 121 | |
| 122 | ar = arvif->ar; |
| 123 | |
| 124 | ath11k_dbg(ar->ab, ATH11K_DBG_AFC, "Received AFC response event\n"); |
| 125 | |
| 126 | ret = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_AFC_RESPONSE_MAX, data, data_len, |
| 127 | ath11k_cfg80211_afc_event_policy, NULL); |
| 128 | if (ret) { |
| 129 | ath11k_warn(ar->ab, "invalid set afc config policy attribute\n"); |
| 130 | return ret; |
| 131 | } |
| 132 | |
| 133 | if (!tb[QCA_WLAN_VENDOR_ATTR_AFC_RESPONSE_DATA_TYPE]) |
| 134 | return -EINVAL; |
| 135 | |
| 136 | data_type = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_AFC_RESPONSE_DATA_TYPE]); |
| 137 | |
| 138 | if (!tb[QCA_WLAN_VENDOR_ATTR_AFC_RESPONSE_DATA]) { |
| 139 | ath11k_warn(ar->ab, "AFC response data not found\n"); |
| 140 | return -EINVAL; |
| 141 | } |
| 142 | |
| 143 | afc_resp_len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_AFC_RESPONSE_DATA]); |
| 144 | |
| 145 | if (!afc_resp_len) { |
| 146 | ath11k_warn(ar->ab, "AFC response data is not present!\n"); |
| 147 | return -EINVAL; |
| 148 | } |
| 149 | |
| 150 | afc_rsp = kzalloc(afc_resp_len, GFP_KERNEL); |
| 151 | if (!afc_rsp) |
| 152 | return -ENOMEM; |
| 153 | |
| 154 | nla_memcpy((void *)afc_rsp, tb[QCA_WLAN_VENDOR_ATTR_AFC_RESPONSE_DATA], |
| 155 | afc_resp_len); |
| 156 | |
| 157 | switch (data_type) { |
| 158 | case QCA_WLAN_VENDOR_ATTR_AFC_JSON_RESP: |
| 159 | /* No processing required in Driver for JSON data */ |
| 160 | break; |
| 161 | |
| 162 | case QCA_WLAN_VENDOR_ATTR_AFC_BIN_RESP: |
| 163 | /* The AFC response received from the user space application |
| 164 | * is expected to be packed in network byte order(Big endian). |
| 165 | * Since q6 is little endian, Host needs to convert the afc |
| 166 | * response to little endian format. |
| 167 | * |
| 168 | * Note: This conversion of data to little endian format is only |
| 169 | * required for Binary type data. For raw JSON data, |
| 170 | * no conversion is required since it is text string. |
| 171 | * |
| 172 | * Since all the members of the AFC response structure are defined |
| 173 | * to be 32-bit words, convert the length appropriately for |
| 174 | * conversion to little endian format. |
| 175 | */ |
| 176 | afc_resp_len_32 = (afc_resp_len / sizeof(uint32_t)); |
| 177 | ath11k_afc_resp_ntoh_conv(ar->ab, (u32 *)afc_rsp, afc_resp_len_32); |
| 178 | ath11k_dbg_dump(ar->ab, ATH11K_DBG_AFC, NULL, "", afc_rsp, afc_resp_len); |
| 179 | |
| 180 | break; |
| 181 | |
| 182 | default: |
| 183 | ath11k_warn(ar->ab, "Invalid response format type %d\n", data_type); |
| 184 | ret = -EINVAL; |
| 185 | goto exit; |
| 186 | } |
| 187 | |
| 188 | /* Copy the data buffer to AFC memory location */ |
| 189 | ret = ath11k_copy_afc_response(ar, (char *)afc_rsp, afc_resp_len); |
| 190 | if (ret) |
| 191 | goto exit; |
| 192 | |
| 193 | ath11k_dbg(ar->ab, ATH11K_DBG_AFC, "AFC response copied to AFC memory\n"); |
| 194 | |
| 195 | ret = ath11k_wmi_send_afc_resp_rx_ind(ar, data_type); |
| 196 | if (ret) { |
| 197 | ath11k_warn(ar->ab, "AFC Rx indication to FW failed: %d\n", ret); |
| 198 | goto exit; |
| 199 | } |
| 200 | ath11k_dbg(ar->ab, ATH11K_DBG_AFC, "AFC Resp RX indication sent to target\n"); |
| 201 | |
| 202 | exit: |
| 203 | kfree(afc_rsp); |
| 204 | return ret; |
| 205 | } |
| 206 | |
| 207 | static struct wiphy_vendor_command ath11k_vendor_commands[] = { |
| 208 | { |
| 209 | .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| 210 | .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION, |
| 211 | .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| 212 | WIPHY_VENDOR_CMD_NEED_RUNNING, |
| 213 | .doit = ath11k_vendor_set_wifi_config, |
| 214 | .policy = ath11k_vendor_set_wifi_config_policy, |
| 215 | .maxattr = QCA_WLAN_VENDOR_ATTR_CONFIG_MAX |
| 216 | }, |
| 217 | { |
| 218 | .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| 219 | .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_AFC_RESPONSE, |
| 220 | .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| 221 | WIPHY_VENDOR_CMD_NEED_NETDEV | |
| 222 | WIPHY_VENDOR_CMD_NEED_RUNNING, |
| 223 | .doit = ath11k_vendor_receive_afc_response, |
| 224 | .policy = ath11k_cfg80211_afc_response_policy, |
| 225 | .maxattr = QCA_WLAN_VENDOR_ATTR_AFC_RESPONSE_MAX |
| 226 | }, |
| 227 | }; |
| 228 | |
| 229 | int ath11k_send_power_update_complete(struct ath11k *ar) |
| 230 | { |
| 231 | struct ath11k_base *ab = ar->ab; |
| 232 | struct ath11k_afc_req_fixed_params fixed_param = {0}; |
| 233 | struct ath11k_afc_info *afc = &ar->afc; |
| 234 | struct sk_buff *nl_skb; |
| 235 | int ret = 0; |
| 236 | |
| 237 | fixed_param.req_id = afc->request_id; |
| 238 | fixed_param.min_des_power = DEFAULT_MIN_POWER; |
| 239 | fixed_param.req_length = sizeof(struct ath11k_afc_req_fixed_params); |
| 240 | fixed_param.status_code = afc->afc_reg_info->fw_status_code; |
| 241 | |
| 242 | nl_skb = cfg80211_vendor_event_alloc(ar->hw->wiphy, NULL, |
| 243 | nla_total_size(sizeof(u8) + |
| 244 | sizeof(struct ath11k_afc_req_fixed_params)), |
| 245 | QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT_INDEX, |
| 246 | GFP_ATOMIC); |
| 247 | if (!nl_skb) { |
| 248 | ath11k_warn(ab, "failed to allocate skb for power update complete event\n"); |
| 249 | return -ENOMEM; |
| 250 | } |
| 251 | |
| 252 | ret = nla_put_u8(nl_skb, QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE, |
| 253 | QCA_WLAN_VENDOR_AFC_POWER_UPDATE_COMPLETE_EVENT); |
| 254 | if (ret) { |
| 255 | ath11k_warn(ar->ab, "failed to put afc event type for power cmd\n"); |
| 256 | kfree_skb(nl_skb); |
| 257 | return -EFAULT; |
| 258 | } |
| 259 | |
| 260 | ret = nla_put(nl_skb, QCA_WLAN_VENDOR_ATTR_AFC_EVENT_DATA, |
| 261 | sizeof(struct ath11k_afc_req_fixed_params), &fixed_param); |
| 262 | |
| 263 | if (ret) { |
| 264 | ath11k_warn(ar->ab, "failed to put afc event data for power cmd\n"); |
| 265 | kfree_skb(nl_skb); |
| 266 | return -EFAULT; |
| 267 | } |
| 268 | |
| 269 | ath11k_dbg(ab, ATH11K_DBG_AFC, |
| 270 | "Sending power update complete for afc request id %llu status code %d\n", |
| 271 | fixed_param.req_id, fixed_param.status_code); |
| 272 | cfg80211_vendor_event(nl_skb, GFP_ATOMIC); |
| 273 | return 0; |
| 274 | } |
| 275 | |
| 276 | int ath11k_send_afc_start(struct ath11k *ar, struct ath11k_afc_req_fixed_params *afc_data) |
| 277 | { |
| 278 | struct sk_buff *nl_skb; |
| 279 | int ret = 0; |
| 280 | |
| 281 | nl_skb = cfg80211_vendor_event_alloc(ar->hw->wiphy, NULL, |
| 282 | nla_total_size(sizeof(u8) + |
| 283 | sizeof(struct ath11k_afc_req_fixed_params)), |
| 284 | QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT_INDEX, |
| 285 | GFP_ATOMIC); |
| 286 | if (!nl_skb) { |
| 287 | ath11k_warn(ar->ab, "failed to allocate skb for afc expiry event\n"); |
| 288 | goto out; |
| 289 | } |
| 290 | |
| 291 | ret = nla_put_u8(nl_skb, QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE, |
| 292 | QCA_WLAN_VENDOR_AFC_EXPIRY_EVENT); |
| 293 | if (ret) { |
| 294 | ath11k_warn(ar->ab, "failed to put afc event type\n"); |
| 295 | kfree_skb(nl_skb); |
| 296 | goto out; |
| 297 | } |
| 298 | |
| 299 | ret = nla_put(nl_skb, QCA_WLAN_VENDOR_ATTR_AFC_EVENT_DATA, |
| 300 | sizeof(struct ath11k_afc_req_fixed_params), afc_data); |
| 301 | |
| 302 | if (ret) { |
| 303 | ath11k_warn(ar->ab, "failed to put afc event data\n"); |
| 304 | kfree_skb(nl_skb); |
| 305 | goto out; |
| 306 | } |
| 307 | |
| 308 | cfg80211_vendor_event(nl_skb, GFP_ATOMIC); |
| 309 | ath11k_dbg(ar->ab, ATH11K_DBG_AFC, |
| 310 | "Sending expiry event to higher layer of type %d\n", |
| 311 | QCA_WLAN_VENDOR_AFC_EXPIRY_EVENT); |
| 312 | out: |
| 313 | return ret; |
| 314 | } |
| 315 | |
| 316 | static const struct nl80211_vendor_cmd_info ath11k_vendor_events[] = { |
| 317 | [QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT_INDEX] = { |
| 318 | .vendor_id = QCA_NL80211_VENDOR_ID, |
| 319 | .subcmd = QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT |
| 320 | }, |
| 321 | }; |
| 322 | |
| 323 | int ath11k_vendor_register(struct ath11k *ar) |
| 324 | { |
| 325 | ar->hw->wiphy->vendor_commands = ath11k_vendor_commands; |
| 326 | ar->hw->wiphy->n_vendor_commands = ARRAY_SIZE(ath11k_vendor_commands); |
| 327 | ar->hw->wiphy->vendor_events = ath11k_vendor_events; |
| 328 | ar->hw->wiphy->n_vendor_events = ARRAY_SIZE(ath11k_vendor_events); |
| 329 | |
| 330 | return 0; |
| 331 | } |