blob: cc8543e0ded3d80d0ebcbe88cb2993818564684b [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
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"
42
43/* Structure definitions for WLAN_SET_DOT11P_CHANNEL_SCHED */
44#define AIFSN_MIN (2)
45#define AIFSN_MAX (15)
46#define CW_MIN (1)
47#define CW_MAX (10)
48
49/* Maximum time(ms) to wait for OCB operations */
50#define WLAN_WAIT_TIME_OCB_CMD 1500
51#define HDD_OCB_MAGIC 0x489a154f
52
53/**
54 * struct hdd_ocb_ctxt - Context for OCB operations
55 * adapter: the ocb adapter
56 * completion_evt: the completion event
57 * status: status of the request
58 */
59struct hdd_ocb_ctxt {
60 uint32_t magic;
61 hdd_adapter_t *adapter;
62 struct completion completion_evt;
63 int status;
64};
65
66/**
67 * hdd_set_dot11p_config() - Set 802.11p config flag
68 * @hdd_ctx: HDD Context pointer
69 *
70 * TODO-OCB: This has been temporarily added to ensure this paramter
71 * is set in CSR when we init the channel list. This should be removed
72 * once the 5.9 GHz channels are added to the regulatory domain.
73 */
74void hdd_set_dot11p_config(hdd_context_t *hdd_ctx)
75{
76 sme_set_dot11p_config(hdd_ctx->hHal,
77 hdd_ctx->config->dot11p_mode !=
78 WLAN_HDD_11P_DISABLED);
79}
80
81/**
82 * dot11p_validate_qos_params() - Check if QoS parameters are valid
83 * @qos_params: Array of QoS parameters
84 *
85 * Return: 0 on success. error code on failure.
86 */
87static int dot11p_validate_qos_params(struct sir_qos_params qos_params[])
88{
89 int i;
90
91 for (i = 0; i < MAX_NUM_AC; i++) {
92 if ((!qos_params[i].aifsn) && (!qos_params[i].cwmin)
93 && (!qos_params[i].cwmax))
94 continue;
95
96 /* Validate AIFSN */
97 if ((qos_params[i].aifsn < AIFSN_MIN)
98 || (qos_params[i].aifsn > AIFSN_MAX)) {
99 hddLog(LOGE, FL("Invalid QoS parameter aifsn %d"),
100 qos_params[i].aifsn);
101 return -EINVAL;
102 }
103
104 /* Validate CWMin */
105 if ((qos_params[i].cwmin < CW_MIN)
106 || (qos_params[i].cwmin > CW_MAX)) {
107 hddLog(LOGE, FL("Invalid QoS parameter cwmin %d"),
108 qos_params[i].cwmin);
109 return -EINVAL;
110 }
111
112 /* Validate CWMax */
113 if ((qos_params[i].cwmax < CW_MIN)
114 || (qos_params[i].cwmax > CW_MAX)) {
115 hddLog(LOGE, FL("Invalid QoS parameter cwmax %d"),
116 qos_params[i].cwmax);
117 return -EINVAL;
118 }
119 }
120
121 return 0;
122}
123
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800124/**
125 * dot11p_validate_channel() - validates a DSRC channel
126 * @center_freq: the channel's center frequency
127 * @bandwidth: the channel's bandwidth
128 * @tx_power: transmit power
129 * @reg_power: (output) the max tx power from the regulatory domain
130 * @antenna_max: (output) the max antenna gain from the regulatory domain
131 *
132 * Return: 0 if the channel is valid, error code otherwise.
133 */
134static int dot11p_validate_channel(struct wiphy *wiphy,
135 uint32_t channel_freq, uint32_t bandwidth,
136 uint32_t tx_power, uint8_t *reg_power,
137 uint8_t *antenna_max)
138{
139 int band_idx, channel_idx;
140 struct ieee80211_supported_band *current_band;
141 struct ieee80211_channel *current_channel;
142
143 for (band_idx = 0; band_idx < IEEE80211_NUM_BANDS; band_idx++) {
144 current_band = wiphy->bands[band_idx];
145 if (!current_band)
146 continue;
147
148 for (channel_idx = 0; channel_idx < current_band->n_channels;
149 channel_idx++) {
150 current_channel = &current_band->channels[channel_idx];
151
152 if (channel_freq == current_channel->center_freq) {
153 if (current_channel->flags &
154 IEEE80211_CHAN_DISABLED)
155 return -EINVAL;
156
157 if (reg_power)
158 *reg_power =
159 current_channel->max_reg_power;
160 if (antenna_max)
161 *antenna_max =
162 current_channel->
163 max_antenna_gain;
164
165 switch (bandwidth) {
166 case 0:
167 if (current_channel->flags &
168 IEEE80211_CHAN_NO_10MHZ)
169 bandwidth = 5;
170 else if (current_channel->flags &
171 IEEE80211_CHAN_NO_20MHZ)
172 bandwidth = 10;
173 else
174 bandwidth = 20;
175 break;
176 case 5:
177 break;
178 case 10:
179 if (current_channel->flags &
180 IEEE80211_CHAN_NO_10MHZ)
181 return -EINVAL;
182 break;
183 case 20:
184 if (current_channel->flags &
185 IEEE80211_CHAN_NO_20MHZ)
186 return -EINVAL;
187 break;
188 default:
189 return -EINVAL;
190 }
191
192 if (tx_power > current_channel->max_power)
193 return -EINVAL;
194
195 return 0;
196 }
197 }
198 }
199
Amar Singhalfda6eda2015-11-02 15:08:59 -0800200 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800201}
202
203/**
204 * hdd_ocb_validate_config() - Validates the config data
205 * @config: configuration to be validated
206 *
207 * Return: 0 on success.
208 */
209static int hdd_ocb_validate_config(hdd_adapter_t *adapter,
210 struct sir_ocb_config *config)
211{
212 int i;
213 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
214
215 for (i = 0; i < config->channel_count; i++) {
216 if (dot11p_validate_channel(hdd_ctx->wiphy,
217 config->channels[i].chan_freq,
218 config->channels[i].bandwidth,
219 config->channels[i].max_pwr,
220 &config->channels[i].reg_pwr,
221 &config->channels[i].antenna_max)) {
222 hddLog(LOGE, FL("Invalid channel frequency %d"),
223 config->channels[i].chan_freq);
224 return -EINVAL;
225 }
226 if (dot11p_validate_qos_params(config->channels[i].qos_params))
227 return -EINVAL;
228 }
229
230 return 0;
231}
232
233/**
234 * hdd_ocb_register_sta() - Register station with Transport Layer
235 * @adapter: Pointer to HDD Adapter
236 *
237 * This function should be invoked in the OCB Set Schedule callback
238 * to enable the data path in the TL by calling RegisterSTAClient
239 *
240 * Return: 0 on success. -1 on failure.
241 */
242static int hdd_ocb_register_sta(hdd_adapter_t *adapter)
243{
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530244 QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800245 struct ol_txrx_desc_type sta_desc = {0};
246 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
247 hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
248 uint8_t peer_id;
249
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530250 qdf_status = ol_txrx_register_ocb_peer(hdd_ctx->pcds_context,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800251 adapter->macAddressCurrent.bytes,
252 &peer_id);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530253 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800254 hddLog(LOGE, FL("Error registering OCB Self Peer!"));
255 return -EINVAL;
256 }
257
258 hdd_ctx->sta_to_adapter[peer_id] = adapter;
259
260 sta_desc.sta_id = peer_id;
261 sta_desc.is_qos_enabled = 1;
262
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530263 qdf_status = ol_txrx_register_peer(hdd_rx_packet_cbk,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800264 &sta_desc);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530265 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800266 hddLog(LOGE, FL("Failed to register. Status= %d [0x%08X]"),
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530267 qdf_status, qdf_status);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800268 return -EINVAL;
269 }
270
271 if (pHddStaCtx->conn_info.staId[0] != 0 &&
272 pHddStaCtx->conn_info.staId[0] != peer_id) {
273 hddLog(LOGE, FL("The ID for the OCB station has changed."));
274 }
275
276 pHddStaCtx->conn_info.staId[0] = peer_id;
277 cdf_copy_macaddr(&pHddStaCtx->conn_info.peerMacAddress[0],
278 &adapter->macAddressCurrent);
279
280 return 0;
281}
282
283/**
284 * hdd_ocb_config_new() - Creates a new OCB configuration
285 * @num_channels: the number of channels
286 * @num_schedule: the schedule size
287 * @ndl_chan_list_len: length in bytes of the NDL chan blob
288 * @ndl_active_state_list_len: length in bytes of the active state blob
289 *
290 * Return: A pointer to the OCB configuration struct, NULL on failure.
291 */
292static struct sir_ocb_config *hdd_ocb_config_new(int num_channels,
293 int num_schedule,
294 int ndl_chan_list_len,
295 int ndl_active_state_list_len)
296{
297 struct sir_ocb_config *ret = 0;
298 uint32_t len;
299 void *cursor;
300
301 if (num_channels > CFG_TGT_NUM_OCB_CHANNELS ||
302 num_schedule > CFG_TGT_NUM_OCB_SCHEDULES)
303 return NULL;
304
305 len = sizeof(*ret) +
306 num_channels * sizeof(struct sir_ocb_config_channel) +
307 num_schedule * sizeof(struct sir_ocb_config_sched) +
308 ndl_chan_list_len +
309 ndl_active_state_list_len;
310
311 cursor = cdf_mem_malloc(len);
312 if (!cursor)
313 goto fail;
314
315 cdf_mem_zero(cursor, len);
316 ret = cursor;
317 cursor += sizeof(*ret);
318
319 ret->channel_count = num_channels;
320 ret->channels = cursor;
321 cursor += num_channels * sizeof(*ret->channels);
322
323 ret->schedule_size = num_schedule;
324 ret->schedule = cursor;
325 cursor += num_schedule * sizeof(*ret->schedule);
326
327 ret->dcc_ndl_chan_list = cursor;
328 cursor += ndl_chan_list_len;
329
330 ret->dcc_ndl_active_state_list = cursor;
331 cursor += ndl_active_state_list_len;
332
333 return ret;
334
335fail:
336 cdf_mem_free(ret);
337 return NULL;
338}
339
340/**
341 * hdd_ocb_set_config_callback() - OCB set config callback function
342 * @context_ptr: OCB call context
343 * @response_ptr: Pointer to response structure
344 *
345 * This function is registered as a callback with the lower layers
346 * and is used to respond with the status of a OCB set config command.
347 */
348static void hdd_ocb_set_config_callback(void *context_ptr, void *response_ptr)
349{
350 struct hdd_ocb_ctxt *context = context_ptr;
351 struct sir_ocb_set_config_response *resp = response_ptr;
352
353 if (!context)
354 return;
355
356 if (resp && resp->status)
357 hddLog(LOGE, FL("Operation failed: %d"), resp->status);
358
359 spin_lock(&hdd_context_lock);
360 if (context->magic == HDD_OCB_MAGIC) {
361 hdd_adapter_t *adapter = context->adapter;
362 if (!resp) {
363 context->status = -EINVAL;
364 complete(&context->completion_evt);
365 spin_unlock(&hdd_context_lock);
366 return;
367 }
368
369 context->adapter->ocb_set_config_resp = *resp;
370 spin_unlock(&hdd_context_lock);
371 if (!resp->status) {
372 /*
373 * OCB set config command successful.
374 * Open the TX data path
375 */
376 if (!hdd_ocb_register_sta(adapter)) {
377 netif_carrier_on(adapter->dev);
378 netif_tx_start_all_queues(
379 adapter->dev);
380 }
381 }
382
383 spin_lock(&hdd_context_lock);
384 if (context->magic == HDD_OCB_MAGIC)
385 complete(&context->completion_evt);
386 spin_unlock(&hdd_context_lock);
387 } else {
388 spin_unlock(&hdd_context_lock);
389 }
390}
391
392/**
393 * hdd_ocb_set_config_req() - Send an OCB set config request
394 * @adapter: a pointer to the adapter
395 * @config: a pointer to the OCB configuration
396 *
397 * Return: 0 on success.
398 */
399static int hdd_ocb_set_config_req(hdd_adapter_t *adapter,
400 struct sir_ocb_config *config)
401{
402 int rc;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530403 QDF_STATUS qdf_status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800404 struct hdd_ocb_ctxt context = {0};
405
406 if (hdd_ocb_validate_config(adapter, config)) {
407 hddLog(LOGE, FL("The configuration is invalid"));
408 return -EINVAL;
409 }
410
411 init_completion(&context.completion_evt);
412 context.adapter = adapter;
413 context.magic = HDD_OCB_MAGIC;
414
415 hddLog(LOG1, FL("Disabling queues"));
416 netif_tx_disable(adapter->dev);
417 netif_carrier_off(adapter->dev);
418
419 /* Call the SME API to set the config */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530420 qdf_status = sme_ocb_set_config(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800421 ((hdd_context_t *)adapter->pHddCtx)->hHal, &context,
422 hdd_ocb_set_config_callback, config);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530423 if (qdf_status != QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800424 hddLog(LOGE, FL("Error calling SME function."));
425 /* Convert from ecdf_status to errno */
426 return -EINVAL;
427 }
428
429 /* Wait for the function to complete. */
430 rc = wait_for_completion_timeout(&context.completion_evt,
431 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
432 if (rc == 0) {
433 rc = -ETIMEDOUT;
434 goto end;
435 }
436 rc = 0;
437
438 if (context.status) {
439 rc = context.status;
440 goto end;
441 }
442
443 if (adapter->ocb_set_config_resp.status) {
444 rc = -EINVAL;
445 goto end;
446 }
447
448 /* fall through */
449end:
450 spin_lock(&hdd_context_lock);
451 context.magic = 0;
452 spin_unlock(&hdd_context_lock);
453 if (rc)
454 hddLog(LOGE, FL("Operation failed: %d"), rc);
455 return rc;
456}
457
458/**
459 * __iw_set_dot11p_channel_sched() - Handler for WLAN_SET_DOT11P_CHANNEL_SCHED
460 * ioctl
461 * @dev: Pointer to net_device structure
462 * @iw_request_info: IW Request Info
463 * @wrqu: IW Request Userspace Data Pointer
464 * @extra: IW Request Kernel Data Pointer
465 *
466 * Return: 0 on success
467 */
468static int __iw_set_dot11p_channel_sched(struct net_device *dev,
469 struct iw_request_info *info,
470 union iwreq_data *wrqu, char *extra)
471{
472 int rc = 0;
473 struct dot11p_channel_sched *sched;
474 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
475 struct sir_ocb_config *config = NULL;
476 uint8_t *mac_addr;
477 int i, j;
478 struct sir_ocb_config_channel *curr_chan;
479
480 if (wlan_hdd_validate_context(WLAN_HDD_GET_CTX(adapter))) {
481 hddLog(LOGE, FL("HDD context is not valid"));
482 return -EINVAL;
483 }
484
485 if (adapter->device_mode != WLAN_HDD_OCB) {
486 hddLog(LOGE, FL("Device not in OCB mode!"));
487 return -EINVAL;
488 }
489
490 sched = (struct dot11p_channel_sched *)extra;
491
492 /* Scheduled slots same as num channels for compatibility */
493 config = hdd_ocb_config_new(sched->num_channels, sched->num_channels,
494 0, 0);
495 if (config == NULL) {
496 hddLog(LOGE, FL("Failed to allocate memory!"));
497 return -ENOMEM;
498 }
499
500 /* Identify the vdev interface */
501 config->session_id = adapter->sessionId;
502
503 /* Release all the mac addresses used for OCB */
504 for (i = 0; i < adapter->ocb_mac_addr_count; i++) {
505 wlan_hdd_release_intf_addr(adapter->pHddCtx,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800506 adapter->ocb_mac_address[i].bytes);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800507 }
508 adapter->ocb_mac_addr_count = 0;
509
510 config->channel_count = 0;
511 for (i = 0; i < sched->num_channels; i++) {
512 if (0 == sched->channels[i].channel_freq)
513 continue;
514
515 curr_chan = &(config->channels[config->channel_count]);
516
517 curr_chan->chan_freq = sched->channels[i].channel_freq;
518 /*
519 * tx_power is divided by 2 because ocb_channel.tx_power is
520 * in half dB increments and sir_ocb_config_channel.max_pwr
521 * is in 1 dB increments.
522 */
523 curr_chan->max_pwr = sched->channels[i].tx_power / 2;
524 curr_chan->bandwidth = sched->channels[i].channel_bandwidth;
525 /* assume 10 as default if not provided */
526 if (curr_chan->bandwidth == 0)
527 curr_chan->bandwidth = 10;
528
529 /*
530 * Setup locally administered mac addresses for each channel.
531 * First channel uses the adapter's address.
532 */
533 if (i == 0) {
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800534 cdf_copy_macaddr(&curr_chan->mac_address,
535 &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800536 } else {
537 mac_addr = wlan_hdd_get_intf_addr(adapter->pHddCtx);
538 if (mac_addr == NULL) {
539 hddLog(LOGE, FL("Cannot obtain mac address"));
540 rc = -EINVAL;
541 goto fail;
542 }
543 cdf_mem_copy(config->channels[
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800544 config->channel_count].mac_address.bytes,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800545 mac_addr, sizeof(tSirMacAddr));
546 /* Save the mac address to release later */
547 cdf_mem_copy(adapter->ocb_mac_address[
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800548 adapter->ocb_mac_addr_count].bytes,
549 mac_addr, CDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800550 adapter->ocb_mac_addr_count++;
551 }
552
553 for (j = 0; j < MAX_NUM_AC; j++) {
554 curr_chan->qos_params[j].aifsn =
555 sched->channels[i].qos_params[j].aifsn;
556 curr_chan->qos_params[j].cwmin =
557 sched->channels[i].qos_params[j].cwmin;
558 curr_chan->qos_params[j].cwmax =
559 sched->channels[i].qos_params[j].cwmax;
560 }
561
562 config->channel_count++;
563 }
564
565 /*
566 * Scheduled slots same as num channels for compatibility with
567 * legacy use.
568 */
569 for (i = 0; i < sched->num_channels; i++) {
570 config->schedule[i].chan_freq = sched->channels[i].channel_freq;
571 config->schedule[i].guard_interval =
572 sched->channels[i].start_guard_interval;
573 config->schedule[i].total_duration =
574 sched->channels[i].duration;
575 }
576
577 rc = hdd_ocb_set_config_req(adapter, config);
578 if (rc) {
579 hddLog(LOGE, FL("Error while setting OCB config"));
580 goto fail;
581 }
582
583 rc = 0;
584
585fail:
586 cdf_mem_free(config);
587 return rc;
588}
589
590/**
591 * iw_set_dot11p_channel_sched() - IOCTL interface for setting channel schedule
592 * @dev: Pointer to net_device structure
593 * @iw_request_info: IW Request Info
594 * @wrqu: IW Request Userspace Data Pointer
595 * @extra: IW Request Kernel Data Pointer
596 *
597 * Return: 0 on success.
598 */
599int iw_set_dot11p_channel_sched(struct net_device *dev,
600 struct iw_request_info *info,
601 union iwreq_data *wrqu, char *extra)
602{
603 int ret;
604
605 cds_ssr_protect(__func__);
606 ret = __iw_set_dot11p_channel_sched(dev, info, wrqu, extra);
607 cds_ssr_unprotect(__func__);
608
609 return ret;
610}
611
612static const struct nla_policy qca_wlan_vendor_ocb_set_config_policy[
613 QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX + 1] = {
614 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT] = {
615 .type = NLA_U32
616 },
617 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE] = {
618 .type = NLA_U32
619 },
620 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY] = {
621 .type = NLA_BINARY
622 },
623 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY] = {
624 .type = NLA_BINARY
625 },
626 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY] = {
627 .type = NLA_BINARY
628 },
629 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY] = {
630 .type = NLA_BINARY
631 },
632 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS] = {
633 .type = NLA_U32
634 },
635};
636
637static const struct nla_policy qca_wlan_vendor_ocb_set_utc_time_policy[
638 QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX + 1] = {
639 [QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE] = {
640 .type = NLA_BINARY, .len = SIZE_UTC_TIME
641 },
642 [QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR] = {
643 .type = NLA_BINARY, .len = SIZE_UTC_TIME_ERROR
644 },
645};
646
647static const struct nla_policy qca_wlan_vendor_ocb_start_timing_advert_policy[
648 QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX + 1] = {
649 [QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ] = {
650 .type = NLA_U32
651 },
652 [QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE] = {
653 .type = NLA_U32
654 },
655};
656
657static const struct nla_policy qca_wlan_vendor_ocb_stop_timing_advert_policy[
658 QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX + 1] = {
659 [QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ] = {
660 .type = NLA_U32
661 },
662};
663
664static const struct nla_policy qca_wlan_vendor_ocb_get_tsf_timer_resp[] = {
665 [QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH] = {
666 .type = NLA_U32
667 },
668 [QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW] = {
669 .type = NLA_U32
670 },
671};
672
673static const struct nla_policy qca_wlan_vendor_dcc_get_stats[] = {
674 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT] = {
675 .type = NLA_U32
676 },
677 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY] = {
678 .type = NLA_BINARY
679 },
680};
681
682static const struct nla_policy qca_wlan_vendor_dcc_get_stats_resp[] = {
683 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT] = {
684 .type = NLA_U32
685 },
686 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY] = {
687 .type = NLA_BINARY
688 },
689};
690
691static const struct nla_policy qca_wlan_vendor_dcc_clear_stats[] = {
692 [QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP] = {
693 .type = NLA_U32
694 },
695};
696
697static const struct nla_policy qca_wlan_vendor_dcc_update_ndl[
698 QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX + 1] = {
699 [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT] = {
700 .type = NLA_U32
701 },
702 [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY] = {
703 .type = NLA_BINARY
704 },
705 [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY] = {
706 .type = NLA_BINARY
707 },
708};
709
710/**
711 * struct wlan_hdd_ocb_config_channel
712 * @chan_freq: frequency of the channel
713 * @bandwidth: bandwidth of the channel, either 10 or 20 MHz
714 * @mac_address: MAC address assigned to this channel
715 * @qos_params: QoS parameters
716 * @max_pwr: maximum transmit power of the channel (1/2 dBm)
717 * @min_pwr: minimum transmit power of the channel (1/2 dBm)
718 */
719struct wlan_hdd_ocb_config_channel {
720 uint32_t chan_freq;
721 uint32_t bandwidth;
722 uint16_t flags;
723 uint8_t reserved[4];
724 struct sir_qos_params qos_params[MAX_NUM_AC];
725 uint32_t max_pwr;
726 uint32_t min_pwr;
727};
728
729static void wlan_hdd_ocb_config_channel_to_sir_ocb_config_channel(
730 struct sir_ocb_config_channel *dest,
731 struct wlan_hdd_ocb_config_channel *src,
732 uint32_t channel_count)
733{
734 uint32_t i;
735
736 cdf_mem_zero(dest, channel_count * sizeof(*dest));
737
738 for (i = 0; i < channel_count; i++) {
739 dest[i].chan_freq = src[i].chan_freq;
740 dest[i].bandwidth = src[i].bandwidth;
741 cdf_mem_copy(dest[i].qos_params, src[i].qos_params,
742 sizeof(dest[i].qos_params));
743 /*
744 * max_pwr and min_pwr are divided by 2 because
745 * wlan_hdd_ocb_config_channel.max_pwr and min_pwr
746 * are in 1/2 dB increments and
747 * sir_ocb_config_channel.max_pwr and min_pwr are in
748 * 1 dB increments.
749 */
750 dest[i].max_pwr = src[i].max_pwr / 2;
751 dest[i].min_pwr = (src[i].min_pwr + 1) / 2;
752 dest[i].flags = src[i].flags;
753 }
754}
755
756/**
757 * __wlan_hdd_cfg80211_ocb_set_config() - Interface for set config command
758 * @wiphy: pointer to the wiphy
759 * @wdev: pointer to the wdev
760 * @data: The netlink data
761 * @data_len: The length of the netlink data in bytes
762 *
763 * Return: 0 on success.
764 */
765static int __wlan_hdd_cfg80211_ocb_set_config(struct wiphy *wiphy,
766 struct wireless_dev *wdev,
767 const void *data,
768 int data_len)
769{
770 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
771 struct net_device *dev = wdev->netdev;
772 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
773 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX + 1];
774 struct nlattr *channel_array;
775 struct nlattr *sched_array;
776 struct nlattr *ndl_chan_list;
777 uint32_t ndl_chan_list_len;
778 struct nlattr *ndl_active_state_list;
779 uint32_t ndl_active_state_list_len;
780 uint32_t flags = 0;
781 int i;
782 int channel_count, schedule_size;
783 struct sir_ocb_config *config;
784 int rc = -EINVAL;
785 uint8_t *mac_addr;
786
787 ENTER();
788
789 if (wlan_hdd_validate_context(hdd_ctx)) {
790 hddLog(LOGE, FL("HDD context is not valid"));
791 return -EINVAL;
792 }
793
794 if (adapter->device_mode != WLAN_HDD_OCB) {
795 hddLog(LOGE, FL("Device not in OCB mode!"));
796 return -EINVAL;
797 }
798
799 /* Parse the netlink message */
800 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX,
801 data,
802 data_len, qca_wlan_vendor_ocb_set_config_policy)) {
803 hddLog(LOGE, FL("Invalid ATTR"));
804 return -EINVAL;
805 }
806
807 /* Get the number of channels in the schedule */
808 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT]) {
809 hddLog(LOGE, FL("CHANNEL_COUNT is not present"));
810 return -EINVAL;
811 }
812 channel_count = nla_get_u32(
813 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT]);
814
815 /* Get the size of the channel schedule */
816 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE]) {
817 hddLog(LOGE, FL("SCHEDULE_SIZE is not present"));
818 return -EINVAL;
819 }
820 schedule_size = nla_get_u32(
821 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE]);
822
823 /* Get the ndl chan array and the ndl active state array. */
824 ndl_chan_list =
825 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY];
826 ndl_chan_list_len = (ndl_chan_list ? nla_len(ndl_chan_list) : 0);
827
828 ndl_active_state_list =
829 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY];
830 ndl_active_state_list_len = (ndl_active_state_list ?
831 nla_len(ndl_active_state_list) : 0);
832
833 /* Get the flags */
834 if (tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS])
835 flags = nla_get_u32(tb[
836 QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS]);
837
838 config = hdd_ocb_config_new(channel_count, schedule_size,
839 ndl_chan_list_len,
840 ndl_active_state_list_len);
841 if (config == NULL) {
842 hddLog(LOGE, FL("Failed to allocate memory!"));
843 return -ENOMEM;
844 }
845
846 config->channel_count = channel_count;
847 config->schedule_size = schedule_size;
848 config->flags = flags;
849
850 /* Read the channel array */
851 channel_array = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY];
852 if (!channel_array) {
853 hddLog(LOGE, FL("No channel present"));
854 goto fail;
855 }
856 if (nla_len(channel_array) != channel_count *
857 sizeof(struct wlan_hdd_ocb_config_channel)) {
858 hddLog(LOGE, FL("CHANNEL_ARRAY is not the correct size"));
859 goto fail;
860 }
861 wlan_hdd_ocb_config_channel_to_sir_ocb_config_channel(
862 config->channels, nla_data(channel_array), channel_count);
863
864 /* Identify the vdev interface */
865 config->session_id = adapter->sessionId;
866
867 /* Release all the mac addresses used for OCB */
868 for (i = 0; i < adapter->ocb_mac_addr_count; i++) {
869 wlan_hdd_release_intf_addr(adapter->pHddCtx,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800870 adapter->ocb_mac_address[i].bytes);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800871 }
872 adapter->ocb_mac_addr_count = 0;
873
874 /*
875 * Setup locally administered mac addresses for each channel.
876 * First channel uses the adapter's address.
877 */
878 for (i = 0; i < config->channel_count; i++) {
879 if (i == 0) {
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800880 cdf_copy_macaddr(&config->channels[i].mac_address,
881 &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800882 } else {
883 mac_addr = wlan_hdd_get_intf_addr(adapter->pHddCtx);
884 if (mac_addr == NULL) {
885 hddLog(LOGE, FL("Cannot obtain mac address"));
886 goto fail;
887 }
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800888 cdf_mem_copy(config->channels[i].mac_address.bytes,
889 mac_addr, CDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800890 /* Save the mac address to release later */
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800891 cdf_copy_macaddr(&adapter->ocb_mac_address[
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800892 adapter->ocb_mac_addr_count],
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800893 &config->channels[i].mac_address);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800894 adapter->ocb_mac_addr_count++;
895 }
896 }
897
898 /* Read the schedule array */
899 sched_array = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY];
900 if (!sched_array) {
901 hddLog(LOGE, FL("No channel present"));
902 goto fail;
903 }
904 if (nla_len(sched_array) != schedule_size * sizeof(*config->schedule)) {
905 hddLog(LOGE, FL("SCHEDULE_ARRAY is not the correct size"));
906 goto fail;
907 }
908 cdf_mem_copy(config->schedule, nla_data(sched_array),
909 nla_len(sched_array));
910
911 /* Copy the NDL chan array */
912 if (ndl_chan_list_len) {
913 config->dcc_ndl_chan_list_len = ndl_chan_list_len;
914 cdf_mem_copy(config->dcc_ndl_chan_list, nla_data(ndl_chan_list),
915 nla_len(ndl_chan_list));
916 }
917
918 /* Copy the NDL active state array */
919 if (ndl_active_state_list_len) {
920 config->dcc_ndl_active_state_list_len =
921 ndl_active_state_list_len;
922 cdf_mem_copy(config->dcc_ndl_active_state_list,
923 nla_data(ndl_active_state_list),
924 nla_len(ndl_active_state_list));
925 }
926
927 rc = hdd_ocb_set_config_req(adapter, config);
928 if (rc)
929 hddLog(LOGE, FL("Error while setting OCB config: %d"), rc);
930
931fail:
932 cdf_mem_free(config);
933 return rc;
934}
935
936/**
937 * wlan_hdd_cfg80211_ocb_set_config() - Interface for set config command
938 * @wiphy: pointer to the wiphy
939 * @wdev: pointer to the wdev
940 * @data: The netlink data
941 * @data_len: The length of the netlink data in bytes
942 *
943 * Return: 0 on success.
944 */
945int wlan_hdd_cfg80211_ocb_set_config(struct wiphy *wiphy,
946 struct wireless_dev *wdev,
947 const void *data,
948 int data_len)
949{
950 int ret;
951
952 cds_ssr_protect(__func__);
953 ret = __wlan_hdd_cfg80211_ocb_set_config(wiphy, wdev, data, data_len);
954 cds_ssr_unprotect(__func__);
955
956 return ret;
957}
958
959/**
960 * __wlan_hdd_cfg80211_ocb_set_utc_time() - Interface for set UTC time command
961 * @wiphy: pointer to the wiphy
962 * @wdev: pointer to the wdev
963 * @data: The netlink data
964 * @data_len: The length of the netlink data in bytes
965 *
966 * Return: 0 on success.
967 */
968static int __wlan_hdd_cfg80211_ocb_set_utc_time(struct wiphy *wiphy,
969 struct wireless_dev *wdev,
970 const void *data,
971 int data_len)
972{
973 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
974 struct net_device *dev = wdev->netdev;
975 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
976 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX + 1];
977 struct nlattr *utc_attr;
978 struct nlattr *time_error_attr;
979 struct sir_ocb_utc *utc;
980 int rc = -EINVAL;
981
982 ENTER();
983
984 if (wlan_hdd_validate_context(hdd_ctx)) {
985 hddLog(LOGE, FL("HDD context is not valid"));
986 return -EINVAL;
987 }
988
989 if (adapter->device_mode != WLAN_HDD_OCB) {
990 hddLog(LOGE, FL("Device not in OCB mode!"));
991 return -EINVAL;
992 }
993
994 if (!wma_is_vdev_up(adapter->sessionId)) {
995 hddLog(LOGE, FL("The device has not been started"));
996 return -EINVAL;
997 }
998
999 /* Parse the netlink message */
1000 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX,
1001 data,
1002 data_len, qca_wlan_vendor_ocb_set_utc_time_policy)) {
1003 hddLog(LOGE, FL("Invalid ATTR"));
1004 return -EINVAL;
1005 }
1006
1007 /* Read the UTC time */
1008 utc_attr = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE];
1009 if (!utc_attr) {
1010 hddLog(LOGE, FL("UTC_TIME is not present"));
1011 return -EINVAL;
1012 }
1013 if (nla_len(utc_attr) != SIZE_UTC_TIME) {
1014 hddLog(LOGE, FL("UTC_TIME is not the correct size"));
1015 return -EINVAL;
1016 }
1017
1018 /* Read the time error */
1019 time_error_attr = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR];
1020 if (!time_error_attr) {
1021 hddLog(LOGE, FL("UTC_TIME is not present"));
1022 return -EINVAL;
1023 }
1024 if (nla_len(time_error_attr) != SIZE_UTC_TIME_ERROR) {
1025 hddLog(LOGE, FL("UTC_TIME is not the correct size"));
1026 return -EINVAL;
1027 }
1028
1029 utc = cdf_mem_malloc(sizeof(*utc));
1030 if (!utc) {
1031 hddLog(LOGE, FL("cdf_mem_malloc failed"));
1032 return -ENOMEM;
1033 }
1034 utc->vdev_id = adapter->sessionId;
1035 cdf_mem_copy(utc->utc_time, nla_data(utc_attr), SIZE_UTC_TIME);
1036 cdf_mem_copy(utc->time_error, nla_data(time_error_attr),
1037 SIZE_UTC_TIME_ERROR);
1038
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301039 if (sme_ocb_set_utc_time(hdd_ctx->hHal, utc) != QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001040 hddLog(LOGE, FL("Error while setting UTC time"));
1041 rc = -EINVAL;
1042 } else {
1043 rc = 0;
1044 }
1045
1046 cdf_mem_free(utc);
1047 return rc;
1048}
1049
1050/**
1051 * wlan_hdd_cfg80211_ocb_set_utc_time() - Interface for the set UTC time command
1052 * @wiphy: pointer to the wiphy
1053 * @wdev: pointer to the wdev
1054 * @data: The netlink data
1055 * @data_len: The length of the netlink data in bytes
1056 *
1057 * Return: 0 on success.
1058 */
1059int wlan_hdd_cfg80211_ocb_set_utc_time(struct wiphy *wiphy,
1060 struct wireless_dev *wdev,
1061 const void *data,
1062 int data_len)
1063{
1064 int ret;
1065
1066 cds_ssr_protect(__func__);
1067 ret = __wlan_hdd_cfg80211_ocb_set_utc_time(wiphy, wdev, data, data_len);
1068 cds_ssr_unprotect(__func__);
1069
1070 return ret;
1071}
1072
1073/**
1074 * __wlan_hdd_cfg80211_ocb_start_timing_advert() - Interface for start TA cmd
1075 * @wiphy: pointer to the wiphy
1076 * @wdev: pointer to the wdev
1077 * @data: The netlink data
1078 * @data_len: The length of the netlink data in bytes
1079 *
1080 * Return: 0 on success.
1081 */
1082static int
1083__wlan_hdd_cfg80211_ocb_start_timing_advert(struct wiphy *wiphy,
1084 struct wireless_dev *wdev,
1085 const void *data,
1086 int data_len)
1087{
1088 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1089 struct net_device *dev = wdev->netdev;
1090 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001091 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX + 1];
1092 struct sir_ocb_timing_advert *timing_advert;
1093 int rc = -EINVAL;
1094
1095 ENTER();
1096
1097 if (wlan_hdd_validate_context(hdd_ctx)) {
1098 hddLog(LOGE, FL("HDD context is not valid"));
1099 return -EINVAL;
1100 }
1101
1102 if (adapter->device_mode != WLAN_HDD_OCB) {
1103 hddLog(LOGE, FL("Device not in OCB mode!"));
1104 return -EINVAL;
1105 }
1106
1107 if (!wma_is_vdev_up(adapter->sessionId)) {
1108 hddLog(LOGE, FL("The device has not been started"));
1109 return -EINVAL;
1110 }
1111
1112 timing_advert = cdf_mem_malloc(sizeof(*timing_advert));
1113 if (!timing_advert) {
1114 hddLog(LOGE, FL("cdf_mem_malloc failed"));
1115 return -ENOMEM;
1116 }
1117 cdf_mem_zero(timing_advert, sizeof(*timing_advert));
1118 timing_advert->vdev_id = adapter->sessionId;
1119
1120 /* Parse the netlink message */
1121 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX,
1122 data,
1123 data_len,
1124 qca_wlan_vendor_ocb_start_timing_advert_policy)) {
1125 hddLog(LOGE, FL("Invalid ATTR"));
1126 goto fail;
1127 }
1128
1129 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ]) {
1130 hddLog(LOGE, FL("CHANNEL_FREQ is not present"));
1131 goto fail;
1132 }
1133 timing_advert->chan_freq = nla_get_u32(
1134 tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ]);
1135
1136 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE]) {
1137 hddLog(LOGE, FL("REPEAT_RATE is not present"));
1138 goto fail;
1139 }
1140 timing_advert->repeat_rate = nla_get_u32(
1141 tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE]);
1142
1143 timing_advert->template_length =
Naveen Rawatb4d37622015-11-13 16:15:25 -08001144 sme_ocb_gen_timing_advert_frame(hdd_ctx->hHal,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001145 *(tSirMacAddr *)&adapter->macAddressCurrent.bytes,
1146 &timing_advert->template_value,
1147 &timing_advert->timestamp_offset,
1148 &timing_advert->time_value_offset);
1149 if (timing_advert->template_length <= 0) {
1150 hddLog(LOGE, FL("Error while generating the TA frame"));
1151 goto fail;
1152 }
1153
1154 if (sme_ocb_start_timing_advert(hdd_ctx->hHal, timing_advert) !=
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301155 QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001156 hddLog(LOGE, FL("Error while starting timing advert"));
1157 rc = -EINVAL;
1158 } else {
1159 rc = 0;
1160 }
1161
1162fail:
1163 if (timing_advert->template_value)
1164 cdf_mem_free(timing_advert->template_value);
1165 cdf_mem_free(timing_advert);
1166 return rc;
1167}
1168
1169/**
1170 * wlan_hdd_cfg80211_ocb_start_timing_advert() - Interface for the start TA cmd
1171 * @wiphy: pointer to the wiphy
1172 * @wdev: pointer to the wdev
1173 * @data: The netlink data
1174 * @data_len: The length of the netlink data in bytes
1175 *
1176 * Return: 0 on success.
1177 */
1178int wlan_hdd_cfg80211_ocb_start_timing_advert(struct wiphy *wiphy,
1179 struct wireless_dev *wdev,
1180 const void *data,
1181 int data_len)
1182{
1183 int ret;
1184
1185 cds_ssr_protect(__func__);
1186 ret = __wlan_hdd_cfg80211_ocb_start_timing_advert(wiphy, wdev,
1187 data, data_len);
1188 cds_ssr_unprotect(__func__);
1189
1190 return ret;
1191}
1192
1193/**
1194 * __wlan_hdd_cfg80211_ocb_stop_timing_advert() - Interface for the stop TA cmd
1195 * @wiphy: pointer to the wiphy
1196 * @wdev: pointer to the wdev
1197 * @data: The netlink data
1198 * @data_len: The length of the netlink data in bytes
1199 *
1200 * Return: 0 on success.
1201 */
1202static int
1203__wlan_hdd_cfg80211_ocb_stop_timing_advert(struct wiphy *wiphy,
1204 struct wireless_dev *wdev,
1205 const void *data,
1206 int data_len)
1207{
1208 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1209 struct net_device *dev = wdev->netdev;
1210 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1211 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX + 1];
1212 struct sir_ocb_timing_advert *timing_advert;
1213 int rc = -EINVAL;
1214
1215 ENTER();
1216
1217 if (wlan_hdd_validate_context(hdd_ctx)) {
1218 hddLog(LOGE, FL("HDD context is not valid"));
1219 return -EINVAL;
1220 }
1221
1222 if (adapter->device_mode != WLAN_HDD_OCB) {
1223 hddLog(LOGE, FL("Device not in OCB mode!"));
1224 return -EINVAL;
1225 }
1226
1227 if (!wma_is_vdev_up(adapter->sessionId)) {
1228 hddLog(LOGE, FL("The device has not been started"));
1229 return -EINVAL;
1230 }
1231
1232 timing_advert = cdf_mem_malloc(sizeof(*timing_advert));
1233 if (!timing_advert) {
1234 hddLog(LOGE, FL("cdf_mem_malloc failed"));
1235 return -ENOMEM;
1236 }
1237 cdf_mem_zero(timing_advert, sizeof(sizeof(*timing_advert)));
1238 timing_advert->vdev_id = adapter->sessionId;
1239
1240 /* Parse the netlink message */
1241 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX,
1242 data,
1243 data_len,
1244 qca_wlan_vendor_ocb_stop_timing_advert_policy)) {
1245 hddLog(LOGE, FL("Invalid ATTR"));
1246 goto fail;
1247 }
1248
1249 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ]) {
1250 hddLog(LOGE, FL("CHANNEL_FREQ is not present"));
1251 goto fail;
1252 }
1253 timing_advert->chan_freq = nla_get_u32(
1254 tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ]);
1255
1256 if (sme_ocb_stop_timing_advert(hdd_ctx->hHal, timing_advert) !=
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301257 QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001258 hddLog(LOGE, FL("Error while stopping timing advert"));
1259 rc = -EINVAL;
1260 } else {
1261 rc = 0;
1262 }
1263
1264fail:
1265 cdf_mem_free(timing_advert);
1266 return rc;
1267}
1268
1269/**
1270 * wlan_hdd_cfg80211_ocb_stop_timing_advert() - Interface for the stop TA cmd
1271 * @wiphy: pointer to the wiphy
1272 * @wdev: pointer to the wdev
1273 * @data: The netlink data
1274 * @data_len: The length of the netlink data in bytes
1275 *
1276 * Return: 0 on success.
1277 */
1278int wlan_hdd_cfg80211_ocb_stop_timing_advert(struct wiphy *wiphy,
1279 struct wireless_dev *wdev,
1280 const void *data,
1281 int data_len)
1282{
1283 int ret;
1284
1285 cds_ssr_protect(__func__);
1286 ret = __wlan_hdd_cfg80211_ocb_stop_timing_advert(wiphy, wdev,
1287 data, data_len);
1288 cds_ssr_unprotect(__func__);
1289
1290 return ret;
1291}
1292
1293/**
1294 * hdd_ocb_get_tsf_timer_callback() - Callback to get TSF command
1295 * @context_ptr: request context
1296 * @response_ptr: response data
1297 */
1298static void hdd_ocb_get_tsf_timer_callback(void *context_ptr,
1299 void *response_ptr)
1300{
1301 struct hdd_ocb_ctxt *context = context_ptr;
1302 struct sir_ocb_get_tsf_timer_response *response = response_ptr;
1303
1304 if (!context)
1305 return;
1306
1307 spin_lock(&hdd_context_lock);
1308 if (context->magic == HDD_OCB_MAGIC) {
1309 if (response) {
1310 context->adapter->ocb_get_tsf_timer_resp = *response;
1311 context->status = 0;
1312 } else {
1313 context->status = -EINVAL;
1314 }
1315 complete(&context->completion_evt);
1316 }
1317 spin_unlock(&hdd_context_lock);
1318}
1319
1320/**
1321 * __wlan_hdd_cfg80211_ocb_get_tsf_timer() - Interface for get TSF timer cmd
1322 * @wiphy: pointer to the wiphy
1323 * @wdev: pointer to the wdev
1324 * @data: The netlink data
1325 * @data_len: The length of the netlink data in bytes
1326 *
1327 * Return: 0 on success.
1328 */
1329static int
1330__wlan_hdd_cfg80211_ocb_get_tsf_timer(struct wiphy *wiphy,
1331 struct wireless_dev *wdev,
1332 const void *data,
1333 int data_len)
1334{
1335 struct sk_buff *nl_resp = 0;
1336 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1337 struct net_device *dev = wdev->netdev;
1338 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1339 int rc = -EINVAL;
1340 struct sir_ocb_get_tsf_timer request = {0};
1341 struct hdd_ocb_ctxt context = {0};
1342
1343 ENTER();
1344
1345 if (wlan_hdd_validate_context(hdd_ctx)) {
1346 hddLog(LOGE, FL("HDD context is not valid"));
1347 return -EINVAL;
1348 }
1349
1350 if (adapter->device_mode != WLAN_HDD_OCB) {
1351 hddLog(LOGE, FL("Device not in OCB mode!"));
1352 return -EINVAL;
1353 }
1354
1355 if (!wma_is_vdev_up(adapter->sessionId)) {
1356 hddLog(LOGE, FL("The device has not been started"));
1357 return -EINVAL;
1358 }
1359
1360 /* Initialize the callback context */
1361 init_completion(&context.completion_evt);
1362 context.adapter = adapter;
1363 context.magic = HDD_OCB_MAGIC;
1364
1365 request.vdev_id = adapter->sessionId;
1366 /* Call the SME function */
1367 rc = sme_ocb_get_tsf_timer(hdd_ctx->hHal, &context,
1368 hdd_ocb_get_tsf_timer_callback,
1369 &request);
1370 if (rc) {
1371 hddLog(LOGE, FL("Error calling SME function"));
1372 /* Need to convert from ecdf_status to errno. */
1373 return -EINVAL;
1374 }
1375
1376 rc = wait_for_completion_timeout(&context.completion_evt,
1377 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
1378 if (rc == 0) {
1379 hddLog(LOGE, FL("Operation timed out"));
1380 rc = -ETIMEDOUT;
1381 goto end;
1382 }
1383 rc = 0;
1384
1385 if (context.status) {
1386 hddLog(LOGE, FL("Operation failed: %d"), context.status);
1387 rc = context.status;
1388 goto end;
1389 }
1390
1391 /* Allocate the buffer for the response. */
1392 nl_resp = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
1393 2 * sizeof(uint32_t) + NLMSG_HDRLEN);
1394
1395 if (!nl_resp) {
1396 hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
1397 rc = -ENOMEM;
1398 goto end;
1399 }
1400
1401 hddLog(LOGE, FL("Got TSF timer response, high=%d, low=%d"),
1402 adapter->ocb_get_tsf_timer_resp.timer_high,
1403 adapter->ocb_get_tsf_timer_resp.timer_low);
1404
1405 /* Populate the response. */
1406 rc = nla_put_u32(nl_resp,
1407 QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH,
1408 adapter->ocb_get_tsf_timer_resp.timer_high);
1409 if (rc)
1410 goto end;
1411 rc = nla_put_u32(nl_resp,
1412 QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW,
1413 adapter->ocb_get_tsf_timer_resp.timer_low);
1414 if (rc)
1415 goto end;
1416
1417 /* Send the response. */
1418 rc = cfg80211_vendor_cmd_reply(nl_resp);
1419 nl_resp = NULL;
1420 if (rc) {
1421 hddLog(LOGE, FL("cfg80211_vendor_cmd_reply failed: %d"), rc);
1422 goto end;
1423 }
1424
1425end:
1426 spin_lock(&hdd_context_lock);
1427 context.magic = 0;
1428 spin_unlock(&hdd_context_lock);
1429 if (nl_resp)
1430 kfree_skb(nl_resp);
1431 return rc;
1432}
1433
1434/**
1435 * wlan_hdd_cfg80211_ocb_get_tsf_timer() - Interface for get TSF timer cmd
1436 * @wiphy: pointer to the wiphy
1437 * @wdev: pointer to the wdev
1438 * @data: The netlink data
1439 * @data_len: The length of the netlink data in bytes
1440 *
1441 * Return: 0 on success.
1442 */
1443int wlan_hdd_cfg80211_ocb_get_tsf_timer(struct wiphy *wiphy,
1444 struct wireless_dev *wdev,
1445 const void *data,
1446 int data_len)
1447{
1448 int ret;
1449
1450 cds_ssr_protect(__func__);
1451 ret = __wlan_hdd_cfg80211_ocb_get_tsf_timer(wiphy, wdev,
1452 data, data_len);
1453 cds_ssr_unprotect(__func__);
1454
1455 return ret;
1456}
1457
1458/**
1459 * hdd_dcc_get_stats_callback() - Callback to get stats command
1460 * @context_ptr: request context
1461 * @response_ptr: response data
1462 */
1463static void hdd_dcc_get_stats_callback(void *context_ptr, void *response_ptr)
1464{
1465 struct hdd_ocb_ctxt *context = context_ptr;
1466 struct sir_dcc_get_stats_response *response = response_ptr;
1467 struct sir_dcc_get_stats_response *hdd_resp;
1468
1469 if (!context)
1470 return;
1471
1472 spin_lock(&hdd_context_lock);
1473 if (context->magic == HDD_OCB_MAGIC) {
1474 if (response) {
1475 /*
1476 * If the response is hanging around from the previous
1477 * request, delete it
1478 */
1479 if (context->adapter->dcc_get_stats_resp) {
1480 cdf_mem_free(
1481 context->adapter->dcc_get_stats_resp);
1482 }
1483 context->adapter->dcc_get_stats_resp =
1484 cdf_mem_malloc(sizeof(
1485 *context->adapter->dcc_get_stats_resp) +
1486 response->channel_stats_array_len);
1487 if (context->adapter->dcc_get_stats_resp) {
1488 hdd_resp = context->adapter->dcc_get_stats_resp;
1489 *hdd_resp = *response;
1490 hdd_resp->channel_stats_array =
1491 (void *)hdd_resp + sizeof(*hdd_resp);
1492 cdf_mem_copy(hdd_resp->channel_stats_array,
1493 response->channel_stats_array,
1494 response->channel_stats_array_len);
1495 context->status = 0;
1496 } else {
1497 context->status = -ENOMEM;
1498 }
1499 } else {
1500 context->status = -EINVAL;
1501 }
1502 complete(&context->completion_evt);
1503 }
1504 spin_unlock(&hdd_context_lock);
1505}
1506
1507/**
1508 * __wlan_hdd_cfg80211_dcc_get_stats() - Interface for get dcc stats
1509 * @wiphy: pointer to the wiphy
1510 * @wdev: pointer to the wdev
1511 * @data: The netlink data
1512 * @data_len: The length of the netlink data in bytes
1513 *
1514 * Return: 0 on success.
1515 */
1516static int __wlan_hdd_cfg80211_dcc_get_stats(struct wiphy *wiphy,
1517 struct wireless_dev *wdev,
1518 const void *data,
1519 int data_len)
1520{
1521 uint32_t channel_count = 0;
1522 uint32_t request_array_len = 0;
1523 void *request_array = 0;
1524 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1525 struct net_device *dev = wdev->netdev;
1526 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1527 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_MAX + 1];
1528 struct sk_buff *nl_resp = 0;
1529 int rc = -EINVAL;
1530 struct sir_dcc_get_stats request = {0};
1531 struct hdd_ocb_ctxt context = {0};
1532
1533 ENTER();
1534
1535 if (wlan_hdd_validate_context(hdd_ctx)) {
1536 hddLog(LOGE, FL("HDD context is not valid"));
1537 return -EINVAL;
1538 }
1539
1540 if (adapter->device_mode != WLAN_HDD_OCB) {
1541 hddLog(LOGE, FL("Device not in OCB mode!"));
1542 return -EINVAL;
1543 }
1544
1545 if (!wma_is_vdev_up(adapter->sessionId)) {
1546 hddLog(LOGE, FL("The device has not been started"));
1547 return -EINVAL;
1548 }
1549
1550 /* Parse the netlink message */
1551 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_MAX,
1552 data,
1553 data_len,
1554 qca_wlan_vendor_dcc_get_stats)) {
1555 hddLog(LOGE, FL("Invalid ATTR"));
1556 return -EINVAL;
1557 }
1558
1559 /* Validate all the parameters are present */
1560 if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT] ||
1561 !tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]) {
1562 hddLog(LOGE, FL("Parameters are not present."));
1563 return -EINVAL;
1564 }
1565
1566 channel_count = nla_get_u32(
1567 tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT]);
1568 request_array_len = nla_len(
1569 tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]);
1570 request_array = nla_data(
1571 tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]);
1572
1573 /* Initialize the callback context */
1574 init_completion(&context.completion_evt);
1575 context.adapter = adapter;
1576 context.magic = HDD_OCB_MAGIC;
1577
1578 request.vdev_id = adapter->sessionId;
1579 request.channel_count = channel_count;
1580 request.request_array_len = request_array_len;
1581 request.request_array = request_array;
1582
1583 /* Call the SME function. */
1584 rc = sme_dcc_get_stats(hdd_ctx->hHal, &context,
1585 hdd_dcc_get_stats_callback,
1586 &request);
1587 if (rc) {
1588 hddLog(LOGE, FL("Error calling SME function"));
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301589 /* Need to convert from qdf_status to errno. */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001590 return -EINVAL;
1591 }
1592
1593 /* Wait for the function to complete. */
1594 rc = wait_for_completion_timeout(&context.completion_evt,
1595 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
1596 if (rc == 0) {
1597 hddLog(LOGE, FL("Operation failed: %d"), rc);
1598 rc = -ETIMEDOUT;
1599 goto end;
1600 }
1601
1602 if (context.status) {
1603 hddLog(LOGE, FL("There was error: %d"), context.status);
1604 rc = context.status;
1605 goto end;
1606 }
1607
1608 if (!adapter->dcc_get_stats_resp) {
1609 hddLog(LOGE, FL("The response was NULL"));
1610 rc = -EINVAL;
1611 goto end;
1612 }
1613
1614 /* Allocate the buffer for the response. */
1615 nl_resp = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(uint32_t) +
1616 adapter->dcc_get_stats_resp->channel_stats_array_len +
1617 NLMSG_HDRLEN);
1618 if (!nl_resp) {
1619 hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
1620 rc = -ENOMEM;
1621 goto end;
1622 }
1623
1624 /* Populate the response. */
1625 rc = nla_put_u32(nl_resp,
1626 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT,
1627 adapter->dcc_get_stats_resp->num_channels);
1628 if (rc)
1629 goto end;
1630 rc = nla_put(nl_resp,
1631 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY,
1632 adapter->dcc_get_stats_resp->channel_stats_array_len,
1633 adapter->dcc_get_stats_resp->channel_stats_array);
1634 if (rc)
1635 goto end;
1636
1637 /* Send the response. */
1638 rc = cfg80211_vendor_cmd_reply(nl_resp);
1639 nl_resp = NULL;
1640 if (rc) {
1641 hddLog(LOGE, FL("cfg80211_vendor_cmd_reply failed: %d"), rc);
1642 goto end;
1643 }
1644
1645 /* fall through */
1646end:
1647 spin_lock(&hdd_context_lock);
1648 context.magic = 0;
1649 cdf_mem_free(adapter->dcc_get_stats_resp);
1650 adapter->dcc_get_stats_resp = NULL;
1651 spin_unlock(&hdd_context_lock);
1652 if (nl_resp)
1653 kfree_skb(nl_resp);
1654 return rc;
1655}
1656
1657/**
1658 * wlan_hdd_cfg80211_dcc_get_stats() - Interface for get dcc stats
1659 * @wiphy: pointer to the wiphy
1660 * @wdev: pointer to the wdev
1661 * @data: The netlink data
1662 * @data_len: The length of the netlink data in bytes
1663 *
1664 * Return: 0 on success.
1665 */
1666int wlan_hdd_cfg80211_dcc_get_stats(struct wiphy *wiphy,
1667 struct wireless_dev *wdev,
1668 const void *data,
1669 int data_len)
1670{
1671 int ret;
1672
1673 cds_ssr_protect(__func__);
1674 ret = __wlan_hdd_cfg80211_dcc_get_stats(wiphy, wdev,
1675 data, data_len);
1676 cds_ssr_unprotect(__func__);
1677
1678 return ret;
1679}
1680
1681/**
1682 * __wlan_hdd_cfg80211_dcc_clear_stats() - Interface for clear dcc stats cmd
1683 * @wiphy: pointer to the wiphy
1684 * @wdev: pointer to the wdev
1685 * @data: The netlink data
1686 * @data_len: The length of the netlink data in bytes
1687 *
1688 * Return: 0 on success.
1689 */
1690static int __wlan_hdd_cfg80211_dcc_clear_stats(struct wiphy *wiphy,
1691 struct wireless_dev *wdev,
1692 const void *data,
1693 int data_len)
1694{
1695 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1696 struct net_device *dev = wdev->netdev;
1697 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1698 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_MAX + 1];
1699
1700 ENTER();
1701
1702 if (wlan_hdd_validate_context(hdd_ctx)) {
1703 hddLog(LOGE, FL("HDD context is not valid"));
1704 return -EINVAL;
1705 }
1706
1707 if (adapter->device_mode != WLAN_HDD_OCB) {
1708 hddLog(LOGE, FL("Device not in OCB mode!"));
1709 return -EINVAL;
1710 }
1711
1712 if (!wma_is_vdev_up(adapter->sessionId)) {
1713 hddLog(LOGE, FL("The device has not been started"));
1714 return -EINVAL;
1715 }
1716
1717 /* Parse the netlink message */
1718 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_MAX,
1719 data,
1720 data_len,
1721 qca_wlan_vendor_dcc_clear_stats)) {
1722 hddLog(LOGE, FL("Invalid ATTR"));
1723 return -EINVAL;
1724 }
1725
1726 /* Verify that the parameter is present */
1727 if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP]) {
1728 hddLog(LOGE, FL("Parameters are not present."));
1729 return -EINVAL;
1730 }
1731
1732 /* Call the SME function */
1733 if (sme_dcc_clear_stats(hdd_ctx->hHal, adapter->sessionId,
1734 nla_get_u32(
1735 tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP])) !=
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301736 QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001737 hddLog(LOGE, FL("Error calling SME function."));
1738 return -EINVAL;
1739 }
1740
1741 return 0;
1742}
1743
1744/**
1745 * wlan_hdd_cfg80211_dcc_clear_stats() - Interface for clear dcc stats cmd
1746 * @wiphy: pointer to the wiphy
1747 * @wdev: pointer to the wdev
1748 * @data: The netlink data
1749 * @data_len: The length of the netlink data in bytes
1750 *
1751 * Return: 0 on success.
1752 */
1753int wlan_hdd_cfg80211_dcc_clear_stats(struct wiphy *wiphy,
1754 struct wireless_dev *wdev,
1755 const void *data,
1756 int data_len)
1757{
1758 int ret;
1759
1760 cds_ssr_protect(__func__);
1761 ret = __wlan_hdd_cfg80211_dcc_clear_stats(wiphy, wdev,
1762 data, data_len);
1763 cds_ssr_unprotect(__func__);
1764
1765 return ret;
1766}
1767
1768/**
1769 * hdd_dcc_update_ndl_callback() - Callback to update NDL command
1770 * @context_ptr: request context
1771 * @response_ptr: response data
1772 */
1773static void hdd_dcc_update_ndl_callback(void *context_ptr, void *response_ptr)
1774{
1775 struct hdd_ocb_ctxt *context = context_ptr;
1776 struct sir_dcc_update_ndl_response *response = response_ptr;
1777
1778 if (!context)
1779 return;
1780
1781 spin_lock(&hdd_context_lock);
1782 if (context->magic == HDD_OCB_MAGIC) {
1783 if (response) {
1784 context->adapter->dcc_update_ndl_resp = *response;
1785 context->status = 0;
1786 } else {
1787 context->status = -EINVAL;
1788 }
1789 complete(&context->completion_evt);
1790 }
1791 spin_unlock(&hdd_context_lock);
1792}
1793
1794/**
1795 * __wlan_hdd_cfg80211_dcc_update_ndl() - Interface for update dcc cmd
1796 * @wiphy: pointer to the wiphy
1797 * @wdev: pointer to the wdev
1798 * @data: The netlink data
1799 * @data_len: The length of the netlink data in bytes
1800 *
1801 * Return: 0 on success.
1802 */
1803static int __wlan_hdd_cfg80211_dcc_update_ndl(struct wiphy *wiphy,
1804 struct wireless_dev *wdev,
1805 const void *data,
1806 int data_len)
1807{
1808 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1809 struct net_device *dev = wdev->netdev;
1810 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1811 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX + 1];
1812 struct sir_dcc_update_ndl request;
1813 uint32_t channel_count;
1814 uint32_t ndl_channel_array_len;
1815 void *ndl_channel_array;
1816 uint32_t ndl_active_state_array_len;
1817 void *ndl_active_state_array;
1818 int rc = -EINVAL;
1819 struct hdd_ocb_ctxt context = {0};
1820
1821 ENTER();
1822
1823 if (wlan_hdd_validate_context(hdd_ctx)) {
1824 hddLog(LOGE, FL("HDD context is not valid"));
1825 goto end;
1826 }
1827
1828 if (adapter->device_mode != WLAN_HDD_OCB) {
1829 hddLog(LOGE, FL("Device not in OCB mode!"));
1830 goto end;
1831 }
1832
1833 if (!wma_is_vdev_up(adapter->sessionId)) {
1834 hddLog(LOGE, FL("The device has not been started"));
1835 return -EINVAL;
1836 }
1837
1838 /* Parse the netlink message */
1839 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX,
1840 data,
1841 data_len,
1842 qca_wlan_vendor_dcc_update_ndl)) {
1843 hddLog(LOGE, FL("Invalid ATTR"));
1844 goto end;
1845 }
1846
1847 /* Verify that the parameter is present */
1848 if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT] ||
1849 !tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY] ||
1850 !tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]) {
1851 hddLog(LOGE, FL("Parameters are not present."));
1852 return -EINVAL;
1853 }
1854
1855 channel_count = nla_get_u32(
1856 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT]);
1857 ndl_channel_array_len = nla_len(
1858 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY]);
1859 ndl_channel_array = nla_data(
1860 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY]);
1861 ndl_active_state_array_len = nla_len(
1862 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]);
1863 ndl_active_state_array = nla_data(
1864 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]);
1865
1866 /* Initialize the callback context */
1867 init_completion(&context.completion_evt);
1868 context.adapter = adapter;
1869 context.magic = HDD_OCB_MAGIC;
1870
1871 /* Copy the parameters to the request structure. */
1872 request.vdev_id = adapter->sessionId;
1873 request.channel_count = channel_count;
1874 request.dcc_ndl_chan_list_len = ndl_channel_array_len;
1875 request.dcc_ndl_chan_list = ndl_channel_array;
1876 request.dcc_ndl_active_state_list_len = ndl_active_state_array_len;
1877 request.dcc_ndl_active_state_list = ndl_active_state_array;
1878
1879 /* Call the SME function */
1880 rc = sme_dcc_update_ndl(hdd_ctx->hHal, &context,
1881 hdd_dcc_update_ndl_callback,
1882 &request);
1883 if (rc) {
1884 hddLog(LOGE, FL("Error calling SME function."));
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301885 /* Convert from qdf_status to errno */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001886 return -EINVAL;
1887 }
1888
1889 /* Wait for the function to complete. */
1890 rc = wait_for_completion_timeout(&context.completion_evt,
1891 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
1892 if (rc == 0) {
1893 hddLog(LOGE, FL("Operation timed out"));
1894 rc = -ETIMEDOUT;
1895 goto end;
1896 }
1897 rc = 0;
1898
1899 if (context.status) {
1900 hddLog(LOGE, FL("Operation failed: %d"), context.status);
1901 rc = context.status;
1902 goto end;
1903 }
1904
1905 if (adapter->dcc_update_ndl_resp.status) {
1906 hddLog(LOGE, FL("Operation returned: %d"),
1907 adapter->dcc_update_ndl_resp.status);
1908 rc = -EINVAL;
1909 goto end;
1910 }
1911
1912 /* fall through */
1913end:
1914 spin_lock(&hdd_context_lock);
1915 context.magic = 0;
1916 spin_unlock(&hdd_context_lock);
1917 return rc;
1918}
1919
1920/**
1921 * wlan_hdd_cfg80211_dcc_update_ndl() - Interface for update dcc cmd
1922 * @wiphy: pointer to the wiphy
1923 * @wdev: pointer to the wdev
1924 * @data: The netlink data
1925 * @data_len: The length of the netlink data in bytes
1926 *
1927 * Return: 0 on success.
1928 */
1929int wlan_hdd_cfg80211_dcc_update_ndl(struct wiphy *wiphy,
1930 struct wireless_dev *wdev,
1931 const void *data,
1932 int data_len)
1933{
1934 int ret;
1935
1936 cds_ssr_protect(__func__);
1937 ret = __wlan_hdd_cfg80211_dcc_update_ndl(wiphy, wdev,
1938 data, data_len);
1939 cds_ssr_unprotect(__func__);
1940
1941 return ret;
1942}
1943
1944/**
1945 * wlan_hdd_dcc_stats_event_callback() - Callback to get stats event
1946 * @context_ptr: request context
1947 * @response_ptr: response data
1948 */
1949static void wlan_hdd_dcc_stats_event_callback(void *context_ptr,
1950 void *response_ptr)
1951{
1952 hdd_context_t *hdd_ctx = (hdd_context_t *)context_ptr;
1953 struct sir_dcc_get_stats_response *resp = response_ptr;
1954 struct sk_buff *vendor_event;
1955
1956 ENTER();
1957
1958 vendor_event =
1959 cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
1960 NULL, sizeof(uint32_t) + resp->channel_stats_array_len +
1961 NLMSG_HDRLEN,
1962 QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT_INDEX,
1963 GFP_KERNEL);
1964
1965 if (!vendor_event) {
1966 hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed"));
1967 return;
1968 }
1969
1970 if (nla_put_u32(vendor_event,
1971 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT,
1972 resp->num_channels) ||
1973 nla_put(vendor_event,
1974 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY,
1975 resp->channel_stats_array_len,
1976 resp->channel_stats_array)) {
1977 hddLog(LOGE, FL("nla put failed"));
1978 kfree_skb(vendor_event);
1979 return;
1980 }
1981
1982 cfg80211_vendor_event(vendor_event, GFP_KERNEL);
1983}
1984
1985/**
1986 * wlan_hdd_dcc_register_for_dcc_stats_event() - Register for dcc stats events
1987 * @hdd_ctx: hdd context
1988 */
1989void wlan_hdd_dcc_register_for_dcc_stats_event(hdd_context_t *hdd_ctx)
1990{
1991 int rc;
1992
1993 rc = sme_register_for_dcc_stats_event(hdd_ctx->hHal, hdd_ctx,
1994 wlan_hdd_dcc_stats_event_callback);
1995 if (rc)
1996 hddLog(LOGE, FL("Register callback failed: %d"), rc);
1997}