blob: a6f0bd5d27eec9fd23246f16c34499ad8c467b78 [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
Jeff Johnson441e1f72017-02-07 08:50:49 -08002 * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28/**
29 * DOC: wlan_hdd_ocb.c
30 *
31 * WLAN Host Device Driver 802.11p OCB implementation
32 */
33
34#include "cds_sched.h"
35#include "wlan_hdd_assoc.h"
36#include "wlan_hdd_main.h"
37#include "wlan_hdd_ocb.h"
38#include "wlan_hdd_trace.h"
39#include "wlan_tgt_def_config.h"
40#include "sch_api.h"
41#include "wma_api.h"
Leo Changfdb45c32016-10-28 11:09:23 -070042#include <cdp_txrx_cmn.h>
Manjunathappa Prakash3454fd62016-04-01 08:52:06 -070043#include <cdp_txrx_peer_ops.h>
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080044
45/* Structure definitions for WLAN_SET_DOT11P_CHANNEL_SCHED */
46#define AIFSN_MIN (2)
47#define AIFSN_MAX (15)
48#define CW_MIN (1)
49#define CW_MAX (10)
50
51/* Maximum time(ms) to wait for OCB operations */
52#define WLAN_WAIT_TIME_OCB_CMD 1500
53#define HDD_OCB_MAGIC 0x489a154f
54
55/**
56 * struct hdd_ocb_ctxt - Context for OCB operations
57 * adapter: the ocb adapter
58 * completion_evt: the completion event
59 * status: status of the request
60 */
61struct hdd_ocb_ctxt {
62 uint32_t magic;
63 hdd_adapter_t *adapter;
64 struct completion completion_evt;
65 int status;
66};
67
68/**
69 * hdd_set_dot11p_config() - Set 802.11p config flag
70 * @hdd_ctx: HDD Context pointer
71 *
72 * TODO-OCB: This has been temporarily added to ensure this paramter
73 * is set in CSR when we init the channel list. This should be removed
74 * once the 5.9 GHz channels are added to the regulatory domain.
75 */
76void hdd_set_dot11p_config(hdd_context_t *hdd_ctx)
77{
78 sme_set_dot11p_config(hdd_ctx->hHal,
79 hdd_ctx->config->dot11p_mode !=
80 WLAN_HDD_11P_DISABLED);
81}
82
83/**
84 * dot11p_validate_qos_params() - Check if QoS parameters are valid
85 * @qos_params: Array of QoS parameters
86 *
87 * Return: 0 on success. error code on failure.
88 */
89static int dot11p_validate_qos_params(struct sir_qos_params qos_params[])
90{
91 int i;
92
93 for (i = 0; i < MAX_NUM_AC; i++) {
94 if ((!qos_params[i].aifsn) && (!qos_params[i].cwmin)
95 && (!qos_params[i].cwmax))
96 continue;
97
98 /* Validate AIFSN */
99 if ((qos_params[i].aifsn < AIFSN_MIN)
100 || (qos_params[i].aifsn > AIFSN_MAX)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700101 hdd_err("Invalid QoS parameter aifsn %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800102 qos_params[i].aifsn);
103 return -EINVAL;
104 }
105
106 /* Validate CWMin */
107 if ((qos_params[i].cwmin < CW_MIN)
108 || (qos_params[i].cwmin > CW_MAX)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700109 hdd_err("Invalid QoS parameter cwmin %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800110 qos_params[i].cwmin);
111 return -EINVAL;
112 }
113
114 /* Validate CWMax */
115 if ((qos_params[i].cwmax < CW_MIN)
116 || (qos_params[i].cwmax > CW_MAX)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700117 hdd_err("Invalid QoS parameter cwmax %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800118 qos_params[i].cwmax);
119 return -EINVAL;
120 }
121 }
122
123 return 0;
124}
125
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800126/**
127 * dot11p_validate_channel() - validates a DSRC channel
128 * @center_freq: the channel's center frequency
129 * @bandwidth: the channel's bandwidth
130 * @tx_power: transmit power
131 * @reg_power: (output) the max tx power from the regulatory domain
132 * @antenna_max: (output) the max antenna gain from the regulatory domain
133 *
134 * Return: 0 if the channel is valid, error code otherwise.
135 */
136static int dot11p_validate_channel(struct wiphy *wiphy,
137 uint32_t channel_freq, uint32_t bandwidth,
138 uint32_t tx_power, uint8_t *reg_power,
139 uint8_t *antenna_max)
140{
141 int band_idx, channel_idx;
142 struct ieee80211_supported_band *current_band;
143 struct ieee80211_channel *current_channel;
144
Dustin Browna30892e2016-10-12 17:28:36 -0700145 for (band_idx = 0; band_idx < NUM_NL80211_BANDS; band_idx++) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800146 current_band = wiphy->bands[band_idx];
147 if (!current_band)
148 continue;
149
150 for (channel_idx = 0; channel_idx < current_band->n_channels;
151 channel_idx++) {
152 current_channel = &current_band->channels[channel_idx];
153
154 if (channel_freq == current_channel->center_freq) {
155 if (current_channel->flags &
156 IEEE80211_CHAN_DISABLED)
157 return -EINVAL;
158
159 if (reg_power)
160 *reg_power =
161 current_channel->max_reg_power;
162 if (antenna_max)
163 *antenna_max =
164 current_channel->
165 max_antenna_gain;
166
167 switch (bandwidth) {
168 case 0:
169 if (current_channel->flags &
170 IEEE80211_CHAN_NO_10MHZ)
171 bandwidth = 5;
172 else if (current_channel->flags &
173 IEEE80211_CHAN_NO_20MHZ)
174 bandwidth = 10;
175 else
176 bandwidth = 20;
177 break;
178 case 5:
179 break;
180 case 10:
181 if (current_channel->flags &
182 IEEE80211_CHAN_NO_10MHZ)
183 return -EINVAL;
184 break;
185 case 20:
186 if (current_channel->flags &
187 IEEE80211_CHAN_NO_20MHZ)
188 return -EINVAL;
189 break;
190 default:
191 return -EINVAL;
192 }
193
194 if (tx_power > current_channel->max_power)
195 return -EINVAL;
196
197 return 0;
198 }
199 }
200 }
201
Amar Singhalfda6eda2015-11-02 15:08:59 -0800202 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800203}
204
205/**
206 * hdd_ocb_validate_config() - Validates the config data
207 * @config: configuration to be validated
208 *
209 * Return: 0 on success.
210 */
211static int hdd_ocb_validate_config(hdd_adapter_t *adapter,
212 struct sir_ocb_config *config)
213{
214 int i;
215 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
216
217 for (i = 0; i < config->channel_count; i++) {
218 if (dot11p_validate_channel(hdd_ctx->wiphy,
219 config->channels[i].chan_freq,
220 config->channels[i].bandwidth,
221 config->channels[i].max_pwr,
222 &config->channels[i].reg_pwr,
223 &config->channels[i].antenna_max)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700224 hdd_err("Invalid channel frequency %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800225 config->channels[i].chan_freq);
226 return -EINVAL;
227 }
228 if (dot11p_validate_qos_params(config->channels[i].qos_params))
229 return -EINVAL;
230 }
231
232 return 0;
233}
234
235/**
236 * hdd_ocb_register_sta() - Register station with Transport Layer
237 * @adapter: Pointer to HDD Adapter
238 *
239 * This function should be invoked in the OCB Set Schedule callback
240 * to enable the data path in the TL by calling RegisterSTAClient
241 *
242 * Return: 0 on success. -1 on failure.
243 */
244static int hdd_ocb_register_sta(hdd_adapter_t *adapter)
245{
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530246 QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800247 struct ol_txrx_desc_type sta_desc = {0};
248 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
249 hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
250 uint8_t peer_id;
Dhanashri Atre182b0272016-02-17 15:35:07 -0800251 struct ol_txrx_ops txrx_ops;
Leo Changfdb45c32016-10-28 11:09:23 -0700252 void *soc = cds_get_context(QDF_MODULE_ID_SOC);
253 void *pdev = cds_get_context(QDF_MODULE_ID_TXRX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800254
Leo Changfdb45c32016-10-28 11:09:23 -0700255 qdf_status = cdp_peer_register_ocb_peer(soc, hdd_ctx->pcds_context,
256 adapter->macAddressCurrent.bytes,
257 &peer_id);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530258 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700259 hdd_err("Error registering OCB Self Peer!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800260 return -EINVAL;
261 }
262
263 hdd_ctx->sta_to_adapter[peer_id] = adapter;
264
265 sta_desc.sta_id = peer_id;
266 sta_desc.is_qos_enabled = 1;
267
Dhanashri Atre182b0272016-02-17 15:35:07 -0800268 /* Register the vdev transmit and receive functions */
269 qdf_mem_zero(&txrx_ops, sizeof(txrx_ops));
270 txrx_ops.rx.rx = hdd_rx_packet_cbk;
Leo Changfdb45c32016-10-28 11:09:23 -0700271 cdp_vdev_register(soc,
272 cdp_get_vdev_from_vdev_id(soc, pdev, adapter->sessionId),
Dhanashri Atre182b0272016-02-17 15:35:07 -0800273 adapter, &txrx_ops);
Dhanashri Atre168d2b42016-02-22 14:43:06 -0800274 adapter->tx_fn = txrx_ops.tx.tx;
Dhanashri Atre182b0272016-02-17 15:35:07 -0800275
Leo Changfdb45c32016-10-28 11:09:23 -0700276 qdf_status = cdp_peer_register(soc, pdev, &sta_desc);
Dhanashri Atre50141c52016-04-07 13:15:29 -0700277 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700278 hdd_err("Failed to register. Status= %d [0x%08X]",
Dhanashri Atre50141c52016-04-07 13:15:29 -0700279 qdf_status, qdf_status);
280 return -EINVAL;
281 }
282
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800283 if (pHddStaCtx->conn_info.staId[0] != 0 &&
284 pHddStaCtx->conn_info.staId[0] != peer_id) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700285 hdd_err("The ID for the OCB station has changed.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800286 }
287
288 pHddStaCtx->conn_info.staId[0] = peer_id;
Anurag Chouhanc5548422016-02-24 18:33:27 +0530289 qdf_copy_macaddr(&pHddStaCtx->conn_info.peerMacAddress[0],
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800290 &adapter->macAddressCurrent);
291
292 return 0;
293}
294
295/**
296 * hdd_ocb_config_new() - Creates a new OCB configuration
297 * @num_channels: the number of channels
298 * @num_schedule: the schedule size
299 * @ndl_chan_list_len: length in bytes of the NDL chan blob
300 * @ndl_active_state_list_len: length in bytes of the active state blob
301 *
302 * Return: A pointer to the OCB configuration struct, NULL on failure.
303 */
Jeff Johnson929ad032016-10-19 07:34:42 -0700304static
305struct sir_ocb_config *hdd_ocb_config_new(uint32_t num_channels,
306 uint32_t num_schedule,
307 uint32_t ndl_chan_list_len,
308 uint32_t ndl_active_state_list_len)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800309{
310 struct sir_ocb_config *ret = 0;
311 uint32_t len;
312 void *cursor;
313
314 if (num_channels > CFG_TGT_NUM_OCB_CHANNELS ||
315 num_schedule > CFG_TGT_NUM_OCB_SCHEDULES)
316 return NULL;
317
318 len = sizeof(*ret) +
319 num_channels * sizeof(struct sir_ocb_config_channel) +
320 num_schedule * sizeof(struct sir_ocb_config_sched) +
321 ndl_chan_list_len +
322 ndl_active_state_list_len;
323
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530324 cursor = qdf_mem_malloc(len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800325 if (!cursor)
326 goto fail;
327
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800328 ret = cursor;
329 cursor += sizeof(*ret);
330
331 ret->channel_count = num_channels;
332 ret->channels = cursor;
333 cursor += num_channels * sizeof(*ret->channels);
334
335 ret->schedule_size = num_schedule;
336 ret->schedule = cursor;
337 cursor += num_schedule * sizeof(*ret->schedule);
338
339 ret->dcc_ndl_chan_list = cursor;
340 cursor += ndl_chan_list_len;
341
342 ret->dcc_ndl_active_state_list = cursor;
343 cursor += ndl_active_state_list_len;
344
345 return ret;
346
347fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530348 qdf_mem_free(ret);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800349 return NULL;
350}
351
352/**
353 * hdd_ocb_set_config_callback() - OCB set config callback function
354 * @context_ptr: OCB call context
355 * @response_ptr: Pointer to response structure
356 *
357 * This function is registered as a callback with the lower layers
358 * and is used to respond with the status of a OCB set config command.
359 */
360static void hdd_ocb_set_config_callback(void *context_ptr, void *response_ptr)
361{
362 struct hdd_ocb_ctxt *context = context_ptr;
363 struct sir_ocb_set_config_response *resp = response_ptr;
364
365 if (!context)
366 return;
367
368 if (resp && resp->status)
Jeff Johnson5f735d52016-07-06 15:14:45 -0700369 hdd_err("Operation failed: %d", resp->status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800370
371 spin_lock(&hdd_context_lock);
372 if (context->magic == HDD_OCB_MAGIC) {
373 hdd_adapter_t *adapter = context->adapter;
374 if (!resp) {
375 context->status = -EINVAL;
376 complete(&context->completion_evt);
377 spin_unlock(&hdd_context_lock);
378 return;
379 }
380
381 context->adapter->ocb_set_config_resp = *resp;
382 spin_unlock(&hdd_context_lock);
383 if (!resp->status) {
384 /*
385 * OCB set config command successful.
386 * Open the TX data path
387 */
388 if (!hdd_ocb_register_sta(adapter)) {
Ravi Joshic3f5c8a2016-06-13 16:46:44 -0700389 wlan_hdd_netif_queue_control(adapter,
390 WLAN_START_ALL_NETIF_QUEUE_N_CARRIER,
391 WLAN_CONTROL_PATH);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800392 }
393 }
394
395 spin_lock(&hdd_context_lock);
396 if (context->magic == HDD_OCB_MAGIC)
397 complete(&context->completion_evt);
398 spin_unlock(&hdd_context_lock);
399 } else {
400 spin_unlock(&hdd_context_lock);
401 }
402}
403
404/**
405 * hdd_ocb_set_config_req() - Send an OCB set config request
406 * @adapter: a pointer to the adapter
407 * @config: a pointer to the OCB configuration
408 *
409 * Return: 0 on success.
410 */
411static int hdd_ocb_set_config_req(hdd_adapter_t *adapter,
412 struct sir_ocb_config *config)
413{
414 int rc;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530415 QDF_STATUS qdf_status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800416 struct hdd_ocb_ctxt context = {0};
417
418 if (hdd_ocb_validate_config(adapter, config)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700419 hdd_err("The configuration is invalid");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800420 return -EINVAL;
421 }
422
423 init_completion(&context.completion_evt);
424 context.adapter = adapter;
425 context.magic = HDD_OCB_MAGIC;
426
Jeff Johnson5f735d52016-07-06 15:14:45 -0700427 hdd_notice("Disabling queues");
Ravi Joshic3f5c8a2016-06-13 16:46:44 -0700428 wlan_hdd_netif_queue_control(adapter, WLAN_NETIF_TX_DISABLE_N_CARRIER,
429 WLAN_CONTROL_PATH);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800430
431 /* Call the SME API to set the config */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530432 qdf_status = sme_ocb_set_config(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800433 ((hdd_context_t *)adapter->pHddCtx)->hHal, &context,
434 hdd_ocb_set_config_callback, config);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530435 if (qdf_status != QDF_STATUS_SUCCESS) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700436 hdd_err("Error calling SME function.");
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530437 /* Convert from qdf_status to errno */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800438 return -EINVAL;
439 }
440
441 /* Wait for the function to complete. */
442 rc = wait_for_completion_timeout(&context.completion_evt,
443 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
444 if (rc == 0) {
445 rc = -ETIMEDOUT;
446 goto end;
447 }
448 rc = 0;
449
450 if (context.status) {
451 rc = context.status;
452 goto end;
453 }
454
455 if (adapter->ocb_set_config_resp.status) {
456 rc = -EINVAL;
457 goto end;
458 }
459
460 /* fall through */
461end:
462 spin_lock(&hdd_context_lock);
463 context.magic = 0;
464 spin_unlock(&hdd_context_lock);
465 if (rc)
Jeff Johnson5f735d52016-07-06 15:14:45 -0700466 hdd_err("Operation failed: %d", rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800467 return rc;
468}
469
470/**
471 * __iw_set_dot11p_channel_sched() - Handler for WLAN_SET_DOT11P_CHANNEL_SCHED
472 * ioctl
473 * @dev: Pointer to net_device structure
474 * @iw_request_info: IW Request Info
475 * @wrqu: IW Request Userspace Data Pointer
476 * @extra: IW Request Kernel Data Pointer
477 *
478 * Return: 0 on success
479 */
480static int __iw_set_dot11p_channel_sched(struct net_device *dev,
481 struct iw_request_info *info,
482 union iwreq_data *wrqu, char *extra)
483{
Jeff Johnson441e1f72017-02-07 08:50:49 -0800484 int rc;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800485 struct dot11p_channel_sched *sched;
Jeff Johnson441e1f72017-02-07 08:50:49 -0800486 hdd_context_t *hdd_ctx;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800487 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
488 struct sir_ocb_config *config = NULL;
489 uint8_t *mac_addr;
490 int i, j;
491 struct sir_ocb_config_channel *curr_chan;
492
Jeff Johnson6ee91ee2016-02-11 18:55:30 -0800493 ENTER_DEV(dev);
494
Jeff Johnson441e1f72017-02-07 08:50:49 -0800495 hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
496 rc = wlan_hdd_validate_context(hdd_ctx);
497 if (0 != rc)
498 return rc;
499
500 rc = hdd_check_private_wext_control(hdd_ctx, info);
501 if (0 != rc)
502 return rc;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800503
Krunal Sonibe766b02016-03-10 13:00:44 -0800504 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700505 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800506 return -EINVAL;
507 }
508
509 sched = (struct dot11p_channel_sched *)extra;
510
511 /* Scheduled slots same as num channels for compatibility */
512 config = hdd_ocb_config_new(sched->num_channels, sched->num_channels,
513 0, 0);
514 if (config == NULL) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700515 hdd_err("Failed to allocate memory!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800516 return -ENOMEM;
517 }
518
519 /* Identify the vdev interface */
520 config->session_id = adapter->sessionId;
521
522 /* Release all the mac addresses used for OCB */
523 for (i = 0; i < adapter->ocb_mac_addr_count; i++) {
524 wlan_hdd_release_intf_addr(adapter->pHddCtx,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800525 adapter->ocb_mac_address[i].bytes);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800526 }
527 adapter->ocb_mac_addr_count = 0;
528
529 config->channel_count = 0;
530 for (i = 0; i < sched->num_channels; i++) {
531 if (0 == sched->channels[i].channel_freq)
532 continue;
533
534 curr_chan = &(config->channels[config->channel_count]);
535
536 curr_chan->chan_freq = sched->channels[i].channel_freq;
537 /*
538 * tx_power is divided by 2 because ocb_channel.tx_power is
539 * in half dB increments and sir_ocb_config_channel.max_pwr
540 * is in 1 dB increments.
541 */
542 curr_chan->max_pwr = sched->channels[i].tx_power / 2;
543 curr_chan->bandwidth = sched->channels[i].channel_bandwidth;
544 /* assume 10 as default if not provided */
545 if (curr_chan->bandwidth == 0)
546 curr_chan->bandwidth = 10;
547
548 /*
549 * Setup locally administered mac addresses for each channel.
550 * First channel uses the adapter's address.
551 */
552 if (i == 0) {
Anurag Chouhanc5548422016-02-24 18:33:27 +0530553 qdf_copy_macaddr(&curr_chan->mac_address,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800554 &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800555 } else {
556 mac_addr = wlan_hdd_get_intf_addr(adapter->pHddCtx);
557 if (mac_addr == NULL) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700558 hdd_err("Cannot obtain mac address");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800559 rc = -EINVAL;
560 goto fail;
561 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530562 qdf_mem_copy(config->channels[
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800563 config->channel_count].mac_address.bytes,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800564 mac_addr, sizeof(tSirMacAddr));
565 /* Save the mac address to release later */
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530566 qdf_mem_copy(adapter->ocb_mac_address[
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800567 adapter->ocb_mac_addr_count].bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +0530568 mac_addr, QDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800569 adapter->ocb_mac_addr_count++;
570 }
571
572 for (j = 0; j < MAX_NUM_AC; j++) {
573 curr_chan->qos_params[j].aifsn =
574 sched->channels[i].qos_params[j].aifsn;
575 curr_chan->qos_params[j].cwmin =
576 sched->channels[i].qos_params[j].cwmin;
577 curr_chan->qos_params[j].cwmax =
578 sched->channels[i].qos_params[j].cwmax;
579 }
580
581 config->channel_count++;
582 }
583
584 /*
585 * Scheduled slots same as num channels for compatibility with
586 * legacy use.
587 */
588 for (i = 0; i < sched->num_channels; i++) {
589 config->schedule[i].chan_freq = sched->channels[i].channel_freq;
590 config->schedule[i].guard_interval =
591 sched->channels[i].start_guard_interval;
592 config->schedule[i].total_duration =
593 sched->channels[i].duration;
594 }
595
596 rc = hdd_ocb_set_config_req(adapter, config);
597 if (rc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700598 hdd_err("Error while setting OCB config");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800599 goto fail;
600 }
601
602 rc = 0;
603
604fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530605 qdf_mem_free(config);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800606 return rc;
607}
608
609/**
610 * iw_set_dot11p_channel_sched() - IOCTL interface for setting channel schedule
611 * @dev: Pointer to net_device structure
612 * @iw_request_info: IW Request Info
613 * @wrqu: IW Request Userspace Data Pointer
614 * @extra: IW Request Kernel Data Pointer
615 *
616 * Return: 0 on success.
617 */
618int iw_set_dot11p_channel_sched(struct net_device *dev,
619 struct iw_request_info *info,
620 union iwreq_data *wrqu, char *extra)
621{
622 int ret;
623
624 cds_ssr_protect(__func__);
625 ret = __iw_set_dot11p_channel_sched(dev, info, wrqu, extra);
626 cds_ssr_unprotect(__func__);
627
628 return ret;
629}
630
631static const struct nla_policy qca_wlan_vendor_ocb_set_config_policy[
632 QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX + 1] = {
633 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT] = {
634 .type = NLA_U32
635 },
636 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE] = {
637 .type = NLA_U32
638 },
639 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY] = {
640 .type = NLA_BINARY
641 },
642 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY] = {
643 .type = NLA_BINARY
644 },
645 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY] = {
646 .type = NLA_BINARY
647 },
648 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY] = {
649 .type = NLA_BINARY
650 },
651 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS] = {
652 .type = NLA_U32
653 },
654};
655
656static const struct nla_policy qca_wlan_vendor_ocb_set_utc_time_policy[
657 QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX + 1] = {
658 [QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE] = {
659 .type = NLA_BINARY, .len = SIZE_UTC_TIME
660 },
661 [QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR] = {
662 .type = NLA_BINARY, .len = SIZE_UTC_TIME_ERROR
663 },
664};
665
666static const struct nla_policy qca_wlan_vendor_ocb_start_timing_advert_policy[
667 QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX + 1] = {
668 [QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ] = {
669 .type = NLA_U32
670 },
671 [QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE] = {
672 .type = NLA_U32
673 },
674};
675
676static const struct nla_policy qca_wlan_vendor_ocb_stop_timing_advert_policy[
677 QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX + 1] = {
678 [QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ] = {
679 .type = NLA_U32
680 },
681};
682
683static const struct nla_policy qca_wlan_vendor_ocb_get_tsf_timer_resp[] = {
684 [QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH] = {
685 .type = NLA_U32
686 },
687 [QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW] = {
688 .type = NLA_U32
689 },
690};
691
692static const struct nla_policy qca_wlan_vendor_dcc_get_stats[] = {
693 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT] = {
694 .type = NLA_U32
695 },
696 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY] = {
697 .type = NLA_BINARY
698 },
699};
700
701static const struct nla_policy qca_wlan_vendor_dcc_get_stats_resp[] = {
702 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT] = {
703 .type = NLA_U32
704 },
705 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY] = {
706 .type = NLA_BINARY
707 },
708};
709
710static const struct nla_policy qca_wlan_vendor_dcc_clear_stats[] = {
711 [QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP] = {
712 .type = NLA_U32
713 },
714};
715
716static const struct nla_policy qca_wlan_vendor_dcc_update_ndl[
717 QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX + 1] = {
718 [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT] = {
719 .type = NLA_U32
720 },
721 [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY] = {
722 .type = NLA_BINARY
723 },
724 [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY] = {
725 .type = NLA_BINARY
726 },
727};
728
729/**
730 * struct wlan_hdd_ocb_config_channel
731 * @chan_freq: frequency of the channel
732 * @bandwidth: bandwidth of the channel, either 10 or 20 MHz
733 * @mac_address: MAC address assigned to this channel
734 * @qos_params: QoS parameters
735 * @max_pwr: maximum transmit power of the channel (1/2 dBm)
736 * @min_pwr: minimum transmit power of the channel (1/2 dBm)
737 */
738struct wlan_hdd_ocb_config_channel {
739 uint32_t chan_freq;
740 uint32_t bandwidth;
741 uint16_t flags;
742 uint8_t reserved[4];
743 struct sir_qos_params qos_params[MAX_NUM_AC];
744 uint32_t max_pwr;
745 uint32_t min_pwr;
746};
747
748static void wlan_hdd_ocb_config_channel_to_sir_ocb_config_channel(
749 struct sir_ocb_config_channel *dest,
750 struct wlan_hdd_ocb_config_channel *src,
751 uint32_t channel_count)
752{
753 uint32_t i;
754
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530755 qdf_mem_zero(dest, channel_count * sizeof(*dest));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800756
757 for (i = 0; i < channel_count; i++) {
758 dest[i].chan_freq = src[i].chan_freq;
759 dest[i].bandwidth = src[i].bandwidth;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530760 qdf_mem_copy(dest[i].qos_params, src[i].qos_params,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800761 sizeof(dest[i].qos_params));
762 /*
763 * max_pwr and min_pwr are divided by 2 because
764 * wlan_hdd_ocb_config_channel.max_pwr and min_pwr
765 * are in 1/2 dB increments and
766 * sir_ocb_config_channel.max_pwr and min_pwr are in
767 * 1 dB increments.
768 */
769 dest[i].max_pwr = src[i].max_pwr / 2;
770 dest[i].min_pwr = (src[i].min_pwr + 1) / 2;
771 dest[i].flags = src[i].flags;
772 }
773}
774
775/**
776 * __wlan_hdd_cfg80211_ocb_set_config() - Interface for set config command
777 * @wiphy: pointer to the wiphy
778 * @wdev: pointer to the wdev
779 * @data: The netlink data
780 * @data_len: The length of the netlink data in bytes
781 *
782 * Return: 0 on success.
783 */
784static int __wlan_hdd_cfg80211_ocb_set_config(struct wiphy *wiphy,
785 struct wireless_dev *wdev,
786 const void *data,
787 int data_len)
788{
789 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
790 struct net_device *dev = wdev->netdev;
791 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
792 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX + 1];
793 struct nlattr *channel_array;
794 struct nlattr *sched_array;
795 struct nlattr *ndl_chan_list;
796 uint32_t ndl_chan_list_len;
797 struct nlattr *ndl_active_state_list;
798 uint32_t ndl_active_state_list_len;
799 uint32_t flags = 0;
800 int i;
Jeff Johnson929ad032016-10-19 07:34:42 -0700801 uint32_t channel_count, schedule_size;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800802 struct sir_ocb_config *config;
803 int rc = -EINVAL;
804 uint8_t *mac_addr;
805
Jeff Johnson1f61b612016-02-12 16:28:33 -0800806 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800807
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530808 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800809 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800810
Krunal Sonibe766b02016-03-10 13:00:44 -0800811 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700812 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800813 return -EINVAL;
814 }
815
816 /* Parse the netlink message */
817 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX,
818 data,
819 data_len, qca_wlan_vendor_ocb_set_config_policy)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700820 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800821 return -EINVAL;
822 }
823
824 /* Get the number of channels in the schedule */
825 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700826 hdd_err("CHANNEL_COUNT is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800827 return -EINVAL;
828 }
829 channel_count = nla_get_u32(
830 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT]);
831
832 /* Get the size of the channel schedule */
833 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700834 hdd_err("SCHEDULE_SIZE is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800835 return -EINVAL;
836 }
837 schedule_size = nla_get_u32(
838 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE]);
839
840 /* Get the ndl chan array and the ndl active state array. */
841 ndl_chan_list =
842 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY];
843 ndl_chan_list_len = (ndl_chan_list ? nla_len(ndl_chan_list) : 0);
844
845 ndl_active_state_list =
846 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY];
847 ndl_active_state_list_len = (ndl_active_state_list ?
848 nla_len(ndl_active_state_list) : 0);
849
850 /* Get the flags */
851 if (tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS])
852 flags = nla_get_u32(tb[
853 QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS]);
854
855 config = hdd_ocb_config_new(channel_count, schedule_size,
856 ndl_chan_list_len,
857 ndl_active_state_list_len);
858 if (config == NULL) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700859 hdd_err("Failed to allocate memory!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800860 return -ENOMEM;
861 }
862
863 config->channel_count = channel_count;
864 config->schedule_size = schedule_size;
865 config->flags = flags;
866
867 /* Read the channel array */
868 channel_array = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY];
869 if (!channel_array) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700870 hdd_err("No channel present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800871 goto fail;
872 }
873 if (nla_len(channel_array) != channel_count *
874 sizeof(struct wlan_hdd_ocb_config_channel)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700875 hdd_err("CHANNEL_ARRAY is not the correct size");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800876 goto fail;
877 }
878 wlan_hdd_ocb_config_channel_to_sir_ocb_config_channel(
879 config->channels, nla_data(channel_array), channel_count);
880
881 /* Identify the vdev interface */
882 config->session_id = adapter->sessionId;
883
884 /* Release all the mac addresses used for OCB */
885 for (i = 0; i < adapter->ocb_mac_addr_count; i++) {
886 wlan_hdd_release_intf_addr(adapter->pHddCtx,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800887 adapter->ocb_mac_address[i].bytes);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800888 }
889 adapter->ocb_mac_addr_count = 0;
890
891 /*
892 * Setup locally administered mac addresses for each channel.
893 * First channel uses the adapter's address.
894 */
895 for (i = 0; i < config->channel_count; i++) {
896 if (i == 0) {
Anurag Chouhanc5548422016-02-24 18:33:27 +0530897 qdf_copy_macaddr(&config->channels[i].mac_address,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800898 &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800899 } else {
900 mac_addr = wlan_hdd_get_intf_addr(adapter->pHddCtx);
901 if (mac_addr == NULL) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700902 hdd_err("Cannot obtain mac address");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800903 goto fail;
904 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530905 qdf_mem_copy(config->channels[i].mac_address.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +0530906 mac_addr, QDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800907 /* Save the mac address to release later */
Anurag Chouhanc5548422016-02-24 18:33:27 +0530908 qdf_copy_macaddr(&adapter->ocb_mac_address[
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800909 adapter->ocb_mac_addr_count],
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800910 &config->channels[i].mac_address);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800911 adapter->ocb_mac_addr_count++;
912 }
913 }
914
915 /* Read the schedule array */
916 sched_array = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY];
917 if (!sched_array) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700918 hdd_err("No channel present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800919 goto fail;
920 }
921 if (nla_len(sched_array) != schedule_size * sizeof(*config->schedule)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700922 hdd_err("SCHEDULE_ARRAY is not the correct size");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800923 goto fail;
924 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530925 qdf_mem_copy(config->schedule, nla_data(sched_array),
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800926 nla_len(sched_array));
927
928 /* Copy the NDL chan array */
929 if (ndl_chan_list_len) {
930 config->dcc_ndl_chan_list_len = ndl_chan_list_len;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530931 qdf_mem_copy(config->dcc_ndl_chan_list, nla_data(ndl_chan_list),
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800932 nla_len(ndl_chan_list));
933 }
934
935 /* Copy the NDL active state array */
936 if (ndl_active_state_list_len) {
937 config->dcc_ndl_active_state_list_len =
938 ndl_active_state_list_len;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530939 qdf_mem_copy(config->dcc_ndl_active_state_list,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800940 nla_data(ndl_active_state_list),
941 nla_len(ndl_active_state_list));
942 }
943
944 rc = hdd_ocb_set_config_req(adapter, config);
945 if (rc)
Jeff Johnson5f735d52016-07-06 15:14:45 -0700946 hdd_err("Error while setting OCB config: %d", rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800947
948fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530949 qdf_mem_free(config);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800950 return rc;
951}
952
953/**
954 * wlan_hdd_cfg80211_ocb_set_config() - Interface for set config command
955 * @wiphy: pointer to the wiphy
956 * @wdev: pointer to the wdev
957 * @data: The netlink data
958 * @data_len: The length of the netlink data in bytes
959 *
960 * Return: 0 on success.
961 */
962int wlan_hdd_cfg80211_ocb_set_config(struct wiphy *wiphy,
963 struct wireless_dev *wdev,
964 const void *data,
965 int data_len)
966{
967 int ret;
968
969 cds_ssr_protect(__func__);
970 ret = __wlan_hdd_cfg80211_ocb_set_config(wiphy, wdev, data, data_len);
971 cds_ssr_unprotect(__func__);
972
973 return ret;
974}
975
976/**
977 * __wlan_hdd_cfg80211_ocb_set_utc_time() - Interface for set UTC time command
978 * @wiphy: pointer to the wiphy
979 * @wdev: pointer to the wdev
980 * @data: The netlink data
981 * @data_len: The length of the netlink data in bytes
982 *
983 * Return: 0 on success.
984 */
985static int __wlan_hdd_cfg80211_ocb_set_utc_time(struct wiphy *wiphy,
986 struct wireless_dev *wdev,
987 const void *data,
988 int data_len)
989{
990 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
991 struct net_device *dev = wdev->netdev;
992 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
993 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX + 1];
994 struct nlattr *utc_attr;
995 struct nlattr *time_error_attr;
996 struct sir_ocb_utc *utc;
997 int rc = -EINVAL;
998
Jeff Johnson1f61b612016-02-12 16:28:33 -0800999 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001000
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301001 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001002 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001003
Krunal Sonibe766b02016-03-10 13:00:44 -08001004 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001005 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001006 return -EINVAL;
1007 }
1008
1009 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001010 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001011 return -EINVAL;
1012 }
1013
1014 /* Parse the netlink message */
1015 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX,
1016 data,
1017 data_len, qca_wlan_vendor_ocb_set_utc_time_policy)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001018 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001019 return -EINVAL;
1020 }
1021
1022 /* Read the UTC time */
1023 utc_attr = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE];
1024 if (!utc_attr) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001025 hdd_err("UTC_TIME is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001026 return -EINVAL;
1027 }
1028 if (nla_len(utc_attr) != SIZE_UTC_TIME) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001029 hdd_err("UTC_TIME is not the correct size");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001030 return -EINVAL;
1031 }
1032
1033 /* Read the time error */
1034 time_error_attr = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR];
1035 if (!time_error_attr) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001036 hdd_err("UTC_TIME is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001037 return -EINVAL;
1038 }
1039 if (nla_len(time_error_attr) != SIZE_UTC_TIME_ERROR) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001040 hdd_err("UTC_TIME is not the correct size");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001041 return -EINVAL;
1042 }
1043
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301044 utc = qdf_mem_malloc(sizeof(*utc));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001045 if (!utc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001046 hdd_err("qdf_mem_malloc failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001047 return -ENOMEM;
1048 }
1049 utc->vdev_id = adapter->sessionId;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301050 qdf_mem_copy(utc->utc_time, nla_data(utc_attr), SIZE_UTC_TIME);
1051 qdf_mem_copy(utc->time_error, nla_data(time_error_attr),
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001052 SIZE_UTC_TIME_ERROR);
1053
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301054 if (sme_ocb_set_utc_time(hdd_ctx->hHal, utc) != QDF_STATUS_SUCCESS) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001055 hdd_err("Error while setting UTC time");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001056 rc = -EINVAL;
1057 } else {
1058 rc = 0;
1059 }
1060
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301061 qdf_mem_free(utc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001062 return rc;
1063}
1064
1065/**
1066 * wlan_hdd_cfg80211_ocb_set_utc_time() - Interface for the set UTC time command
1067 * @wiphy: pointer to the wiphy
1068 * @wdev: pointer to the wdev
1069 * @data: The netlink data
1070 * @data_len: The length of the netlink data in bytes
1071 *
1072 * Return: 0 on success.
1073 */
1074int wlan_hdd_cfg80211_ocb_set_utc_time(struct wiphy *wiphy,
1075 struct wireless_dev *wdev,
1076 const void *data,
1077 int data_len)
1078{
1079 int ret;
1080
1081 cds_ssr_protect(__func__);
1082 ret = __wlan_hdd_cfg80211_ocb_set_utc_time(wiphy, wdev, data, data_len);
1083 cds_ssr_unprotect(__func__);
1084
1085 return ret;
1086}
1087
1088/**
1089 * __wlan_hdd_cfg80211_ocb_start_timing_advert() - Interface for start TA cmd
1090 * @wiphy: pointer to the wiphy
1091 * @wdev: pointer to the wdev
1092 * @data: The netlink data
1093 * @data_len: The length of the netlink data in bytes
1094 *
1095 * Return: 0 on success.
1096 */
1097static int
1098__wlan_hdd_cfg80211_ocb_start_timing_advert(struct wiphy *wiphy,
1099 struct wireless_dev *wdev,
1100 const void *data,
1101 int data_len)
1102{
1103 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1104 struct net_device *dev = wdev->netdev;
1105 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001106 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX + 1];
1107 struct sir_ocb_timing_advert *timing_advert;
1108 int rc = -EINVAL;
1109
Jeff Johnson1f61b612016-02-12 16:28:33 -08001110 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001111
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301112 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001113 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001114
Krunal Sonibe766b02016-03-10 13:00:44 -08001115 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001116 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001117 return -EINVAL;
1118 }
1119
1120 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001121 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001122 return -EINVAL;
1123 }
1124
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301125 timing_advert = qdf_mem_malloc(sizeof(*timing_advert));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001126 if (!timing_advert) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001127 hdd_err("qdf_mem_malloc failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001128 return -ENOMEM;
1129 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001130 timing_advert->vdev_id = adapter->sessionId;
1131
1132 /* Parse the netlink message */
1133 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX,
1134 data,
1135 data_len,
1136 qca_wlan_vendor_ocb_start_timing_advert_policy)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001137 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001138 goto fail;
1139 }
1140
1141 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001142 hdd_err("CHANNEL_FREQ is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001143 goto fail;
1144 }
1145 timing_advert->chan_freq = nla_get_u32(
1146 tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ]);
1147
1148 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001149 hdd_err("REPEAT_RATE is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001150 goto fail;
1151 }
1152 timing_advert->repeat_rate = nla_get_u32(
1153 tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE]);
1154
1155 timing_advert->template_length =
Naveen Rawatb4d37622015-11-13 16:15:25 -08001156 sme_ocb_gen_timing_advert_frame(hdd_ctx->hHal,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001157 *(tSirMacAddr *)&adapter->macAddressCurrent.bytes,
1158 &timing_advert->template_value,
1159 &timing_advert->timestamp_offset,
1160 &timing_advert->time_value_offset);
1161 if (timing_advert->template_length <= 0) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001162 hdd_err("Error while generating the TA frame");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001163 goto fail;
1164 }
1165
1166 if (sme_ocb_start_timing_advert(hdd_ctx->hHal, timing_advert) !=
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301167 QDF_STATUS_SUCCESS) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001168 hdd_err("Error while starting timing advert");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001169 rc = -EINVAL;
1170 } else {
1171 rc = 0;
1172 }
1173
1174fail:
1175 if (timing_advert->template_value)
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301176 qdf_mem_free(timing_advert->template_value);
1177 qdf_mem_free(timing_advert);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001178 return rc;
1179}
1180
1181/**
1182 * wlan_hdd_cfg80211_ocb_start_timing_advert() - Interface for the start TA cmd
1183 * @wiphy: pointer to the wiphy
1184 * @wdev: pointer to the wdev
1185 * @data: The netlink data
1186 * @data_len: The length of the netlink data in bytes
1187 *
1188 * Return: 0 on success.
1189 */
1190int wlan_hdd_cfg80211_ocb_start_timing_advert(struct wiphy *wiphy,
1191 struct wireless_dev *wdev,
1192 const void *data,
1193 int data_len)
1194{
1195 int ret;
1196
1197 cds_ssr_protect(__func__);
1198 ret = __wlan_hdd_cfg80211_ocb_start_timing_advert(wiphy, wdev,
1199 data, data_len);
1200 cds_ssr_unprotect(__func__);
1201
1202 return ret;
1203}
1204
1205/**
1206 * __wlan_hdd_cfg80211_ocb_stop_timing_advert() - Interface for the stop TA cmd
1207 * @wiphy: pointer to the wiphy
1208 * @wdev: pointer to the wdev
1209 * @data: The netlink data
1210 * @data_len: The length of the netlink data in bytes
1211 *
1212 * Return: 0 on success.
1213 */
1214static int
1215__wlan_hdd_cfg80211_ocb_stop_timing_advert(struct wiphy *wiphy,
1216 struct wireless_dev *wdev,
1217 const void *data,
1218 int data_len)
1219{
1220 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1221 struct net_device *dev = wdev->netdev;
1222 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1223 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX + 1];
1224 struct sir_ocb_timing_advert *timing_advert;
1225 int rc = -EINVAL;
1226
Jeff Johnson1f61b612016-02-12 16:28:33 -08001227 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001228
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301229 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001230 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001231
Krunal Sonibe766b02016-03-10 13:00:44 -08001232 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001233 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001234 return -EINVAL;
1235 }
1236
1237 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001238 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001239 return -EINVAL;
1240 }
1241
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301242 timing_advert = qdf_mem_malloc(sizeof(*timing_advert));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001243 if (!timing_advert) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001244 hdd_err("qdf_mem_malloc failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001245 return -ENOMEM;
1246 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001247 timing_advert->vdev_id = adapter->sessionId;
1248
1249 /* Parse the netlink message */
1250 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX,
1251 data,
1252 data_len,
1253 qca_wlan_vendor_ocb_stop_timing_advert_policy)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001254 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001255 goto fail;
1256 }
1257
1258 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001259 hdd_err("CHANNEL_FREQ is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001260 goto fail;
1261 }
1262 timing_advert->chan_freq = nla_get_u32(
1263 tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ]);
1264
1265 if (sme_ocb_stop_timing_advert(hdd_ctx->hHal, timing_advert) !=
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301266 QDF_STATUS_SUCCESS) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001267 hdd_err("Error while stopping timing advert");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001268 rc = -EINVAL;
1269 } else {
1270 rc = 0;
1271 }
1272
1273fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301274 qdf_mem_free(timing_advert);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001275 return rc;
1276}
1277
1278/**
1279 * wlan_hdd_cfg80211_ocb_stop_timing_advert() - Interface for the stop TA cmd
1280 * @wiphy: pointer to the wiphy
1281 * @wdev: pointer to the wdev
1282 * @data: The netlink data
1283 * @data_len: The length of the netlink data in bytes
1284 *
1285 * Return: 0 on success.
1286 */
1287int wlan_hdd_cfg80211_ocb_stop_timing_advert(struct wiphy *wiphy,
1288 struct wireless_dev *wdev,
1289 const void *data,
1290 int data_len)
1291{
1292 int ret;
1293
1294 cds_ssr_protect(__func__);
1295 ret = __wlan_hdd_cfg80211_ocb_stop_timing_advert(wiphy, wdev,
1296 data, data_len);
1297 cds_ssr_unprotect(__func__);
1298
1299 return ret;
1300}
1301
1302/**
1303 * hdd_ocb_get_tsf_timer_callback() - Callback to get TSF command
1304 * @context_ptr: request context
1305 * @response_ptr: response data
1306 */
1307static void hdd_ocb_get_tsf_timer_callback(void *context_ptr,
1308 void *response_ptr)
1309{
1310 struct hdd_ocb_ctxt *context = context_ptr;
1311 struct sir_ocb_get_tsf_timer_response *response = response_ptr;
1312
1313 if (!context)
1314 return;
1315
1316 spin_lock(&hdd_context_lock);
1317 if (context->magic == HDD_OCB_MAGIC) {
1318 if (response) {
1319 context->adapter->ocb_get_tsf_timer_resp = *response;
1320 context->status = 0;
1321 } else {
1322 context->status = -EINVAL;
1323 }
1324 complete(&context->completion_evt);
1325 }
1326 spin_unlock(&hdd_context_lock);
1327}
1328
1329/**
1330 * __wlan_hdd_cfg80211_ocb_get_tsf_timer() - Interface for get TSF timer cmd
1331 * @wiphy: pointer to the wiphy
1332 * @wdev: pointer to the wdev
1333 * @data: The netlink data
1334 * @data_len: The length of the netlink data in bytes
1335 *
1336 * Return: 0 on success.
1337 */
1338static int
1339__wlan_hdd_cfg80211_ocb_get_tsf_timer(struct wiphy *wiphy,
1340 struct wireless_dev *wdev,
1341 const void *data,
1342 int data_len)
1343{
1344 struct sk_buff *nl_resp = 0;
1345 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1346 struct net_device *dev = wdev->netdev;
1347 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1348 int rc = -EINVAL;
1349 struct sir_ocb_get_tsf_timer request = {0};
1350 struct hdd_ocb_ctxt context = {0};
1351
Jeff Johnson1f61b612016-02-12 16:28:33 -08001352 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001353
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301354 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001355 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001356
Krunal Sonibe766b02016-03-10 13:00:44 -08001357 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001358 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001359 return -EINVAL;
1360 }
1361
1362 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001363 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001364 return -EINVAL;
1365 }
1366
1367 /* Initialize the callback context */
1368 init_completion(&context.completion_evt);
1369 context.adapter = adapter;
1370 context.magic = HDD_OCB_MAGIC;
1371
1372 request.vdev_id = adapter->sessionId;
1373 /* Call the SME function */
1374 rc = sme_ocb_get_tsf_timer(hdd_ctx->hHal, &context,
1375 hdd_ocb_get_tsf_timer_callback,
1376 &request);
1377 if (rc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001378 hdd_err("Error calling SME function");
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301379 /* Need to convert from qdf_status to errno. */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001380 return -EINVAL;
1381 }
1382
1383 rc = wait_for_completion_timeout(&context.completion_evt,
1384 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
1385 if (rc == 0) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001386 hdd_err("Operation timed out");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001387 rc = -ETIMEDOUT;
1388 goto end;
1389 }
1390 rc = 0;
1391
1392 if (context.status) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001393 hdd_err("Operation failed: %d", context.status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001394 rc = context.status;
1395 goto end;
1396 }
1397
1398 /* Allocate the buffer for the response. */
1399 nl_resp = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
1400 2 * sizeof(uint32_t) + NLMSG_HDRLEN);
1401
1402 if (!nl_resp) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001403 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001404 rc = -ENOMEM;
1405 goto end;
1406 }
1407
Jeff Johnson5f735d52016-07-06 15:14:45 -07001408 hdd_err("Got TSF timer response, high=%d, low=%d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001409 adapter->ocb_get_tsf_timer_resp.timer_high,
1410 adapter->ocb_get_tsf_timer_resp.timer_low);
1411
1412 /* Populate the response. */
1413 rc = nla_put_u32(nl_resp,
1414 QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH,
1415 adapter->ocb_get_tsf_timer_resp.timer_high);
1416 if (rc)
1417 goto end;
1418 rc = nla_put_u32(nl_resp,
1419 QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW,
1420 adapter->ocb_get_tsf_timer_resp.timer_low);
1421 if (rc)
1422 goto end;
1423
1424 /* Send the response. */
1425 rc = cfg80211_vendor_cmd_reply(nl_resp);
1426 nl_resp = NULL;
1427 if (rc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001428 hdd_err("cfg80211_vendor_cmd_reply failed: %d", rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001429 goto end;
1430 }
1431
1432end:
1433 spin_lock(&hdd_context_lock);
1434 context.magic = 0;
1435 spin_unlock(&hdd_context_lock);
1436 if (nl_resp)
1437 kfree_skb(nl_resp);
1438 return rc;
1439}
1440
1441/**
1442 * wlan_hdd_cfg80211_ocb_get_tsf_timer() - Interface for get TSF timer cmd
1443 * @wiphy: pointer to the wiphy
1444 * @wdev: pointer to the wdev
1445 * @data: The netlink data
1446 * @data_len: The length of the netlink data in bytes
1447 *
1448 * Return: 0 on success.
1449 */
1450int wlan_hdd_cfg80211_ocb_get_tsf_timer(struct wiphy *wiphy,
1451 struct wireless_dev *wdev,
1452 const void *data,
1453 int data_len)
1454{
1455 int ret;
1456
1457 cds_ssr_protect(__func__);
1458 ret = __wlan_hdd_cfg80211_ocb_get_tsf_timer(wiphy, wdev,
1459 data, data_len);
1460 cds_ssr_unprotect(__func__);
1461
1462 return ret;
1463}
1464
1465/**
1466 * hdd_dcc_get_stats_callback() - Callback to get stats command
1467 * @context_ptr: request context
1468 * @response_ptr: response data
1469 */
1470static void hdd_dcc_get_stats_callback(void *context_ptr, void *response_ptr)
1471{
1472 struct hdd_ocb_ctxt *context = context_ptr;
1473 struct sir_dcc_get_stats_response *response = response_ptr;
1474 struct sir_dcc_get_stats_response *hdd_resp;
1475
1476 if (!context)
1477 return;
1478
1479 spin_lock(&hdd_context_lock);
1480 if (context->magic == HDD_OCB_MAGIC) {
1481 if (response) {
1482 /*
1483 * If the response is hanging around from the previous
1484 * request, delete it
1485 */
1486 if (context->adapter->dcc_get_stats_resp) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301487 qdf_mem_free(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001488 context->adapter->dcc_get_stats_resp);
1489 }
1490 context->adapter->dcc_get_stats_resp =
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301491 qdf_mem_malloc(sizeof(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001492 *context->adapter->dcc_get_stats_resp) +
1493 response->channel_stats_array_len);
1494 if (context->adapter->dcc_get_stats_resp) {
1495 hdd_resp = context->adapter->dcc_get_stats_resp;
1496 *hdd_resp = *response;
1497 hdd_resp->channel_stats_array =
1498 (void *)hdd_resp + sizeof(*hdd_resp);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301499 qdf_mem_copy(hdd_resp->channel_stats_array,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001500 response->channel_stats_array,
1501 response->channel_stats_array_len);
1502 context->status = 0;
1503 } else {
1504 context->status = -ENOMEM;
1505 }
1506 } else {
1507 context->status = -EINVAL;
1508 }
1509 complete(&context->completion_evt);
1510 }
1511 spin_unlock(&hdd_context_lock);
1512}
1513
1514/**
1515 * __wlan_hdd_cfg80211_dcc_get_stats() - Interface for get dcc stats
1516 * @wiphy: pointer to the wiphy
1517 * @wdev: pointer to the wdev
1518 * @data: The netlink data
1519 * @data_len: The length of the netlink data in bytes
1520 *
1521 * Return: 0 on success.
1522 */
1523static int __wlan_hdd_cfg80211_dcc_get_stats(struct wiphy *wiphy,
1524 struct wireless_dev *wdev,
1525 const void *data,
1526 int data_len)
1527{
1528 uint32_t channel_count = 0;
1529 uint32_t request_array_len = 0;
1530 void *request_array = 0;
1531 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1532 struct net_device *dev = wdev->netdev;
1533 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1534 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_MAX + 1];
1535 struct sk_buff *nl_resp = 0;
1536 int rc = -EINVAL;
1537 struct sir_dcc_get_stats request = {0};
1538 struct hdd_ocb_ctxt context = {0};
1539
Jeff Johnson1f61b612016-02-12 16:28:33 -08001540 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001541
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301542 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001543 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001544
Krunal Sonibe766b02016-03-10 13:00:44 -08001545 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001546 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001547 return -EINVAL;
1548 }
1549
1550 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001551 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001552 return -EINVAL;
1553 }
1554
1555 /* Parse the netlink message */
1556 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_MAX,
1557 data,
1558 data_len,
1559 qca_wlan_vendor_dcc_get_stats)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001560 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001561 return -EINVAL;
1562 }
1563
1564 /* Validate all the parameters are present */
1565 if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT] ||
1566 !tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001567 hdd_err("Parameters are not present.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001568 return -EINVAL;
1569 }
1570
1571 channel_count = nla_get_u32(
1572 tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT]);
1573 request_array_len = nla_len(
1574 tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]);
1575 request_array = nla_data(
1576 tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]);
1577
1578 /* Initialize the callback context */
1579 init_completion(&context.completion_evt);
1580 context.adapter = adapter;
1581 context.magic = HDD_OCB_MAGIC;
1582
1583 request.vdev_id = adapter->sessionId;
1584 request.channel_count = channel_count;
1585 request.request_array_len = request_array_len;
1586 request.request_array = request_array;
1587
1588 /* Call the SME function. */
1589 rc = sme_dcc_get_stats(hdd_ctx->hHal, &context,
1590 hdd_dcc_get_stats_callback,
1591 &request);
1592 if (rc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001593 hdd_err("Error calling SME function");
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301594 /* Need to convert from qdf_status to errno. */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001595 return -EINVAL;
1596 }
1597
1598 /* Wait for the function to complete. */
1599 rc = wait_for_completion_timeout(&context.completion_evt,
1600 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
1601 if (rc == 0) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001602 hdd_err("Operation failed: %d", rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001603 rc = -ETIMEDOUT;
1604 goto end;
1605 }
1606
1607 if (context.status) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001608 hdd_err("There was error: %d", context.status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001609 rc = context.status;
1610 goto end;
1611 }
1612
1613 if (!adapter->dcc_get_stats_resp) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001614 hdd_err("The response was NULL");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001615 rc = -EINVAL;
1616 goto end;
1617 }
1618
1619 /* Allocate the buffer for the response. */
1620 nl_resp = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(uint32_t) +
1621 adapter->dcc_get_stats_resp->channel_stats_array_len +
1622 NLMSG_HDRLEN);
1623 if (!nl_resp) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001624 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001625 rc = -ENOMEM;
1626 goto end;
1627 }
1628
1629 /* Populate the response. */
1630 rc = nla_put_u32(nl_resp,
1631 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT,
1632 adapter->dcc_get_stats_resp->num_channels);
1633 if (rc)
1634 goto end;
1635 rc = nla_put(nl_resp,
1636 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY,
1637 adapter->dcc_get_stats_resp->channel_stats_array_len,
1638 adapter->dcc_get_stats_resp->channel_stats_array);
1639 if (rc)
1640 goto end;
1641
1642 /* Send the response. */
1643 rc = cfg80211_vendor_cmd_reply(nl_resp);
1644 nl_resp = NULL;
1645 if (rc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001646 hdd_err("cfg80211_vendor_cmd_reply failed: %d", rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001647 goto end;
1648 }
1649
1650 /* fall through */
1651end:
1652 spin_lock(&hdd_context_lock);
1653 context.magic = 0;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301654 qdf_mem_free(adapter->dcc_get_stats_resp);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001655 adapter->dcc_get_stats_resp = NULL;
1656 spin_unlock(&hdd_context_lock);
1657 if (nl_resp)
1658 kfree_skb(nl_resp);
1659 return rc;
1660}
1661
1662/**
1663 * wlan_hdd_cfg80211_dcc_get_stats() - Interface for get dcc stats
1664 * @wiphy: pointer to the wiphy
1665 * @wdev: pointer to the wdev
1666 * @data: The netlink data
1667 * @data_len: The length of the netlink data in bytes
1668 *
1669 * Return: 0 on success.
1670 */
1671int wlan_hdd_cfg80211_dcc_get_stats(struct wiphy *wiphy,
1672 struct wireless_dev *wdev,
1673 const void *data,
1674 int data_len)
1675{
1676 int ret;
1677
1678 cds_ssr_protect(__func__);
1679 ret = __wlan_hdd_cfg80211_dcc_get_stats(wiphy, wdev,
1680 data, data_len);
1681 cds_ssr_unprotect(__func__);
1682
1683 return ret;
1684}
1685
1686/**
1687 * __wlan_hdd_cfg80211_dcc_clear_stats() - Interface for clear dcc stats cmd
1688 * @wiphy: pointer to the wiphy
1689 * @wdev: pointer to the wdev
1690 * @data: The netlink data
1691 * @data_len: The length of the netlink data in bytes
1692 *
1693 * Return: 0 on success.
1694 */
1695static int __wlan_hdd_cfg80211_dcc_clear_stats(struct wiphy *wiphy,
1696 struct wireless_dev *wdev,
1697 const void *data,
1698 int data_len)
1699{
1700 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1701 struct net_device *dev = wdev->netdev;
1702 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1703 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_MAX + 1];
1704
Jeff Johnson1f61b612016-02-12 16:28:33 -08001705 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001706
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301707 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001708 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001709
Krunal Sonibe766b02016-03-10 13:00:44 -08001710 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001711 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001712 return -EINVAL;
1713 }
1714
1715 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001716 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001717 return -EINVAL;
1718 }
1719
1720 /* Parse the netlink message */
1721 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_MAX,
1722 data,
1723 data_len,
1724 qca_wlan_vendor_dcc_clear_stats)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001725 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001726 return -EINVAL;
1727 }
1728
1729 /* Verify that the parameter is present */
1730 if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001731 hdd_err("Parameters are not present.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001732 return -EINVAL;
1733 }
1734
1735 /* Call the SME function */
1736 if (sme_dcc_clear_stats(hdd_ctx->hHal, adapter->sessionId,
1737 nla_get_u32(
1738 tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP])) !=
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301739 QDF_STATUS_SUCCESS) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001740 hdd_err("Error calling SME function.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001741 return -EINVAL;
1742 }
1743
1744 return 0;
1745}
1746
1747/**
1748 * wlan_hdd_cfg80211_dcc_clear_stats() - Interface for clear dcc stats cmd
1749 * @wiphy: pointer to the wiphy
1750 * @wdev: pointer to the wdev
1751 * @data: The netlink data
1752 * @data_len: The length of the netlink data in bytes
1753 *
1754 * Return: 0 on success.
1755 */
1756int wlan_hdd_cfg80211_dcc_clear_stats(struct wiphy *wiphy,
1757 struct wireless_dev *wdev,
1758 const void *data,
1759 int data_len)
1760{
1761 int ret;
1762
1763 cds_ssr_protect(__func__);
1764 ret = __wlan_hdd_cfg80211_dcc_clear_stats(wiphy, wdev,
1765 data, data_len);
1766 cds_ssr_unprotect(__func__);
1767
1768 return ret;
1769}
1770
1771/**
1772 * hdd_dcc_update_ndl_callback() - Callback to update NDL command
1773 * @context_ptr: request context
1774 * @response_ptr: response data
1775 */
1776static void hdd_dcc_update_ndl_callback(void *context_ptr, void *response_ptr)
1777{
1778 struct hdd_ocb_ctxt *context = context_ptr;
1779 struct sir_dcc_update_ndl_response *response = response_ptr;
1780
1781 if (!context)
1782 return;
1783
1784 spin_lock(&hdd_context_lock);
1785 if (context->magic == HDD_OCB_MAGIC) {
1786 if (response) {
1787 context->adapter->dcc_update_ndl_resp = *response;
1788 context->status = 0;
1789 } else {
1790 context->status = -EINVAL;
1791 }
1792 complete(&context->completion_evt);
1793 }
1794 spin_unlock(&hdd_context_lock);
1795}
1796
1797/**
1798 * __wlan_hdd_cfg80211_dcc_update_ndl() - Interface for update dcc cmd
1799 * @wiphy: pointer to the wiphy
1800 * @wdev: pointer to the wdev
1801 * @data: The netlink data
1802 * @data_len: The length of the netlink data in bytes
1803 *
1804 * Return: 0 on success.
1805 */
1806static int __wlan_hdd_cfg80211_dcc_update_ndl(struct wiphy *wiphy,
1807 struct wireless_dev *wdev,
1808 const void *data,
1809 int data_len)
1810{
1811 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1812 struct net_device *dev = wdev->netdev;
1813 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1814 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX + 1];
1815 struct sir_dcc_update_ndl request;
1816 uint32_t channel_count;
1817 uint32_t ndl_channel_array_len;
1818 void *ndl_channel_array;
1819 uint32_t ndl_active_state_array_len;
1820 void *ndl_active_state_array;
1821 int rc = -EINVAL;
1822 struct hdd_ocb_ctxt context = {0};
1823
Jeff Johnson1f61b612016-02-12 16:28:33 -08001824 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001825
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301826 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001827 goto end;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001828
Krunal Sonibe766b02016-03-10 13:00:44 -08001829 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001830 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001831 goto end;
1832 }
1833
1834 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001835 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001836 return -EINVAL;
1837 }
1838
1839 /* Parse the netlink message */
1840 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX,
1841 data,
1842 data_len,
1843 qca_wlan_vendor_dcc_update_ndl)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001844 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001845 goto end;
1846 }
1847
1848 /* Verify that the parameter is present */
1849 if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT] ||
1850 !tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY] ||
1851 !tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001852 hdd_err("Parameters are not present.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001853 return -EINVAL;
1854 }
1855
1856 channel_count = nla_get_u32(
1857 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT]);
1858 ndl_channel_array_len = nla_len(
1859 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY]);
1860 ndl_channel_array = nla_data(
1861 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY]);
1862 ndl_active_state_array_len = nla_len(
1863 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]);
1864 ndl_active_state_array = nla_data(
1865 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]);
1866
1867 /* Initialize the callback context */
1868 init_completion(&context.completion_evt);
1869 context.adapter = adapter;
1870 context.magic = HDD_OCB_MAGIC;
1871
1872 /* Copy the parameters to the request structure. */
1873 request.vdev_id = adapter->sessionId;
1874 request.channel_count = channel_count;
1875 request.dcc_ndl_chan_list_len = ndl_channel_array_len;
1876 request.dcc_ndl_chan_list = ndl_channel_array;
1877 request.dcc_ndl_active_state_list_len = ndl_active_state_array_len;
1878 request.dcc_ndl_active_state_list = ndl_active_state_array;
1879
1880 /* Call the SME function */
1881 rc = sme_dcc_update_ndl(hdd_ctx->hHal, &context,
1882 hdd_dcc_update_ndl_callback,
1883 &request);
1884 if (rc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001885 hdd_err("Error calling SME function.");
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301886 /* Convert from qdf_status to errno */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001887 return -EINVAL;
1888 }
1889
1890 /* Wait for the function to complete. */
1891 rc = wait_for_completion_timeout(&context.completion_evt,
1892 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
1893 if (rc == 0) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001894 hdd_err("Operation timed out");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001895 rc = -ETIMEDOUT;
1896 goto end;
1897 }
1898 rc = 0;
1899
1900 if (context.status) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001901 hdd_err("Operation failed: %d", context.status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001902 rc = context.status;
1903 goto end;
1904 }
1905
1906 if (adapter->dcc_update_ndl_resp.status) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001907 hdd_err("Operation returned: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001908 adapter->dcc_update_ndl_resp.status);
1909 rc = -EINVAL;
1910 goto end;
1911 }
1912
1913 /* fall through */
1914end:
1915 spin_lock(&hdd_context_lock);
1916 context.magic = 0;
1917 spin_unlock(&hdd_context_lock);
1918 return rc;
1919}
1920
1921/**
1922 * wlan_hdd_cfg80211_dcc_update_ndl() - Interface for update dcc cmd
1923 * @wiphy: pointer to the wiphy
1924 * @wdev: pointer to the wdev
1925 * @data: The netlink data
1926 * @data_len: The length of the netlink data in bytes
1927 *
1928 * Return: 0 on success.
1929 */
1930int wlan_hdd_cfg80211_dcc_update_ndl(struct wiphy *wiphy,
1931 struct wireless_dev *wdev,
1932 const void *data,
1933 int data_len)
1934{
1935 int ret;
1936
1937 cds_ssr_protect(__func__);
1938 ret = __wlan_hdd_cfg80211_dcc_update_ndl(wiphy, wdev,
1939 data, data_len);
1940 cds_ssr_unprotect(__func__);
1941
1942 return ret;
1943}
1944
1945/**
1946 * wlan_hdd_dcc_stats_event_callback() - Callback to get stats event
1947 * @context_ptr: request context
1948 * @response_ptr: response data
1949 */
1950static void wlan_hdd_dcc_stats_event_callback(void *context_ptr,
1951 void *response_ptr)
1952{
1953 hdd_context_t *hdd_ctx = (hdd_context_t *)context_ptr;
1954 struct sir_dcc_get_stats_response *resp = response_ptr;
1955 struct sk_buff *vendor_event;
1956
1957 ENTER();
1958
1959 vendor_event =
1960 cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
1961 NULL, sizeof(uint32_t) + resp->channel_stats_array_len +
1962 NLMSG_HDRLEN,
1963 QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT_INDEX,
1964 GFP_KERNEL);
1965
1966 if (!vendor_event) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001967 hdd_err("cfg80211_vendor_event_alloc failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001968 return;
1969 }
1970
1971 if (nla_put_u32(vendor_event,
1972 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT,
1973 resp->num_channels) ||
1974 nla_put(vendor_event,
1975 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY,
1976 resp->channel_stats_array_len,
1977 resp->channel_stats_array)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001978 hdd_err("nla put failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001979 kfree_skb(vendor_event);
1980 return;
1981 }
1982
1983 cfg80211_vendor_event(vendor_event, GFP_KERNEL);
1984}
1985
1986/**
1987 * wlan_hdd_dcc_register_for_dcc_stats_event() - Register for dcc stats events
1988 * @hdd_ctx: hdd context
1989 */
1990void wlan_hdd_dcc_register_for_dcc_stats_event(hdd_context_t *hdd_ctx)
1991{
1992 int rc;
1993
1994 rc = sme_register_for_dcc_stats_event(hdd_ctx->hHal, hdd_ctx,
Arun Khandavalli4b55da72016-07-19 19:55:01 +05301995 wlan_hdd_dcc_stats_event_callback);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001996 if (rc)
Jeff Johnson5f735d52016-07-06 15:14:45 -07001997 hdd_err("Register callback failed: %d", rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001998}