iwlwifi: mvm: add basic Time of Flight (802.11mc FTM) support
[deliverable/linux.git] / drivers / net / wireless / iwlwifi / mvm / tof.c
1 /******************************************************************************
2 *
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
5 *
6 * GPL LICENSE SUMMARY
7 *
8 * Copyright(c) 2015 Intel Deutschland GmbH
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
22 * USA
23 *
24 * The full GNU General Public License is included in this distribution
25 * in the file called COPYING.
26 *
27 * Contact Information:
28 * Intel Linux Wireless <ilw@linux.intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 *
31 * BSD LICENSE
32 *
33 * Copyright(c) 2015 Intel Deutschland GmbH
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * * Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
45 * distribution.
46 * * Neither the name Intel Corporation nor the names of its
47 * contributors may be used to endorse or promote products derived
48 * from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 *
62 *****************************************************************************/
63 #include "mvm.h"
64 #include "fw-api-tof.h"
65
66 #define IWL_MVM_TOF_RANGE_REQ_MAX_ID 256
67
68 void iwl_mvm_tof_init(struct iwl_mvm *mvm)
69 {
70 struct iwl_mvm_tof_data *tof_data = &mvm->tof_data;
71
72 if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
73 return;
74
75 memset(tof_data, 0, sizeof(*tof_data));
76
77 tof_data->tof_cfg.hdr.size =
78 cpu_to_le16(sizeof(struct iwl_tof_config_cmd) -
79 sizeof(struct iwl_mvm_umac_cmd_hdr));
80 tof_data->tof_cfg.sub_grp_cmd_id = cpu_to_le32(TOF_CONFIG_CMD);
81
82 #ifdef CONFIG_IWLWIFI_DEBUGFS
83 if (IWL_MVM_TOF_IS_RESPONDER) {
84 tof_data->responder_cfg.hdr.size =
85 cpu_to_le16(sizeof(struct iwl_tof_responder_config_cmd) -
86 sizeof(struct iwl_mvm_umac_cmd_hdr));
87 tof_data->responder_cfg.sub_grp_cmd_id =
88 cpu_to_le32(TOF_RESPONDER_CONFIG_CMD);
89 tof_data->responder_cfg.sta_id = IWL_MVM_STATION_COUNT;
90 }
91 #endif
92
93 tof_data->range_req.hdr.size =
94 cpu_to_le16(sizeof(struct iwl_tof_range_req_cmd) -
95 sizeof(struct iwl_mvm_umac_cmd_hdr));
96 tof_data->range_req.sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_REQ_CMD);
97 tof_data->range_req.req_timeout = 1;
98 tof_data->range_req.initiator = 1;
99 tof_data->range_req.report_policy = 3;
100
101 tof_data->range_req_ext.hdr.size =
102 cpu_to_le16(sizeof(struct iwl_tof_range_req_ext_cmd) -
103 sizeof(struct iwl_mvm_umac_cmd_hdr));
104 tof_data->range_req_ext.sub_grp_cmd_id =
105 cpu_to_le32(TOF_RANGE_REQ_EXT_CMD);
106
107 mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
108 }
109
110 void iwl_mvm_tof_clean(struct iwl_mvm *mvm)
111 {
112 struct iwl_mvm_tof_data *tof_data = &mvm->tof_data;
113
114 if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
115 return;
116
117 memset(tof_data, 0, sizeof(*tof_data));
118 mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
119 }
120
121 static void iwl_tof_iterator(void *_data, u8 *mac,
122 struct ieee80211_vif *vif)
123 {
124 bool *enabled = _data;
125
126 /* non bss vif exists */
127 if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)
128 *enabled = false;
129 }
130
131 int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm)
132 {
133 struct iwl_tof_config_cmd *cmd = &mvm->tof_data.tof_cfg;
134 bool enabled;
135
136 lockdep_assert_held(&mvm->mutex);
137
138 if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
139 return -EINVAL;
140
141 ieee80211_iterate_active_interfaces_atomic(mvm->hw,
142 IEEE80211_IFACE_ITER_NORMAL,
143 iwl_tof_iterator, &enabled);
144 if (!enabled) {
145 IWL_DEBUG_INFO(mvm, "ToF is not supported (non bss vif)\n");
146 return -EINVAL;
147 }
148
149 mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
150 return iwl_mvm_send_cmd_pdu(mvm, TOF_CMD, 0, sizeof(*cmd), cmd);
151 }
152
153 int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id)
154 {
155 struct iwl_tof_range_abort_cmd cmd = {
156 .sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_ABORT_CMD),
157 .hdr.size = cpu_to_le16(sizeof(struct iwl_tof_range_abort_cmd) -
158 sizeof(struct iwl_mvm_umac_cmd_hdr)),
159 .request_id = id,
160 };
161
162 lockdep_assert_held(&mvm->mutex);
163
164 if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
165 return -EINVAL;
166
167 if (id != mvm->tof_data.active_range_request) {
168 IWL_ERR(mvm, "Invalid range request id %d (active %d)\n",
169 id, mvm->tof_data.active_range_request);
170 return -EINVAL;
171 }
172
173 /* after abort is sent there's no active request anymore */
174 mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
175
176 return iwl_mvm_send_cmd_pdu(mvm, TOF_CMD, 0, sizeof(cmd), &cmd);
177 }
178
179 #ifdef CONFIG_IWLWIFI_DEBUGFS
180 int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm,
181 struct ieee80211_vif *vif)
182 {
183 struct iwl_tof_responder_config_cmd *cmd = &mvm->tof_data.responder_cfg;
184 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
185
186 lockdep_assert_held(&mvm->mutex);
187
188 if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
189 return -EINVAL;
190
191 if (vif->p2p || vif->type != NL80211_IFTYPE_AP) {
192 IWL_ERR(mvm, "Cannot start responder, not in AP mode\n");
193 return -EIO;
194 }
195
196 cmd->sta_id = mvmvif->bcast_sta.sta_id;
197 return iwl_mvm_send_cmd_pdu(mvm, TOF_CMD, 0, sizeof(*cmd), cmd);
198 }
199 #endif
200
201 int iwl_mvm_tof_range_request_cmd(struct iwl_mvm *mvm,
202 struct ieee80211_vif *vif)
203 {
204 struct iwl_host_cmd cmd = {
205 .id = TOF_CMD,
206 .len = { sizeof(mvm->tof_data.range_req), },
207 /* no copy because of the command size */
208 .dataflags = { IWL_HCMD_DFL_NOCOPY, },
209 };
210
211 lockdep_assert_held(&mvm->mutex);
212
213 if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
214 return -EINVAL;
215
216 if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) {
217 IWL_ERR(mvm, "Cannot send range request, not STA mode\n");
218 return -EIO;
219 }
220
221 /* nesting of range requests is not supported in FW */
222 if (mvm->tof_data.active_range_request !=
223 IWL_MVM_TOF_RANGE_REQ_MAX_ID) {
224 IWL_ERR(mvm, "Cannot send range req, already active req %d\n",
225 mvm->tof_data.active_range_request);
226 return -EIO;
227 }
228
229 mvm->tof_data.active_range_request = mvm->tof_data.range_req.request_id;
230
231 cmd.data[0] = &mvm->tof_data.range_req;
232 return iwl_mvm_send_cmd(mvm, &cmd);
233 }
234
235 int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm,
236 struct ieee80211_vif *vif)
237 {
238 lockdep_assert_held(&mvm->mutex);
239
240 if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
241 return -EINVAL;
242
243 if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) {
244 IWL_ERR(mvm, "Cannot send ext range req, not in STA mode\n");
245 return -EIO;
246 }
247
248 return iwl_mvm_send_cmd_pdu(mvm, TOF_CMD, 0,
249 sizeof(mvm->tof_data.range_req_ext),
250 &mvm->tof_data.range_req_ext);
251 }
252
253 static int iwl_mvm_tof_range_resp(struct iwl_mvm *mvm, void *data)
254 {
255 struct iwl_tof_range_rsp_ntfy *resp = (void *)data;
256
257 if (resp->request_id != mvm->tof_data.active_range_request) {
258 IWL_ERR(mvm, "Request id mismatch, got %d, active %d\n",
259 resp->request_id, mvm->tof_data.active_range_request);
260 return -EIO;
261 }
262
263 memcpy(&mvm->tof_data.range_resp, resp,
264 sizeof(struct iwl_tof_range_rsp_ntfy));
265 mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
266
267 return 0;
268 }
269
270 static int iwl_mvm_tof_mcsi_notif(struct iwl_mvm *mvm, void *data)
271 {
272 struct iwl_tof_mcsi_notif *resp = (struct iwl_tof_mcsi_notif *)data;
273
274 IWL_DEBUG_INFO(mvm, "MCSI notification, token %d\n", resp->token);
275 return 0;
276 }
277
278 static int iwl_mvm_tof_nb_report_notif(struct iwl_mvm *mvm, void *data)
279 {
280 struct iwl_tof_neighbor_report *report =
281 (struct iwl_tof_neighbor_report *)data;
282
283 IWL_DEBUG_INFO(mvm, "NB report, bssid %pM, token %d, status 0x%x\n",
284 report->bssid, report->request_token, report->status);
285 return 0;
286 }
287
288 void iwl_mvm_tof_resp_handler(struct iwl_mvm *mvm,
289 struct iwl_rx_cmd_buffer *rxb)
290 {
291 struct iwl_rx_packet *pkt = rxb_addr(rxb);
292 struct iwl_tof_gen_resp_cmd *resp = (void *)pkt->data;
293
294 lockdep_assert_held(&mvm->mutex);
295
296 switch (le32_to_cpu(resp->sub_grp_cmd_id)) {
297 case TOF_RANGE_RESPONSE_NOTIF:
298 iwl_mvm_tof_range_resp(mvm, resp->data);
299 break;
300 case TOF_MCSI_DEBUG_NOTIF:
301 iwl_mvm_tof_mcsi_notif(mvm, resp->data);
302 break;
303 case TOF_NEIGHBOR_REPORT_RSP_NOTIF:
304 iwl_mvm_tof_nb_report_notif(mvm, resp->data);
305 break;
306 default:
307 IWL_ERR(mvm, "Unknown sub-group command 0x%x\n",
308 resp->sub_grp_cmd_id);
309 break;
310 }
311 }
This page took 0.061844 seconds and 5 git commands to generate.