blob: 6049680c9ac62228478c9c698a16a26af08c8b5f [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"
Dhanashri Atre182b0272016-02-17 15:35:07 -080042#include "ol_txrx.h"
Manjunathappa Prakash3454fd62016-04-01 08:52:06 -070043#include <cdp_txrx_peer_ops.h>
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080044
45/* Structure definitions for WLAN_SET_DOT11P_CHANNEL_SCHED */
46#define AIFSN_MIN (2)
47#define AIFSN_MAX (15)
48#define CW_MIN (1)
49#define CW_MAX (10)
50
51/* Maximum time(ms) to wait for OCB operations */
52#define WLAN_WAIT_TIME_OCB_CMD 1500
53#define HDD_OCB_MAGIC 0x489a154f
54
55/**
56 * struct hdd_ocb_ctxt - Context for OCB operations
57 * adapter: the ocb adapter
58 * completion_evt: the completion event
59 * status: status of the request
60 */
61struct hdd_ocb_ctxt {
62 uint32_t magic;
63 hdd_adapter_t *adapter;
64 struct completion completion_evt;
65 int status;
66};
67
68/**
69 * hdd_set_dot11p_config() - Set 802.11p config flag
70 * @hdd_ctx: HDD Context pointer
71 *
72 * TODO-OCB: This has been temporarily added to ensure this paramter
73 * is set in CSR when we init the channel list. This should be removed
74 * once the 5.9 GHz channels are added to the regulatory domain.
75 */
76void hdd_set_dot11p_config(hdd_context_t *hdd_ctx)
77{
78 sme_set_dot11p_config(hdd_ctx->hHal,
79 hdd_ctx->config->dot11p_mode !=
80 WLAN_HDD_11P_DISABLED);
81}
82
83/**
84 * dot11p_validate_qos_params() - Check if QoS parameters are valid
85 * @qos_params: Array of QoS parameters
86 *
87 * Return: 0 on success. error code on failure.
88 */
89static int dot11p_validate_qos_params(struct sir_qos_params qos_params[])
90{
91 int i;
92
93 for (i = 0; i < MAX_NUM_AC; i++) {
94 if ((!qos_params[i].aifsn) && (!qos_params[i].cwmin)
95 && (!qos_params[i].cwmax))
96 continue;
97
98 /* Validate AIFSN */
99 if ((qos_params[i].aifsn < AIFSN_MIN)
100 || (qos_params[i].aifsn > AIFSN_MAX)) {
101 hddLog(LOGE, FL("Invalid QoS parameter aifsn %d"),
102 qos_params[i].aifsn);
103 return -EINVAL;
104 }
105
106 /* Validate CWMin */
107 if ((qos_params[i].cwmin < CW_MIN)
108 || (qos_params[i].cwmin > CW_MAX)) {
109 hddLog(LOGE, FL("Invalid QoS parameter cwmin %d"),
110 qos_params[i].cwmin);
111 return -EINVAL;
112 }
113
114 /* Validate CWMax */
115 if ((qos_params[i].cwmax < CW_MIN)
116 || (qos_params[i].cwmax > CW_MAX)) {
117 hddLog(LOGE, FL("Invalid QoS parameter cwmax %d"),
118 qos_params[i].cwmax);
119 return -EINVAL;
120 }
121 }
122
123 return 0;
124}
125
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800126/**
127 * dot11p_validate_channel() - validates a DSRC channel
128 * @center_freq: the channel's center frequency
129 * @bandwidth: the channel's bandwidth
130 * @tx_power: transmit power
131 * @reg_power: (output) the max tx power from the regulatory domain
132 * @antenna_max: (output) the max antenna gain from the regulatory domain
133 *
134 * Return: 0 if the channel is valid, error code otherwise.
135 */
136static int dot11p_validate_channel(struct wiphy *wiphy,
137 uint32_t channel_freq, uint32_t bandwidth,
138 uint32_t tx_power, uint8_t *reg_power,
139 uint8_t *antenna_max)
140{
141 int band_idx, channel_idx;
142 struct ieee80211_supported_band *current_band;
143 struct ieee80211_channel *current_channel;
144
145 for (band_idx = 0; band_idx < IEEE80211_NUM_BANDS; band_idx++) {
146 current_band = wiphy->bands[band_idx];
147 if (!current_band)
148 continue;
149
150 for (channel_idx = 0; channel_idx < current_band->n_channels;
151 channel_idx++) {
152 current_channel = &current_band->channels[channel_idx];
153
154 if (channel_freq == current_channel->center_freq) {
155 if (current_channel->flags &
156 IEEE80211_CHAN_DISABLED)
157 return -EINVAL;
158
159 if (reg_power)
160 *reg_power =
161 current_channel->max_reg_power;
162 if (antenna_max)
163 *antenna_max =
164 current_channel->
165 max_antenna_gain;
166
167 switch (bandwidth) {
168 case 0:
169 if (current_channel->flags &
170 IEEE80211_CHAN_NO_10MHZ)
171 bandwidth = 5;
172 else if (current_channel->flags &
173 IEEE80211_CHAN_NO_20MHZ)
174 bandwidth = 10;
175 else
176 bandwidth = 20;
177 break;
178 case 5:
179 break;
180 case 10:
181 if (current_channel->flags &
182 IEEE80211_CHAN_NO_10MHZ)
183 return -EINVAL;
184 break;
185 case 20:
186 if (current_channel->flags &
187 IEEE80211_CHAN_NO_20MHZ)
188 return -EINVAL;
189 break;
190 default:
191 return -EINVAL;
192 }
193
194 if (tx_power > current_channel->max_power)
195 return -EINVAL;
196
197 return 0;
198 }
199 }
200 }
201
Amar Singhalfda6eda2015-11-02 15:08:59 -0800202 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800203}
204
205/**
206 * hdd_ocb_validate_config() - Validates the config data
207 * @config: configuration to be validated
208 *
209 * Return: 0 on success.
210 */
211static int hdd_ocb_validate_config(hdd_adapter_t *adapter,
212 struct sir_ocb_config *config)
213{
214 int i;
215 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
216
217 for (i = 0; i < config->channel_count; i++) {
218 if (dot11p_validate_channel(hdd_ctx->wiphy,
219 config->channels[i].chan_freq,
220 config->channels[i].bandwidth,
221 config->channels[i].max_pwr,
222 &config->channels[i].reg_pwr,
223 &config->channels[i].antenna_max)) {
224 hddLog(LOGE, FL("Invalid channel frequency %d"),
225 config->channels[i].chan_freq);
226 return -EINVAL;
227 }
228 if (dot11p_validate_qos_params(config->channels[i].qos_params))
229 return -EINVAL;
230 }
231
232 return 0;
233}
234
235/**
236 * hdd_ocb_register_sta() - Register station with Transport Layer
237 * @adapter: Pointer to HDD Adapter
238 *
239 * This function should be invoked in the OCB Set Schedule callback
240 * to enable the data path in the TL by calling RegisterSTAClient
241 *
242 * Return: 0 on success. -1 on failure.
243 */
244static int hdd_ocb_register_sta(hdd_adapter_t *adapter)
245{
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530246 QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800247 struct ol_txrx_desc_type sta_desc = {0};
248 hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
249 hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
250 uint8_t peer_id;
Dhanashri Atre182b0272016-02-17 15:35:07 -0800251 struct ol_txrx_ops txrx_ops;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800252
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530253 qdf_status = ol_txrx_register_ocb_peer(hdd_ctx->pcds_context,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800254 adapter->macAddressCurrent.bytes,
255 &peer_id);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530256 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800257 hddLog(LOGE, FL("Error registering OCB Self Peer!"));
258 return -EINVAL;
259 }
260
261 hdd_ctx->sta_to_adapter[peer_id] = adapter;
262
263 sta_desc.sta_id = peer_id;
264 sta_desc.is_qos_enabled = 1;
265
Dhanashri Atre182b0272016-02-17 15:35:07 -0800266 /* Register the vdev transmit and receive functions */
267 qdf_mem_zero(&txrx_ops, sizeof(txrx_ops));
268 txrx_ops.rx.rx = hdd_rx_packet_cbk;
269 ol_txrx_vdev_register(
270 ol_txrx_get_vdev_from_vdev_id(adapter->sessionId),
271 adapter, &txrx_ops);
Dhanashri Atre168d2b42016-02-22 14:43:06 -0800272 adapter->tx_fn = txrx_ops.tx.tx;
Dhanashri Atre182b0272016-02-17 15:35:07 -0800273
Dhanashri Atre50141c52016-04-07 13:15:29 -0700274 qdf_status = ol_txrx_register_peer(&sta_desc);
275 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
276 hddLog(LOGE, FL("Failed to register. Status= %d [0x%08X]"),
277 qdf_status, qdf_status);
278 return -EINVAL;
279 }
280
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800281 if (pHddStaCtx->conn_info.staId[0] != 0 &&
282 pHddStaCtx->conn_info.staId[0] != peer_id) {
283 hddLog(LOGE, FL("The ID for the OCB station has changed."));
284 }
285
286 pHddStaCtx->conn_info.staId[0] = peer_id;
Anurag Chouhanc5548422016-02-24 18:33:27 +0530287 qdf_copy_macaddr(&pHddStaCtx->conn_info.peerMacAddress[0],
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800288 &adapter->macAddressCurrent);
289
290 return 0;
291}
292
293/**
294 * hdd_ocb_config_new() - Creates a new OCB configuration
295 * @num_channels: the number of channels
296 * @num_schedule: the schedule size
297 * @ndl_chan_list_len: length in bytes of the NDL chan blob
298 * @ndl_active_state_list_len: length in bytes of the active state blob
299 *
300 * Return: A pointer to the OCB configuration struct, NULL on failure.
301 */
302static struct sir_ocb_config *hdd_ocb_config_new(int num_channels,
303 int num_schedule,
304 int ndl_chan_list_len,
305 int ndl_active_state_list_len)
306{
307 struct sir_ocb_config *ret = 0;
308 uint32_t len;
309 void *cursor;
310
311 if (num_channels > CFG_TGT_NUM_OCB_CHANNELS ||
312 num_schedule > CFG_TGT_NUM_OCB_SCHEDULES)
313 return NULL;
314
315 len = sizeof(*ret) +
316 num_channels * sizeof(struct sir_ocb_config_channel) +
317 num_schedule * sizeof(struct sir_ocb_config_sched) +
318 ndl_chan_list_len +
319 ndl_active_state_list_len;
320
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530321 cursor = qdf_mem_malloc(len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800322 if (!cursor)
323 goto fail;
324
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530325 qdf_mem_zero(cursor, len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800326 ret = cursor;
327 cursor += sizeof(*ret);
328
329 ret->channel_count = num_channels;
330 ret->channels = cursor;
331 cursor += num_channels * sizeof(*ret->channels);
332
333 ret->schedule_size = num_schedule;
334 ret->schedule = cursor;
335 cursor += num_schedule * sizeof(*ret->schedule);
336
337 ret->dcc_ndl_chan_list = cursor;
338 cursor += ndl_chan_list_len;
339
340 ret->dcc_ndl_active_state_list = cursor;
341 cursor += ndl_active_state_list_len;
342
343 return ret;
344
345fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530346 qdf_mem_free(ret);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800347 return NULL;
348}
349
350/**
351 * hdd_ocb_set_config_callback() - OCB set config callback function
352 * @context_ptr: OCB call context
353 * @response_ptr: Pointer to response structure
354 *
355 * This function is registered as a callback with the lower layers
356 * and is used to respond with the status of a OCB set config command.
357 */
358static void hdd_ocb_set_config_callback(void *context_ptr, void *response_ptr)
359{
360 struct hdd_ocb_ctxt *context = context_ptr;
361 struct sir_ocb_set_config_response *resp = response_ptr;
362
363 if (!context)
364 return;
365
366 if (resp && resp->status)
367 hddLog(LOGE, FL("Operation failed: %d"), resp->status);
368
369 spin_lock(&hdd_context_lock);
370 if (context->magic == HDD_OCB_MAGIC) {
371 hdd_adapter_t *adapter = context->adapter;
372 if (!resp) {
373 context->status = -EINVAL;
374 complete(&context->completion_evt);
375 spin_unlock(&hdd_context_lock);
376 return;
377 }
378
379 context->adapter->ocb_set_config_resp = *resp;
380 spin_unlock(&hdd_context_lock);
381 if (!resp->status) {
382 /*
383 * OCB set config command successful.
384 * Open the TX data path
385 */
386 if (!hdd_ocb_register_sta(adapter)) {
Ravi Joshic3f5c8a2016-06-13 16:46:44 -0700387 wlan_hdd_netif_queue_control(adapter,
388 WLAN_START_ALL_NETIF_QUEUE_N_CARRIER,
389 WLAN_CONTROL_PATH);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800390 }
391 }
392
393 spin_lock(&hdd_context_lock);
394 if (context->magic == HDD_OCB_MAGIC)
395 complete(&context->completion_evt);
396 spin_unlock(&hdd_context_lock);
397 } else {
398 spin_unlock(&hdd_context_lock);
399 }
400}
401
402/**
403 * hdd_ocb_set_config_req() - Send an OCB set config request
404 * @adapter: a pointer to the adapter
405 * @config: a pointer to the OCB configuration
406 *
407 * Return: 0 on success.
408 */
409static int hdd_ocb_set_config_req(hdd_adapter_t *adapter,
410 struct sir_ocb_config *config)
411{
412 int rc;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530413 QDF_STATUS qdf_status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800414 struct hdd_ocb_ctxt context = {0};
415
416 if (hdd_ocb_validate_config(adapter, config)) {
417 hddLog(LOGE, FL("The configuration is invalid"));
418 return -EINVAL;
419 }
420
421 init_completion(&context.completion_evt);
422 context.adapter = adapter;
423 context.magic = HDD_OCB_MAGIC;
424
425 hddLog(LOG1, FL("Disabling queues"));
Ravi Joshic3f5c8a2016-06-13 16:46:44 -0700426 wlan_hdd_netif_queue_control(adapter, WLAN_NETIF_TX_DISABLE_N_CARRIER,
427 WLAN_CONTROL_PATH);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800428
429 /* Call the SME API to set the config */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530430 qdf_status = sme_ocb_set_config(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800431 ((hdd_context_t *)adapter->pHddCtx)->hHal, &context,
432 hdd_ocb_set_config_callback, config);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530433 if (qdf_status != QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800434 hddLog(LOGE, FL("Error calling SME function."));
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530435 /* Convert from qdf_status to errno */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800436 return -EINVAL;
437 }
438
439 /* Wait for the function to complete. */
440 rc = wait_for_completion_timeout(&context.completion_evt,
441 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
442 if (rc == 0) {
443 rc = -ETIMEDOUT;
444 goto end;
445 }
446 rc = 0;
447
448 if (context.status) {
449 rc = context.status;
450 goto end;
451 }
452
453 if (adapter->ocb_set_config_resp.status) {
454 rc = -EINVAL;
455 goto end;
456 }
457
458 /* fall through */
459end:
460 spin_lock(&hdd_context_lock);
461 context.magic = 0;
462 spin_unlock(&hdd_context_lock);
463 if (rc)
464 hddLog(LOGE, FL("Operation failed: %d"), rc);
465 return rc;
466}
467
468/**
469 * __iw_set_dot11p_channel_sched() - Handler for WLAN_SET_DOT11P_CHANNEL_SCHED
470 * ioctl
471 * @dev: Pointer to net_device structure
472 * @iw_request_info: IW Request Info
473 * @wrqu: IW Request Userspace Data Pointer
474 * @extra: IW Request Kernel Data Pointer
475 *
476 * Return: 0 on success
477 */
478static int __iw_set_dot11p_channel_sched(struct net_device *dev,
479 struct iw_request_info *info,
480 union iwreq_data *wrqu, char *extra)
481{
482 int rc = 0;
483 struct dot11p_channel_sched *sched;
484 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
485 struct sir_ocb_config *config = NULL;
486 uint8_t *mac_addr;
487 int i, j;
488 struct sir_ocb_config_channel *curr_chan;
489
Jeff Johnson6ee91ee2016-02-11 18:55:30 -0800490 ENTER_DEV(dev);
491
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530492 if (wlan_hdd_validate_context(WLAN_HDD_GET_CTX(adapter)))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800493 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800494
Krunal Sonibe766b02016-03-10 13:00:44 -0800495 if (adapter->device_mode != QDF_OCB_MODE) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800496 hddLog(LOGE, FL("Device not in OCB mode!"));
497 return -EINVAL;
498 }
499
500 sched = (struct dot11p_channel_sched *)extra;
501
502 /* Scheduled slots same as num channels for compatibility */
503 config = hdd_ocb_config_new(sched->num_channels, sched->num_channels,
504 0, 0);
505 if (config == NULL) {
506 hddLog(LOGE, FL("Failed to allocate memory!"));
507 return -ENOMEM;
508 }
509
510 /* Identify the vdev interface */
511 config->session_id = adapter->sessionId;
512
513 /* Release all the mac addresses used for OCB */
514 for (i = 0; i < adapter->ocb_mac_addr_count; i++) {
515 wlan_hdd_release_intf_addr(adapter->pHddCtx,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800516 adapter->ocb_mac_address[i].bytes);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800517 }
518 adapter->ocb_mac_addr_count = 0;
519
520 config->channel_count = 0;
521 for (i = 0; i < sched->num_channels; i++) {
522 if (0 == sched->channels[i].channel_freq)
523 continue;
524
525 curr_chan = &(config->channels[config->channel_count]);
526
527 curr_chan->chan_freq = sched->channels[i].channel_freq;
528 /*
529 * tx_power is divided by 2 because ocb_channel.tx_power is
530 * in half dB increments and sir_ocb_config_channel.max_pwr
531 * is in 1 dB increments.
532 */
533 curr_chan->max_pwr = sched->channels[i].tx_power / 2;
534 curr_chan->bandwidth = sched->channels[i].channel_bandwidth;
535 /* assume 10 as default if not provided */
536 if (curr_chan->bandwidth == 0)
537 curr_chan->bandwidth = 10;
538
539 /*
540 * Setup locally administered mac addresses for each channel.
541 * First channel uses the adapter's address.
542 */
543 if (i == 0) {
Anurag Chouhanc5548422016-02-24 18:33:27 +0530544 qdf_copy_macaddr(&curr_chan->mac_address,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800545 &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800546 } else {
547 mac_addr = wlan_hdd_get_intf_addr(adapter->pHddCtx);
548 if (mac_addr == NULL) {
549 hddLog(LOGE, FL("Cannot obtain mac address"));
550 rc = -EINVAL;
551 goto fail;
552 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530553 qdf_mem_copy(config->channels[
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800554 config->channel_count].mac_address.bytes,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800555 mac_addr, sizeof(tSirMacAddr));
556 /* Save the mac address to release later */
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530557 qdf_mem_copy(adapter->ocb_mac_address[
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800558 adapter->ocb_mac_addr_count].bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +0530559 mac_addr, QDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800560 adapter->ocb_mac_addr_count++;
561 }
562
563 for (j = 0; j < MAX_NUM_AC; j++) {
564 curr_chan->qos_params[j].aifsn =
565 sched->channels[i].qos_params[j].aifsn;
566 curr_chan->qos_params[j].cwmin =
567 sched->channels[i].qos_params[j].cwmin;
568 curr_chan->qos_params[j].cwmax =
569 sched->channels[i].qos_params[j].cwmax;
570 }
571
572 config->channel_count++;
573 }
574
575 /*
576 * Scheduled slots same as num channels for compatibility with
577 * legacy use.
578 */
579 for (i = 0; i < sched->num_channels; i++) {
580 config->schedule[i].chan_freq = sched->channels[i].channel_freq;
581 config->schedule[i].guard_interval =
582 sched->channels[i].start_guard_interval;
583 config->schedule[i].total_duration =
584 sched->channels[i].duration;
585 }
586
587 rc = hdd_ocb_set_config_req(adapter, config);
588 if (rc) {
589 hddLog(LOGE, FL("Error while setting OCB config"));
590 goto fail;
591 }
592
593 rc = 0;
594
595fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530596 qdf_mem_free(config);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800597 return rc;
598}
599
600/**
601 * iw_set_dot11p_channel_sched() - IOCTL interface for setting channel schedule
602 * @dev: Pointer to net_device structure
603 * @iw_request_info: IW Request Info
604 * @wrqu: IW Request Userspace Data Pointer
605 * @extra: IW Request Kernel Data Pointer
606 *
607 * Return: 0 on success.
608 */
609int iw_set_dot11p_channel_sched(struct net_device *dev,
610 struct iw_request_info *info,
611 union iwreq_data *wrqu, char *extra)
612{
613 int ret;
614
615 cds_ssr_protect(__func__);
616 ret = __iw_set_dot11p_channel_sched(dev, info, wrqu, extra);
617 cds_ssr_unprotect(__func__);
618
619 return ret;
620}
621
622static const struct nla_policy qca_wlan_vendor_ocb_set_config_policy[
623 QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX + 1] = {
624 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT] = {
625 .type = NLA_U32
626 },
627 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE] = {
628 .type = NLA_U32
629 },
630 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY] = {
631 .type = NLA_BINARY
632 },
633 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY] = {
634 .type = NLA_BINARY
635 },
636 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY] = {
637 .type = NLA_BINARY
638 },
639 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY] = {
640 .type = NLA_BINARY
641 },
642 [QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS] = {
643 .type = NLA_U32
644 },
645};
646
647static const struct nla_policy qca_wlan_vendor_ocb_set_utc_time_policy[
648 QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX + 1] = {
649 [QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE] = {
650 .type = NLA_BINARY, .len = SIZE_UTC_TIME
651 },
652 [QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR] = {
653 .type = NLA_BINARY, .len = SIZE_UTC_TIME_ERROR
654 },
655};
656
657static const struct nla_policy qca_wlan_vendor_ocb_start_timing_advert_policy[
658 QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX + 1] = {
659 [QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ] = {
660 .type = NLA_U32
661 },
662 [QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE] = {
663 .type = NLA_U32
664 },
665};
666
667static const struct nla_policy qca_wlan_vendor_ocb_stop_timing_advert_policy[
668 QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX + 1] = {
669 [QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ] = {
670 .type = NLA_U32
671 },
672};
673
674static const struct nla_policy qca_wlan_vendor_ocb_get_tsf_timer_resp[] = {
675 [QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH] = {
676 .type = NLA_U32
677 },
678 [QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW] = {
679 .type = NLA_U32
680 },
681};
682
683static const struct nla_policy qca_wlan_vendor_dcc_get_stats[] = {
684 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT] = {
685 .type = NLA_U32
686 },
687 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY] = {
688 .type = NLA_BINARY
689 },
690};
691
692static const struct nla_policy qca_wlan_vendor_dcc_get_stats_resp[] = {
693 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT] = {
694 .type = NLA_U32
695 },
696 [QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY] = {
697 .type = NLA_BINARY
698 },
699};
700
701static const struct nla_policy qca_wlan_vendor_dcc_clear_stats[] = {
702 [QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP] = {
703 .type = NLA_U32
704 },
705};
706
707static const struct nla_policy qca_wlan_vendor_dcc_update_ndl[
708 QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX + 1] = {
709 [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT] = {
710 .type = NLA_U32
711 },
712 [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY] = {
713 .type = NLA_BINARY
714 },
715 [QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY] = {
716 .type = NLA_BINARY
717 },
718};
719
720/**
721 * struct wlan_hdd_ocb_config_channel
722 * @chan_freq: frequency of the channel
723 * @bandwidth: bandwidth of the channel, either 10 or 20 MHz
724 * @mac_address: MAC address assigned to this channel
725 * @qos_params: QoS parameters
726 * @max_pwr: maximum transmit power of the channel (1/2 dBm)
727 * @min_pwr: minimum transmit power of the channel (1/2 dBm)
728 */
729struct wlan_hdd_ocb_config_channel {
730 uint32_t chan_freq;
731 uint32_t bandwidth;
732 uint16_t flags;
733 uint8_t reserved[4];
734 struct sir_qos_params qos_params[MAX_NUM_AC];
735 uint32_t max_pwr;
736 uint32_t min_pwr;
737};
738
739static void wlan_hdd_ocb_config_channel_to_sir_ocb_config_channel(
740 struct sir_ocb_config_channel *dest,
741 struct wlan_hdd_ocb_config_channel *src,
742 uint32_t channel_count)
743{
744 uint32_t i;
745
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530746 qdf_mem_zero(dest, channel_count * sizeof(*dest));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800747
748 for (i = 0; i < channel_count; i++) {
749 dest[i].chan_freq = src[i].chan_freq;
750 dest[i].bandwidth = src[i].bandwidth;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530751 qdf_mem_copy(dest[i].qos_params, src[i].qos_params,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800752 sizeof(dest[i].qos_params));
753 /*
754 * max_pwr and min_pwr are divided by 2 because
755 * wlan_hdd_ocb_config_channel.max_pwr and min_pwr
756 * are in 1/2 dB increments and
757 * sir_ocb_config_channel.max_pwr and min_pwr are in
758 * 1 dB increments.
759 */
760 dest[i].max_pwr = src[i].max_pwr / 2;
761 dest[i].min_pwr = (src[i].min_pwr + 1) / 2;
762 dest[i].flags = src[i].flags;
763 }
764}
765
766/**
767 * __wlan_hdd_cfg80211_ocb_set_config() - Interface for set config command
768 * @wiphy: pointer to the wiphy
769 * @wdev: pointer to the wdev
770 * @data: The netlink data
771 * @data_len: The length of the netlink data in bytes
772 *
773 * Return: 0 on success.
774 */
775static int __wlan_hdd_cfg80211_ocb_set_config(struct wiphy *wiphy,
776 struct wireless_dev *wdev,
777 const void *data,
778 int data_len)
779{
780 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
781 struct net_device *dev = wdev->netdev;
782 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
783 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX + 1];
784 struct nlattr *channel_array;
785 struct nlattr *sched_array;
786 struct nlattr *ndl_chan_list;
787 uint32_t ndl_chan_list_len;
788 struct nlattr *ndl_active_state_list;
789 uint32_t ndl_active_state_list_len;
790 uint32_t flags = 0;
791 int i;
792 int channel_count, schedule_size;
793 struct sir_ocb_config *config;
794 int rc = -EINVAL;
795 uint8_t *mac_addr;
796
Jeff Johnson1f61b612016-02-12 16:28:33 -0800797 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800798
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530799 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800800 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800801
Krunal Sonibe766b02016-03-10 13:00:44 -0800802 if (adapter->device_mode != QDF_OCB_MODE) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800803 hddLog(LOGE, FL("Device not in OCB mode!"));
804 return -EINVAL;
805 }
806
807 /* Parse the netlink message */
808 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX,
809 data,
810 data_len, qca_wlan_vendor_ocb_set_config_policy)) {
811 hddLog(LOGE, FL("Invalid ATTR"));
812 return -EINVAL;
813 }
814
815 /* Get the number of channels in the schedule */
816 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT]) {
817 hddLog(LOGE, FL("CHANNEL_COUNT is not present"));
818 return -EINVAL;
819 }
820 channel_count = nla_get_u32(
821 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT]);
822
823 /* Get the size of the channel schedule */
824 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE]) {
825 hddLog(LOGE, FL("SCHEDULE_SIZE is not present"));
826 return -EINVAL;
827 }
828 schedule_size = nla_get_u32(
829 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE]);
830
831 /* Get the ndl chan array and the ndl active state array. */
832 ndl_chan_list =
833 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY];
834 ndl_chan_list_len = (ndl_chan_list ? nla_len(ndl_chan_list) : 0);
835
836 ndl_active_state_list =
837 tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY];
838 ndl_active_state_list_len = (ndl_active_state_list ?
839 nla_len(ndl_active_state_list) : 0);
840
841 /* Get the flags */
842 if (tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS])
843 flags = nla_get_u32(tb[
844 QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS]);
845
846 config = hdd_ocb_config_new(channel_count, schedule_size,
847 ndl_chan_list_len,
848 ndl_active_state_list_len);
849 if (config == NULL) {
850 hddLog(LOGE, FL("Failed to allocate memory!"));
851 return -ENOMEM;
852 }
853
854 config->channel_count = channel_count;
855 config->schedule_size = schedule_size;
856 config->flags = flags;
857
858 /* Read the channel array */
859 channel_array = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY];
860 if (!channel_array) {
861 hddLog(LOGE, FL("No channel present"));
862 goto fail;
863 }
864 if (nla_len(channel_array) != channel_count *
865 sizeof(struct wlan_hdd_ocb_config_channel)) {
866 hddLog(LOGE, FL("CHANNEL_ARRAY is not the correct size"));
867 goto fail;
868 }
869 wlan_hdd_ocb_config_channel_to_sir_ocb_config_channel(
870 config->channels, nla_data(channel_array), channel_count);
871
872 /* Identify the vdev interface */
873 config->session_id = adapter->sessionId;
874
875 /* Release all the mac addresses used for OCB */
876 for (i = 0; i < adapter->ocb_mac_addr_count; i++) {
877 wlan_hdd_release_intf_addr(adapter->pHddCtx,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800878 adapter->ocb_mac_address[i].bytes);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800879 }
880 adapter->ocb_mac_addr_count = 0;
881
882 /*
883 * Setup locally administered mac addresses for each channel.
884 * First channel uses the adapter's address.
885 */
886 for (i = 0; i < config->channel_count; i++) {
887 if (i == 0) {
Anurag Chouhanc5548422016-02-24 18:33:27 +0530888 qdf_copy_macaddr(&config->channels[i].mac_address,
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800889 &adapter->macAddressCurrent);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800890 } else {
891 mac_addr = wlan_hdd_get_intf_addr(adapter->pHddCtx);
892 if (mac_addr == NULL) {
893 hddLog(LOGE, FL("Cannot obtain mac address"));
894 goto fail;
895 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530896 qdf_mem_copy(config->channels[i].mac_address.bytes,
Anurag Chouhan6d760662016-02-20 16:05:43 +0530897 mac_addr, QDF_MAC_ADDR_SIZE);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800898 /* Save the mac address to release later */
Anurag Chouhanc5548422016-02-24 18:33:27 +0530899 qdf_copy_macaddr(&adapter->ocb_mac_address[
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800900 adapter->ocb_mac_addr_count],
Srinivas Girigowda117e7fb2015-11-16 12:33:45 -0800901 &config->channels[i].mac_address);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800902 adapter->ocb_mac_addr_count++;
903 }
904 }
905
906 /* Read the schedule array */
907 sched_array = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY];
908 if (!sched_array) {
909 hddLog(LOGE, FL("No channel present"));
910 goto fail;
911 }
912 if (nla_len(sched_array) != schedule_size * sizeof(*config->schedule)) {
913 hddLog(LOGE, FL("SCHEDULE_ARRAY is not the correct size"));
914 goto fail;
915 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530916 qdf_mem_copy(config->schedule, nla_data(sched_array),
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800917 nla_len(sched_array));
918
919 /* Copy the NDL chan array */
920 if (ndl_chan_list_len) {
921 config->dcc_ndl_chan_list_len = ndl_chan_list_len;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530922 qdf_mem_copy(config->dcc_ndl_chan_list, nla_data(ndl_chan_list),
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800923 nla_len(ndl_chan_list));
924 }
925
926 /* Copy the NDL active state array */
927 if (ndl_active_state_list_len) {
928 config->dcc_ndl_active_state_list_len =
929 ndl_active_state_list_len;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530930 qdf_mem_copy(config->dcc_ndl_active_state_list,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800931 nla_data(ndl_active_state_list),
932 nla_len(ndl_active_state_list));
933 }
934
935 rc = hdd_ocb_set_config_req(adapter, config);
936 if (rc)
937 hddLog(LOGE, FL("Error while setting OCB config: %d"), rc);
938
939fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530940 qdf_mem_free(config);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800941 return rc;
942}
943
944/**
945 * wlan_hdd_cfg80211_ocb_set_config() - Interface for set config command
946 * @wiphy: pointer to the wiphy
947 * @wdev: pointer to the wdev
948 * @data: The netlink data
949 * @data_len: The length of the netlink data in bytes
950 *
951 * Return: 0 on success.
952 */
953int wlan_hdd_cfg80211_ocb_set_config(struct wiphy *wiphy,
954 struct wireless_dev *wdev,
955 const void *data,
956 int data_len)
957{
958 int ret;
959
960 cds_ssr_protect(__func__);
961 ret = __wlan_hdd_cfg80211_ocb_set_config(wiphy, wdev, data, data_len);
962 cds_ssr_unprotect(__func__);
963
964 return ret;
965}
966
967/**
968 * __wlan_hdd_cfg80211_ocb_set_utc_time() - Interface for set UTC time command
969 * @wiphy: pointer to the wiphy
970 * @wdev: pointer to the wdev
971 * @data: The netlink data
972 * @data_len: The length of the netlink data in bytes
973 *
974 * Return: 0 on success.
975 */
976static int __wlan_hdd_cfg80211_ocb_set_utc_time(struct wiphy *wiphy,
977 struct wireless_dev *wdev,
978 const void *data,
979 int data_len)
980{
981 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
982 struct net_device *dev = wdev->netdev;
983 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
984 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX + 1];
985 struct nlattr *utc_attr;
986 struct nlattr *time_error_attr;
987 struct sir_ocb_utc *utc;
988 int rc = -EINVAL;
989
Jeff Johnson1f61b612016-02-12 16:28:33 -0800990 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800991
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530992 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800993 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800994
Krunal Sonibe766b02016-03-10 13:00:44 -0800995 if (adapter->device_mode != QDF_OCB_MODE) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800996 hddLog(LOGE, FL("Device not in OCB mode!"));
997 return -EINVAL;
998 }
999
1000 if (!wma_is_vdev_up(adapter->sessionId)) {
1001 hddLog(LOGE, FL("The device has not been started"));
1002 return -EINVAL;
1003 }
1004
1005 /* Parse the netlink message */
1006 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX,
1007 data,
1008 data_len, qca_wlan_vendor_ocb_set_utc_time_policy)) {
1009 hddLog(LOGE, FL("Invalid ATTR"));
1010 return -EINVAL;
1011 }
1012
1013 /* Read the UTC time */
1014 utc_attr = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE];
1015 if (!utc_attr) {
1016 hddLog(LOGE, FL("UTC_TIME is not present"));
1017 return -EINVAL;
1018 }
1019 if (nla_len(utc_attr) != SIZE_UTC_TIME) {
1020 hddLog(LOGE, FL("UTC_TIME is not the correct size"));
1021 return -EINVAL;
1022 }
1023
1024 /* Read the time error */
1025 time_error_attr = tb[QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR];
1026 if (!time_error_attr) {
1027 hddLog(LOGE, FL("UTC_TIME is not present"));
1028 return -EINVAL;
1029 }
1030 if (nla_len(time_error_attr) != SIZE_UTC_TIME_ERROR) {
1031 hddLog(LOGE, FL("UTC_TIME is not the correct size"));
1032 return -EINVAL;
1033 }
1034
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301035 utc = qdf_mem_malloc(sizeof(*utc));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001036 if (!utc) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301037 hddLog(LOGE, FL("qdf_mem_malloc failed"));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001038 return -ENOMEM;
1039 }
1040 utc->vdev_id = adapter->sessionId;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301041 qdf_mem_copy(utc->utc_time, nla_data(utc_attr), SIZE_UTC_TIME);
1042 qdf_mem_copy(utc->time_error, nla_data(time_error_attr),
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001043 SIZE_UTC_TIME_ERROR);
1044
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301045 if (sme_ocb_set_utc_time(hdd_ctx->hHal, utc) != QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001046 hddLog(LOGE, FL("Error while setting UTC time"));
1047 rc = -EINVAL;
1048 } else {
1049 rc = 0;
1050 }
1051
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301052 qdf_mem_free(utc);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001053 return rc;
1054}
1055
1056/**
1057 * wlan_hdd_cfg80211_ocb_set_utc_time() - Interface for the set UTC time command
1058 * @wiphy: pointer to the wiphy
1059 * @wdev: pointer to the wdev
1060 * @data: The netlink data
1061 * @data_len: The length of the netlink data in bytes
1062 *
1063 * Return: 0 on success.
1064 */
1065int wlan_hdd_cfg80211_ocb_set_utc_time(struct wiphy *wiphy,
1066 struct wireless_dev *wdev,
1067 const void *data,
1068 int data_len)
1069{
1070 int ret;
1071
1072 cds_ssr_protect(__func__);
1073 ret = __wlan_hdd_cfg80211_ocb_set_utc_time(wiphy, wdev, data, data_len);
1074 cds_ssr_unprotect(__func__);
1075
1076 return ret;
1077}
1078
1079/**
1080 * __wlan_hdd_cfg80211_ocb_start_timing_advert() - Interface for start TA cmd
1081 * @wiphy: pointer to the wiphy
1082 * @wdev: pointer to the wdev
1083 * @data: The netlink data
1084 * @data_len: The length of the netlink data in bytes
1085 *
1086 * Return: 0 on success.
1087 */
1088static int
1089__wlan_hdd_cfg80211_ocb_start_timing_advert(struct wiphy *wiphy,
1090 struct wireless_dev *wdev,
1091 const void *data,
1092 int data_len)
1093{
1094 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1095 struct net_device *dev = wdev->netdev;
1096 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001097 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX + 1];
1098 struct sir_ocb_timing_advert *timing_advert;
1099 int rc = -EINVAL;
1100
Jeff Johnson1f61b612016-02-12 16:28:33 -08001101 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001102
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301103 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001104 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001105
Krunal Sonibe766b02016-03-10 13:00:44 -08001106 if (adapter->device_mode != QDF_OCB_MODE) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001107 hddLog(LOGE, FL("Device not in OCB mode!"));
1108 return -EINVAL;
1109 }
1110
1111 if (!wma_is_vdev_up(adapter->sessionId)) {
1112 hddLog(LOGE, FL("The device has not been started"));
1113 return -EINVAL;
1114 }
1115
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301116 timing_advert = qdf_mem_malloc(sizeof(*timing_advert));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001117 if (!timing_advert) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301118 hddLog(LOGE, FL("qdf_mem_malloc failed"));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001119 return -ENOMEM;
1120 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301121 qdf_mem_zero(timing_advert, sizeof(*timing_advert));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001122 timing_advert->vdev_id = adapter->sessionId;
1123
1124 /* Parse the netlink message */
1125 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX,
1126 data,
1127 data_len,
1128 qca_wlan_vendor_ocb_start_timing_advert_policy)) {
1129 hddLog(LOGE, FL("Invalid ATTR"));
1130 goto fail;
1131 }
1132
1133 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ]) {
1134 hddLog(LOGE, FL("CHANNEL_FREQ is not present"));
1135 goto fail;
1136 }
1137 timing_advert->chan_freq = nla_get_u32(
1138 tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ]);
1139
1140 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE]) {
1141 hddLog(LOGE, FL("REPEAT_RATE is not present"));
1142 goto fail;
1143 }
1144 timing_advert->repeat_rate = nla_get_u32(
1145 tb[QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE]);
1146
1147 timing_advert->template_length =
Naveen Rawatb4d37622015-11-13 16:15:25 -08001148 sme_ocb_gen_timing_advert_frame(hdd_ctx->hHal,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001149 *(tSirMacAddr *)&adapter->macAddressCurrent.bytes,
1150 &timing_advert->template_value,
1151 &timing_advert->timestamp_offset,
1152 &timing_advert->time_value_offset);
1153 if (timing_advert->template_length <= 0) {
1154 hddLog(LOGE, FL("Error while generating the TA frame"));
1155 goto fail;
1156 }
1157
1158 if (sme_ocb_start_timing_advert(hdd_ctx->hHal, timing_advert) !=
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301159 QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001160 hddLog(LOGE, FL("Error while starting timing advert"));
1161 rc = -EINVAL;
1162 } else {
1163 rc = 0;
1164 }
1165
1166fail:
1167 if (timing_advert->template_value)
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301168 qdf_mem_free(timing_advert->template_value);
1169 qdf_mem_free(timing_advert);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001170 return rc;
1171}
1172
1173/**
1174 * wlan_hdd_cfg80211_ocb_start_timing_advert() - Interface for the start TA cmd
1175 * @wiphy: pointer to the wiphy
1176 * @wdev: pointer to the wdev
1177 * @data: The netlink data
1178 * @data_len: The length of the netlink data in bytes
1179 *
1180 * Return: 0 on success.
1181 */
1182int wlan_hdd_cfg80211_ocb_start_timing_advert(struct wiphy *wiphy,
1183 struct wireless_dev *wdev,
1184 const void *data,
1185 int data_len)
1186{
1187 int ret;
1188
1189 cds_ssr_protect(__func__);
1190 ret = __wlan_hdd_cfg80211_ocb_start_timing_advert(wiphy, wdev,
1191 data, data_len);
1192 cds_ssr_unprotect(__func__);
1193
1194 return ret;
1195}
1196
1197/**
1198 * __wlan_hdd_cfg80211_ocb_stop_timing_advert() - Interface for the stop TA cmd
1199 * @wiphy: pointer to the wiphy
1200 * @wdev: pointer to the wdev
1201 * @data: The netlink data
1202 * @data_len: The length of the netlink data in bytes
1203 *
1204 * Return: 0 on success.
1205 */
1206static int
1207__wlan_hdd_cfg80211_ocb_stop_timing_advert(struct wiphy *wiphy,
1208 struct wireless_dev *wdev,
1209 const void *data,
1210 int data_len)
1211{
1212 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1213 struct net_device *dev = wdev->netdev;
1214 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1215 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX + 1];
1216 struct sir_ocb_timing_advert *timing_advert;
1217 int rc = -EINVAL;
1218
Jeff Johnson1f61b612016-02-12 16:28:33 -08001219 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001220
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301221 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001222 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001223
Krunal Sonibe766b02016-03-10 13:00:44 -08001224 if (adapter->device_mode != QDF_OCB_MODE) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001225 hddLog(LOGE, FL("Device not in OCB mode!"));
1226 return -EINVAL;
1227 }
1228
1229 if (!wma_is_vdev_up(adapter->sessionId)) {
1230 hddLog(LOGE, FL("The device has not been started"));
1231 return -EINVAL;
1232 }
1233
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301234 timing_advert = qdf_mem_malloc(sizeof(*timing_advert));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001235 if (!timing_advert) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301236 hddLog(LOGE, FL("qdf_mem_malloc failed"));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001237 return -ENOMEM;
1238 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301239 qdf_mem_zero(timing_advert, sizeof(sizeof(*timing_advert)));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001240 timing_advert->vdev_id = adapter->sessionId;
1241
1242 /* Parse the netlink message */
1243 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX,
1244 data,
1245 data_len,
1246 qca_wlan_vendor_ocb_stop_timing_advert_policy)) {
1247 hddLog(LOGE, FL("Invalid ATTR"));
1248 goto fail;
1249 }
1250
1251 if (!tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ]) {
1252 hddLog(LOGE, FL("CHANNEL_FREQ is not present"));
1253 goto fail;
1254 }
1255 timing_advert->chan_freq = nla_get_u32(
1256 tb[QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ]);
1257
1258 if (sme_ocb_stop_timing_advert(hdd_ctx->hHal, timing_advert) !=
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301259 QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001260 hddLog(LOGE, FL("Error while stopping timing advert"));
1261 rc = -EINVAL;
1262 } else {
1263 rc = 0;
1264 }
1265
1266fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301267 qdf_mem_free(timing_advert);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001268 return rc;
1269}
1270
1271/**
1272 * wlan_hdd_cfg80211_ocb_stop_timing_advert() - Interface for the stop TA cmd
1273 * @wiphy: pointer to the wiphy
1274 * @wdev: pointer to the wdev
1275 * @data: The netlink data
1276 * @data_len: The length of the netlink data in bytes
1277 *
1278 * Return: 0 on success.
1279 */
1280int wlan_hdd_cfg80211_ocb_stop_timing_advert(struct wiphy *wiphy,
1281 struct wireless_dev *wdev,
1282 const void *data,
1283 int data_len)
1284{
1285 int ret;
1286
1287 cds_ssr_protect(__func__);
1288 ret = __wlan_hdd_cfg80211_ocb_stop_timing_advert(wiphy, wdev,
1289 data, data_len);
1290 cds_ssr_unprotect(__func__);
1291
1292 return ret;
1293}
1294
1295/**
1296 * hdd_ocb_get_tsf_timer_callback() - Callback to get TSF command
1297 * @context_ptr: request context
1298 * @response_ptr: response data
1299 */
1300static void hdd_ocb_get_tsf_timer_callback(void *context_ptr,
1301 void *response_ptr)
1302{
1303 struct hdd_ocb_ctxt *context = context_ptr;
1304 struct sir_ocb_get_tsf_timer_response *response = response_ptr;
1305
1306 if (!context)
1307 return;
1308
1309 spin_lock(&hdd_context_lock);
1310 if (context->magic == HDD_OCB_MAGIC) {
1311 if (response) {
1312 context->adapter->ocb_get_tsf_timer_resp = *response;
1313 context->status = 0;
1314 } else {
1315 context->status = -EINVAL;
1316 }
1317 complete(&context->completion_evt);
1318 }
1319 spin_unlock(&hdd_context_lock);
1320}
1321
1322/**
1323 * __wlan_hdd_cfg80211_ocb_get_tsf_timer() - Interface for get TSF timer cmd
1324 * @wiphy: pointer to the wiphy
1325 * @wdev: pointer to the wdev
1326 * @data: The netlink data
1327 * @data_len: The length of the netlink data in bytes
1328 *
1329 * Return: 0 on success.
1330 */
1331static int
1332__wlan_hdd_cfg80211_ocb_get_tsf_timer(struct wiphy *wiphy,
1333 struct wireless_dev *wdev,
1334 const void *data,
1335 int data_len)
1336{
1337 struct sk_buff *nl_resp = 0;
1338 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1339 struct net_device *dev = wdev->netdev;
1340 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1341 int rc = -EINVAL;
1342 struct sir_ocb_get_tsf_timer request = {0};
1343 struct hdd_ocb_ctxt context = {0};
1344
Jeff Johnson1f61b612016-02-12 16:28:33 -08001345 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001346
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301347 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001348 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001349
Krunal Sonibe766b02016-03-10 13:00:44 -08001350 if (adapter->device_mode != QDF_OCB_MODE) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001351 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"));
Anurag Chouhanf04e84f2016-03-03 10:12:12 +05301372 /* Need to convert from qdf_status to errno. */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001373 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) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301480 qdf_mem_free(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001481 context->adapter->dcc_get_stats_resp);
1482 }
1483 context->adapter->dcc_get_stats_resp =
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301484 qdf_mem_malloc(sizeof(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001485 *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);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301492 qdf_mem_copy(hdd_resp->channel_stats_array,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001493 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
Jeff Johnson1f61b612016-02-12 16:28:33 -08001533 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001534
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301535 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001536 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001537
Krunal Sonibe766b02016-03-10 13:00:44 -08001538 if (adapter->device_mode != QDF_OCB_MODE) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001539 hddLog(LOGE, FL("Device not in OCB mode!"));
1540 return -EINVAL;
1541 }
1542
1543 if (!wma_is_vdev_up(adapter->sessionId)) {
1544 hddLog(LOGE, FL("The device has not been started"));
1545 return -EINVAL;
1546 }
1547
1548 /* Parse the netlink message */
1549 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_MAX,
1550 data,
1551 data_len,
1552 qca_wlan_vendor_dcc_get_stats)) {
1553 hddLog(LOGE, FL("Invalid ATTR"));
1554 return -EINVAL;
1555 }
1556
1557 /* Validate all the parameters are present */
1558 if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT] ||
1559 !tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]) {
1560 hddLog(LOGE, FL("Parameters are not present."));
1561 return -EINVAL;
1562 }
1563
1564 channel_count = nla_get_u32(
1565 tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_CHANNEL_COUNT]);
1566 request_array_len = nla_len(
1567 tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]);
1568 request_array = nla_data(
1569 tb[QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_REQUEST_ARRAY]);
1570
1571 /* Initialize the callback context */
1572 init_completion(&context.completion_evt);
1573 context.adapter = adapter;
1574 context.magic = HDD_OCB_MAGIC;
1575
1576 request.vdev_id = adapter->sessionId;
1577 request.channel_count = channel_count;
1578 request.request_array_len = request_array_len;
1579 request.request_array = request_array;
1580
1581 /* Call the SME function. */
1582 rc = sme_dcc_get_stats(hdd_ctx->hHal, &context,
1583 hdd_dcc_get_stats_callback,
1584 &request);
1585 if (rc) {
1586 hddLog(LOGE, FL("Error calling SME function"));
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301587 /* Need to convert from qdf_status to errno. */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001588 return -EINVAL;
1589 }
1590
1591 /* Wait for the function to complete. */
1592 rc = wait_for_completion_timeout(&context.completion_evt,
1593 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
1594 if (rc == 0) {
1595 hddLog(LOGE, FL("Operation failed: %d"), rc);
1596 rc = -ETIMEDOUT;
1597 goto end;
1598 }
1599
1600 if (context.status) {
1601 hddLog(LOGE, FL("There was error: %d"), context.status);
1602 rc = context.status;
1603 goto end;
1604 }
1605
1606 if (!adapter->dcc_get_stats_resp) {
1607 hddLog(LOGE, FL("The response was NULL"));
1608 rc = -EINVAL;
1609 goto end;
1610 }
1611
1612 /* Allocate the buffer for the response. */
1613 nl_resp = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(uint32_t) +
1614 adapter->dcc_get_stats_resp->channel_stats_array_len +
1615 NLMSG_HDRLEN);
1616 if (!nl_resp) {
1617 hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
1618 rc = -ENOMEM;
1619 goto end;
1620 }
1621
1622 /* Populate the response. */
1623 rc = nla_put_u32(nl_resp,
1624 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT,
1625 adapter->dcc_get_stats_resp->num_channels);
1626 if (rc)
1627 goto end;
1628 rc = nla_put(nl_resp,
1629 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY,
1630 adapter->dcc_get_stats_resp->channel_stats_array_len,
1631 adapter->dcc_get_stats_resp->channel_stats_array);
1632 if (rc)
1633 goto end;
1634
1635 /* Send the response. */
1636 rc = cfg80211_vendor_cmd_reply(nl_resp);
1637 nl_resp = NULL;
1638 if (rc) {
1639 hddLog(LOGE, FL("cfg80211_vendor_cmd_reply failed: %d"), rc);
1640 goto end;
1641 }
1642
1643 /* fall through */
1644end:
1645 spin_lock(&hdd_context_lock);
1646 context.magic = 0;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301647 qdf_mem_free(adapter->dcc_get_stats_resp);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001648 adapter->dcc_get_stats_resp = NULL;
1649 spin_unlock(&hdd_context_lock);
1650 if (nl_resp)
1651 kfree_skb(nl_resp);
1652 return rc;
1653}
1654
1655/**
1656 * wlan_hdd_cfg80211_dcc_get_stats() - Interface for get dcc stats
1657 * @wiphy: pointer to the wiphy
1658 * @wdev: pointer to the wdev
1659 * @data: The netlink data
1660 * @data_len: The length of the netlink data in bytes
1661 *
1662 * Return: 0 on success.
1663 */
1664int wlan_hdd_cfg80211_dcc_get_stats(struct wiphy *wiphy,
1665 struct wireless_dev *wdev,
1666 const void *data,
1667 int data_len)
1668{
1669 int ret;
1670
1671 cds_ssr_protect(__func__);
1672 ret = __wlan_hdd_cfg80211_dcc_get_stats(wiphy, wdev,
1673 data, data_len);
1674 cds_ssr_unprotect(__func__);
1675
1676 return ret;
1677}
1678
1679/**
1680 * __wlan_hdd_cfg80211_dcc_clear_stats() - Interface for clear dcc stats cmd
1681 * @wiphy: pointer to the wiphy
1682 * @wdev: pointer to the wdev
1683 * @data: The netlink data
1684 * @data_len: The length of the netlink data in bytes
1685 *
1686 * Return: 0 on success.
1687 */
1688static int __wlan_hdd_cfg80211_dcc_clear_stats(struct wiphy *wiphy,
1689 struct wireless_dev *wdev,
1690 const void *data,
1691 int data_len)
1692{
1693 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1694 struct net_device *dev = wdev->netdev;
1695 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1696 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_MAX + 1];
1697
Jeff Johnson1f61b612016-02-12 16:28:33 -08001698 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001699
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301700 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001701 return -EINVAL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001702
Krunal Sonibe766b02016-03-10 13:00:44 -08001703 if (adapter->device_mode != QDF_OCB_MODE) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001704 hddLog(LOGE, FL("Device not in OCB mode!"));
1705 return -EINVAL;
1706 }
1707
1708 if (!wma_is_vdev_up(adapter->sessionId)) {
1709 hddLog(LOGE, FL("The device has not been started"));
1710 return -EINVAL;
1711 }
1712
1713 /* Parse the netlink message */
1714 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_MAX,
1715 data,
1716 data_len,
1717 qca_wlan_vendor_dcc_clear_stats)) {
1718 hddLog(LOGE, FL("Invalid ATTR"));
1719 return -EINVAL;
1720 }
1721
1722 /* Verify that the parameter is present */
1723 if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP]) {
1724 hddLog(LOGE, FL("Parameters are not present."));
1725 return -EINVAL;
1726 }
1727
1728 /* Call the SME function */
1729 if (sme_dcc_clear_stats(hdd_ctx->hHal, adapter->sessionId,
1730 nla_get_u32(
1731 tb[QCA_WLAN_VENDOR_ATTR_DCC_CLEAR_STATS_BITMAP])) !=
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301732 QDF_STATUS_SUCCESS) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001733 hddLog(LOGE, FL("Error calling SME function."));
1734 return -EINVAL;
1735 }
1736
1737 return 0;
1738}
1739
1740/**
1741 * wlan_hdd_cfg80211_dcc_clear_stats() - Interface for clear dcc stats cmd
1742 * @wiphy: pointer to the wiphy
1743 * @wdev: pointer to the wdev
1744 * @data: The netlink data
1745 * @data_len: The length of the netlink data in bytes
1746 *
1747 * Return: 0 on success.
1748 */
1749int wlan_hdd_cfg80211_dcc_clear_stats(struct wiphy *wiphy,
1750 struct wireless_dev *wdev,
1751 const void *data,
1752 int data_len)
1753{
1754 int ret;
1755
1756 cds_ssr_protect(__func__);
1757 ret = __wlan_hdd_cfg80211_dcc_clear_stats(wiphy, wdev,
1758 data, data_len);
1759 cds_ssr_unprotect(__func__);
1760
1761 return ret;
1762}
1763
1764/**
1765 * hdd_dcc_update_ndl_callback() - Callback to update NDL command
1766 * @context_ptr: request context
1767 * @response_ptr: response data
1768 */
1769static void hdd_dcc_update_ndl_callback(void *context_ptr, void *response_ptr)
1770{
1771 struct hdd_ocb_ctxt *context = context_ptr;
1772 struct sir_dcc_update_ndl_response *response = response_ptr;
1773
1774 if (!context)
1775 return;
1776
1777 spin_lock(&hdd_context_lock);
1778 if (context->magic == HDD_OCB_MAGIC) {
1779 if (response) {
1780 context->adapter->dcc_update_ndl_resp = *response;
1781 context->status = 0;
1782 } else {
1783 context->status = -EINVAL;
1784 }
1785 complete(&context->completion_evt);
1786 }
1787 spin_unlock(&hdd_context_lock);
1788}
1789
1790/**
1791 * __wlan_hdd_cfg80211_dcc_update_ndl() - Interface for update dcc cmd
1792 * @wiphy: pointer to the wiphy
1793 * @wdev: pointer to the wdev
1794 * @data: The netlink data
1795 * @data_len: The length of the netlink data in bytes
1796 *
1797 * Return: 0 on success.
1798 */
1799static int __wlan_hdd_cfg80211_dcc_update_ndl(struct wiphy *wiphy,
1800 struct wireless_dev *wdev,
1801 const void *data,
1802 int data_len)
1803{
1804 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
1805 struct net_device *dev = wdev->netdev;
1806 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
1807 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX + 1];
1808 struct sir_dcc_update_ndl request;
1809 uint32_t channel_count;
1810 uint32_t ndl_channel_array_len;
1811 void *ndl_channel_array;
1812 uint32_t ndl_active_state_array_len;
1813 void *ndl_active_state_array;
1814 int rc = -EINVAL;
1815 struct hdd_ocb_ctxt context = {0};
1816
Jeff Johnson1f61b612016-02-12 16:28:33 -08001817 ENTER_DEV(dev);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001818
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301819 if (wlan_hdd_validate_context(hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001820 goto end;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001821
Krunal Sonibe766b02016-03-10 13:00:44 -08001822 if (adapter->device_mode != QDF_OCB_MODE) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001823 hddLog(LOGE, FL("Device not in OCB mode!"));
1824 goto end;
1825 }
1826
1827 if (!wma_is_vdev_up(adapter->sessionId)) {
1828 hddLog(LOGE, FL("The device has not been started"));
1829 return -EINVAL;
1830 }
1831
1832 /* Parse the netlink message */
1833 if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_MAX,
1834 data,
1835 data_len,
1836 qca_wlan_vendor_dcc_update_ndl)) {
1837 hddLog(LOGE, FL("Invalid ATTR"));
1838 goto end;
1839 }
1840
1841 /* Verify that the parameter is present */
1842 if (!tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT] ||
1843 !tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY] ||
1844 !tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]) {
1845 hddLog(LOGE, FL("Parameters are not present."));
1846 return -EINVAL;
1847 }
1848
1849 channel_count = nla_get_u32(
1850 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_COUNT]);
1851 ndl_channel_array_len = nla_len(
1852 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY]);
1853 ndl_channel_array = nla_data(
1854 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_CHANNEL_ARRAY]);
1855 ndl_active_state_array_len = nla_len(
1856 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]);
1857 ndl_active_state_array = nla_data(
1858 tb[QCA_WLAN_VENDOR_ATTR_DCC_UPDATE_NDL_ACTIVE_STATE_ARRAY]);
1859
1860 /* Initialize the callback context */
1861 init_completion(&context.completion_evt);
1862 context.adapter = adapter;
1863 context.magic = HDD_OCB_MAGIC;
1864
1865 /* Copy the parameters to the request structure. */
1866 request.vdev_id = adapter->sessionId;
1867 request.channel_count = channel_count;
1868 request.dcc_ndl_chan_list_len = ndl_channel_array_len;
1869 request.dcc_ndl_chan_list = ndl_channel_array;
1870 request.dcc_ndl_active_state_list_len = ndl_active_state_array_len;
1871 request.dcc_ndl_active_state_list = ndl_active_state_array;
1872
1873 /* Call the SME function */
1874 rc = sme_dcc_update_ndl(hdd_ctx->hHal, &context,
1875 hdd_dcc_update_ndl_callback,
1876 &request);
1877 if (rc) {
1878 hddLog(LOGE, FL("Error calling SME function."));
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301879 /* Convert from qdf_status to errno */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001880 return -EINVAL;
1881 }
1882
1883 /* Wait for the function to complete. */
1884 rc = wait_for_completion_timeout(&context.completion_evt,
1885 msecs_to_jiffies(WLAN_WAIT_TIME_OCB_CMD));
1886 if (rc == 0) {
1887 hddLog(LOGE, FL("Operation timed out"));
1888 rc = -ETIMEDOUT;
1889 goto end;
1890 }
1891 rc = 0;
1892
1893 if (context.status) {
1894 hddLog(LOGE, FL("Operation failed: %d"), context.status);
1895 rc = context.status;
1896 goto end;
1897 }
1898
1899 if (adapter->dcc_update_ndl_resp.status) {
1900 hddLog(LOGE, FL("Operation returned: %d"),
1901 adapter->dcc_update_ndl_resp.status);
1902 rc = -EINVAL;
1903 goto end;
1904 }
1905
1906 /* fall through */
1907end:
1908 spin_lock(&hdd_context_lock);
1909 context.magic = 0;
1910 spin_unlock(&hdd_context_lock);
1911 return rc;
1912}
1913
1914/**
1915 * wlan_hdd_cfg80211_dcc_update_ndl() - Interface for update dcc cmd
1916 * @wiphy: pointer to the wiphy
1917 * @wdev: pointer to the wdev
1918 * @data: The netlink data
1919 * @data_len: The length of the netlink data in bytes
1920 *
1921 * Return: 0 on success.
1922 */
1923int wlan_hdd_cfg80211_dcc_update_ndl(struct wiphy *wiphy,
1924 struct wireless_dev *wdev,
1925 const void *data,
1926 int data_len)
1927{
1928 int ret;
1929
1930 cds_ssr_protect(__func__);
1931 ret = __wlan_hdd_cfg80211_dcc_update_ndl(wiphy, wdev,
1932 data, data_len);
1933 cds_ssr_unprotect(__func__);
1934
1935 return ret;
1936}
1937
1938/**
1939 * wlan_hdd_dcc_stats_event_callback() - Callback to get stats event
1940 * @context_ptr: request context
1941 * @response_ptr: response data
1942 */
1943static void wlan_hdd_dcc_stats_event_callback(void *context_ptr,
1944 void *response_ptr)
1945{
1946 hdd_context_t *hdd_ctx = (hdd_context_t *)context_ptr;
1947 struct sir_dcc_get_stats_response *resp = response_ptr;
1948 struct sk_buff *vendor_event;
1949
1950 ENTER();
1951
1952 vendor_event =
1953 cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
1954 NULL, sizeof(uint32_t) + resp->channel_stats_array_len +
1955 NLMSG_HDRLEN,
1956 QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT_INDEX,
1957 GFP_KERNEL);
1958
1959 if (!vendor_event) {
1960 hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed"));
1961 return;
1962 }
1963
1964 if (nla_put_u32(vendor_event,
1965 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_CHANNEL_COUNT,
1966 resp->num_channels) ||
1967 nla_put(vendor_event,
1968 QCA_WLAN_VENDOR_ATTR_DCC_GET_STATS_RESP_STATS_ARRAY,
1969 resp->channel_stats_array_len,
1970 resp->channel_stats_array)) {
1971 hddLog(LOGE, FL("nla put failed"));
1972 kfree_skb(vendor_event);
1973 return;
1974 }
1975
1976 cfg80211_vendor_event(vendor_event, GFP_KERNEL);
1977}
1978
1979/**
1980 * wlan_hdd_dcc_register_for_dcc_stats_event() - Register for dcc stats events
1981 * @hdd_ctx: hdd context
1982 */
1983void wlan_hdd_dcc_register_for_dcc_stats_event(hdd_context_t *hdd_ctx)
1984{
1985 int rc;
1986
1987 rc = sme_register_for_dcc_stats_event(hdd_ctx->hHal, hdd_ctx,
1988 wlan_hdd_dcc_stats_event_callback);
1989 if (rc)
1990 hddLog(LOGE, FL("Register callback failed: %d"), rc);
1991}