blob: 9b435ce7dac3b33d03b8b465e660f937f28c60cc [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
Amar Singhalfda6eda2015-11-02 15:08:59 -08002 * Copyright (c) 2011-2016 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
Jeff Johnson5f735d52016-07-06 15:14:45 -070034/* denote that this file does not allow legacy hddLog */
35#define HDD_DISALLOW_LEGACY_HDDLOG 1
36
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080037#include "cds_sched.h"
38#include "wlan_hdd_assoc.h"
39#include "wlan_hdd_main.h"
40#include "wlan_hdd_ocb.h"
41#include "wlan_hdd_trace.h"
42#include "wlan_tgt_def_config.h"
43#include "sch_api.h"
44#include "wma_api.h"
Dhanashri Atre182b0272016-02-17 15:35:07 -080045#include "ol_txrx.h"
Manjunathappa Prakash3454fd62016-04-01 08:52:06 -070046#include <cdp_txrx_peer_ops.h>
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080047
48/* Structure definitions for WLAN_SET_DOT11P_CHANNEL_SCHED */
49#define AIFSN_MIN (2)
50#define AIFSN_MAX (15)
51#define CW_MIN (1)
52#define CW_MAX (10)
53
54/* Maximum time(ms) to wait for OCB operations */
55#define WLAN_WAIT_TIME_OCB_CMD 1500
56#define HDD_OCB_MAGIC 0x489a154f
57
58/**
59 * struct hdd_ocb_ctxt - Context for OCB operations
60 * adapter: the ocb adapter
61 * completion_evt: the completion event
62 * status: status of the request
63 */
64struct hdd_ocb_ctxt {
65 uint32_t magic;
66 hdd_adapter_t *adapter;
67 struct completion completion_evt;
68 int status;
69};
70
71/**
72 * hdd_set_dot11p_config() - Set 802.11p config flag
73 * @hdd_ctx: HDD Context pointer
74 *
75 * TODO-OCB: This has been temporarily added to ensure this paramter
76 * is set in CSR when we init the channel list. This should be removed
77 * once the 5.9 GHz channels are added to the regulatory domain.
78 */
79void hdd_set_dot11p_config(hdd_context_t *hdd_ctx)
80{
81 sme_set_dot11p_config(hdd_ctx->hHal,
82 hdd_ctx->config->dot11p_mode !=
83 WLAN_HDD_11P_DISABLED);
84}
85
86/**
87 * dot11p_validate_qos_params() - Check if QoS parameters are valid
88 * @qos_params: Array of QoS parameters
89 *
90 * Return: 0 on success. error code on failure.
91 */
92static int dot11p_validate_qos_params(struct sir_qos_params qos_params[])
93{
94 int i;
95
96 for (i = 0; i < MAX_NUM_AC; i++) {
97 if ((!qos_params[i].aifsn) && (!qos_params[i].cwmin)
98 && (!qos_params[i].cwmax))
99 continue;
100
101 /* Validate AIFSN */
102 if ((qos_params[i].aifsn < AIFSN_MIN)
103 || (qos_params[i].aifsn > AIFSN_MAX)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700104 hdd_err("Invalid QoS parameter aifsn %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800105 qos_params[i].aifsn);
106 return -EINVAL;
107 }
108
109 /* Validate CWMin */
110 if ((qos_params[i].cwmin < CW_MIN)
111 || (qos_params[i].cwmin > CW_MAX)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700112 hdd_err("Invalid QoS parameter cwmin %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800113 qos_params[i].cwmin);
114 return -EINVAL;
115 }
116
117 /* Validate CWMax */
118 if ((qos_params[i].cwmax < CW_MIN)
119 || (qos_params[i].cwmax > CW_MAX)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700120 hdd_err("Invalid QoS parameter cwmax %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800121 qos_params[i].cwmax);
122 return -EINVAL;
123 }
124 }
125
126 return 0;
127}
128
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800129/**
130 * dot11p_validate_channel() - validates a DSRC channel
131 * @center_freq: the channel's center frequency
132 * @bandwidth: the channel's bandwidth
133 * @tx_power: transmit power
134 * @reg_power: (output) the max tx power from the regulatory domain
135 * @antenna_max: (output) the max antenna gain from the regulatory domain
136 *
137 * Return: 0 if the channel is valid, error code otherwise.
138 */
139static int dot11p_validate_channel(struct wiphy *wiphy,
140 uint32_t channel_freq, uint32_t bandwidth,
141 uint32_t tx_power, uint8_t *reg_power,
142 uint8_t *antenna_max)
143{
144 int band_idx, channel_idx;
145 struct ieee80211_supported_band *current_band;
146 struct ieee80211_channel *current_channel;
147
148 for (band_idx = 0; band_idx < IEEE80211_NUM_BANDS; band_idx++) {
149 current_band = wiphy->bands[band_idx];
150 if (!current_band)
151 continue;
152
153 for (channel_idx = 0; channel_idx < current_band->n_channels;
154 channel_idx++) {
155 current_channel = &current_band->channels[channel_idx];
156
157 if (channel_freq == current_channel->center_freq) {
158 if (current_channel->flags &
159 IEEE80211_CHAN_DISABLED)
160 return -EINVAL;
161
162 if (reg_power)
163 *reg_power =
164 current_channel->max_reg_power;
165 if (antenna_max)
166 *antenna_max =
167 current_channel->
168 max_antenna_gain;
169
170 switch (bandwidth) {
171 case 0:
172 if (current_channel->flags &
173 IEEE80211_CHAN_NO_10MHZ)
174 bandwidth = 5;
175 else if (current_channel->flags &
176 IEEE80211_CHAN_NO_20MHZ)
177 bandwidth = 10;
178 else
179 bandwidth = 20;
180 break;
181 case 5:
182 break;
183 case 10:
184 if (current_channel->flags &
185 IEEE80211_CHAN_NO_10MHZ)
186 return -EINVAL;
187 break;
188 case 20:
189 if (current_channel->flags &
190 IEEE80211_CHAN_NO_20MHZ)
191 return -EINVAL;
192 break;
193 default:
194 return -EINVAL;
195 }
196
197 if (tx_power > current_channel->max_power)
198 return -EINVAL;
199
200 return 0;
201 }
202 }
203 }
204
Amar Singhalfda6eda2015-11-02 15:08:59 -0800205 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800206}
207
208/**
209 * hdd_ocb_validate_config() - Validates the config data
210 * @config: configuration to be validated
211 *
212 * Return: 0 on success.
213 */
214static int hdd_ocb_validate_config(hdd_adapter_t *adapter,
215 struct sir_ocb_config *config)
216{
217 int i;
218 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
219
220 for (i = 0; i < config->channel_count; i++) {
221 if (dot11p_validate_channel(hdd_ctx->wiphy,
222 config->channels[i].chan_freq,
223 config->channels[i].bandwidth,
224 config->channels[i].max_pwr,
225 &config->channels[i].reg_pwr,
226 &config->channels[i].antenna_max)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700227 hdd_err("Invalid channel frequency %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800228 config->channels[i].chan_freq);
229 return -EINVAL;
230 }
231 if (dot11p_validate_qos_params(config->channels[i].qos_params))
232 return -EINVAL;
233 }
234
235 return 0;
236}
237
238/**
239 * hdd_ocb_register_sta() - Register station with Transport Layer
240 * @adapter: Pointer to HDD Adapter
241 *
242 * This function should be invoked in the OCB Set Schedule callback
243 * to enable the data path in the TL by calling RegisterSTAClient
244 *
245 * Return: 0 on success. -1 on failure.
246 */
247static int hdd_ocb_register_sta(hdd_adapter_t *adapter)
248{
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530249 QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800250 struct ol_txrx_desc_type sta_desc = {0};
251 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
252 hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
253 uint8_t peer_id;
Dhanashri Atre182b0272016-02-17 15:35:07 -0800254 struct ol_txrx_ops txrx_ops;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800255
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530256 qdf_status = ol_txrx_register_ocb_peer(hdd_ctx->pcds_context,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800257 adapter->macAddressCurrent.bytes,
258 &peer_id);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530259 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700260 hdd_err("Error registering OCB Self Peer!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800261 return -EINVAL;
262 }
263
264 hdd_ctx->sta_to_adapter[peer_id] = adapter;
265
266 sta_desc.sta_id = peer_id;
267 sta_desc.is_qos_enabled = 1;
268
Dhanashri Atre182b0272016-02-17 15:35:07 -0800269 /* Register the vdev transmit and receive functions */
270 qdf_mem_zero(&txrx_ops, sizeof(txrx_ops));
271 txrx_ops.rx.rx = hdd_rx_packet_cbk;
272 ol_txrx_vdev_register(
273 ol_txrx_get_vdev_from_vdev_id(adapter->sessionId),
274 adapter, &txrx_ops);
Dhanashri Atre168d2b42016-02-22 14:43:06 -0800275 adapter->tx_fn = txrx_ops.tx.tx;
Dhanashri Atre182b0272016-02-17 15:35:07 -0800276
Dhanashri Atre50141c52016-04-07 13:15:29 -0700277 qdf_status = ol_txrx_register_peer(&sta_desc);
278 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700279 hdd_err("Failed to register. Status= %d [0x%08X]",
Dhanashri Atre50141c52016-04-07 13:15:29 -0700280 qdf_status, qdf_status);
281 return -EINVAL;
282 }
283
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800284 if (pHddStaCtx->conn_info.staId[0] != 0 &&
285 pHddStaCtx->conn_info.staId[0] != peer_id) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700286 hdd_err("The ID for the OCB station has changed.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800287 }
288
289 pHddStaCtx->conn_info.staId[0] = peer_id;
Anurag Chouhanc5548422016-02-24 18:33:27 +0530290 qdf_copy_macaddr(&pHddStaCtx->conn_info.peerMacAddress[0],
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800291 &adapter->macAddressCurrent);
292
293 return 0;
294}
295
296/**
297 * hdd_ocb_config_new() - Creates a new OCB configuration
298 * @num_channels: the number of channels
299 * @num_schedule: the schedule size
300 * @ndl_chan_list_len: length in bytes of the NDL chan blob
301 * @ndl_active_state_list_len: length in bytes of the active state blob
302 *
303 * Return: A pointer to the OCB configuration struct, NULL on failure.
304 */
305static struct sir_ocb_config *hdd_ocb_config_new(int num_channels,
306 int num_schedule,
307 int ndl_chan_list_len,
308 int ndl_active_state_list_len)
309{
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
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530328 qdf_mem_zero(cursor, len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800329 ret = cursor;
330 cursor += sizeof(*ret);
331
332 ret->channel_count = num_channels;
333 ret->channels = cursor;
334 cursor += num_channels * sizeof(*ret->channels);
335
336 ret->schedule_size = num_schedule;
337 ret->schedule = cursor;
338 cursor += num_schedule * sizeof(*ret->schedule);
339
340 ret->dcc_ndl_chan_list = cursor;
341 cursor += ndl_chan_list_len;
342
343 ret->dcc_ndl_active_state_list = cursor;
344 cursor += ndl_active_state_list_len;
345
346 return ret;
347
348fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530349 qdf_mem_free(ret);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800350 return NULL;
351}
352
353/**
354 * hdd_ocb_set_config_callback() - OCB set config callback function
355 * @context_ptr: OCB call context
356 * @response_ptr: Pointer to response structure
357 *
358 * This function is registered as a callback with the lower layers
359 * and is used to respond with the status of a OCB set config command.
360 */
361static void hdd_ocb_set_config_callback(void *context_ptr, void *response_ptr)
362{
363 struct hdd_ocb_ctxt *context = context_ptr;
364 struct sir_ocb_set_config_response *resp = response_ptr;
365
366 if (!context)
367 return;
368
369 if (resp && resp->status)
Jeff Johnson5f735d52016-07-06 15:14:45 -0700370 hdd_err("Operation failed: %d", resp->status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800371
372 spin_lock(&hdd_context_lock);
373 if (context->magic == HDD_OCB_MAGIC) {
374 hdd_adapter_t *adapter = context->adapter;
375 if (!resp) {
376 context->status = -EINVAL;
377 complete(&context->completion_evt);
378 spin_unlock(&hdd_context_lock);
379 return;
380 }
381
382 context->adapter->ocb_set_config_resp = *resp;
383 spin_unlock(&hdd_context_lock);
384 if (!resp->status) {
385 /*
386 * OCB set config command successful.
387 * Open the TX data path
388 */
389 if (!hdd_ocb_register_sta(adapter)) {
Ravi Joshic3f5c8a2016-06-13 16:46:44 -0700390 wlan_hdd_netif_queue_control(adapter,
391 WLAN_START_ALL_NETIF_QUEUE_N_CARRIER,
392 WLAN_CONTROL_PATH);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800393 }
394 }
395
396 spin_lock(&hdd_context_lock);
397 if (context->magic == HDD_OCB_MAGIC)
398 complete(&context->completion_evt);
399 spin_unlock(&hdd_context_lock);
400 } else {
401 spin_unlock(&hdd_context_lock);
402 }
403}
404
405/**
406 * hdd_ocb_set_config_req() - Send an OCB set config request
407 * @adapter: a pointer to the adapter
408 * @config: a pointer to the OCB configuration
409 *
410 * Return: 0 on success.
411 */
412static int hdd_ocb_set_config_req(hdd_adapter_t *adapter,
413 struct sir_ocb_config *config)
414{
415 int rc;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530416 QDF_STATUS qdf_status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800417 struct hdd_ocb_ctxt context = {0};
418
419 if (hdd_ocb_validate_config(adapter, config)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700420 hdd_err("The configuration is invalid");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800421 return -EINVAL;
422 }
423
424 init_completion(&context.completion_evt);
425 context.adapter = adapter;
426 context.magic = HDD_OCB_MAGIC;
427
Jeff Johnson5f735d52016-07-06 15:14:45 -0700428 hdd_notice("Disabling queues");
Ravi Joshic3f5c8a2016-06-13 16:46:44 -0700429 wlan_hdd_netif_queue_control(adapter, WLAN_NETIF_TX_DISABLE_N_CARRIER,
430 WLAN_CONTROL_PATH);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800431
432 /* Call the SME API to set the config */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530433 qdf_status = sme_ocb_set_config(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800434 ((hdd_context_t *)adapter->pHddCtx)->hHal, &context,
435 hdd_ocb_set_config_callback, config);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530436 if (qdf_status != QDF_STATUS_SUCCESS) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700437 hdd_err("Error calling SME function.");
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530438 /* Convert from qdf_status to errno */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800439 return -EINVAL;
440 }
441
442 /* Wait for the function to complete. */
443 rc = wait_for_completion_timeout(&context.completion_evt,
444 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
445 if (rc == 0) {
446 rc = -ETIMEDOUT;
447 goto end;
448 }
449 rc = 0;
450
451 if (context.status) {
452 rc = context.status;
453 goto end;
454 }
455
456 if (adapter->ocb_set_config_resp.status) {
457 rc = -EINVAL;
458 goto end;
459 }
460
461 /* fall through */
462end:
463 spin_lock(&hdd_context_lock);
464 context.magic = 0;
465 spin_unlock(&hdd_context_lock);
466 if (rc)
Jeff Johnson5f735d52016-07-06 15:14:45 -0700467 hdd_err("Operation failed: %d", rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800468 return rc;
469}
470
471/**
472 * __iw_set_dot11p_channel_sched() - Handler for WLAN_SET_DOT11P_CHANNEL_SCHED
473 * ioctl
474 * @dev: Pointer to net_device structure
475 * @iw_request_info: IW Request Info
476 * @wrqu: IW Request Userspace Data Pointer
477 * @extra: IW Request Kernel Data Pointer
478 *
479 * Return: 0 on success
480 */
481static int __iw_set_dot11p_channel_sched(struct net_device *dev,
482 struct iw_request_info *info,
483 union iwreq_data *wrqu, char *extra)
484{
485 int rc = 0;
486 struct dot11p_channel_sched *sched;
487 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
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530495 if (wlan_hdd_validate_context(WLAN_HDD_GET_CTX(adapter)))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800496 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800497
Krunal Sonibe766b02016-03-10 13:00:44 -0800498 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700499 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800500 return -EINVAL;
501 }
502
503 sched = (struct dot11p_channel_sched *)extra;
504
505 /* Scheduled slots same as num channels for compatibility */
506 config = hdd_ocb_config_new(sched->num_channels, sched->num_channels,
507 0, 0);
508 if (config == NULL) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700509 hdd_err("Failed to allocate memory!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800510 return -ENOMEM;
511 }
512
513 /* Identify the vdev interface */
514 config->session_id = adapter->sessionId;
515
516 /* Release all the mac addresses used for OCB */
517 for (i = 0; i < adapter->ocb_mac_addr_count; i++) {
518 wlan_hdd_release_intf_addr(adapter->pHddCtx,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800519 adapter->ocb_mac_address[i].bytes);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800520 }
521 adapter->ocb_mac_addr_count = 0;
522
523 config->channel_count = 0;
524 for (i = 0; i < sched->num_channels; i++) {
525 if (0 == sched->channels[i].channel_freq)
526 continue;
527
528 curr_chan = &(config->channels[config->channel_count]);
529
530 curr_chan->chan_freq = sched->channels[i].channel_freq;
531 /*
532 * tx_power is divided by 2 because ocb_channel.tx_power is
533 * in half dB increments and sir_ocb_config_channel.max_pwr
534 * is in 1 dB increments.
535 */
536 curr_chan->max_pwr = sched->channels[i].tx_power / 2;
537 curr_chan->bandwidth = sched->channels[i].channel_bandwidth;
538 /* assume 10 as default if not provided */
539 if (curr_chan->bandwidth == 0)
540 curr_chan->bandwidth = 10;
541
542 /*
543 * Setup locally administered mac addresses for each channel.
544 * First channel uses the adapter's address.
545 */
546 if (i == 0) {
Anurag Chouhanc5548422016-02-24 18:33:27 +0530547 qdf_copy_macaddr(&curr_chan->mac_address,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800548 &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800549 } else {
550 mac_addr = wlan_hdd_get_intf_addr(adapter->pHddCtx);
551 if (mac_addr == NULL) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700552 hdd_err("Cannot obtain mac address");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800553 rc = -EINVAL;
554 goto fail;
555 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530556 qdf_mem_copy(config->channels[
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800557 config->channel_count].mac_address.bytes,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800558 mac_addr, sizeof(tSirMacAddr));
559 /* Save the mac address to release later */
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530560 qdf_mem_copy(adapter->ocb_mac_address[
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800561 adapter->ocb_mac_addr_count].bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +0530562 mac_addr, QDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800563 adapter->ocb_mac_addr_count++;
564 }
565
566 for (j = 0; j < MAX_NUM_AC; j++) {
567 curr_chan->qos_params[j].aifsn =
568 sched->channels[i].qos_params[j].aifsn;
569 curr_chan->qos_params[j].cwmin =
570 sched->channels[i].qos_params[j].cwmin;
571 curr_chan->qos_params[j].cwmax =
572 sched->channels[i].qos_params[j].cwmax;
573 }
574
575 config->channel_count++;
576 }
577
578 /*
579 * Scheduled slots same as num channels for compatibility with
580 * legacy use.
581 */
582 for (i = 0; i < sched->num_channels; i++) {
583 config->schedule[i].chan_freq = sched->channels[i].channel_freq;
584 config->schedule[i].guard_interval =
585 sched->channels[i].start_guard_interval;
586 config->schedule[i].total_duration =
587 sched->channels[i].duration;
588 }
589
590 rc = hdd_ocb_set_config_req(adapter, config);
591 if (rc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700592 hdd_err("Error while setting OCB config");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800593 goto fail;
594 }
595
596 rc = 0;
597
598fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530599 qdf_mem_free(config);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800600 return rc;
601}
602
603/**
604 * iw_set_dot11p_channel_sched() - IOCTL interface for setting channel schedule
605 * @dev: Pointer to net_device structure
606 * @iw_request_info: IW Request Info
607 * @wrqu: IW Request Userspace Data Pointer
608 * @extra: IW Request Kernel Data Pointer
609 *
610 * Return: 0 on success.
611 */
612int iw_set_dot11p_channel_sched(struct net_device *dev,
613 struct iw_request_info *info,
614 union iwreq_data *wrqu, char *extra)
615{
616 int ret;
617
618 cds_ssr_protect(__func__);
619 ret = __iw_set_dot11p_channel_sched(dev, info, wrqu, extra);
620 cds_ssr_unprotect(__func__);
621
622 return ret;
623}
624
625static const struct nla_policy qca_wlan_vendor_ocb_set_config_policy[
626 QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX + 1] = {
627 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT] = {
628 .type = NLA_U32
629 },
630 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE] = {
631 .type = NLA_U32
632 },
633 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY] = {
634 .type = NLA_BINARY
635 },
636 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY] = {
637 .type = NLA_BINARY
638 },
639 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY] = {
640 .type = NLA_BINARY
641 },
642 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY] = {
643 .type = NLA_BINARY
644 },
645 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS] = {
646 .type = NLA_U32
647 },
648};
649
650static const struct nla_policy qca_wlan_vendor_ocb_set_utc_time_policy[
651 QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX + 1] = {
652 [QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE] = {
653 .type = NLA_BINARY, .len = SIZE_UTC_TIME
654 },
655 [QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR] = {
656 .type = NLA_BINARY, .len = SIZE_UTC_TIME_ERROR
657 },
658};
659
660static const struct nla_policy qca_wlan_vendor_ocb_start_timing_advert_policy[
661 QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX + 1] = {
662 [QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ] = {
663 .type = NLA_U32
664 },
665 [QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE] = {
666 .type = NLA_U32
667 },
668};
669
670static const struct nla_policy qca_wlan_vendor_ocb_stop_timing_advert_policy[
671 QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX + 1] = {
672 [QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ] = {
673 .type = NLA_U32
674 },
675};
676
677static const struct nla_policy qca_wlan_vendor_ocb_get_tsf_timer_resp[] = {
678 [QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH] = {
679 .type = NLA_U32
680 },
681 [QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW] = {
682 .type = NLA_U32
683 },
684};
685
686static const struct nla_policy qca_wlan_vendor_dcc_get_stats[] = {
687 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT] = {
688 .type = NLA_U32
689 },
690 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY] = {
691 .type = NLA_BINARY
692 },
693};
694
695static const struct nla_policy qca_wlan_vendor_dcc_get_stats_resp[] = {
696 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT] = {
697 .type = NLA_U32
698 },
699 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY] = {
700 .type = NLA_BINARY
701 },
702};
703
704static const struct nla_policy qca_wlan_vendor_dcc_clear_stats[] = {
705 [QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP] = {
706 .type = NLA_U32
707 },
708};
709
710static const struct nla_policy qca_wlan_vendor_dcc_update_ndl[
711 QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX + 1] = {
712 [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT] = {
713 .type = NLA_U32
714 },
715 [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY] = {
716 .type = NLA_BINARY
717 },
718 [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY] = {
719 .type = NLA_BINARY
720 },
721};
722
723/**
724 * struct wlan_hdd_ocb_config_channel
725 * @chan_freq: frequency of the channel
726 * @bandwidth: bandwidth of the channel, either 10 or 20 MHz
727 * @mac_address: MAC address assigned to this channel
728 * @qos_params: QoS parameters
729 * @max_pwr: maximum transmit power of the channel (1/2 dBm)
730 * @min_pwr: minimum transmit power of the channel (1/2 dBm)
731 */
732struct wlan_hdd_ocb_config_channel {
733 uint32_t chan_freq;
734 uint32_t bandwidth;
735 uint16_t flags;
736 uint8_t reserved[4];
737 struct sir_qos_params qos_params[MAX_NUM_AC];
738 uint32_t max_pwr;
739 uint32_t min_pwr;
740};
741
742static void wlan_hdd_ocb_config_channel_to_sir_ocb_config_channel(
743 struct sir_ocb_config_channel *dest,
744 struct wlan_hdd_ocb_config_channel *src,
745 uint32_t channel_count)
746{
747 uint32_t i;
748
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530749 qdf_mem_zero(dest, channel_count * sizeof(*dest));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800750
751 for (i = 0; i < channel_count; i++) {
752 dest[i].chan_freq = src[i].chan_freq;
753 dest[i].bandwidth = src[i].bandwidth;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530754 qdf_mem_copy(dest[i].qos_params, src[i].qos_params,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800755 sizeof(dest[i].qos_params));
756 /*
757 * max_pwr and min_pwr are divided by 2 because
758 * wlan_hdd_ocb_config_channel.max_pwr and min_pwr
759 * are in 1/2 dB increments and
760 * sir_ocb_config_channel.max_pwr and min_pwr are in
761 * 1 dB increments.
762 */
763 dest[i].max_pwr = src[i].max_pwr / 2;
764 dest[i].min_pwr = (src[i].min_pwr + 1) / 2;
765 dest[i].flags = src[i].flags;
766 }
767}
768
769/**
770 * __wlan_hdd_cfg80211_ocb_set_config() - Interface for set config command
771 * @wiphy: pointer to the wiphy
772 * @wdev: pointer to the wdev
773 * @data: The netlink data
774 * @data_len: The length of the netlink data in bytes
775 *
776 * Return: 0 on success.
777 */
778static int __wlan_hdd_cfg80211_ocb_set_config(struct wiphy *wiphy,
779 struct wireless_dev *wdev,
780 const void *data,
781 int data_len)
782{
783 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
784 struct net_device *dev = wdev->netdev;
785 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
786 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX + 1];
787 struct nlattr *channel_array;
788 struct nlattr *sched_array;
789 struct nlattr *ndl_chan_list;
790 uint32_t ndl_chan_list_len;
791 struct nlattr *ndl_active_state_list;
792 uint32_t ndl_active_state_list_len;
793 uint32_t flags = 0;
794 int i;
795 int channel_count, schedule_size;
796 struct sir_ocb_config *config;
797 int rc = -EINVAL;
798 uint8_t *mac_addr;
799
Jeff Johnson1f61b612016-02-12 16:28:33 -0800800 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800801
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530802 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800803 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800804
Krunal Sonibe766b02016-03-10 13:00:44 -0800805 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700806 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800807 return -EINVAL;
808 }
809
810 /* Parse the netlink message */
811 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX,
812 data,
813 data_len, qca_wlan_vendor_ocb_set_config_policy)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700814 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800815 return -EINVAL;
816 }
817
818 /* Get the number of channels in the schedule */
819 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700820 hdd_err("CHANNEL_COUNT is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800821 return -EINVAL;
822 }
823 channel_count = nla_get_u32(
824 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT]);
825
826 /* Get the size of the channel schedule */
827 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700828 hdd_err("SCHEDULE_SIZE is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800829 return -EINVAL;
830 }
831 schedule_size = nla_get_u32(
832 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE]);
833
834 /* Get the ndl chan array and the ndl active state array. */
835 ndl_chan_list =
836 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY];
837 ndl_chan_list_len = (ndl_chan_list ? nla_len(ndl_chan_list) : 0);
838
839 ndl_active_state_list =
840 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY];
841 ndl_active_state_list_len = (ndl_active_state_list ?
842 nla_len(ndl_active_state_list) : 0);
843
844 /* Get the flags */
845 if (tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS])
846 flags = nla_get_u32(tb[
847 QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS]);
848
849 config = hdd_ocb_config_new(channel_count, schedule_size,
850 ndl_chan_list_len,
851 ndl_active_state_list_len);
852 if (config == NULL) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700853 hdd_err("Failed to allocate memory!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800854 return -ENOMEM;
855 }
856
857 config->channel_count = channel_count;
858 config->schedule_size = schedule_size;
859 config->flags = flags;
860
861 /* Read the channel array */
862 channel_array = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY];
863 if (!channel_array) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700864 hdd_err("No channel present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800865 goto fail;
866 }
867 if (nla_len(channel_array) != channel_count *
868 sizeof(struct wlan_hdd_ocb_config_channel)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700869 hdd_err("CHANNEL_ARRAY is not the correct size");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800870 goto fail;
871 }
872 wlan_hdd_ocb_config_channel_to_sir_ocb_config_channel(
873 config->channels, nla_data(channel_array), channel_count);
874
875 /* Identify the vdev interface */
876 config->session_id = adapter->sessionId;
877
878 /* Release all the mac addresses used for OCB */
879 for (i = 0; i < adapter->ocb_mac_addr_count; i++) {
880 wlan_hdd_release_intf_addr(adapter->pHddCtx,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800881 adapter->ocb_mac_address[i].bytes);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800882 }
883 adapter->ocb_mac_addr_count = 0;
884
885 /*
886 * Setup locally administered mac addresses for each channel.
887 * First channel uses the adapter's address.
888 */
889 for (i = 0; i < config->channel_count; i++) {
890 if (i == 0) {
Anurag Chouhanc5548422016-02-24 18:33:27 +0530891 qdf_copy_macaddr(&config->channels[i].mac_address,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800892 &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800893 } else {
894 mac_addr = wlan_hdd_get_intf_addr(adapter->pHddCtx);
895 if (mac_addr == NULL) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700896 hdd_err("Cannot obtain mac address");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800897 goto fail;
898 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530899 qdf_mem_copy(config->channels[i].mac_address.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +0530900 mac_addr, QDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800901 /* Save the mac address to release later */
Anurag Chouhanc5548422016-02-24 18:33:27 +0530902 qdf_copy_macaddr(&adapter->ocb_mac_address[
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800903 adapter->ocb_mac_addr_count],
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800904 &config->channels[i].mac_address);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800905 adapter->ocb_mac_addr_count++;
906 }
907 }
908
909 /* Read the schedule array */
910 sched_array = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY];
911 if (!sched_array) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700912 hdd_err("No channel present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800913 goto fail;
914 }
915 if (nla_len(sched_array) != schedule_size * sizeof(*config->schedule)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700916 hdd_err("SCHEDULE_ARRAY is not the correct size");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800917 goto fail;
918 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530919 qdf_mem_copy(config->schedule, nla_data(sched_array),
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800920 nla_len(sched_array));
921
922 /* Copy the NDL chan array */
923 if (ndl_chan_list_len) {
924 config->dcc_ndl_chan_list_len = ndl_chan_list_len;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530925 qdf_mem_copy(config->dcc_ndl_chan_list, nla_data(ndl_chan_list),
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800926 nla_len(ndl_chan_list));
927 }
928
929 /* Copy the NDL active state array */
930 if (ndl_active_state_list_len) {
931 config->dcc_ndl_active_state_list_len =
932 ndl_active_state_list_len;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530933 qdf_mem_copy(config->dcc_ndl_active_state_list,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800934 nla_data(ndl_active_state_list),
935 nla_len(ndl_active_state_list));
936 }
937
938 rc = hdd_ocb_set_config_req(adapter, config);
939 if (rc)
Jeff Johnson5f735d52016-07-06 15:14:45 -0700940 hdd_err("Error while setting OCB config: %d", rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800941
942fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530943 qdf_mem_free(config);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800944 return rc;
945}
946
947/**
948 * wlan_hdd_cfg80211_ocb_set_config() - Interface for set config command
949 * @wiphy: pointer to the wiphy
950 * @wdev: pointer to the wdev
951 * @data: The netlink data
952 * @data_len: The length of the netlink data in bytes
953 *
954 * Return: 0 on success.
955 */
956int wlan_hdd_cfg80211_ocb_set_config(struct wiphy *wiphy,
957 struct wireless_dev *wdev,
958 const void *data,
959 int data_len)
960{
961 int ret;
962
963 cds_ssr_protect(__func__);
964 ret = __wlan_hdd_cfg80211_ocb_set_config(wiphy, wdev, data, data_len);
965 cds_ssr_unprotect(__func__);
966
967 return ret;
968}
969
970/**
971 * __wlan_hdd_cfg80211_ocb_set_utc_time() - Interface for set UTC time command
972 * @wiphy: pointer to the wiphy
973 * @wdev: pointer to the wdev
974 * @data: The netlink data
975 * @data_len: The length of the netlink data in bytes
976 *
977 * Return: 0 on success.
978 */
979static int __wlan_hdd_cfg80211_ocb_set_utc_time(struct wiphy *wiphy,
980 struct wireless_dev *wdev,
981 const void *data,
982 int data_len)
983{
984 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
985 struct net_device *dev = wdev->netdev;
986 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
987 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX + 1];
988 struct nlattr *utc_attr;
989 struct nlattr *time_error_attr;
990 struct sir_ocb_utc *utc;
991 int rc = -EINVAL;
992
Jeff Johnson1f61b612016-02-12 16:28:33 -0800993 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800994
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530995 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800996 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800997
Krunal Sonibe766b02016-03-10 13:00:44 -0800998 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -0700999 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001000 return -EINVAL;
1001 }
1002
1003 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001004 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001005 return -EINVAL;
1006 }
1007
1008 /* Parse the netlink message */
1009 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX,
1010 data,
1011 data_len, qca_wlan_vendor_ocb_set_utc_time_policy)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001012 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001013 return -EINVAL;
1014 }
1015
1016 /* Read the UTC time */
1017 utc_attr = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE];
1018 if (!utc_attr) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001019 hdd_err("UTC_TIME is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001020 return -EINVAL;
1021 }
1022 if (nla_len(utc_attr) != SIZE_UTC_TIME) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001023 hdd_err("UTC_TIME is not the correct size");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001024 return -EINVAL;
1025 }
1026
1027 /* Read the time error */
1028 time_error_attr = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR];
1029 if (!time_error_attr) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001030 hdd_err("UTC_TIME is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001031 return -EINVAL;
1032 }
1033 if (nla_len(time_error_attr) != SIZE_UTC_TIME_ERROR) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001034 hdd_err("UTC_TIME is not the correct size");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001035 return -EINVAL;
1036 }
1037
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301038 utc = qdf_mem_malloc(sizeof(*utc));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001039 if (!utc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001040 hdd_err("qdf_mem_malloc failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001041 return -ENOMEM;
1042 }
1043 utc->vdev_id = adapter->sessionId;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301044 qdf_mem_copy(utc->utc_time, nla_data(utc_attr), SIZE_UTC_TIME);
1045 qdf_mem_copy(utc->time_error, nla_data(time_error_attr),
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001046 SIZE_UTC_TIME_ERROR);
1047
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301048 if (sme_ocb_set_utc_time(hdd_ctx->hHal, utc) != QDF_STATUS_SUCCESS) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001049 hdd_err("Error while setting UTC time");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001050 rc = -EINVAL;
1051 } else {
1052 rc = 0;
1053 }
1054
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301055 qdf_mem_free(utc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001056 return rc;
1057}
1058
1059/**
1060 * wlan_hdd_cfg80211_ocb_set_utc_time() - Interface for the set UTC time command
1061 * @wiphy: pointer to the wiphy
1062 * @wdev: pointer to the wdev
1063 * @data: The netlink data
1064 * @data_len: The length of the netlink data in bytes
1065 *
1066 * Return: 0 on success.
1067 */
1068int wlan_hdd_cfg80211_ocb_set_utc_time(struct wiphy *wiphy,
1069 struct wireless_dev *wdev,
1070 const void *data,
1071 int data_len)
1072{
1073 int ret;
1074
1075 cds_ssr_protect(__func__);
1076 ret = __wlan_hdd_cfg80211_ocb_set_utc_time(wiphy, wdev, data, data_len);
1077 cds_ssr_unprotect(__func__);
1078
1079 return ret;
1080}
1081
1082/**
1083 * __wlan_hdd_cfg80211_ocb_start_timing_advert() - Interface for start TA cmd
1084 * @wiphy: pointer to the wiphy
1085 * @wdev: pointer to the wdev
1086 * @data: The netlink data
1087 * @data_len: The length of the netlink data in bytes
1088 *
1089 * Return: 0 on success.
1090 */
1091static int
1092__wlan_hdd_cfg80211_ocb_start_timing_advert(struct wiphy *wiphy,
1093 struct wireless_dev *wdev,
1094 const void *data,
1095 int data_len)
1096{
1097 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1098 struct net_device *dev = wdev->netdev;
1099 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001100 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX + 1];
1101 struct sir_ocb_timing_advert *timing_advert;
1102 int rc = -EINVAL;
1103
Jeff Johnson1f61b612016-02-12 16:28:33 -08001104 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001105
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301106 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001107 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001108
Krunal Sonibe766b02016-03-10 13:00:44 -08001109 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001110 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001111 return -EINVAL;
1112 }
1113
1114 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001115 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001116 return -EINVAL;
1117 }
1118
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301119 timing_advert = qdf_mem_malloc(sizeof(*timing_advert));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001120 if (!timing_advert) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001121 hdd_err("qdf_mem_malloc failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001122 return -ENOMEM;
1123 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301124 qdf_mem_zero(timing_advert, sizeof(*timing_advert));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001125 timing_advert->vdev_id = adapter->sessionId;
1126
1127 /* Parse the netlink message */
1128 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX,
1129 data,
1130 data_len,
1131 qca_wlan_vendor_ocb_start_timing_advert_policy)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001132 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001133 goto fail;
1134 }
1135
1136 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001137 hdd_err("CHANNEL_FREQ is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001138 goto fail;
1139 }
1140 timing_advert->chan_freq = nla_get_u32(
1141 tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ]);
1142
1143 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001144 hdd_err("REPEAT_RATE is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001145 goto fail;
1146 }
1147 timing_advert->repeat_rate = nla_get_u32(
1148 tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE]);
1149
1150 timing_advert->template_length =
Naveen Rawatb4d37622015-11-13 16:15:25 -08001151 sme_ocb_gen_timing_advert_frame(hdd_ctx->hHal,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001152 *(tSirMacAddr *)&adapter->macAddressCurrent.bytes,
1153 &timing_advert->template_value,
1154 &timing_advert->timestamp_offset,
1155 &timing_advert->time_value_offset);
1156 if (timing_advert->template_length <= 0) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001157 hdd_err("Error while generating the TA frame");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001158 goto fail;
1159 }
1160
1161 if (sme_ocb_start_timing_advert(hdd_ctx->hHal, timing_advert) !=
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301162 QDF_STATUS_SUCCESS) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001163 hdd_err("Error while starting timing advert");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001164 rc = -EINVAL;
1165 } else {
1166 rc = 0;
1167 }
1168
1169fail:
1170 if (timing_advert->template_value)
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301171 qdf_mem_free(timing_advert->template_value);
1172 qdf_mem_free(timing_advert);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001173 return rc;
1174}
1175
1176/**
1177 * wlan_hdd_cfg80211_ocb_start_timing_advert() - Interface for the start TA cmd
1178 * @wiphy: pointer to the wiphy
1179 * @wdev: pointer to the wdev
1180 * @data: The netlink data
1181 * @data_len: The length of the netlink data in bytes
1182 *
1183 * Return: 0 on success.
1184 */
1185int wlan_hdd_cfg80211_ocb_start_timing_advert(struct wiphy *wiphy,
1186 struct wireless_dev *wdev,
1187 const void *data,
1188 int data_len)
1189{
1190 int ret;
1191
1192 cds_ssr_protect(__func__);
1193 ret = __wlan_hdd_cfg80211_ocb_start_timing_advert(wiphy, wdev,
1194 data, data_len);
1195 cds_ssr_unprotect(__func__);
1196
1197 return ret;
1198}
1199
1200/**
1201 * __wlan_hdd_cfg80211_ocb_stop_timing_advert() - Interface for the stop TA cmd
1202 * @wiphy: pointer to the wiphy
1203 * @wdev: pointer to the wdev
1204 * @data: The netlink data
1205 * @data_len: The length of the netlink data in bytes
1206 *
1207 * Return: 0 on success.
1208 */
1209static int
1210__wlan_hdd_cfg80211_ocb_stop_timing_advert(struct wiphy *wiphy,
1211 struct wireless_dev *wdev,
1212 const void *data,
1213 int data_len)
1214{
1215 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1216 struct net_device *dev = wdev->netdev;
1217 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1218 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX + 1];
1219 struct sir_ocb_timing_advert *timing_advert;
1220 int rc = -EINVAL;
1221
Jeff Johnson1f61b612016-02-12 16:28:33 -08001222 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001223
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301224 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001225 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001226
Krunal Sonibe766b02016-03-10 13:00:44 -08001227 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001228 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001229 return -EINVAL;
1230 }
1231
1232 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001233 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001234 return -EINVAL;
1235 }
1236
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301237 timing_advert = qdf_mem_malloc(sizeof(*timing_advert));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001238 if (!timing_advert) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001239 hdd_err("qdf_mem_malloc failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001240 return -ENOMEM;
1241 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301242 qdf_mem_zero(timing_advert, sizeof(sizeof(*timing_advert)));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001243 timing_advert->vdev_id = adapter->sessionId;
1244
1245 /* Parse the netlink message */
1246 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX,
1247 data,
1248 data_len,
1249 qca_wlan_vendor_ocb_stop_timing_advert_policy)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001250 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001251 goto fail;
1252 }
1253
1254 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001255 hdd_err("CHANNEL_FREQ is not present");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001256 goto fail;
1257 }
1258 timing_advert->chan_freq = nla_get_u32(
1259 tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ]);
1260
1261 if (sme_ocb_stop_timing_advert(hdd_ctx->hHal, timing_advert) !=
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301262 QDF_STATUS_SUCCESS) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001263 hdd_err("Error while stopping timing advert");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001264 rc = -EINVAL;
1265 } else {
1266 rc = 0;
1267 }
1268
1269fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301270 qdf_mem_free(timing_advert);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001271 return rc;
1272}
1273
1274/**
1275 * wlan_hdd_cfg80211_ocb_stop_timing_advert() - Interface for the stop TA cmd
1276 * @wiphy: pointer to the wiphy
1277 * @wdev: pointer to the wdev
1278 * @data: The netlink data
1279 * @data_len: The length of the netlink data in bytes
1280 *
1281 * Return: 0 on success.
1282 */
1283int wlan_hdd_cfg80211_ocb_stop_timing_advert(struct wiphy *wiphy,
1284 struct wireless_dev *wdev,
1285 const void *data,
1286 int data_len)
1287{
1288 int ret;
1289
1290 cds_ssr_protect(__func__);
1291 ret = __wlan_hdd_cfg80211_ocb_stop_timing_advert(wiphy, wdev,
1292 data, data_len);
1293 cds_ssr_unprotect(__func__);
1294
1295 return ret;
1296}
1297
1298/**
1299 * hdd_ocb_get_tsf_timer_callback() - Callback to get TSF command
1300 * @context_ptr: request context
1301 * @response_ptr: response data
1302 */
1303static void hdd_ocb_get_tsf_timer_callback(void *context_ptr,
1304 void *response_ptr)
1305{
1306 struct hdd_ocb_ctxt *context = context_ptr;
1307 struct sir_ocb_get_tsf_timer_response *response = response_ptr;
1308
1309 if (!context)
1310 return;
1311
1312 spin_lock(&hdd_context_lock);
1313 if (context->magic == HDD_OCB_MAGIC) {
1314 if (response) {
1315 context->adapter->ocb_get_tsf_timer_resp = *response;
1316 context->status = 0;
1317 } else {
1318 context->status = -EINVAL;
1319 }
1320 complete(&context->completion_evt);
1321 }
1322 spin_unlock(&hdd_context_lock);
1323}
1324
1325/**
1326 * __wlan_hdd_cfg80211_ocb_get_tsf_timer() - Interface for get TSF timer cmd
1327 * @wiphy: pointer to the wiphy
1328 * @wdev: pointer to the wdev
1329 * @data: The netlink data
1330 * @data_len: The length of the netlink data in bytes
1331 *
1332 * Return: 0 on success.
1333 */
1334static int
1335__wlan_hdd_cfg80211_ocb_get_tsf_timer(struct wiphy *wiphy,
1336 struct wireless_dev *wdev,
1337 const void *data,
1338 int data_len)
1339{
1340 struct sk_buff *nl_resp = 0;
1341 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1342 struct net_device *dev = wdev->netdev;
1343 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1344 int rc = -EINVAL;
1345 struct sir_ocb_get_tsf_timer request = {0};
1346 struct hdd_ocb_ctxt context = {0};
1347
Jeff Johnson1f61b612016-02-12 16:28:33 -08001348 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001349
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301350 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001351 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001352
Krunal Sonibe766b02016-03-10 13:00:44 -08001353 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001354 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001355 return -EINVAL;
1356 }
1357
1358 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001359 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001360 return -EINVAL;
1361 }
1362
1363 /* Initialize the callback context */
1364 init_completion(&context.completion_evt);
1365 context.adapter = adapter;
1366 context.magic = HDD_OCB_MAGIC;
1367
1368 request.vdev_id = adapter->sessionId;
1369 /* Call the SME function */
1370 rc = sme_ocb_get_tsf_timer(hdd_ctx->hHal, &context,
1371 hdd_ocb_get_tsf_timer_callback,
1372 &request);
1373 if (rc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001374 hdd_err("Error calling SME function");
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301375 /* Need to convert from qdf_status to errno. */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001376 return -EINVAL;
1377 }
1378
1379 rc = wait_for_completion_timeout(&context.completion_evt,
1380 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
1381 if (rc == 0) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001382 hdd_err("Operation timed out");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001383 rc = -ETIMEDOUT;
1384 goto end;
1385 }
1386 rc = 0;
1387
1388 if (context.status) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001389 hdd_err("Operation failed: %d", context.status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001390 rc = context.status;
1391 goto end;
1392 }
1393
1394 /* Allocate the buffer for the response. */
1395 nl_resp = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
1396 2 * sizeof(uint32_t) + NLMSG_HDRLEN);
1397
1398 if (!nl_resp) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001399 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001400 rc = -ENOMEM;
1401 goto end;
1402 }
1403
Jeff Johnson5f735d52016-07-06 15:14:45 -07001404 hdd_err("Got TSF timer response, high=%d, low=%d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001405 adapter->ocb_get_tsf_timer_resp.timer_high,
1406 adapter->ocb_get_tsf_timer_resp.timer_low);
1407
1408 /* Populate the response. */
1409 rc = nla_put_u32(nl_resp,
1410 QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH,
1411 adapter->ocb_get_tsf_timer_resp.timer_high);
1412 if (rc)
1413 goto end;
1414 rc = nla_put_u32(nl_resp,
1415 QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW,
1416 adapter->ocb_get_tsf_timer_resp.timer_low);
1417 if (rc)
1418 goto end;
1419
1420 /* Send the response. */
1421 rc = cfg80211_vendor_cmd_reply(nl_resp);
1422 nl_resp = NULL;
1423 if (rc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001424 hdd_err("cfg80211_vendor_cmd_reply failed: %d", rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001425 goto end;
1426 }
1427
1428end:
1429 spin_lock(&hdd_context_lock);
1430 context.magic = 0;
1431 spin_unlock(&hdd_context_lock);
1432 if (nl_resp)
1433 kfree_skb(nl_resp);
1434 return rc;
1435}
1436
1437/**
1438 * wlan_hdd_cfg80211_ocb_get_tsf_timer() - Interface for get TSF timer cmd
1439 * @wiphy: pointer to the wiphy
1440 * @wdev: pointer to the wdev
1441 * @data: The netlink data
1442 * @data_len: The length of the netlink data in bytes
1443 *
1444 * Return: 0 on success.
1445 */
1446int wlan_hdd_cfg80211_ocb_get_tsf_timer(struct wiphy *wiphy,
1447 struct wireless_dev *wdev,
1448 const void *data,
1449 int data_len)
1450{
1451 int ret;
1452
1453 cds_ssr_protect(__func__);
1454 ret = __wlan_hdd_cfg80211_ocb_get_tsf_timer(wiphy, wdev,
1455 data, data_len);
1456 cds_ssr_unprotect(__func__);
1457
1458 return ret;
1459}
1460
1461/**
1462 * hdd_dcc_get_stats_callback() - Callback to get stats command
1463 * @context_ptr: request context
1464 * @response_ptr: response data
1465 */
1466static void hdd_dcc_get_stats_callback(void *context_ptr, void *response_ptr)
1467{
1468 struct hdd_ocb_ctxt *context = context_ptr;
1469 struct sir_dcc_get_stats_response *response = response_ptr;
1470 struct sir_dcc_get_stats_response *hdd_resp;
1471
1472 if (!context)
1473 return;
1474
1475 spin_lock(&hdd_context_lock);
1476 if (context->magic == HDD_OCB_MAGIC) {
1477 if (response) {
1478 /*
1479 * If the response is hanging around from the previous
1480 * request, delete it
1481 */
1482 if (context->adapter->dcc_get_stats_resp) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301483 qdf_mem_free(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001484 context->adapter->dcc_get_stats_resp);
1485 }
1486 context->adapter->dcc_get_stats_resp =
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301487 qdf_mem_malloc(sizeof(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001488 *context->adapter->dcc_get_stats_resp) +
1489 response->channel_stats_array_len);
1490 if (context->adapter->dcc_get_stats_resp) {
1491 hdd_resp = context->adapter->dcc_get_stats_resp;
1492 *hdd_resp = *response;
1493 hdd_resp->channel_stats_array =
1494 (void *)hdd_resp + sizeof(*hdd_resp);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301495 qdf_mem_copy(hdd_resp->channel_stats_array,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001496 response->channel_stats_array,
1497 response->channel_stats_array_len);
1498 context->status = 0;
1499 } else {
1500 context->status = -ENOMEM;
1501 }
1502 } else {
1503 context->status = -EINVAL;
1504 }
1505 complete(&context->completion_evt);
1506 }
1507 spin_unlock(&hdd_context_lock);
1508}
1509
1510/**
1511 * __wlan_hdd_cfg80211_dcc_get_stats() - Interface for get dcc stats
1512 * @wiphy: pointer to the wiphy
1513 * @wdev: pointer to the wdev
1514 * @data: The netlink data
1515 * @data_len: The length of the netlink data in bytes
1516 *
1517 * Return: 0 on success.
1518 */
1519static int __wlan_hdd_cfg80211_dcc_get_stats(struct wiphy *wiphy,
1520 struct wireless_dev *wdev,
1521 const void *data,
1522 int data_len)
1523{
1524 uint32_t channel_count = 0;
1525 uint32_t request_array_len = 0;
1526 void *request_array = 0;
1527 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1528 struct net_device *dev = wdev->netdev;
1529 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1530 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_MAX + 1];
1531 struct sk_buff *nl_resp = 0;
1532 int rc = -EINVAL;
1533 struct sir_dcc_get_stats request = {0};
1534 struct hdd_ocb_ctxt context = {0};
1535
Jeff Johnson1f61b612016-02-12 16:28:33 -08001536 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001537
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301538 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001539 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001540
Krunal Sonibe766b02016-03-10 13:00:44 -08001541 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001542 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001543 return -EINVAL;
1544 }
1545
1546 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001547 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001548 return -EINVAL;
1549 }
1550
1551 /* Parse the netlink message */
1552 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_MAX,
1553 data,
1554 data_len,
1555 qca_wlan_vendor_dcc_get_stats)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001556 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001557 return -EINVAL;
1558 }
1559
1560 /* Validate all the parameters are present */
1561 if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT] ||
1562 !tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001563 hdd_err("Parameters are not present.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001564 return -EINVAL;
1565 }
1566
1567 channel_count = nla_get_u32(
1568 tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT]);
1569 request_array_len = nla_len(
1570 tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]);
1571 request_array = nla_data(
1572 tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]);
1573
1574 /* Initialize the callback context */
1575 init_completion(&context.completion_evt);
1576 context.adapter = adapter;
1577 context.magic = HDD_OCB_MAGIC;
1578
1579 request.vdev_id = adapter->sessionId;
1580 request.channel_count = channel_count;
1581 request.request_array_len = request_array_len;
1582 request.request_array = request_array;
1583
1584 /* Call the SME function. */
1585 rc = sme_dcc_get_stats(hdd_ctx->hHal, &context,
1586 hdd_dcc_get_stats_callback,
1587 &request);
1588 if (rc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001589 hdd_err("Error calling SME function");
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301590 /* Need to convert from qdf_status to errno. */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001591 return -EINVAL;
1592 }
1593
1594 /* Wait for the function to complete. */
1595 rc = wait_for_completion_timeout(&context.completion_evt,
1596 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
1597 if (rc == 0) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001598 hdd_err("Operation failed: %d", rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001599 rc = -ETIMEDOUT;
1600 goto end;
1601 }
1602
1603 if (context.status) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001604 hdd_err("There was error: %d", context.status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001605 rc = context.status;
1606 goto end;
1607 }
1608
1609 if (!adapter->dcc_get_stats_resp) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001610 hdd_err("The response was NULL");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001611 rc = -EINVAL;
1612 goto end;
1613 }
1614
1615 /* Allocate the buffer for the response. */
1616 nl_resp = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(uint32_t) +
1617 adapter->dcc_get_stats_resp->channel_stats_array_len +
1618 NLMSG_HDRLEN);
1619 if (!nl_resp) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001620 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001621 rc = -ENOMEM;
1622 goto end;
1623 }
1624
1625 /* Populate the response. */
1626 rc = nla_put_u32(nl_resp,
1627 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT,
1628 adapter->dcc_get_stats_resp->num_channels);
1629 if (rc)
1630 goto end;
1631 rc = nla_put(nl_resp,
1632 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY,
1633 adapter->dcc_get_stats_resp->channel_stats_array_len,
1634 adapter->dcc_get_stats_resp->channel_stats_array);
1635 if (rc)
1636 goto end;
1637
1638 /* Send the response. */
1639 rc = cfg80211_vendor_cmd_reply(nl_resp);
1640 nl_resp = NULL;
1641 if (rc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001642 hdd_err("cfg80211_vendor_cmd_reply failed: %d", rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001643 goto end;
1644 }
1645
1646 /* fall through */
1647end:
1648 spin_lock(&hdd_context_lock);
1649 context.magic = 0;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301650 qdf_mem_free(adapter->dcc_get_stats_resp);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001651 adapter->dcc_get_stats_resp = NULL;
1652 spin_unlock(&hdd_context_lock);
1653 if (nl_resp)
1654 kfree_skb(nl_resp);
1655 return rc;
1656}
1657
1658/**
1659 * wlan_hdd_cfg80211_dcc_get_stats() - Interface for get dcc stats
1660 * @wiphy: pointer to the wiphy
1661 * @wdev: pointer to the wdev
1662 * @data: The netlink data
1663 * @data_len: The length of the netlink data in bytes
1664 *
1665 * Return: 0 on success.
1666 */
1667int wlan_hdd_cfg80211_dcc_get_stats(struct wiphy *wiphy,
1668 struct wireless_dev *wdev,
1669 const void *data,
1670 int data_len)
1671{
1672 int ret;
1673
1674 cds_ssr_protect(__func__);
1675 ret = __wlan_hdd_cfg80211_dcc_get_stats(wiphy, wdev,
1676 data, data_len);
1677 cds_ssr_unprotect(__func__);
1678
1679 return ret;
1680}
1681
1682/**
1683 * __wlan_hdd_cfg80211_dcc_clear_stats() - Interface for clear dcc stats cmd
1684 * @wiphy: pointer to the wiphy
1685 * @wdev: pointer to the wdev
1686 * @data: The netlink data
1687 * @data_len: The length of the netlink data in bytes
1688 *
1689 * Return: 0 on success.
1690 */
1691static int __wlan_hdd_cfg80211_dcc_clear_stats(struct wiphy *wiphy,
1692 struct wireless_dev *wdev,
1693 const void *data,
1694 int data_len)
1695{
1696 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1697 struct net_device *dev = wdev->netdev;
1698 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1699 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_MAX + 1];
1700
Jeff Johnson1f61b612016-02-12 16:28:33 -08001701 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001702
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301703 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001704 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001705
Krunal Sonibe766b02016-03-10 13:00:44 -08001706 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001707 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001708 return -EINVAL;
1709 }
1710
1711 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001712 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001713 return -EINVAL;
1714 }
1715
1716 /* Parse the netlink message */
1717 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_MAX,
1718 data,
1719 data_len,
1720 qca_wlan_vendor_dcc_clear_stats)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001721 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001722 return -EINVAL;
1723 }
1724
1725 /* Verify that the parameter is present */
1726 if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001727 hdd_err("Parameters are not present.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001728 return -EINVAL;
1729 }
1730
1731 /* Call the SME function */
1732 if (sme_dcc_clear_stats(hdd_ctx->hHal, adapter->sessionId,
1733 nla_get_u32(
1734 tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP])) !=
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301735 QDF_STATUS_SUCCESS) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001736 hdd_err("Error calling SME function.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001737 return -EINVAL;
1738 }
1739
1740 return 0;
1741}
1742
1743/**
1744 * wlan_hdd_cfg80211_dcc_clear_stats() - Interface for clear dcc stats cmd
1745 * @wiphy: pointer to the wiphy
1746 * @wdev: pointer to the wdev
1747 * @data: The netlink data
1748 * @data_len: The length of the netlink data in bytes
1749 *
1750 * Return: 0 on success.
1751 */
1752int wlan_hdd_cfg80211_dcc_clear_stats(struct wiphy *wiphy,
1753 struct wireless_dev *wdev,
1754 const void *data,
1755 int data_len)
1756{
1757 int ret;
1758
1759 cds_ssr_protect(__func__);
1760 ret = __wlan_hdd_cfg80211_dcc_clear_stats(wiphy, wdev,
1761 data, data_len);
1762 cds_ssr_unprotect(__func__);
1763
1764 return ret;
1765}
1766
1767/**
1768 * hdd_dcc_update_ndl_callback() - Callback to update NDL command
1769 * @context_ptr: request context
1770 * @response_ptr: response data
1771 */
1772static void hdd_dcc_update_ndl_callback(void *context_ptr, void *response_ptr)
1773{
1774 struct hdd_ocb_ctxt *context = context_ptr;
1775 struct sir_dcc_update_ndl_response *response = response_ptr;
1776
1777 if (!context)
1778 return;
1779
1780 spin_lock(&hdd_context_lock);
1781 if (context->magic == HDD_OCB_MAGIC) {
1782 if (response) {
1783 context->adapter->dcc_update_ndl_resp = *response;
1784 context->status = 0;
1785 } else {
1786 context->status = -EINVAL;
1787 }
1788 complete(&context->completion_evt);
1789 }
1790 spin_unlock(&hdd_context_lock);
1791}
1792
1793/**
1794 * __wlan_hdd_cfg80211_dcc_update_ndl() - Interface for update dcc cmd
1795 * @wiphy: pointer to the wiphy
1796 * @wdev: pointer to the wdev
1797 * @data: The netlink data
1798 * @data_len: The length of the netlink data in bytes
1799 *
1800 * Return: 0 on success.
1801 */
1802static int __wlan_hdd_cfg80211_dcc_update_ndl(struct wiphy *wiphy,
1803 struct wireless_dev *wdev,
1804 const void *data,
1805 int data_len)
1806{
1807 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1808 struct net_device *dev = wdev->netdev;
1809 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1810 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX + 1];
1811 struct sir_dcc_update_ndl request;
1812 uint32_t channel_count;
1813 uint32_t ndl_channel_array_len;
1814 void *ndl_channel_array;
1815 uint32_t ndl_active_state_array_len;
1816 void *ndl_active_state_array;
1817 int rc = -EINVAL;
1818 struct hdd_ocb_ctxt context = {0};
1819
Jeff Johnson1f61b612016-02-12 16:28:33 -08001820 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001821
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301822 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001823 goto end;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001824
Krunal Sonibe766b02016-03-10 13:00:44 -08001825 if (adapter->device_mode != QDF_OCB_MODE) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001826 hdd_err("Device not in OCB mode!");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001827 goto end;
1828 }
1829
1830 if (!wma_is_vdev_up(adapter->sessionId)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001831 hdd_err("The device has not been started");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001832 return -EINVAL;
1833 }
1834
1835 /* Parse the netlink message */
1836 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX,
1837 data,
1838 data_len,
1839 qca_wlan_vendor_dcc_update_ndl)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001840 hdd_err("Invalid ATTR");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001841 goto end;
1842 }
1843
1844 /* Verify that the parameter is present */
1845 if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT] ||
1846 !tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY] ||
1847 !tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001848 hdd_err("Parameters are not present.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001849 return -EINVAL;
1850 }
1851
1852 channel_count = nla_get_u32(
1853 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT]);
1854 ndl_channel_array_len = nla_len(
1855 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY]);
1856 ndl_channel_array = nla_data(
1857 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY]);
1858 ndl_active_state_array_len = nla_len(
1859 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]);
1860 ndl_active_state_array = nla_data(
1861 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]);
1862
1863 /* Initialize the callback context */
1864 init_completion(&context.completion_evt);
1865 context.adapter = adapter;
1866 context.magic = HDD_OCB_MAGIC;
1867
1868 /* Copy the parameters to the request structure. */
1869 request.vdev_id = adapter->sessionId;
1870 request.channel_count = channel_count;
1871 request.dcc_ndl_chan_list_len = ndl_channel_array_len;
1872 request.dcc_ndl_chan_list = ndl_channel_array;
1873 request.dcc_ndl_active_state_list_len = ndl_active_state_array_len;
1874 request.dcc_ndl_active_state_list = ndl_active_state_array;
1875
1876 /* Call the SME function */
1877 rc = sme_dcc_update_ndl(hdd_ctx->hHal, &context,
1878 hdd_dcc_update_ndl_callback,
1879 &request);
1880 if (rc) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001881 hdd_err("Error calling SME function.");
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301882 /* Convert from qdf_status to errno */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001883 return -EINVAL;
1884 }
1885
1886 /* Wait for the function to complete. */
1887 rc = wait_for_completion_timeout(&context.completion_evt,
1888 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
1889 if (rc == 0) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001890 hdd_err("Operation timed out");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001891 rc = -ETIMEDOUT;
1892 goto end;
1893 }
1894 rc = 0;
1895
1896 if (context.status) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001897 hdd_err("Operation failed: %d", context.status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001898 rc = context.status;
1899 goto end;
1900 }
1901
1902 if (adapter->dcc_update_ndl_resp.status) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001903 hdd_err("Operation returned: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001904 adapter->dcc_update_ndl_resp.status);
1905 rc = -EINVAL;
1906 goto end;
1907 }
1908
1909 /* fall through */
1910end:
1911 spin_lock(&hdd_context_lock);
1912 context.magic = 0;
1913 spin_unlock(&hdd_context_lock);
1914 return rc;
1915}
1916
1917/**
1918 * wlan_hdd_cfg80211_dcc_update_ndl() - Interface for update dcc cmd
1919 * @wiphy: pointer to the wiphy
1920 * @wdev: pointer to the wdev
1921 * @data: The netlink data
1922 * @data_len: The length of the netlink data in bytes
1923 *
1924 * Return: 0 on success.
1925 */
1926int wlan_hdd_cfg80211_dcc_update_ndl(struct wiphy *wiphy,
1927 struct wireless_dev *wdev,
1928 const void *data,
1929 int data_len)
1930{
1931 int ret;
1932
1933 cds_ssr_protect(__func__);
1934 ret = __wlan_hdd_cfg80211_dcc_update_ndl(wiphy, wdev,
1935 data, data_len);
1936 cds_ssr_unprotect(__func__);
1937
1938 return ret;
1939}
1940
1941/**
1942 * wlan_hdd_dcc_stats_event_callback() - Callback to get stats event
1943 * @context_ptr: request context
1944 * @response_ptr: response data
1945 */
1946static void wlan_hdd_dcc_stats_event_callback(void *context_ptr,
1947 void *response_ptr)
1948{
1949 hdd_context_t *hdd_ctx = (hdd_context_t *)context_ptr;
1950 struct sir_dcc_get_stats_response *resp = response_ptr;
1951 struct sk_buff *vendor_event;
1952
1953 ENTER();
1954
1955 vendor_event =
1956 cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
1957 NULL, sizeof(uint32_t) + resp->channel_stats_array_len +
1958 NLMSG_HDRLEN,
1959 QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT_INDEX,
1960 GFP_KERNEL);
1961
1962 if (!vendor_event) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001963 hdd_err("cfg80211_vendor_event_alloc failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001964 return;
1965 }
1966
1967 if (nla_put_u32(vendor_event,
1968 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT,
1969 resp->num_channels) ||
1970 nla_put(vendor_event,
1971 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY,
1972 resp->channel_stats_array_len,
1973 resp->channel_stats_array)) {
Jeff Johnson5f735d52016-07-06 15:14:45 -07001974 hdd_err("nla put failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001975 kfree_skb(vendor_event);
1976 return;
1977 }
1978
1979 cfg80211_vendor_event(vendor_event, GFP_KERNEL);
1980}
1981
1982/**
1983 * wlan_hdd_dcc_register_for_dcc_stats_event() - Register for dcc stats events
1984 * @hdd_ctx: hdd context
1985 */
1986void wlan_hdd_dcc_register_for_dcc_stats_event(hdd_context_t *hdd_ctx)
1987{
1988 int rc;
1989
1990 rc = sme_register_for_dcc_stats_event(hdd_ctx->hHal, hdd_ctx,
Arun Khandavalli4b55da72016-07-19 19:55:01 +05301991 wlan_hdd_dcc_stats_event_callback);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001992 if (rc)
Jeff Johnson5f735d52016-07-06 15:14:45 -07001993 hdd_err("Register callback failed: %d", rc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001994}