Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. |
| 3 | * |
| 4 | * Permission to use, copy, modify, and/or distribute this software for |
| 5 | * any purpose with or without fee is hereby granted, provided that the |
| 6 | * above copyright notice and this permission notice appear in all |
| 7 | * copies. |
| 8 | * |
| 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| 13 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| 14 | * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| 15 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| 16 | * PERFORMANCE OF THIS SOFTWARE. |
| 17 | */ |
| 18 | |
| 19 | /** |
| 20 | * DOC: wlan_hdd_apf.c |
| 21 | * |
| 22 | * Android Packet Filter support and implementation |
| 23 | */ |
| 24 | |
| 25 | #include "wlan_hdd_apf.h" |
| 26 | #include "qca_vendor.h" |
Jeff Johnson | f1a99ea | 2018-06-28 13:27:01 -0700 | [diff] [blame] | 27 | #include "wlan_osif_request_manager.h" |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 28 | |
| 29 | /* |
| 30 | * define short names for the global vendor params |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 31 | * used by __wlan_hdd_cfg80211_apf_offload() |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 32 | */ |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 33 | #define APF_INVALID \ |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 34 | QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 35 | #define APF_SUBCMD \ |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 36 | QCA_WLAN_VENDOR_ATTR_SET_RESET_PACKET_FILTER |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 37 | #define APF_VERSION \ |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 38 | QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 39 | #define APF_FILTER_ID \ |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 40 | QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 41 | #define APF_PACKET_SIZE \ |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 42 | QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 43 | #define APF_CURRENT_OFFSET \ |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 44 | QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 45 | #define APF_PROGRAM \ |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 46 | QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 47 | #define APF_PROG_LEN \ |
| 48 | QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 49 | #define APF_MAX \ |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 50 | QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX |
| 51 | |
| 52 | static const struct nla_policy |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 53 | wlan_hdd_apf_offload_policy[APF_MAX + 1] = { |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 54 | [APF_SUBCMD] = {.type = NLA_U32}, |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 55 | [APF_VERSION] = {.type = NLA_U32}, |
| 56 | [APF_FILTER_ID] = {.type = NLA_U32}, |
| 57 | [APF_PACKET_SIZE] = {.type = NLA_U32}, |
| 58 | [APF_CURRENT_OFFSET] = {.type = NLA_U32}, |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 59 | [APF_PROGRAM] = {.type = NLA_BINARY, |
| 60 | .len = MAX_APF_MEMORY_LEN}, |
| 61 | [APF_PROG_LEN] = {.type = NLA_U32}, |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 62 | }; |
| 63 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 64 | void hdd_apf_context_init(struct hdd_adapter *adapter) |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 65 | { |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 66 | qdf_event_create(&adapter->apf_context.qdf_apf_event); |
| 67 | qdf_spinlock_create(&adapter->apf_context.lock); |
| 68 | adapter->apf_context.apf_enabled = true; |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 69 | } |
| 70 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 71 | void hdd_apf_context_destroy(struct hdd_adapter *adapter) |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 72 | { |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 73 | qdf_event_destroy(&adapter->apf_context.qdf_apf_event); |
| 74 | qdf_spinlock_destroy(&adapter->apf_context.lock); |
| 75 | qdf_mem_zero(&adapter->apf_context, |
| 76 | sizeof(struct hdd_apf_context)); |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 77 | } |
| 78 | |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 79 | struct apf_offload_priv { |
| 80 | struct sir_apf_get_offload apf_get_offload; |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 81 | }; |
| 82 | |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 83 | void hdd_get_apf_capabilities_cb(void *context, |
| 84 | struct sir_apf_get_offload *data) |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 85 | { |
Jeff Johnson | f1a99ea | 2018-06-28 13:27:01 -0700 | [diff] [blame] | 86 | struct osif_request *request; |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 87 | struct apf_offload_priv *priv; |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 88 | |
| 89 | hdd_enter(); |
| 90 | |
Jeff Johnson | f1a99ea | 2018-06-28 13:27:01 -0700 | [diff] [blame] | 91 | request = osif_request_get(context); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 92 | if (!request) { |
| 93 | hdd_err("Obsolete request"); |
| 94 | return; |
| 95 | } |
| 96 | |
Jeff Johnson | f1a99ea | 2018-06-28 13:27:01 -0700 | [diff] [blame] | 97 | priv = osif_request_priv(request); |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 98 | priv->apf_get_offload = *data; |
Jeff Johnson | f1a99ea | 2018-06-28 13:27:01 -0700 | [diff] [blame] | 99 | osif_request_complete(request); |
| 100 | osif_request_put(request); |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 101 | |
| 102 | hdd_exit(); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 103 | } |
| 104 | |
| 105 | /** |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 106 | * hdd_post_get_apf_capabilities_rsp() - Callback function to APF Offload |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 107 | * @hdd_context: hdd_context |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 108 | * @apf_get_offload: struct for get offload |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 109 | * |
| 110 | * Return: 0 on success, error number otherwise. |
| 111 | */ |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 112 | static int |
| 113 | hdd_post_get_apf_capabilities_rsp(struct hdd_context *hdd_ctx, |
| 114 | struct sir_apf_get_offload *apf_get_offload) |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 115 | { |
| 116 | struct sk_buff *skb; |
| 117 | uint32_t nl_buf_len; |
| 118 | |
| 119 | hdd_enter(); |
| 120 | |
| 121 | nl_buf_len = NLMSG_HDRLEN; |
| 122 | nl_buf_len += |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 123 | (sizeof(apf_get_offload->max_bytes_for_apf_inst) + NLA_HDRLEN) + |
| 124 | (sizeof(apf_get_offload->apf_version) + NLA_HDRLEN); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 125 | |
| 126 | skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| 127 | if (!skb) { |
| 128 | hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| 129 | return -ENOMEM; |
| 130 | } |
| 131 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 132 | hdd_ctx->apf_version = apf_get_offload->apf_version; |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 133 | hdd_debug("APF Version: %u APF max bytes: %u", |
| 134 | apf_get_offload->apf_version, |
| 135 | apf_get_offload->max_bytes_for_apf_inst); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 136 | |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 137 | if (nla_put_u32(skb, APF_PACKET_SIZE, |
| 138 | apf_get_offload->max_bytes_for_apf_inst) || |
| 139 | nla_put_u32(skb, APF_VERSION, apf_get_offload->apf_version)) { |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 140 | hdd_err("nla put failure"); |
| 141 | goto nla_put_failure; |
| 142 | } |
| 143 | |
| 144 | cfg80211_vendor_cmd_reply(skb); |
| 145 | hdd_exit(); |
| 146 | return 0; |
| 147 | |
| 148 | nla_put_failure: |
| 149 | kfree_skb(skb); |
| 150 | return -EINVAL; |
| 151 | } |
| 152 | |
| 153 | /** |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 154 | * hdd_get_apf_capabilities - Get APF offload Capabilities |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 155 | * @hdd_ctx: Hdd context |
| 156 | * |
| 157 | * Return: 0 on success, errno on failure |
| 158 | */ |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 159 | static int hdd_get_apf_capabilities(struct hdd_context *hdd_ctx) |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 160 | { |
| 161 | QDF_STATUS status; |
| 162 | int ret; |
| 163 | void *cookie; |
Jeff Johnson | f1a99ea | 2018-06-28 13:27:01 -0700 | [diff] [blame] | 164 | struct osif_request *request; |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 165 | struct apf_offload_priv *priv; |
Jeff Johnson | f1a99ea | 2018-06-28 13:27:01 -0700 | [diff] [blame] | 166 | static const struct osif_request_params params = { |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 167 | .priv_size = sizeof(*priv), |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 168 | .timeout_ms = WLAN_WAIT_TIME_APF, |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 169 | }; |
| 170 | |
| 171 | hdd_enter(); |
| 172 | |
Jeff Johnson | f1a99ea | 2018-06-28 13:27:01 -0700 | [diff] [blame] | 173 | request = osif_request_alloc(¶ms); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 174 | if (!request) { |
| 175 | hdd_err("Unable to allocate request"); |
| 176 | return -EINVAL; |
| 177 | } |
Jeff Johnson | f1a99ea | 2018-06-28 13:27:01 -0700 | [diff] [blame] | 178 | cookie = osif_request_cookie(request); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 179 | |
Jeff Johnson | c616223 | 2018-06-20 13:33:07 -0700 | [diff] [blame] | 180 | status = sme_get_apf_capabilities(hdd_ctx->mac_handle, |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 181 | hdd_get_apf_capabilities_cb, |
| 182 | cookie); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 183 | if (!QDF_IS_STATUS_SUCCESS(status)) { |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 184 | hdd_err("Unable to retrieve APF caps"); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 185 | ret = qdf_status_to_os_return(status); |
| 186 | goto cleanup; |
| 187 | } |
Jeff Johnson | f1a99ea | 2018-06-28 13:27:01 -0700 | [diff] [blame] | 188 | ret = osif_request_wait_for_response(request); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 189 | if (ret) { |
| 190 | hdd_err("Target response timed out"); |
| 191 | goto cleanup; |
| 192 | } |
Jeff Johnson | f1a99ea | 2018-06-28 13:27:01 -0700 | [diff] [blame] | 193 | priv = osif_request_priv(request); |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 194 | ret = hdd_post_get_apf_capabilities_rsp(hdd_ctx, |
| 195 | &priv->apf_get_offload); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 196 | if (ret) |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 197 | hdd_err("Failed to post get apf capabilities"); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 198 | |
| 199 | cleanup: |
| 200 | /* |
| 201 | * either we never sent a request to SME, we sent a request to |
| 202 | * SME and timed out, or we sent a request to SME, received a |
| 203 | * response from SME, and posted the response to userspace. |
| 204 | * regardless we are done with the request. |
| 205 | */ |
Jeff Johnson | f1a99ea | 2018-06-28 13:27:01 -0700 | [diff] [blame] | 206 | osif_request_put(request); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 207 | hdd_exit(); |
| 208 | |
| 209 | return ret; |
| 210 | } |
| 211 | |
| 212 | /** |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 213 | * hdd_set_reset_apf_offload - Post set/reset apf to SME |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 214 | * @hdd_ctx: Hdd context |
| 215 | * @tb: Length of @data |
| 216 | * @adapter: pointer to adapter struct |
| 217 | * |
| 218 | * Return: 0 on success; errno on failure |
| 219 | */ |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 220 | static int hdd_set_reset_apf_offload(struct hdd_context *hdd_ctx, |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 221 | struct nlattr **tb, |
| 222 | struct hdd_adapter *adapter) |
| 223 | { |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 224 | struct sir_apf_set_offload *apf_set_offload; |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 225 | QDF_STATUS status; |
| 226 | int prog_len; |
| 227 | int ret = 0; |
| 228 | |
| 229 | hdd_enter(); |
| 230 | |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 231 | if (!hdd_conn_is_connected( |
| 232 | WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { |
| 233 | hdd_err("Not in Connected state!"); |
| 234 | return -ENOTSUPP; |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 235 | } |
| 236 | |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 237 | apf_set_offload = qdf_mem_malloc(sizeof(*apf_set_offload)); |
| 238 | if (!apf_set_offload) { |
| 239 | hdd_err("qdf_mem_malloc failed for apf_set_offload"); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 240 | return -ENOMEM; |
| 241 | } |
| 242 | |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 243 | /* Parse and fetch apf packet size */ |
| 244 | if (!tb[APF_PACKET_SIZE]) { |
| 245 | hdd_err("attr apf packet size failed"); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 246 | ret = -EINVAL; |
| 247 | goto fail; |
| 248 | } |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 249 | apf_set_offload->total_length = nla_get_u32(tb[APF_PACKET_SIZE]); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 250 | |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 251 | if (!apf_set_offload->total_length) { |
| 252 | hdd_debug("APF reset packet filter received"); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 253 | goto post_sme; |
| 254 | } |
| 255 | |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 256 | /* Parse and fetch apf program */ |
| 257 | if (!tb[APF_PROGRAM]) { |
| 258 | hdd_err("attr apf program failed"); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 259 | ret = -EINVAL; |
| 260 | goto fail; |
| 261 | } |
| 262 | |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 263 | prog_len = nla_len(tb[APF_PROGRAM]); |
| 264 | apf_set_offload->program = qdf_mem_malloc(sizeof(uint8_t) * prog_len); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 265 | |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 266 | if (!apf_set_offload->program) { |
| 267 | hdd_err("qdf_mem_malloc failed for apf offload program"); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 268 | ret = -ENOMEM; |
| 269 | goto fail; |
| 270 | } |
| 271 | |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 272 | apf_set_offload->current_length = prog_len; |
| 273 | nla_memcpy(apf_set_offload->program, tb[APF_PROGRAM], prog_len); |
| 274 | apf_set_offload->session_id = adapter->session_id; |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 275 | |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 276 | hdd_debug("APF set instructions"); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 277 | QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 278 | apf_set_offload->program, prog_len); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 279 | |
| 280 | /* Parse and fetch filter Id */ |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 281 | if (!tb[APF_FILTER_ID]) { |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 282 | hdd_err("attr filter id failed"); |
| 283 | ret = -EINVAL; |
| 284 | goto fail; |
| 285 | } |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 286 | apf_set_offload->filter_id = nla_get_u32(tb[APF_FILTER_ID]); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 287 | |
| 288 | /* Parse and fetch current offset */ |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 289 | if (!tb[APF_CURRENT_OFFSET]) { |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 290 | hdd_err("attr current offset failed"); |
| 291 | ret = -EINVAL; |
| 292 | goto fail; |
| 293 | } |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 294 | apf_set_offload->current_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 295 | |
| 296 | post_sme: |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 297 | hdd_debug("Posting APF SET/RESET to SME, session_id: %d APF Version: %d filter ID: %d total_length: %d current_length: %d current offset: %d", |
| 298 | apf_set_offload->session_id, apf_set_offload->version, |
| 299 | apf_set_offload->filter_id, apf_set_offload->total_length, |
| 300 | apf_set_offload->current_length, |
| 301 | apf_set_offload->current_offset); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 302 | |
Jeff Johnson | c616223 | 2018-06-20 13:33:07 -0700 | [diff] [blame] | 303 | status = sme_set_apf_instructions(hdd_ctx->mac_handle, apf_set_offload); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 304 | if (!QDF_IS_STATUS_SUCCESS(status)) { |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 305 | hdd_err("sme_set_apf_instructions failed(err=%d)", status); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 306 | ret = -EINVAL; |
| 307 | goto fail; |
| 308 | } |
| 309 | hdd_exit(); |
| 310 | |
| 311 | fail: |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 312 | if (apf_set_offload->current_length) |
| 313 | qdf_mem_free(apf_set_offload->program); |
| 314 | qdf_mem_free(apf_set_offload); |
Rajeev Kumar Sirasanagandla | 85f8b02 | 2018-03-12 12:52:59 +0530 | [diff] [blame^] | 315 | |
| 316 | if (!ret) |
| 317 | hdd_ctx->apf_enabled_v2 = true; |
| 318 | |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 319 | return ret; |
| 320 | } |
| 321 | |
| 322 | /** |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 323 | * hdd_enable_disable_apf - Enable or Disable the APF interpreter |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 324 | * @adapter: HDD Adapter |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 325 | * @apf_enable: true: Enable APF Int., false: disable APF Int. |
| 326 | * |
| 327 | * Return: 0 on success, errno on failure |
| 328 | */ |
| 329 | static int |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 330 | hdd_enable_disable_apf(struct hdd_adapter *adapter, bool apf_enable) |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 331 | { |
| 332 | QDF_STATUS status; |
| 333 | |
| 334 | hdd_enter(); |
| 335 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 336 | status = sme_set_apf_enable_disable(hdd_adapter_get_mac_handle(adapter), |
| 337 | adapter->session_id, apf_enable); |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 338 | if (!QDF_IS_STATUS_SUCCESS(status)) { |
| 339 | hdd_err("Unable to post sme apf enable/disable message (status-%d)", |
| 340 | status); |
| 341 | return -EINVAL; |
| 342 | } |
| 343 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 344 | adapter->apf_context.apf_enabled = apf_enable; |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 345 | |
| 346 | hdd_exit(); |
| 347 | return 0; |
| 348 | } |
| 349 | |
| 350 | /** |
| 351 | * hdd_apf_write_memory - Write into the apf work memory |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 352 | * @adapter: HDD Adapter |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 353 | * @tb: list of attributes |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 354 | * |
| 355 | * This function writes code/data into the APF work memory and |
| 356 | * provides program length that is passed on to the interpreter. |
| 357 | * |
| 358 | * Return: 0 on success, errno on failure |
| 359 | */ |
| 360 | static int |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 361 | hdd_apf_write_memory(struct hdd_adapter *adapter, struct nlattr **tb) |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 362 | { |
| 363 | struct wmi_apf_write_memory_params write_mem_params = {0}; |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 364 | struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 365 | QDF_STATUS status; |
| 366 | int ret = 0; |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 367 | |
| 368 | hdd_enter(); |
| 369 | |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 370 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 371 | write_mem_params.vdev_id = adapter->session_id; |
| 372 | if (adapter->apf_context.apf_enabled) { |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 373 | hdd_err("Cannot get/set when APF interpreter is enabled"); |
| 374 | return -EINVAL; |
| 375 | } |
| 376 | |
| 377 | /* Read program length */ |
| 378 | if (!tb[APF_PROG_LEN]) { |
| 379 | hdd_err("attr program length failed"); |
| 380 | return -EINVAL; |
| 381 | } |
| 382 | write_mem_params.program_len = nla_get_u32(tb[APF_PROG_LEN]); |
| 383 | |
| 384 | /* Read APF work memory offset */ |
| 385 | if (!tb[APF_CURRENT_OFFSET]) { |
| 386 | hdd_err("attr apf packet size failed"); |
| 387 | return -EINVAL; |
| 388 | } |
| 389 | write_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]); |
| 390 | |
| 391 | /* Parse and fetch apf program */ |
| 392 | if (!tb[APF_PROGRAM]) { |
| 393 | hdd_err("attr apf program failed"); |
| 394 | return -EINVAL; |
| 395 | } |
| 396 | |
| 397 | write_mem_params.length = nla_len(tb[APF_PROGRAM]); |
| 398 | if (!write_mem_params.length) { |
| 399 | hdd_err("Program attr with empty data"); |
| 400 | return -EINVAL; |
| 401 | } |
| 402 | |
| 403 | write_mem_params.buf = qdf_mem_malloc(sizeof(uint8_t) |
| 404 | * write_mem_params.length); |
| 405 | if (write_mem_params.buf == NULL) { |
| 406 | hdd_err("failed to alloc mem for apf write mem operation"); |
| 407 | return -EINVAL; |
| 408 | } |
| 409 | nla_memcpy(write_mem_params.buf, tb[APF_PROGRAM], |
| 410 | write_mem_params.length); |
| 411 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 412 | write_mem_params.apf_version = hdd_ctx->apf_version; |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 413 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 414 | status = sme_apf_write_work_memory(hdd_adapter_get_mac_handle(adapter), |
Jeff Johnson | c616223 | 2018-06-20 13:33:07 -0700 | [diff] [blame] | 415 | &write_mem_params); |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 416 | if (!QDF_IS_STATUS_SUCCESS(status)) { |
| 417 | hdd_err("Unable to retrieve APF caps"); |
| 418 | ret = -EINVAL; |
| 419 | } |
| 420 | |
| 421 | if (write_mem_params.buf) |
| 422 | qdf_mem_free(write_mem_params.buf); |
| 423 | |
| 424 | hdd_exit(); |
| 425 | return ret; |
| 426 | } |
| 427 | |
| 428 | /** |
| 429 | * hdd_apf_read_memory_callback - HDD Callback for the APF read memory |
| 430 | * operation |
| 431 | * @context: Hdd context |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 432 | * @evt: APF read memory event response parameters |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 433 | * |
| 434 | * Return: 0 on success, errno on failure |
| 435 | */ |
| 436 | static void |
| 437 | hdd_apf_read_memory_callback(void *hdd_context, |
| 438 | struct wmi_apf_read_memory_resp_event_params *evt) |
| 439 | { |
| 440 | struct hdd_context *hdd_ctx = hdd_context; |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 441 | struct hdd_adapter *adapter; |
| 442 | struct hdd_apf_context *context; |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 443 | uint8_t *buf_ptr; |
| 444 | uint32_t pkt_offset; |
| 445 | |
| 446 | hdd_enter(); |
| 447 | |
| 448 | if (wlan_hdd_validate_context(hdd_ctx) || !evt) { |
| 449 | hdd_err("HDD context is invalid or event buf(%pK) is null", |
| 450 | evt); |
| 451 | return; |
| 452 | } |
| 453 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 454 | adapter = hdd_get_adapter_by_vdev(hdd_ctx, evt->vdev_id); |
| 455 | context = &adapter->apf_context; |
| 456 | |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 457 | if (context->magic != APF_CONTEXT_MAGIC) { |
| 458 | /* The caller presumably timed out, nothing to do */ |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 459 | hdd_err("Caller timed out or corrupt magic, simply return"); |
| 460 | return; |
| 461 | } |
| 462 | |
| 463 | if (evt->offset < context->offset) { |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 464 | hdd_err("Offset in read event(%d) smaller than offset in request(%d)!", |
| 465 | evt->offset, context->offset); |
| 466 | return; |
| 467 | } |
| 468 | |
| 469 | /* |
| 470 | * offset in the event is relative to the APF work memory. |
| 471 | * Calculate the packet offset, which gives us the relative |
| 472 | * location in the buffer to start copy into. |
| 473 | */ |
| 474 | pkt_offset = evt->offset - context->offset; |
| 475 | |
| 476 | if (context->buf_len < pkt_offset + evt->length) { |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 477 | hdd_err("Read chunk exceeding allocated space"); |
| 478 | return; |
| 479 | } |
| 480 | buf_ptr = context->buf + pkt_offset; |
| 481 | |
| 482 | qdf_mem_copy(buf_ptr, evt->data, evt->length); |
| 483 | |
| 484 | if (!evt->more_data) { |
| 485 | /* Release the caller after last event, clear magic */ |
| 486 | context->magic = 0; |
| 487 | qdf_event_set(&context->qdf_apf_event); |
| 488 | } |
| 489 | |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 490 | hdd_exit(); |
| 491 | } |
| 492 | |
| 493 | /** |
| 494 | * hdd_apf_read_memory - Read part of the apf work memory |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 495 | * @adapter: HDD Adapter |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 496 | * @tb: list of attributes |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 497 | * |
| 498 | * Return: 0 on success, errno on failure |
| 499 | */ |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 500 | static int hdd_apf_read_memory(struct hdd_adapter *adapter, struct nlattr **tb) |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 501 | { |
| 502 | struct wmi_apf_read_memory_params read_mem_params = {0}; |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 503 | struct hdd_apf_context *context = &adapter->apf_context; |
| 504 | struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 505 | QDF_STATUS status; |
| 506 | unsigned long nl_buf_len = NLMSG_HDRLEN; |
| 507 | int ret = 0; |
| 508 | struct sk_buff *skb = NULL; |
| 509 | uint8_t *bufptr; |
| 510 | |
| 511 | hdd_enter(); |
| 512 | |
Rajeev Kumar | 75fc36e | 2018-09-04 11:41:10 -0700 | [diff] [blame] | 513 | if (context->apf_enabled) { |
| 514 | hdd_err("Cannot get/set while interpreter is enabled"); |
| 515 | return -EINVAL; |
| 516 | } |
| 517 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 518 | read_mem_params.vdev_id = adapter->session_id; |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 519 | |
| 520 | /* Read APF work memory offset */ |
| 521 | if (!tb[APF_CURRENT_OFFSET]) { |
| 522 | hdd_err("attr apf memory offset failed"); |
| 523 | return -EINVAL; |
| 524 | } |
| 525 | read_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]); |
| 526 | |
| 527 | /* Read length */ |
| 528 | if (!tb[APF_PACKET_SIZE]) { |
| 529 | hdd_err("attr apf packet size failed"); |
| 530 | return -EINVAL; |
| 531 | } |
| 532 | read_mem_params.length = nla_get_u32(tb[APF_PACKET_SIZE]); |
| 533 | if (!read_mem_params.length) { |
| 534 | hdd_err("apf read length cannot be zero!"); |
| 535 | return -EINVAL; |
| 536 | } |
| 537 | bufptr = qdf_mem_malloc(read_mem_params.length); |
| 538 | if (bufptr == NULL) { |
| 539 | hdd_err("alloc failed for cumulative event buffer"); |
| 540 | return -ENOMEM; |
| 541 | } |
| 542 | |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 543 | qdf_event_reset(&context->qdf_apf_event); |
| 544 | context->offset = read_mem_params.addr_offset; |
| 545 | |
| 546 | context->buf = bufptr; |
| 547 | context->buf_len = read_mem_params.length; |
| 548 | context->magic = APF_CONTEXT_MAGIC; |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 549 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 550 | status = sme_apf_read_work_memory(hdd_adapter_get_mac_handle(adapter), |
Jeff Johnson | c616223 | 2018-06-20 13:33:07 -0700 | [diff] [blame] | 551 | &read_mem_params, |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 552 | hdd_apf_read_memory_callback); |
| 553 | if (QDF_IS_STATUS_ERROR(status)) { |
| 554 | hdd_err("Unable to post sme APF read memory message (status-%d)", |
| 555 | status); |
| 556 | ret = -EINVAL; |
| 557 | goto fail; |
| 558 | } |
| 559 | |
| 560 | /* request was sent -- wait for the response */ |
| 561 | status = qdf_wait_for_event_completion(&context->qdf_apf_event, |
| 562 | WLAN_WAIT_TIME_APF_READ_MEM); |
| 563 | if (QDF_IS_STATUS_ERROR(status)) { |
| 564 | hdd_err("Target response timed out"); |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 565 | context->magic = 0; |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 566 | ret = -ETIMEDOUT; |
| 567 | goto fail; |
| 568 | } |
| 569 | |
| 570 | nl_buf_len += sizeof(uint32_t) + NLA_HDRLEN; |
| 571 | nl_buf_len += context->buf_len + NLA_HDRLEN; |
| 572 | |
| 573 | skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| 574 | if (!skb) { |
| 575 | hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| 576 | ret = -ENOMEM; |
| 577 | goto fail; |
| 578 | } |
| 579 | |
| 580 | if (nla_put_u32(skb, APF_SUBCMD, QCA_WLAN_READ_PACKET_FILTER) || |
| 581 | nla_put(skb, APF_PROGRAM, read_mem_params.length, context->buf)) { |
| 582 | hdd_err("put fail"); |
| 583 | kfree_skb(skb); |
| 584 | ret = -EINVAL; |
| 585 | goto fail; |
| 586 | } |
| 587 | |
| 588 | cfg80211_vendor_cmd_reply(skb); |
| 589 | fail: |
| 590 | if (context->buf) { |
| 591 | qdf_mem_free(context->buf); |
| 592 | context->buf = NULL; |
| 593 | } |
| 594 | |
| 595 | hdd_exit(); |
| 596 | return ret; |
| 597 | } |
| 598 | |
| 599 | /** |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 600 | * wlan_hdd_cfg80211_apf_offload() - Set/Reset to APF Offload |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 601 | * @wiphy: wiphy structure pointer |
| 602 | * @wdev: Wireless device structure pointer |
| 603 | * @data: Pointer to the data received |
| 604 | * @data_len: Length of @data |
| 605 | * |
| 606 | * Return: 0 on success; errno on failure |
| 607 | */ |
| 608 | static int |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 609 | __wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy, |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 610 | struct wireless_dev *wdev, |
| 611 | const void *data, int data_len) |
| 612 | { |
| 613 | struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| 614 | struct net_device *dev = wdev->netdev; |
| 615 | struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 616 | struct nlattr *tb[APF_MAX + 1]; |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 617 | int ret_val = 0, apf_subcmd; |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 618 | struct hdd_apf_context *context; |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 619 | |
| 620 | hdd_enter(); |
| 621 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 622 | if (!adapter) { |
| 623 | hdd_err("Adapter is null"); |
| 624 | return -EINVAL; |
| 625 | } |
| 626 | |
| 627 | context = &adapter->apf_context; |
| 628 | |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 629 | ret_val = wlan_hdd_validate_context(hdd_ctx); |
| 630 | if (ret_val) |
| 631 | return ret_val; |
| 632 | |
| 633 | if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| 634 | hdd_err("Command not allowed in FTM mode"); |
| 635 | return -EINVAL; |
| 636 | } |
| 637 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 638 | if (!hdd_ctx->apf_supported) { |
| 639 | hdd_err("APF is not supported or disabled through INI"); |
| 640 | return -ENOTSUPP; |
| 641 | } |
| 642 | |
| 643 | if (!(adapter->device_mode == QDF_STA_MODE || |
| 644 | adapter->device_mode == QDF_P2P_CLIENT_MODE)) { |
| 645 | hdd_err("APF only supported in STA or P2P CLI modes!"); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 646 | return -ENOTSUPP; |
| 647 | } |
| 648 | |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 649 | if (wlan_cfg80211_nla_parse(tb, APF_MAX, data, data_len, |
| 650 | wlan_hdd_apf_offload_policy)) { |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 651 | hdd_err("Invalid ATTR"); |
| 652 | return -EINVAL; |
| 653 | } |
| 654 | |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 655 | if (!tb[APF_SUBCMD]) { |
| 656 | hdd_err("attr apf sub-command failed"); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 657 | return -EINVAL; |
| 658 | } |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 659 | apf_subcmd = nla_get_u32(tb[APF_SUBCMD]); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 660 | |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 661 | /* Do not allow simultaneous new APF commands on the same adapter */ |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 662 | qdf_spin_lock(&context->lock); |
| 663 | if (context->cmd_in_progress) { |
| 664 | qdf_spin_unlock(&context->lock); |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 665 | hdd_err("Another cmd in progress for same session!"); |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 666 | return -EAGAIN; |
| 667 | } |
| 668 | context->cmd_in_progress = true; |
| 669 | qdf_spin_unlock(&context->lock); |
| 670 | |
| 671 | switch (apf_subcmd) { |
| 672 | /* Legacy APF sub-commands */ |
| 673 | case QCA_WLAN_SET_PACKET_FILTER: |
| 674 | ret_val = hdd_set_reset_apf_offload(hdd_ctx, tb, |
| 675 | adapter); |
| 676 | break; |
| 677 | case QCA_WLAN_GET_PACKET_FILTER: |
| 678 | ret_val = hdd_get_apf_capabilities(hdd_ctx); |
| 679 | break; |
| 680 | |
| 681 | /* APF 3.0 sub-commands */ |
| 682 | case QCA_WLAN_WRITE_PACKET_FILTER: |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 683 | ret_val = hdd_apf_write_memory(adapter, tb); |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 684 | break; |
| 685 | case QCA_WLAN_READ_PACKET_FILTER: |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 686 | ret_val = hdd_apf_read_memory(adapter, tb); |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 687 | break; |
| 688 | case QCA_WLAN_ENABLE_PACKET_FILTER: |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 689 | ret_val = hdd_enable_disable_apf(adapter, true); |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 690 | break; |
| 691 | case QCA_WLAN_DISABLE_PACKET_FILTER: |
Nachiket Kukade | 5f0ce4f | 2018-06-15 19:47:37 +0530 | [diff] [blame] | 692 | ret_val = hdd_enable_disable_apf(adapter, false); |
Nachiket Kukade | 177b5b0 | 2018-05-22 20:52:17 +0530 | [diff] [blame] | 693 | break; |
| 694 | default: |
| 695 | hdd_err("Unknown APF Sub-command: %d", apf_subcmd); |
| 696 | ret_val = -ENOTSUPP; |
| 697 | } |
| 698 | |
| 699 | qdf_spin_lock(&context->lock); |
| 700 | context->cmd_in_progress = false; |
| 701 | qdf_spin_unlock(&context->lock); |
| 702 | |
| 703 | return ret_val; |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 704 | } |
| 705 | |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 706 | int |
| 707 | wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy, struct wireless_dev *wdev, |
| 708 | const void *data, int data_len) |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 709 | { |
| 710 | int ret; |
| 711 | |
| 712 | cds_ssr_protect(__func__); |
Nachiket Kukade | e547a48 | 2018-05-22 16:43:30 +0530 | [diff] [blame] | 713 | ret = __wlan_hdd_cfg80211_apf_offload(wiphy, wdev, data, data_len); |
Nachiket Kukade | d0dd62e | 2018-05-21 18:39:22 +0530 | [diff] [blame] | 714 | cds_ssr_unprotect(__func__); |
| 715 | |
| 716 | return ret; |
| 717 | } |
| 718 | |