1  /*
2   * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
3   * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4   *
5   * Permission to use, copy, modify, and/or distribute this software for any
6   * purpose with or without fee is hereby granted, provided that the above
7   * copyright notice and this permission notice appear in all copies.
8   *
9   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16   */
17  
18  /**
19   * DOC: wlan_hdd_medium_assess.c
20   *
21   * WLAN Host Device Driver medium assess related implementation
22   *
23   */
24  
25  #include "wlan_hdd_medium_assess.h"
26  #include <osif_sync.h>
27  #include <wlan_hdd_main.h>
28  #include <wlan_hdd_object_manager.h>
29  #include <wlan_dcs_ucfg_api.h>
30  #include <wlan_cp_stats_mc_ucfg_api.h>
31  #include <sme_api.h>
32  #include <wma_api.h>
33  #include "wlan_cmn.h"
34  
35  /* define short names for get station info attributes */
36  #define MEDIUM_ASSESS_TYPE \
37  	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE
38  #define PERIOD \
39  	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_PERIOD
40  #define TOTAL_CYCLE_COUNT \
41  	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TOTAL_CYCLE_COUNT
42  #define IDLE_COUNT \
43  	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IDLE_COUNT
44  #define IBSS_RX_COUNT \
45  	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IBSS_RX_COUNT
46  #define OBSS_RX_COUNT \
47  	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_OBSS_RX_COUNT
48  #define MAX_IBSS_RSSI \
49  	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX_IBSS_RSSI
50  #define MIN_IBSS_RSSI \
51  	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MIN_IBSS_RSSI
52  #define CONGESTION_REPORT_ENABLE \
53  	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_ENABLE
54  #define CONGESTION_REPORT_THRESHOLD \
55  	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_THRESHOLD
56  #define CONGESTION_REPORT_INTERVAL \
57  	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_INTERVAL
58  #define CONGESTION_PERCENTAGE \
59  	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_PERCENTAGE
60  #define MEDIUM_ASSESS_MAX \
61  	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX
62  
63  #define DEFAULT_CCA_PERIOD 10	/* seconds */
64  #define CCA_GET_RSSI_INTERVAL 1000	/* 1 second */
65  
66  #define REPORT_DISABLE 0
67  #define REPORT_ENABLE 1
68  
69  #define MAX_CONGESTION_THRESHOLD 100
70  #define CYCLE_THRESHOLD 6400000 /* 40000us * 160M */
71  
72  #define MEDIUM_ASSESS_TIMER_INTERVAL 1000 /* 1000ms */
73  static qdf_mc_timer_t hdd_medium_assess_timer;
74  static bool ssr_flag;
75  static bool timer_enable;
76  struct hdd_medium_assess_info medium_assess_info[WLAN_UMAC_MAX_RP_PID];
77  unsigned long stime;
78  
79  const struct nla_policy
80  hdd_medium_assess_policy[MEDIUM_ASSESS_MAX + 1] = {
81  	[MEDIUM_ASSESS_TYPE] = {.type = NLA_U8},
82  	[PERIOD] = {.type = NLA_U32},
83  	[CONGESTION_REPORT_ENABLE] = {.type = NLA_U8},
84  	[CONGESTION_REPORT_THRESHOLD] = {.type = NLA_U8},
85  	[CONGESTION_REPORT_INTERVAL] = {.type = NLA_U8},
86  };
87  
88  /*
89   * get_cca_report_len() - Calculate length for CCA report
90   * to allocate skb buffer
91   *
92   * Return: skb buffer length
93   */
get_cca_report_len(void)94  static int get_cca_report_len(void)
95  {
96  	uint32_t data_len = NLMSG_HDRLEN;
97  
98  	/* QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE */
99  	data_len += nla_total_size(sizeof(uint8_t));
100  
101  	/* QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TOTAL_CYCLE_COUNT */
102  	data_len += nla_total_size(sizeof(uint32_t));
103  
104  	/* QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IDLE_COUNT */
105  	data_len += nla_total_size(sizeof(uint32_t));
106  
107  	/* QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IBSS_RX_COUNT */
108  	data_len += nla_total_size(sizeof(uint32_t));
109  
110  	/* QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_OBSS_RX_COUNT */
111  	data_len += nla_total_size(sizeof(uint32_t));
112  
113  	/* QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX_IBSS_RSSI */
114  	data_len += nla_total_size(sizeof(uint32_t));
115  
116  	/* QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MIN_IBSS_RSSI */
117  	data_len += nla_total_size(sizeof(uint32_t));
118  
119  	return data_len;
120  }
121  
122  /**
123   * hdd_cca_notification_cb() - cca notification callback function
124   * @vdev_id: vdev id
125   * @stats: dcs im stats
126   * @status: status of cca statistics
127   *
128   * Return: None
129   */
hdd_cca_notification_cb(uint8_t vdev_id,struct wlan_host_dcs_im_user_stats * stats,int status)130  static void hdd_cca_notification_cb(uint8_t vdev_id,
131  				    struct wlan_host_dcs_im_user_stats *stats,
132  				    int status)
133  {
134  	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
135  	struct wlan_hdd_link_info *link_info;
136  	struct sk_buff *event;
137  
138  	if (wlan_hdd_validate_context(hdd_ctx))
139  		return;
140  
141  	link_info = hdd_get_link_info_by_vdev(hdd_ctx, vdev_id);
142  	if (!link_info) {
143  		hdd_err("Failed to find adapter of vdev %d", vdev_id);
144  		return;
145  	}
146  
147  	event = wlan_cfg80211_vendor_event_alloc(
148  				hdd_ctx->wiphy, &link_info->adapter->wdev,
149  				get_cca_report_len(),
150  				QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS_INDEX,
151  				GFP_KERNEL);
152  	if (!event) {
153  		hdd_err("wlan_cfg80211_vendor_event_alloc failed");
154  		return;
155  	}
156  
157  	if (nla_put_u8(event, MEDIUM_ASSESS_TYPE,
158  		       QCA_WLAN_MEDIUM_ASSESS_CCA) ||
159  	    nla_put_u32(event, TOTAL_CYCLE_COUNT, stats->cycle_count) ||
160  	    nla_put_u32(event, IDLE_COUNT,
161  			stats->cycle_count - stats->rxclr_count) ||
162  	    nla_put_u32(event, IBSS_RX_COUNT, stats->my_bss_rx_cycle_count) ||
163  	    nla_put_u32(event, OBSS_RX_COUNT,
164  			stats->rx_frame_count - stats->my_bss_rx_cycle_count) ||
165  	    nla_put_u32(event, MAX_IBSS_RSSI, stats->max_rssi) ||
166  	    nla_put_u32(event, MIN_IBSS_RSSI, stats->min_rssi)) {
167  		hdd_err("nla put failed");
168  		wlan_cfg80211_vendor_free_skb(event);
169  		return;
170  	}
171  
172  	wlan_cfg80211_vendor_event(event, GFP_KERNEL);
173  }
174  
175  /**
176   * hdd_medium_assess_cca() - clear channel assessment
177   * @hdd_ctx: pointer to HDD context
178   * @adapter: pointer to adapter
179   * @tb: list of attributes
180   *
181   * Return: success(0) or reason code for failure
182   */
hdd_medium_assess_cca(struct hdd_context * hdd_ctx,struct hdd_adapter * adapter,struct nlattr ** tb)183  static int hdd_medium_assess_cca(struct hdd_context *hdd_ctx,
184  				 struct hdd_adapter *adapter,
185  				 struct nlattr **tb)
186  {
187  	struct wlan_objmgr_vdev *vdev;
188  	uint32_t cca_period = DEFAULT_CCA_PERIOD;
189  	uint8_t mac_id, dcs_enable;
190  	QDF_STATUS status;
191  	int errno = 0;
192  
193  	vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink, WLAN_DCS_ID);
194  	if (!vdev)
195  		return -EINVAL;
196  
197  	status = policy_mgr_get_mac_id_by_session_id(hdd_ctx->psoc,
198  						     adapter->deflink->vdev_id,
199  						     &mac_id);
200  	if (QDF_IS_STATUS_ERROR(status)) {
201  		hdd_err_rl("Failed to get mac_id");
202  		errno = -EINVAL;
203  		goto out;
204  	}
205  
206  	dcs_enable = ucfg_get_dcs_enable(hdd_ctx->psoc, mac_id);
207  	if (!(dcs_enable & WLAN_HOST_DCS_WLANIM)) {
208  		hdd_err_rl("DCS_WLANIM is not enabled");
209  		errno = -EINVAL;
210  		goto out;
211  	}
212  
213  	if (qdf_atomic_read(&adapter->deflink->session.ap.acs_in_progress)) {
214  		hdd_err_rl("ACS is in progress");
215  		errno = -EBUSY;
216  		goto out;
217  	}
218  
219  	if (tb[PERIOD])
220  		cca_period = nla_get_u32(tb[PERIOD]);
221  	if (cca_period == 0)
222  		cca_period = DEFAULT_CCA_PERIOD;
223  
224  	ucfg_dcs_reset_user_stats(hdd_ctx->psoc, mac_id);
225  	ucfg_dcs_register_user_cb(hdd_ctx->psoc, mac_id,
226  				  adapter->deflink->vdev_id,
227  				  hdd_cca_notification_cb);
228  	/* dcs is already enabled and dcs event is reported every second
229  	 * set the user request counter to collect user stats
230  	 */
231  	ucfg_dcs_set_user_request(hdd_ctx->psoc, mac_id, cca_period);
232  
233  out:
234  	hdd_objmgr_put_vdev_by_user(vdev, WLAN_DCS_ID);
235  	return errno;
236  }
237  
238  /*
239   * get_congestion_report_len() - Calculate length for congestion report
240   * to allocate skb buffer
241   *
242   * Return: skb buffer length
243   */
get_congestion_report_len(void)244  static int get_congestion_report_len(void)
245  {
246  	uint32_t data_len = NLMSG_HDRLEN;
247  
248  	/* QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE */
249  	data_len += nla_total_size(sizeof(uint8_t));
250  
251  	/* QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_PERCENTAGE */
252  	data_len += nla_total_size(sizeof(uint8_t));
253  
254  	return data_len;
255  }
256  
257  /**
258   * hdd_congestion_reset_data() - reset/invalid the previous data
259   * @pdev_id: pdev id
260   *
261   * Return: None
262   */
hdd_congestion_reset_data(uint8_t pdev_id)263  static void hdd_congestion_reset_data(uint8_t pdev_id)
264  {
265  	struct hdd_medium_assess_info *mdata;
266  
267  	mdata = &medium_assess_info[pdev_id];
268  	qdf_mem_zero(mdata->data, sizeof(mdata->data));
269  }
270  
271  /**
272   * hdd_congestion_notification_cb() - congestion notification callback function
273   * @vdev_id: vdev id
274   * @data: medium assess data from firmware
275   * @last: indicate whether the callback from final WMI_STATS_EVENT in a series
276   *
277   * Return: None
278   */
hdd_congestion_notification_cb(uint8_t vdev_id,struct medium_assess_data * data,bool last)279  static void hdd_congestion_notification_cb(uint8_t vdev_id,
280  					   struct medium_assess_data *data,
281  					   bool last)
282  {
283  	struct hdd_medium_assess_info *mdata;
284  	uint8_t i;
285  	int32_t index;
286  
287  	/* the cb should not be delay more than 40 ms or drop it */
288  	if (qdf_system_time_after(jiffies, stime)) {
289  		hdd_debug("medium assess interference data drop");
290  		return;
291  	}
292  
293  	for (i = 0; i < WLAN_UMAC_MAX_RP_PID; i++) {
294  		mdata = &medium_assess_info[i];
295  
296  		index = mdata->index - 1;
297  		if (index < 0)
298  			index = MEDIUM_ASSESS_NUM - 1;
299  
300  		if (data[i].part1_valid && mdata->data[index].part1_valid) {
301  			if (CYCLE_THRESHOLD > (data[i].cycle_count -
302  			    mdata->data[index].cycle_count))
303  				continue;
304  		}
305  
306  		if (data[i].part1_valid) {
307  			mdata->data[mdata->index].part1_valid = true;
308  			mdata->data[mdata->index].cycle_count =
309  						data[i].cycle_count;
310  			mdata->data[mdata->index].rx_clear_count =
311  						data[i].rx_clear_count;
312  			mdata->data[mdata->index].tx_frame_count =
313  						data[i].tx_frame_count;
314  		}
315  
316  		if (data[i].part2_valid) {
317  			mdata->data[mdata->index].part2_valid = true;
318  			mdata->data[mdata->index].my_rx_count =
319  						data[i].my_rx_count;
320  		}
321  
322  		if (last) {
323  			mdata->index++;
324  			if (mdata->index >= MEDIUM_ASSESS_NUM)
325  				mdata->index = 0;
326  			mdata->data[mdata->index].part1_valid = false;
327  			mdata->data[mdata->index].part2_valid = false;
328  		}
329  	}
330  }
331  
332  /**
333   * hdd_congestion_notification_report() - congestion report function
334   * @vdev_id: vdev id
335   * @congestion: congestion percentage
336   *
337   * Return: None
338   */
hdd_congestion_notification_report(uint8_t vdev_id,uint8_t congestion)339  static void hdd_congestion_notification_report(uint8_t vdev_id,
340  					       uint8_t congestion)
341  {
342  	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
343  	struct wlan_hdd_link_info *link_info;
344  	struct sk_buff *event;
345  	enum qca_nl80211_vendor_subcmds_index index =
346  		QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS_INDEX;
347  
348  	if (wlan_hdd_validate_context(hdd_ctx))
349  		return;
350  
351  	link_info = hdd_get_link_info_by_vdev(hdd_ctx, vdev_id);
352  	if (!link_info) {
353  		hdd_err("Failed to find adapter of vdev %d", vdev_id);
354  		return;
355  	}
356  
357  	event = wlan_cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
358  						 &link_info->adapter->wdev,
359  						 get_congestion_report_len(),
360  						 index, GFP_KERNEL);
361  	if (!event) {
362  		hdd_err("wlan_cfg80211_vendor_event_alloc failed");
363  		return;
364  	}
365  
366  	if (nla_put_u8(event, MEDIUM_ASSESS_TYPE,
367  		       QCA_WLAN_MEDIUM_ASSESS_CONGESTION_REPORT) ||
368  	    nla_put_u8(event, CONGESTION_PERCENTAGE, congestion)) {
369  		hdd_err("nla put failed");
370  		wlan_cfg80211_vendor_free_skb(event);
371  		return;
372  	}
373  
374  	wlan_cfg80211_vendor_event(event, GFP_KERNEL);
375  }
376  
hdd_medium_assess_ssr_enable_flag(void)377  void hdd_medium_assess_ssr_enable_flag(void)
378  {
379  	uint8_t i;
380  
381  	ssr_flag = true;
382  	for (i = 0; i < WLAN_UMAC_MAX_RP_PID; i++)
383  		hdd_congestion_reset_data(i);
384  
385  	if (timer_enable)
386  		qdf_mc_timer_destroy(&hdd_medium_assess_timer);
387  }
388  
hdd_medium_assess_stop_timer(uint8_t pdev_id,struct hdd_context * hdd_ctx)389  void hdd_medium_assess_stop_timer(uint8_t pdev_id, struct hdd_context *hdd_ctx)
390  {
391  	struct request_info info = {0};
392  	bool pending = false;
393  	uint8_t i, interval = 0;
394  
395  	if (ssr_flag)
396  		return;
397  
398  	medium_assess_info[pdev_id].config.threshold = MAX_CONGESTION_THRESHOLD;
399  	medium_assess_info[pdev_id].config.interval = 0;
400  	medium_assess_info[pdev_id].index = 0;
401  	medium_assess_info[pdev_id].count = 0;
402  	hdd_congestion_reset_data(pdev_id);
403  
404  	for (i = 0; i < WLAN_UMAC_MAX_RP_PID; i++)
405  		interval += medium_assess_info[i].config.interval;
406  
407  	if (!interval && timer_enable) {
408  		ucfg_mc_cp_stats_reset_pending_req(hdd_ctx->psoc,
409  						   TYPE_CONGESTION_STATS,
410  						   &info, &pending);
411  		qdf_mc_timer_stop(&hdd_medium_assess_timer);
412  		hdd_debug("medium assess atimer stop");
413  	} else {
414  		hdd_debug("medium assess timer already disabled");
415  	}
416  }
417  
418  /**
419   * hdd_congestion_notification_calculation() - medium assess congestion
420   * calculation.
421   * @info: structure hdd_medium_assess_info
422   *
423   * Return: None
424   */
425  static void
hdd_congestion_notification_calculation(struct hdd_medium_assess_info * info)426  hdd_congestion_notification_calculation(struct hdd_medium_assess_info *info)
427  {
428  	struct medium_assess_data *h_data, *t_data;
429  	int32_t h_index, t_index;
430  	uint32_t rx_clear_count_delta, tx_frame_count_delta;
431  	uint32_t cycle_count_delta, my_rx_count_delta;
432  	uint32_t congestion = 0;
433  	uint64_t diff;
434  
435  	h_index = info->index - 1;
436  	if (h_index < 0)
437  		h_index = MEDIUM_ASSESS_NUM - 1;
438  
439  	if (h_index >= info->config.interval)
440  		t_index = h_index - info->config.interval;
441  	else
442  		t_index = MEDIUM_ASSESS_NUM - info->config.interval - h_index;
443  
444  	if (h_index < 0 || h_index >= MEDIUM_ASSESS_NUM ||
445  	    t_index < 0 || t_index >= MEDIUM_ASSESS_NUM) {
446  		hdd_err("medium assess index is not valid.");
447  		return;
448  	}
449  
450  	h_data = &info->data[h_index];
451  	t_data = &info->data[t_index];
452  
453  	if (!(h_data->part1_valid || h_data->part2_valid ||
454  	      t_data->part1_valid || t_data->part2_valid)) {
455  		hdd_err("medium assess data is not valid.");
456  		return;
457  	}
458  
459  	if (h_data->rx_clear_count >= t_data->rx_clear_count) {
460  		rx_clear_count_delta = h_data->rx_clear_count -
461  						t_data->rx_clear_count;
462  	} else {
463  		rx_clear_count_delta = U32_MAX - t_data->rx_clear_count;
464  		rx_clear_count_delta += h_data->rx_clear_count;
465  	}
466  
467  	if (h_data->tx_frame_count >= t_data->tx_frame_count) {
468  		tx_frame_count_delta = h_data->tx_frame_count -
469  						t_data->tx_frame_count;
470  	} else {
471  		tx_frame_count_delta = U32_MAX - t_data->tx_frame_count;
472  		tx_frame_count_delta += h_data->tx_frame_count;
473  	}
474  
475  	if (h_data->my_rx_count >= t_data->my_rx_count) {
476  		my_rx_count_delta = h_data->my_rx_count - t_data->my_rx_count;
477  	} else {
478  		my_rx_count_delta = U32_MAX - t_data->my_rx_count;
479  		my_rx_count_delta += h_data->my_rx_count;
480  	}
481  
482  	if (h_data->cycle_count >= t_data->cycle_count) {
483  		cycle_count_delta = h_data->cycle_count - t_data->cycle_count;
484  	} else {
485  		cycle_count_delta = U32_MAX - t_data->cycle_count;
486  		cycle_count_delta += h_data->cycle_count;
487  	}
488  
489  	if (rx_clear_count_delta > tx_frame_count_delta &&
490  	    rx_clear_count_delta - tx_frame_count_delta > my_rx_count_delta) {
491  		diff = rx_clear_count_delta - tx_frame_count_delta
492  		       - my_rx_count_delta;
493  		if (cycle_count_delta)
494  			congestion = qdf_do_div(diff * 100, cycle_count_delta);
495  
496  		if (congestion > 100)
497  			congestion = 100;
498  	}
499  
500  	hdd_debug("pdev: %d, rx_c %u, tx %u myrx %u cycle %u congestion: %u",
501  		  info->pdev_id, rx_clear_count_delta, tx_frame_count_delta,
502  		  my_rx_count_delta, cycle_count_delta, congestion);
503  	if (congestion >= info->config.threshold)
504  		hdd_congestion_notification_report(info->vdev_id, congestion);
505  }
506  
507  /**
508   * hdd_congestion_notification_report_multi() - medium assess report
509   * multi interface.
510   * @pdev_id: pdev id
511   *
512   * Return: None
513   */
hdd_congestion_notification_report_multi(uint8_t pdev_id)514  static void hdd_congestion_notification_report_multi(uint8_t pdev_id)
515  {
516  	struct hdd_medium_assess_info *info;
517  
518  	info = &medium_assess_info[pdev_id];
519  	info->count++;
520  	if (info->count % info->config.interval == 0)
521  		hdd_congestion_notification_calculation(info);
522  }
523  
524  /**
525   * hdd_medium_assess_expire_handler() - timer callback
526   * @arg: argument
527   *
528   * Return: None
529   */
hdd_medium_assess_expire_handler(void * arg)530  static void hdd_medium_assess_expire_handler(void *arg)
531  {
532  	struct wlan_objmgr_vdev *vdev;
533  	struct request_info info = {0};
534  	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
535  	struct wlan_hdd_link_info *link_info;
536  	uint8_t vdev_id = INVALID_VDEV_ID, pdev_id;
537  	uint8_t index, i;
538  
539  	if (wlan_hdd_validate_context(hdd_ctx))
540  		return;
541  
542  	for (i = 0; i < WLAN_UMAC_MAX_RP_PID; i++)
543  		if (medium_assess_info[i].config.interval != 0) {
544  			vdev_id = medium_assess_info[i].vdev_id;
545  			pdev_id = medium_assess_info[i].pdev_id;
546  			hdd_congestion_notification_report_multi(pdev_id);
547  
548  			/* ensure events are reveived at the 'same' time */
549  			index = medium_assess_info[i].index;
550  			medium_assess_info[i].data[index].part1_valid = false;
551  			medium_assess_info[i].data[index].part2_valid = false;
552  		}
553  
554  	if (vdev_id == INVALID_VDEV_ID)
555  		return;
556  
557  	link_info = hdd_get_link_info_by_vdev(hdd_ctx, vdev_id);
558  	if (!link_info) {
559  		hdd_err("Failed to find adapter of vdev %d", vdev_id);
560  		return;
561  	}
562  
563  	vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_CP_STATS_ID);
564  	if (!vdev)
565  		return;
566  
567  	info.vdev_id = vdev_id;
568  	info.pdev_id = WMI_HOST_PDEV_ID_SOC;
569  	info.u.congestion_notif_cb = hdd_congestion_notification_cb;
570  	stime = jiffies + msecs_to_jiffies(40);
571  	ucfg_mc_cp_stats_send_stats_request(vdev,
572  					    TYPE_CONGESTION_STATS,
573  					    &info);
574  	hdd_objmgr_put_vdev_by_user(vdev, WLAN_CP_STATS_ID);
575  	qdf_mc_timer_start(&hdd_medium_assess_timer,
576  			   MEDIUM_ASSESS_TIMER_INTERVAL);
577  }
578  
579  /**
580   * hdd_medium_assess_congestion_report() - congestion report
581   * @hdd_ctx: pointer to HDD context
582   * @adapter: pointer to adapter
583   * @tb: list of attributes
584   *
585   * Return: success(0) or reason code for failure
586   */
hdd_medium_assess_congestion_report(struct hdd_context * hdd_ctx,struct hdd_adapter * adapter,struct nlattr ** tb)587  static int hdd_medium_assess_congestion_report(struct hdd_context *hdd_ctx,
588  					       struct hdd_adapter *adapter,
589  					       struct nlattr **tb)
590  {
591  	QDF_STATUS status;
592  	struct wlan_objmgr_vdev *vdev;
593  	uint8_t enable, threshold, interval = 0;
594  	uint8_t pdev_id, vdev_id;
595  	int errno = 0;
596  
597  	if (!tb[CONGESTION_REPORT_ENABLE]) {
598  		hdd_err_rl("Congestion report enable is not present");
599  		return -EINVAL;
600  	}
601  
602  	vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink, WLAN_CP_STATS_ID);
603  	if (!vdev)
604  		return -EINVAL;
605  
606  	vdev_id = adapter->deflink->vdev_id;
607  	status = policy_mgr_get_mac_id_by_session_id(hdd_ctx->psoc, vdev_id,
608  						     &pdev_id);
609  	if (QDF_IS_STATUS_ERROR(status)) {
610  		hdd_err("get mac id failed");
611  		goto out;
612  	}
613  
614  	medium_assess_info[pdev_id].vdev_id = vdev_id;
615  	medium_assess_info[pdev_id].pdev_id = pdev_id;
616  
617  	enable = nla_get_u8(tb[CONGESTION_REPORT_ENABLE]);
618  	switch (enable) {
619  	case REPORT_DISABLE:
620  		hdd_debug("medium assess disable: pdev_id %d, vdev_id: %d",
621  			  pdev_id, vdev_id);
622  		hdd_medium_assess_stop_timer(pdev_id, hdd_ctx);
623  		if (timer_enable &&
624  		    (qdf_mc_timer_get_current_state(&hdd_medium_assess_timer) ==
625  		     QDF_TIMER_STATE_STOPPED)) {
626  			qdf_mc_timer_destroy(&hdd_medium_assess_timer);
627  			timer_enable = false;
628  		}
629  		break;
630  	case REPORT_ENABLE:
631  		if (!tb[CONGESTION_REPORT_THRESHOLD]) {
632  			hdd_err_rl("Congestion threshold is not present");
633  			errno = -EINVAL;
634  			goto out;
635  		}
636  		threshold = nla_get_u8(tb[CONGESTION_REPORT_THRESHOLD]);
637  		if (threshold > MAX_CONGESTION_THRESHOLD) {
638  			hdd_err_rl("Invalid threshold %d", threshold);
639  			errno = -EINVAL;
640  			goto out;
641  		}
642  		if (tb[CONGESTION_REPORT_INTERVAL]) {
643  			interval = nla_get_u8(tb[CONGESTION_REPORT_INTERVAL]);
644  			if (interval >= MEDIUM_ASSESS_NUM)
645  				interval = MEDIUM_ASSESS_NUM - 1;
646  		} else {
647  			interval = 1;
648  		}
649  
650  		medium_assess_info[pdev_id].config.threshold = threshold;
651  		medium_assess_info[pdev_id].config.interval = interval;
652  		medium_assess_info[pdev_id].index = 0;
653  		medium_assess_info[pdev_id].count = 0;
654  		hdd_congestion_reset_data(pdev_id);
655  		hdd_debug("medium assess enable: pdev_id %d, vdev_id: %d",
656  			  pdev_id, vdev_id);
657  
658  		if (!timer_enable) {
659  			status =
660  			    qdf_mc_timer_init(&hdd_medium_assess_timer,
661  					      QDF_TIMER_TYPE_SW,
662  					      hdd_medium_assess_expire_handler,
663  					      NULL);
664  			if (QDF_IS_STATUS_ERROR(status)) {
665  				hdd_debug("medium assess init timer failed");
666  				errno = -EINVAL;
667  				goto out;
668  			}
669  			timer_enable = true;
670  		}
671  
672  		if (qdf_mc_timer_get_current_state(&hdd_medium_assess_timer) !=
673  		    QDF_TIMER_STATE_RUNNING) {
674  			hdd_debug("medium assess atimer start");
675  			qdf_mc_timer_start(&hdd_medium_assess_timer,
676  					   MEDIUM_ASSESS_TIMER_INTERVAL);
677  		}
678  		break;
679  	default:
680  		hdd_err_rl("Invalid enable: %d", enable);
681  		errno = -EINVAL;
682  		break;
683  	}
684  
685  out:
686  	hdd_objmgr_put_vdev_by_user(vdev, WLAN_CP_STATS_ID);
687  	return errno;
688  }
689  
690  /**
691   * __hdd_cfg80211_medium_assess() - medium assess
692   * @wiphy: pointer to wireless phy
693   * @wdev: wireless device
694   * @data: data
695   * @data_len: data length
696   *
697   * Return: success(0) or reason code for failure
698   */
__hdd_cfg80211_medium_assess(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)699  static int __hdd_cfg80211_medium_assess(struct wiphy *wiphy,
700  					struct wireless_dev *wdev,
701  					const void *data,
702  					int data_len)
703  {
704  	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
705  	struct net_device *dev = wdev->netdev;
706  	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
707  	enum QDF_GLOBAL_MODE driver_mode = hdd_get_conparam();
708  	struct nlattr *tb[MEDIUM_ASSESS_MAX + 1];
709  	uint8_t type;
710  	int errno;
711  
712  	hdd_enter_dev(dev);
713  
714  	if (driver_mode == QDF_GLOBAL_FTM_MODE ||
715  	    driver_mode == QDF_GLOBAL_MONITOR_MODE) {
716  		hdd_err_rl("Command not allowed in FTM / Monitor mode");
717  		return -EPERM;
718  	}
719  
720  	errno = wlan_hdd_validate_context(hdd_ctx);
721  	if (errno)
722  		return errno;
723  
724  	errno = wlan_cfg80211_nla_parse(tb, MEDIUM_ASSESS_MAX, data, data_len,
725  					hdd_medium_assess_policy);
726  	if (errno) {
727  		hdd_err_rl("Invalid ATTR");
728  		return errno;
729  	}
730  
731  	if (!tb[MEDIUM_ASSESS_TYPE]) {
732  		hdd_err_rl("Medium assess type is not present");
733  		return -EINVAL;
734  	}
735  	type = nla_get_u8(tb[MEDIUM_ASSESS_TYPE]);
736  
737  	switch (type) {
738  	case QCA_WLAN_MEDIUM_ASSESS_CCA:
739  		errno = hdd_medium_assess_cca(hdd_ctx, adapter, tb);
740  		break;
741  	case QCA_WLAN_MEDIUM_ASSESS_CONGESTION_REPORT:
742  		errno = hdd_medium_assess_congestion_report(hdd_ctx, adapter,
743  							     tb);
744  		break;
745  	default:
746  		hdd_err_rl("Invalid medium assess type: %d", type);
747  		return -EINVAL;
748  	}
749  
750  	hdd_exit();
751  
752  	return errno;
753  }
754  
hdd_cfg80211_medium_assess(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)755  int hdd_cfg80211_medium_assess(struct wiphy *wiphy,
756  			       struct wireless_dev *wdev,
757  			       const void *data,
758  			       int data_len)
759  {
760  	struct osif_vdev_sync *vdev_sync;
761  	int errno;
762  
763  	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
764  	if (errno)
765  		return errno;
766  
767  	errno = __hdd_cfg80211_medium_assess(wiphy, wdev, data, data_len);
768  
769  	osif_vdev_sync_op_stop(vdev_sync);
770  
771  	return errno;
772  }
773  
hdd_medium_assess_ssr_reinit(void)774  void hdd_medium_assess_ssr_reinit(void)
775  {
776  	QDF_STATUS status;
777  
778  	if (timer_enable && ssr_flag) {
779  		hdd_debug("medium assess init timer in ssr");
780  		status = qdf_mc_timer_init(&hdd_medium_assess_timer,
781  					   QDF_TIMER_TYPE_SW,
782  					   hdd_medium_assess_expire_handler,
783  					   NULL);
784  		if (QDF_IS_STATUS_ERROR(status)) {
785  			hdd_debug("medium assess init timer failed in ssr");
786  			return;
787  		}
788  
789  		ssr_flag = false;
790  		qdf_mc_timer_start(&hdd_medium_assess_timer,
791  				   MEDIUM_ASSESS_TIMER_INTERVAL);
792  	}
793  }
794