Просмотр исходного кода

qcacmn: Add AFC OSIF common changes

Add AFC common North Bound vendor command handlers and common AFC
PSOC/PDEV register functions.

Change-Id: Id87c3c4878362a48546d39e10230d60c2c573154
CRs-Fixed: 3375632
Will Huang 2 лет назад
Родитель
Сommit
ebc483b28a

+ 75 - 0
os_if/linux/afc/inc/wlan_cfg80211_afc.h

@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_cfg80211_afc.h
+ *
+ * This Header file provide declaration for cfg80211 vendor command handler API
+ * and structures.
+ */
+
+#ifndef __WLAN_CFG80211_AFC_H__
+#define __WLAN_CFG80211_AFC_H__
+
+#include <wlan_objmgr_cmn.h>
+#include <qdf_types.h>
+#include <net/cfg80211.h>
+#include <qca_vendor.h>
+#include <wlan_reg_ucfg_api.h>
+
+#ifdef CONFIG_AFC_SUPPORT
+
+extern const struct nla_policy
+wlan_cfg80211_afc_response_policy[QCA_WLAN_VENDOR_ATTR_AFC_RESP_MAX + 1];
+
+/**
+ * wlan_cfg80211_vendor_afc_response() - To handle vendor AFC response command
+ * @psoc: Pointer to PSOC object
+ * @pdev: Pointer to PDEV object
+ * @data: Pointer to the data received via the vendor interface
+ * @data_len: Length of the data to be passed
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int wlan_cfg80211_vendor_afc_response(struct wlan_objmgr_psoc *psoc,
+				      struct wlan_objmgr_pdev *pdev,
+				      const void *data,
+				      int data_len);
+
+/**
+ * wlan_cfg80211_afc_send_request() - To handle AFC request from
+ * regulatory AFC component.
+ * @pdev: Pointer to PDEV object
+ * @afc_req: Pointer to AFC request
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int wlan_cfg80211_afc_send_request(struct wlan_objmgr_pdev *pdev,
+				   struct wlan_afc_host_request *afc_req);
+
+/**
+ * wlan_cfg80211_afc_send_update_complete() - To handle AFC update complete
+ * event from regulatory AFC component.
+ * @pdev: Pointer to PDEV object
+ * @afc_evt: Pointer to AFC power update complete event
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int
+wlan_cfg80211_afc_send_update_complete(struct wlan_objmgr_pdev *pdev,
+				       struct reg_fw_afc_power_event *afc_evt);
+#endif
+#endif

+ 1077 - 0
os_if/linux/afc/src/wlan_cfg80211_afc.c

@@ -0,0 +1,1077 @@
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_cfg80211_afc.c
+ *
+ * Defines AFC cfg80211 vendor command interface handles
+ */
+
+#include <wlan_cfg80211.h>
+#include <wlan_cfg80211_afc.h>
+#include <wlan_reg_ucfg_api.h>
+#include <wlan_objmgr_pdev_obj.h>
+#include <wlan_osif_priv.h>
+#include <wlan_hdd_object_manager.h>
+#include <wlan_afc_ucfg_api.h>
+
+/* Maximum AFC data length can pass to target limited by platform driver */
+#define IF_AFC_RESPONSE_MAX_LEN  4096
+
+/*
+ * JSON format AFC response data maximum length, limited by interface,
+ * struct wlan_afc_host_resp is AFC response format pass to target.
+ */
+#define QCA_NL80211_AFC_RESP_DATA_MAX_SIZE  \
+	(IF_AFC_RESPONSE_MAX_LEN - sizeof(struct wlan_afc_host_resp))
+
+/**
+ * struct frange_obj - Structure of channel frequency range with psd
+ * @freq_start: Frequency range start in MHz
+ * @freq_end: Frequency range end in MHz
+ * @psd: The PSD power info (dBm/MHz) multiplied by a factor of 100 to
+ * preserve granularity up to 2 decimal places
+ */
+struct frange_obj {
+	qdf_freq_t freq_start;
+	qdf_freq_t freq_end;
+	uint32_t psd;
+};
+
+/**
+ * struct channel_eirp - Structure of channel with eirp
+ * @channel_cfi: Channel center frequency index
+ * @eirp: The EIRP power info (dBm) multiplied by a factor of 100 to
+ * preserve granularity up to 2 decimal places
+ */
+struct channel_eirp {
+	uint8_t channel_cfi;
+	uint32_t eirp;
+};
+
+/**
+ * struct opclass_eirp_obj - Structure of operation class eirp object
+ * @opclass: Operation class number
+ * @num_channel: Number of channels belongs to this opclass
+ * @chan_eirp: Channel eirp structure list
+ */
+struct opclass_eirp_obj {
+	uint8_t opclass;
+	uint8_t num_channel;
+	struct channel_eirp chan_eirp[REG_MAX_CHANNELS_PER_OPERATING_CLASS];
+};
+
+/**
+ * struct afc_resp_extracted - Structure of AFC response extracted from
+ * AFC vendor response
+ * @json_data: Pointer to JSON data buffer
+ * @json_len: JSON data length
+ * @time_to_live: Time to live of AFC response in seconds
+ * @request_id: Request ID
+ * @avail_exp_date: Expire date
+ * Date format: bits 7:0   - DD (Day 1-31)
+ *              bits 15:8  - MM (Month 1-12)
+ *              bits 31:16 - YYYY (Year)
+ * @avail_exp_time: Expire time
+ * Time format: bits 7:0   - SS (Seconds 0-59)
+ *              bits 15:8  - MM (Minutes 0-59)
+ *              bits 23:16 - HH (Hours 0-23)
+ *              bits 31:24 - Reserved
+ * @afc_serv_resp_code: AFC server respond code
+ * -1: General Failure.
+ * 0: Success.
+ * 100 - 199: General errors related to protocol.
+ * 300 - 399: Error events specific to message exchange
+ *            for the Available Spectrum Inquiry.
+ * @num_frange_obj: Number of frequency range objects
+ * @frange: Array of frequency range object
+ * @num_opclass: Number of operation class channel eirp objects
+ * @op_obj: Array of operation class channel eirp objects
+ */
+struct afc_resp_extracted {
+	uint8_t *json_data;
+	uint32_t json_len;
+	uint32_t time_to_live;
+	uint32_t request_id;
+	uint32_t avail_exp_date;
+	uint32_t avail_exp_time;
+	int32_t  afc_serv_resp_code;
+	uint32_t num_frange_obj;
+	struct frange_obj frange[NUM_6GHZ_CHANNELS];
+	uint32_t num_opclass;
+	struct opclass_eirp_obj op_obj[REG_MAX_SUPP_OPER_CLASSES];
+};
+
+const struct nla_policy
+wlan_cfg80211_afc_response_policy[QCA_WLAN_VENDOR_ATTR_AFC_RESP_MAX + 1] = {
+	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_DATA] = { .type = NLA_STRING,
+				.len = QCA_NL80211_AFC_RESP_DATA_MAX_SIZE },
+	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_TIME_TO_LIVE] = { .type = NLA_U32 },
+	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID] = { .type = NLA_U32 },
+	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_DATE] = { .type = NLA_U32 },
+	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_TIME] = { .type = NLA_U32 },
+	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFC_SERVER_RESP_CODE] = {
+							.type = NLA_S32 },
+	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO] = { .type = NLA_NESTED },
+	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO] = {
+							.type = NLA_NESTED },
+};
+
+#define nla_nest_end_checked(skb, start) do {		\
+	if ((skb) && (start))				\
+		nla_nest_end(skb, start);		\
+} while (0)
+
+/**
+ * afc_expiry_event_update_or_get_len() - Function to fill vendor event buffer
+ * with info extracted from AFC request, or get required vendor buffer length.
+ * @vendor_event: Pointer to vendor event SK buffer structure
+ * @afc_req: Pointer to AFC request from regulatory component
+ *
+ * If vendor_event is NULL, to get vendor buffer length, otherwise
+ * to fill vendor event buffer with info
+ *
+ * Return: If get vendor buffer length, return positive value as length,
+ * If fill vendor event  0 if success, otherwise negative error code
+ */
+static int
+afc_expiry_event_update_or_get_len(struct sk_buff *vendor_event,
+				   struct wlan_afc_host_request *afc_req)
+{
+	struct nlattr *nla_attr;
+	struct nlattr *freq_info;
+	struct nlattr *opclass_info = NULL;
+	struct nlattr *chan_list = NULL;
+	struct nlattr *chan_info = NULL;
+	int i, j, len = NLMSG_HDRLEN;
+	struct wlan_afc_opclass_obj *afc_opclass_obj;
+
+	if (vendor_event &&
+	    nla_put_u8(vendor_event,
+		       QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE,
+		       QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY)) {
+		osif_err("QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY put fail");
+		goto fail;
+	} else {
+		len += nla_total_size(sizeof(u8));
+	}
+
+	if (vendor_event &&
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID,
+			afc_req->req_id)) {
+		osif_err("QCA_WLAN_VENDOR_ATTR_AFC_REQ_ID put fail");
+		goto fail;
+	} else {
+		len += nla_total_size(sizeof(u32));
+	}
+
+	if (vendor_event &&
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFC_WFA_VERSION,
+			(afc_req->version_major << 16) |
+			afc_req->version_minor)) {
+		osif_err("AFC EVENT WFA version put fail");
+		goto fail;
+	} else {
+		len += nla_total_size(sizeof(u32));
+	}
+
+	if (vendor_event &&
+	    nla_put_u16(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_MIN_DES_POWER,
+			afc_req->min_des_power)) {
+		osif_err("QCA_WLAN_VENDOR_ATTR_AFC_REQ_MIN_DES_PWR put fail");
+		goto fail;
+	} else {
+		len += nla_total_size(sizeof(u16));
+	}
+
+	if (vendor_event &&
+	    nla_put_u8(vendor_event,
+		       QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AP_DEPLOYMENT,
+		       afc_req->afc_location->deployment_type)) {
+		osif_err("AFC EVENT AP deployment put fail");
+		goto fail;
+	} else {
+		len += nla_total_size(sizeof(u8));
+	}
+
+	if (vendor_event) {
+		/* Update the frequency range list from the Expiry event */
+		nla_attr = nla_nest_start(vendor_event,
+					  QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST);
+		if (!nla_attr) {
+			osif_err("AFC FREQ RANGE LIST start put fail");
+			goto fail;
+		}
+	} else {
+		len += nla_total_size(0);
+	}
+
+	for (i = 0; i < afc_req->freq_lst->num_ranges; i++) {
+		if (vendor_event) {
+			freq_info = nla_nest_start(vendor_event, i);
+			if (!freq_info) {
+				osif_err("Fail to put freq list nest %d", i);
+				goto fail;
+			}
+		} else {
+			len += nla_total_size(0);
+		}
+
+		if (vendor_event &&
+		    (nla_put_u32(vendor_event,
+				 QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START,
+				 afc_req->freq_lst->range_objs[i].lowfreq) ||
+		    nla_put_u32(vendor_event,
+				QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END,
+				afc_req->freq_lst->range_objs[i].highfreq))) {
+			osif_err("AFC REQ FREQ RANGE LIST put fail, num %d",
+				 afc_req->freq_lst->num_ranges);
+			goto fail;
+		} else {
+			len += nla_total_size(sizeof(u32)) * 2;
+		}
+		nla_nest_end_checked(vendor_event, freq_info);
+	}
+	nla_nest_end_checked(vendor_event, nla_attr);
+
+	if (vendor_event) {
+		/* Update the Operating class and channel list */
+		nla_attr = nla_nest_start(vendor_event,
+					  QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_LIST);
+		if (!nla_attr) {
+			osif_err("AFC OPCLASS CHAN LIST start put fail");
+			goto fail;
+		}
+	} else {
+		len += nla_total_size(0);
+	}
+
+	for (i = 0; i < afc_req->opclass_obj_lst->num_opclass_objs; i++) {
+		if (vendor_event) {
+			opclass_info = nla_nest_start(vendor_event, i);
+			if (!opclass_info) {
+				osif_err("Fail to put opclass nest %d", i);
+				goto fail;
+			}
+		} else {
+			len += nla_total_size(0);
+		}
+
+		afc_opclass_obj = &afc_req->opclass_obj_lst->opclass_objs[i];
+
+		if (vendor_event &&
+		    nla_put_u8(vendor_event,
+			       QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS,
+			       afc_opclass_obj->opclass)) {
+			osif_err("AFC OPCLASS INFO OPCLASS put fail, num %d",
+				 afc_req->opclass_obj_lst->num_opclass_objs);
+			goto fail;
+		} else {
+			len += nla_total_size(sizeof(u8));
+		}
+
+		if (vendor_event) {
+			chan_list = nla_nest_start(vendor_event,
+						   QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST);
+			if (!chan_list) {
+				osif_err("AFC OPCLASS INFO CHAN LIST start put fail");
+				goto fail;
+			}
+		} else {
+			len += nla_total_size(0);
+		}
+
+		for (j = 0; j < afc_opclass_obj->opclass_num_cfis; j++) {
+			if (vendor_event) {
+				chan_info = nla_nest_start(vendor_event, j);
+				if (!chan_info) {
+					osif_err("Fail to put opclass cfis nest %d", j);
+					goto fail;
+				}
+			} else {
+				len += nla_total_size(0);
+			}
+
+			if (vendor_event &&
+			    nla_put_u8(vendor_event,
+				       QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM,
+				       afc_opclass_obj->cfis[j])) {
+				osif_err("AFC EIRP INFO CHAN NUM put fail, num %d",
+					 afc_opclass_obj->opclass_num_cfis);
+				goto fail;
+			} else {
+				len += nla_total_size(sizeof(u8));
+			}
+			nla_nest_end_checked(vendor_event, chan_info);
+		}
+		nla_nest_end_checked(vendor_event, chan_list);
+		nla_nest_end_checked(vendor_event, opclass_info);
+	}
+	nla_nest_end_checked(vendor_event, nla_attr);
+
+	return vendor_event ? 0 : len;
+
+fail:
+	return -EINVAL;
+}
+
+/**
+ * afc_power_event_update_or_get_len() - Function to fill vendor event buffer
+ * with AFC power update event or get required vendor buffer length
+ * @vendor_event: Pointer to vendor event SK buffer
+ * @pwr_evt: Pointer to AFC power event
+ *
+ * If vendor_event is NULL, to get vendor buffer length, otherwise
+ * to fill vendor event buffer with info
+ *
+ * Return: If get vendor buffer length, return positive value as length,
+ * If fill vendor event, 0 if success, otherwise negative error code
+ */
+static int
+afc_power_event_update_or_get_len(struct sk_buff *vendor_event,
+				  struct reg_fw_afc_power_event *pwr_evt)
+{
+	struct afc_chan_obj *pow_evt_chan_info = NULL;
+	struct chan_eirp_obj *pow_evt_eirp_info = NULL;
+	struct nlattr *nla_attr;
+	struct nlattr *freq_info;
+	struct nlattr *opclass_info;
+	struct nlattr *chan_list;
+	struct nlattr *chan_info = NULL;
+	int i, j, len = NLMSG_HDRLEN;
+
+	if (vendor_event &&
+	    nla_put_u8(vendor_event,
+		       QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE,
+		       QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE)) {
+		osif_err("AFC power update complete event type put fail");
+		goto fail;
+	} else {
+		len += nla_total_size(sizeof(u8));
+	}
+
+	if (vendor_event &&
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID,
+			pwr_evt->resp_id)) {
+		osif_err("QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID put fail");
+		goto fail;
+	} else {
+		len += nla_total_size(sizeof(u32));
+	}
+
+	if (vendor_event &&
+	    nla_put_u8(vendor_event,
+		       QCA_WLAN_VENDOR_ATTR_AFC_EVENT_STATUS_CODE,
+		       pwr_evt->fw_status_code)) {
+		osif_err("AFC EVENT STATUS CODE put fail");
+		goto fail;
+	} else {
+		len += nla_total_size(sizeof(u8));
+	}
+
+	if (vendor_event &&
+	    nla_put_s32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_SERVER_RESP_CODE,
+			pwr_evt->serv_resp_code)) {
+		osif_err("AFC EVENT SERVER RESP CODE put fail");
+		goto fail;
+	} else {
+		len += nla_total_size(sizeof(s32));
+	}
+
+	if (vendor_event &&
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_DATE,
+			pwr_evt->avail_exp_time_d)) {
+		osif_err("AFC EVENT EXPIRE DATE put fail");
+		goto fail;
+	} else {
+		len += nla_total_size(sizeof(u32));
+	}
+
+	if (vendor_event &&
+	    nla_put_u32(vendor_event,
+			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_TIME,
+			pwr_evt->avail_exp_time_t)) {
+		osif_err("AFC EVENT EXPIRE TIME put fail");
+		goto fail;
+	} else {
+		len += nla_total_size(sizeof(u32));
+	}
+
+	if (vendor_event) {
+		/* Update the Frequency and corresponding PSD info */
+		nla_attr = nla_nest_start(vendor_event,
+					  QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST);
+		if (!nla_attr)
+			goto fail;
+	} else {
+		len += nla_total_size(0);
+	}
+
+	for (i = 0; i < pwr_evt->num_freq_objs; i++) {
+		if (vendor_event) {
+			freq_info = nla_nest_start(vendor_event, i);
+			if (!freq_info)
+				goto fail;
+		} else {
+			len += nla_total_size(0);
+		}
+
+		if (vendor_event &&
+		    (nla_put_u32(vendor_event,
+				 QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START,
+				 pwr_evt->afc_freq_info[i].low_freq) ||
+		    nla_put_u32(vendor_event,
+				QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END,
+				pwr_evt->afc_freq_info[i].high_freq) ||
+		    nla_put_u32(vendor_event,
+				QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_PSD,
+				pwr_evt->afc_freq_info[i].max_psd))) {
+			osif_err("AFC FREQUENCY PSD INFO put failed, num %d",
+				 pwr_evt->num_freq_objs);
+			goto fail;
+		} else {
+			len += nla_total_size(sizeof(u32)) * 3;
+		}
+		nla_nest_end_checked(vendor_event, freq_info);
+	}
+	nla_nest_end_checked(vendor_event, nla_attr);
+
+	if (vendor_event) {
+		/* Update the Operating class, channel list and EIRP info */
+		nla_attr = nla_nest_start(vendor_event,
+					  QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_LIST);
+		if (!nla_attr)
+			goto fail;
+	} else {
+		len += nla_total_size(0);
+	}
+
+	pow_evt_chan_info = pwr_evt->afc_chan_info;
+
+	for (i = 0; i < pwr_evt->num_chan_objs; i++) {
+		if (vendor_event) {
+			opclass_info = nla_nest_start(vendor_event, i);
+			if (!opclass_info)
+				goto fail;
+		} else {
+			len += nla_total_size(0);
+		}
+
+		if (vendor_event &&
+		    nla_put_u8(vendor_event,
+			       QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS,
+			       pow_evt_chan_info[i].global_opclass)) {
+			osif_err("AFC OPCLASS INFO put fail, num %d",
+				 pwr_evt->num_chan_objs);
+			goto fail;
+		} else {
+			len += nla_total_size(sizeof(u8));
+		}
+
+		if (vendor_event) {
+			chan_list = nla_nest_start(vendor_event,
+						   QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST);
+			if (!chan_list)
+				goto fail;
+		} else {
+			len += nla_total_size(0);
+		}
+
+		pow_evt_eirp_info = pow_evt_chan_info[i].chan_eirp_info;
+
+		for (j = 0; j < pow_evt_chan_info[i].num_chans; j++) {
+			if (vendor_event) {
+				chan_info = nla_nest_start(vendor_event, j);
+				if (!chan_info)
+					goto fail;
+			} else {
+				len += nla_total_size(0);
+			}
+
+			if (vendor_event &&
+			    (nla_put_u8(vendor_event,
+					QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM,
+					pow_evt_eirp_info[j].cfi) ||
+			    nla_put_u32(vendor_event,
+					QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP,
+					pow_evt_eirp_info[j].eirp_power))) {
+				osif_err("AFC CHAN EIRP_INFO put fail, num %d",
+					 pow_evt_chan_info[i].num_chans);
+				goto fail;
+			} else {
+				len += nla_total_size(sizeof(u8));
+				len += nla_total_size(sizeof(u32));
+			}
+			nla_nest_end_checked(vendor_event, chan_info);
+		}
+		nla_nest_end_checked(vendor_event, chan_list);
+		nla_nest_end_checked(vendor_event, opclass_info);
+	}
+
+	nla_nest_end_checked(vendor_event, nla_attr);
+
+	return vendor_event ? 0 : len;
+
+fail:
+	return -EINVAL;
+}
+
+int wlan_cfg80211_afc_send_request(struct wlan_objmgr_pdev *pdev,
+				   struct wlan_afc_host_request *afc_req)
+{
+	struct sk_buff *vendor_event;
+	struct pdev_osif_priv *osif_priv;
+	int ret, vendor_buffer_len;
+
+	osif_priv = wlan_pdev_get_ospriv(pdev);
+	if (!osif_priv) {
+		osif_err("PDEV OS private structure is NULL");
+		return -EINVAL;
+	}
+
+	if (!afc_req) {
+		osif_err("afc host request is NULL");
+		return -EINVAL;
+	}
+
+	vendor_buffer_len = afc_expiry_event_update_or_get_len(NULL, afc_req);
+
+	vendor_event = wlan_cfg80211_vendor_event_alloc(osif_priv->wiphy,
+							NULL,
+							vendor_buffer_len,
+							QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT_INDEX,
+							GFP_ATOMIC);
+	if (!vendor_event) {
+		osif_err("cfg80211 vendor event alloc failed");
+		return -ENOMEM;
+	}
+
+	ret = afc_expiry_event_update_or_get_len(vendor_event, afc_req);
+
+	if (ret) {
+		osif_err("Failed to update AFC request vendor event");
+		goto fail;
+	}
+
+	osif_debug("Sending AFC expiry event to user application");
+	wlan_cfg80211_vendor_event(vendor_event, GFP_ATOMIC);
+
+	return 0;
+
+fail:
+	wlan_cfg80211_vendor_free_skb(vendor_event);
+	return -EINVAL;
+}
+
+int
+wlan_cfg80211_afc_send_update_complete(struct wlan_objmgr_pdev *pdev,
+				       struct reg_fw_afc_power_event *afc_evt)
+{
+	struct sk_buff *vendor_event;
+	struct pdev_osif_priv *osif_priv;
+	int vendor_buffer_len;
+
+	osif_priv = wlan_pdev_get_ospriv(pdev);
+	if (!osif_priv) {
+		osif_err("PDEV OS private structure is NULL");
+		return -EINVAL;
+	}
+
+	if (!afc_evt) {
+		osif_err("afc power event is NULL");
+		return -EINVAL;
+	}
+
+	vendor_buffer_len = afc_power_event_update_or_get_len(NULL, afc_evt);
+
+	vendor_event = wlan_cfg80211_vendor_event_alloc(osif_priv->wiphy,
+							NULL,
+							vendor_buffer_len,
+							QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT_INDEX,
+							GFP_ATOMIC);
+	if (!vendor_event) {
+		osif_err("cfg80211 vendor event alloc failed");
+		return -ENOMEM;
+	}
+
+	if (afc_power_event_update_or_get_len(vendor_event, afc_evt)) {
+		osif_err("Failed to update AFC power vendor event");
+		goto fail;
+	}
+
+	osif_debug("Sending AFC update complete event to user application");
+	wlan_cfg80211_vendor_event(vendor_event, GFP_ATOMIC);
+
+	return 0;
+
+fail:
+	wlan_cfg80211_vendor_free_skb(vendor_event);
+	return -EINVAL;
+}
+
+/**
+ * afc_response_display() - Function to display AFC response information
+ * @rsp: Pointer to AFC response structure which is extracted from vendor
+ * command
+ *
+ * Return: None
+ */
+static void afc_response_display(struct afc_resp_extracted *rsp)
+{
+	int iter, j;
+
+	if (rsp->json_data)
+		return;
+
+	osif_debug("Req ID: %u TTL: %u Date: 0x%x Time: 0x%x Resp code: %u Freq objs: %u Opclass objs: %u",
+		   rsp->request_id,
+		   rsp->time_to_live,
+		   rsp->avail_exp_date,
+		   rsp->avail_exp_time,
+		   rsp->afc_serv_resp_code,
+		   rsp->num_frange_obj,
+		   rsp->num_opclass);
+
+	for (iter = 0; iter < rsp->num_frange_obj; iter++)
+		osif_debug("Freq Info[%d]: start %u end %u PSD %u",
+			   iter,
+			   rsp->frange[iter].freq_start,
+			   rsp->frange[iter].freq_end,
+			   rsp->frange[iter].psd);
+
+	for (iter = 0; iter < rsp->num_opclass; iter++) {
+		osif_debug("Opclass[%d]: %u Num channels: %u",
+			   iter,
+			   rsp->op_obj[iter].opclass,
+			   rsp->op_obj[iter].num_channel);
+
+		for (j = 0; j < rsp->op_obj[iter].num_channel; j++)
+			osif_debug("Channel Info[%d]:CFI: %u EIRP: %u",
+				   j,
+				   rsp->op_obj[iter].chan_eirp[j].channel_cfi,
+				   rsp->op_obj[iter].chan_eirp[j].eirp);
+	}
+}
+
+/**
+ * wlan_parse_afc_rsp_freq_psd() - Function to parse AFC response channel
+ * frequency range PSD information from NL attribute.
+ * @attr: Pointer to NL AFC frequency PSD attributes
+ * @rsp: Pointer to AFC extracted response
+ *
+ * Return: Negative error number if failed, otherwise success
+ */
+static int
+wlan_parse_afc_rsp_freq_psd(struct nlattr *attr, struct afc_resp_extracted *rsp)
+{
+	int ret = -EINVAL;
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_MAX + 1];
+	struct nlattr *cur_attr = NULL, *tb2;
+	uint32_t rem;
+	qdf_size_t i = 0;
+
+	nla_for_each_nested(cur_attr, attr, rem) {
+		if (i >= NUM_6GHZ_CHANNELS) {
+			osif_err("Ignore exceed");
+			break;
+		}
+		if (wlan_cfg80211_nla_parse(tb,
+					    QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_MAX,
+					    nla_data(cur_attr),
+					    nla_len(cur_attr),
+					    NULL)) {
+			osif_err("Invalid ATTR");
+			return ret;
+		}
+
+		tb2 = tb[QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START];
+		if (tb2)
+			rsp->frange[i].freq_start = nla_get_u32(tb2);
+
+		tb2 = tb[QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END];
+		if (tb2)
+			rsp->frange[i].freq_end = nla_get_u32(tb2);
+
+		tb2 = tb[QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_PSD];
+		if (tb2)
+			rsp->frange[i].psd = nla_get_u32(tb2);
+
+		i++;
+	}
+
+	rsp->num_frange_obj = i;
+	return i;
+}
+
+/**
+ * wlan_parse_afc_rsp_opclass_eirp() - Function to parse AFC response operation
+ * class EIRP information from NL attributes.
+ * @attr: Pointer to NL AFC operation class EIRP attributes
+ * @rsp: Pointer to AFC extracted response
+ *
+ * Return: Negative error number if failed, otherwise success
+ */
+static int
+wlan_parse_afc_rsp_opclass_eirp(struct nlattr *attr,
+				struct afc_resp_extracted *rsp)
+{
+	int ret = -EINVAL;
+	struct nlattr *tb1[QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_MAX + 1];
+	struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_MAX + 1];
+	struct nlattr *cur_attr = NULL, *sub_attr = NULL, *tb;
+	uint32_t rem, sub_rem;
+	int i = 0, ch_idx;
+
+	nla_for_each_nested(cur_attr, attr, rem) {
+		if (i >= REG_MAX_SUPP_OPER_CLASSES) {
+			osif_err("Ignore opclass list exceed");
+			break;
+		}
+		if (wlan_cfg80211_nla_parse(tb1,
+					    QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_MAX,
+					    nla_data(cur_attr),
+					    nla_len(cur_attr),
+					    NULL)) {
+			osif_err("Invalid ATTR");
+			return ret;
+		}
+		tb = tb1[QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS];
+		if (tb)
+			rsp->op_obj[i].opclass = nla_get_u8(tb);
+
+		tb = tb1[QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST];
+		if (!tb) {
+			osif_err("No opclass channel list");
+			return ret;
+		}
+
+		ch_idx = 0;
+
+		nla_for_each_nested(sub_attr, tb, sub_rem) {
+			if (ch_idx >= NUM_6GHZ_CHANNELS) {
+				osif_err("Ignore eirp list exceed");
+				break;
+			}
+			if (wlan_cfg80211_nla_parse(tb2,
+						    QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_MAX,
+						    nla_data(sub_attr),
+						    nla_len(sub_attr),
+						    NULL)) {
+				osif_err("Invalid ATTR");
+				return ret;
+			}
+			tb = tb2[QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM];
+			if (tb)
+				rsp->op_obj[i].chan_eirp[ch_idx].channel_cfi =
+						nla_get_u8(tb);
+			tb = tb2[QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP];
+			if (tb)
+				rsp->op_obj[i].chan_eirp[ch_idx].eirp =
+						nla_get_u32(tb);
+			ch_idx++;
+		}
+		rsp->op_obj[i].num_channel = ch_idx;
+
+		i++;
+	}
+	rsp->num_opclass = i;
+	return i;
+}
+
+/**
+ * free_extract_afc_rsp() - Function to free AFC extracted response
+ * @rsp: Pointer to AFC extracted response
+ *
+ * Return: None
+ */
+static inline void free_extract_afc_rsp(struct afc_resp_extracted *rsp)
+{
+	if (!rsp)
+		return;
+
+	qdf_mem_free(rsp->json_data);
+	qdf_mem_free(rsp);
+}
+
+/**
+ * extract_afc_resp() - Function to extract AFC response
+ * @attr: Pointer to NL attribute array
+ *
+ * Return: Pointer to AFC response axtracted
+ */
+static struct afc_resp_extracted *extract_afc_resp(struct nlattr **attr)
+{
+	struct afc_resp_extracted *afc_rsp;
+	struct nlattr *nl;
+
+	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_TIME_TO_LIVE]) {
+		osif_err("ATTR AFC RESP TIME TO LIVE is required");
+		return NULL;
+	}
+
+	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID]) {
+		osif_err("ATTR AFC RESP REQ ID is required");
+		return NULL;
+	}
+
+	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_DATE]) {
+		osif_err("ATTR AFC RESP EXP DATE is required");
+		return NULL;
+	}
+
+	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_TIME]) {
+		osif_err("ATTR AFC RESP EXP TIME is required");
+		return NULL;
+	}
+
+	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFC_SERVER_RESP_CODE]) {
+		osif_err("ATTR AFC RESP SERVER RESP CODE is required");
+		return NULL;
+	}
+
+	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO]) {
+		osif_err("ATTR AFC RESP FREQ PSD INFO is required");
+		return NULL;
+	}
+
+	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO]) {
+		osif_err("ATTR AFC RESP OPCLASS CHAN EIRP INFO is required");
+		return NULL;
+	}
+
+	afc_rsp = qdf_mem_malloc(sizeof(*afc_rsp));
+	if (!afc_rsp)
+		return NULL;
+
+	if (attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_DATA]) {
+		nl = attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_DATA];
+		afc_rsp->json_data = qdf_mem_malloc(nla_len(nl));
+		if (!afc_rsp)
+			goto fail;
+
+		afc_rsp->json_len = nla_len(nl);
+		nla_memcpy(afc_rsp->json_data, nl, afc_rsp->json_len);
+	}
+
+	afc_rsp->time_to_live =
+		nla_get_u32(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_TIME_TO_LIVE]);
+
+	afc_rsp->request_id =
+		nla_get_u32(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID]);
+
+	afc_rsp->avail_exp_date =
+		nla_get_u32(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_DATE]);
+
+	afc_rsp->avail_exp_time =
+		nla_get_u32(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_TIME]);
+
+	afc_rsp->afc_serv_resp_code =
+		nla_get_s32(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFC_SERVER_RESP_CODE]);
+
+	if (wlan_parse_afc_rsp_freq_psd(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO],
+					afc_rsp) <= 0) {
+		osif_err("parse freq psd err");
+		goto fail;
+	}
+
+	if (wlan_parse_afc_rsp_opclass_eirp(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO],
+					    afc_rsp) <= 0) {
+		osif_err("parse opclass eirp err");
+		goto fail;
+	}
+
+	return afc_rsp;
+fail:
+	osif_err("Error parsing the AFC response from application");
+	free_extract_afc_rsp(afc_rsp);
+	return NULL;
+}
+
+/**
+ * is_target_support_json_format() - API to get whether target support JSON
+ * format AFC response.
+ * @psoc: Pointer to PSOC object
+ *
+ * Return: Boolean
+ */
+
+static inline bool is_target_support_json_format(struct wlan_objmgr_psoc *psoc)
+{
+	return false;
+}
+
+/**
+ * fill_host_afc_response_buffer() - Function to fill AFC response buffer which
+ * pass to target.
+ * @psoc: Pointer to PSOC object
+ * @afc_rsp: Pointer to AFC extracted response
+ * @host_afc: Pointer to interface AFC response buffer with target
+ *
+ * Return: Negative error number if failed, otherwise success
+ */
+static int
+fill_host_afc_response_buffer(struct wlan_objmgr_psoc *psoc,
+			      struct afc_resp_extracted *afc_rsp,
+			      struct wlan_afc_host_resp *host_afc)
+{
+	int ret = -EINVAL;
+	uint32_t bin_len, tmp_len;
+	struct wlan_afc_bin_resp_data *afc_bin;
+	struct wlan_afc_resp_freq_psd_info *freq_psd;
+	struct wlan_afc_resp_opclass_info *op;
+	struct wlan_afc_resp_eirp_info *chan_eirp;
+	int i, j;
+
+	if (!afc_rsp || !host_afc)
+		return ret;
+
+	host_afc->time_to_live = afc_rsp->time_to_live;
+	if (is_target_support_json_format(psoc)) {
+		if (!afc_rsp->json_data) {
+			osif_err("No JSON data");
+			return ret;
+		}
+		if (afc_rsp->json_len >
+		    IF_AFC_RESPONSE_MAX_LEN - sizeof(*host_afc)) {
+			osif_err("Invalid JSON data len %d", afc_rsp->json_len);
+			return ret;
+		}
+		host_afc->resp_format = REG_AFC_SERV_RESP_FORMAT_JSON;
+		host_afc->length = sizeof(*host_afc) + afc_rsp->json_len;
+		qdf_mem_copy(host_afc->afc_resp,
+			     afc_rsp->json_data,
+			     afc_rsp->json_len);
+		return host_afc->length;
+	}
+	host_afc->resp_format = REG_AFC_SERV_RESP_FORMAT_BINARY;
+	afc_bin = (struct wlan_afc_bin_resp_data *)host_afc->afc_resp;
+	afc_bin->request_id = afc_rsp->request_id;
+	afc_bin->avail_exp_time_d = afc_rsp->avail_exp_date;
+	afc_bin->avail_exp_time_t = afc_rsp->avail_exp_time;
+	afc_bin->afc_serv_resp_code = afc_rsp->afc_serv_resp_code;
+	afc_bin->num_frequency_obj = afc_rsp->num_frange_obj;
+	afc_bin->num_channel_obj = afc_rsp->num_opclass;
+	bin_len = sizeof(*host_afc) + sizeof(*afc_bin);
+
+	if (bin_len + sizeof(*freq_psd) * afc_bin->num_frequency_obj >
+	    IF_AFC_RESPONSE_MAX_LEN) {
+		osif_err("Invalid number frequency obj %d",
+			 afc_bin->num_frequency_obj);
+		return ret;
+	}
+	freq_psd = (struct wlan_afc_resp_freq_psd_info *)
+		   ((uint8_t *)host_afc + bin_len);
+	for (i = 0; i < afc_bin->num_frequency_obj; i++) {
+		freq_psd->freq_info =
+			(afc_rsp->frange[i].freq_start & 0x0000FFFF) |
+			(afc_rsp->frange[i].freq_end << 16);
+		freq_psd->max_psd = afc_rsp->frange[i].psd;
+		freq_psd++;
+	}
+	bin_len += sizeof(*freq_psd) * afc_bin->num_frequency_obj;
+
+	tmp_len = bin_len;
+	for (i = 0; i < afc_rsp->num_opclass; i++) {
+		tmp_len += sizeof(*op) +
+			   sizeof(*chan_eirp) * afc_rsp->op_obj[i].num_channel;
+	}
+	if (tmp_len > IF_AFC_RESPONSE_MAX_LEN) {
+		osif_err("Invalid opclass channel eirp info");
+		return ret;
+	}
+
+	op = (struct wlan_afc_resp_opclass_info *)
+	     ((uint8_t *)host_afc + bin_len);
+	for (i = 0; i < afc_rsp->num_opclass; i++) {
+		op->opclass = afc_rsp->op_obj[i].opclass;
+		op->num_channels = afc_rsp->op_obj[i].num_channel;
+		chan_eirp = (struct wlan_afc_resp_eirp_info *)
+			    ((uint8_t *)op + sizeof(*op));
+		for (j = 0; j < afc_rsp->op_obj[i].num_channel; j++) {
+			chan_eirp->channel_cfi =
+				afc_rsp->op_obj[i].chan_eirp[j].channel_cfi;
+			chan_eirp->max_eirp_pwr =
+				afc_rsp->op_obj[i].chan_eirp[j].eirp;
+			chan_eirp++;
+		}
+		op = (struct wlan_afc_resp_opclass_info *)chan_eirp;
+	}
+
+	host_afc->length = tmp_len;
+
+	return tmp_len;
+}
+
+int wlan_cfg80211_vendor_afc_response(struct wlan_objmgr_psoc *psoc,
+				      struct wlan_objmgr_pdev *pdev,
+				      const void *data,
+				      int data_len)
+{
+	int ret = -EINVAL;
+	struct nlattr *attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_MAX + 1];
+	struct afc_resp_extracted *afc_rsp;
+	struct wlan_afc_host_resp *host_afc;
+	struct reg_afc_resp_rx_ind_info afc_ind_obj;
+	bool is_json = is_target_support_json_format(psoc);
+
+	if (wlan_cfg80211_nla_parse(attr, QCA_WLAN_VENDOR_ATTR_AFC_RESP_MAX,
+				    data, data_len,
+				    wlan_cfg80211_afc_response_policy)) {
+		osif_err("Invalid AFC RESP ATTR");
+		return ret;
+	}
+
+	afc_rsp = extract_afc_resp(attr);
+	if (!afc_rsp)
+		return ret;
+
+	afc_response_display(afc_rsp);
+
+	host_afc = qdf_mem_malloc(IF_AFC_RESPONSE_MAX_LEN);
+	if (!host_afc)
+		goto fail;
+
+	ret = fill_host_afc_response_buffer(psoc, afc_rsp, host_afc);
+	if (ret <= 0)
+		goto fail;
+
+	ret = ucfg_afc_data_send(psoc, pdev, host_afc, ret);
+	if (ret) {
+		osif_err("Failed to send afc data");
+		goto fail;
+	}
+
+	afc_ind_obj.cmd_type = REG_AFC_CMD_SERV_RESP_READY;
+	afc_ind_obj.serv_resp_format =
+				is_json ? REG_AFC_SERV_RESP_FORMAT_JSON :
+				REG_AFC_SERV_RESP_FORMAT_BINARY;
+	if (ucfg_reg_send_afc_resp_rx_ind(pdev, &afc_ind_obj) !=
+	    QDF_STATUS_SUCCESS) {
+		osif_err("Failed to send afc rx indication");
+		ret = -EINVAL;
+	}
+
+fail:
+	free_extract_afc_rsp(afc_rsp);
+	qdf_mem_free(host_afc);
+	return ret;
+}

+ 174 - 0
umac/afc/core/inc/wlan_afc_main.h

@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_afc_main.h
+ *
+ * This Head file provides declaration for UMAC AFC common APIs.
+ */
+
+#ifndef __WLAN_AFC_MAIN_H__
+#define __WLAN_AFC_MAIN_H__
+
+#include <qdf_trace.h>
+#include <wlan_objmgr_psoc_obj.h>
+#include <wlan_objmgr_pdev_obj.h>
+#include <reg_services_public_struct.h>
+
+#ifdef CONFIG_AFC_SUPPORT
+
+#define afc_alert(params...) \
+	QDF_TRACE_FATAL(QDF_MODULE_ID_AFC, params)
+#define afc_err(params...) \
+	QDF_TRACE_ERROR(QDF_MODULE_ID_AFC, params)
+#define afc_warn(params...) \
+	QDF_TRACE_WARN(QDF_MODULE_ID_AFC, params)
+#define afc_notice(params...) \
+	QDF_TRACE_INFO(QDF_MODULE_ID_AFC, params)
+#define afc_info(params...) \
+	QDF_TRACE_INFO(QDF_MODULE_ID_AFC, params)
+#define afc_debug(params...) \
+	QDF_TRACE_DEBUG(QDF_MODULE_ID_AFC, params)
+
+/**
+ * afc_psoc_priv() - Helper function to get AFC private object of PSOC
+ * @psoc: Pointer to PSOC object
+ *
+ * Return: AFC private object of PSOC
+ */
+static inline struct wlan_afc_psoc_priv *
+afc_psoc_priv(struct wlan_objmgr_psoc *psoc)
+{
+	struct wlan_afc_psoc_priv *afc_priv;
+
+	afc_priv = wlan_objmgr_psoc_get_comp_private_obj(psoc,
+							 WLAN_UMAC_COMP_AFC);
+	return afc_priv;
+}
+
+/**
+ * typedef send_response_to_afcmem() - Function prototype of send AFC response
+ * to share memory.
+ * @psoc: Pointer to PSOC object
+ * @pdev: Pointer to PDEV object
+ * @data: AFC response data
+ * @len: AFC response length
+ *
+ * Return: 0 if success, otherwise failure
+ */
+typedef int (*send_response_to_afcmem)(struct wlan_objmgr_psoc *psoc,
+				       struct wlan_objmgr_pdev *pdev,
+				       struct wlan_afc_host_resp *data,
+				       uint32_t len);
+
+/**
+ * typedef osif_send_afc_request() - Function prototype of sending
+ * AFC request to osif.
+ * @pdev: Pointer to PDEV object
+ * @afc_req: Pointer to AFC request
+ *
+ * Return: 0 if success, otherwise failure
+ */
+typedef int
+(*osif_send_afc_request)(struct wlan_objmgr_pdev *pdev,
+			 struct wlan_afc_host_request *afc_req);
+
+/**
+ * typedef osif_send_afc_power_update_complete() - Function prototype of sending
+ * AFC update complete event to osif.
+ * @pdev: Pointer to PDEV object
+ * @afc_pwr_evt: Pointer to AFC power update event
+ *
+ * Return: 0 if success, otherwise failure
+ */
+typedef int
+(*osif_send_afc_power_update_complete)(struct wlan_objmgr_pdev *pdev,
+				       struct reg_fw_afc_power_event *afc_pwr_evt);
+
+/**
+ * wlan_afc_data_send() - Function to send AFC response through platform
+ * interface
+ * @psoc: Pointer to PSOC object
+ * @pdev: Pointer to PDEV object
+ * @data: Pointer to AFC response data
+ * @len: AFC response data length in bytes
+ *
+ * Return: 0 if success, otherwise failure
+ */
+int wlan_afc_data_send(struct wlan_objmgr_psoc *psoc,
+		       struct wlan_objmgr_pdev *pdev,
+		       struct wlan_afc_host_resp *data,
+		       uint32_t len);
+
+/**
+ * wlan_afc_register_data_send_cb() - Function to register data send AFC
+ * callback
+ * @psoc: Pointer to PSOC object
+ * @func: AFC PLD function to be register
+ *
+ * Return: QDF STATUS
+ */
+QDF_STATUS wlan_afc_register_data_send_cb(struct wlan_objmgr_psoc *psoc,
+					  send_response_to_afcmem func);
+
+/**
+ * wlan_afc_psoc_created_notification() - Callback function to be invoked when
+ * POSC create AFC component.
+ * @psoc: Pointer to PSOC object
+ * @arg: Pointer to argument list
+ *
+ * Return: QDF STATUS
+ */
+QDF_STATUS
+wlan_afc_psoc_created_notification(struct wlan_objmgr_psoc *psoc,
+				   void *arg);
+
+/**
+ * wlan_afc_psoc_destroyed_notification() - Callback function to be invoked when
+ * PSOC destroy AFC component.
+ * @psoc: Pointer to PSOC object
+ * @arg: Pointer to argument list
+ *
+ * Return: QDF STATUS
+ */
+QDF_STATUS
+wlan_afc_psoc_destroyed_notification(struct wlan_objmgr_psoc *psoc,
+				     void *arg);
+
+/**
+ * wlan_afc_pdev_obj_create_handler() - Callback function of PDEV create AFC
+ * component.
+ * @pdev: Pointer to PDEV object
+ * @arg: Pointer to argument list
+ *
+ * Return: QDF STATUS
+ */
+QDF_STATUS
+wlan_afc_pdev_obj_create_handler(struct wlan_objmgr_pdev *pdev,
+				 void *arg);
+
+/**
+ * wlan_afc_pdev_obj_destroy_handler() - Callback function of PDEV destroy AFC
+ * component.
+ * @pdev: Pointer to PDEV object
+ * @arg: Pointer to argument list
+ *
+ * Return: QDF STATUS
+ */
+QDF_STATUS
+wlan_afc_pdev_obj_destroy_handler(struct wlan_objmgr_pdev *pdev, void *arg);
+#endif
+#endif

+ 208 - 0
umac/afc/core/src/wlan_afc_main.c

@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_afc_main.c
+ * This file provides implementation for UMAC AFC common APIs.
+ */
+
+#include <wlan_afc_main.h>
+#include "wlan_afc_priv.h"
+#include "wlan_reg_ucfg_api.h"
+#include "wlan_cfg80211_afc.h"
+
+/**
+ * wlan_send_afc_request() - PDEV callback function to send AFC request
+ * @pdev: Pointer to PDEV object
+ * @afc_req: Pointer to AFC request from regulatory component
+ * @arg: Pointer to argument list of callback function
+ *
+ * Return: None
+ */
+static void
+wlan_send_afc_request(struct wlan_objmgr_pdev *pdev,
+		      struct wlan_afc_host_request *afc_req,
+		      void *arg)
+{
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_afc_psoc_priv *afc_priv;
+
+	psoc = wlan_pdev_get_psoc(pdev);
+
+	afc_priv = afc_psoc_priv(psoc);
+	if (!afc_priv) {
+		afc_err("psoc AFC private null");
+		return;
+	}
+
+	if (afc_priv->cbs.afc_req_func)
+		afc_priv->cbs.afc_req_func(pdev, afc_req);
+}
+
+/**
+ * wlan_send_afc_power_event() - PDEV callback function to send AFC power
+ * update complete event.
+ * @pdev: Pointer to PDEV object
+ * @afc_pwr_evt: Pointer to AFC power event from regulatory component
+ * @arg: Pointer to argument list of callback function
+ *
+ * Return: None
+ */
+static void
+wlan_send_afc_power_event(struct wlan_objmgr_pdev *pdev,
+			  struct reg_fw_afc_power_event *afc_pwr_evt,
+			  void *arg)
+{
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_afc_psoc_priv *afc_priv;
+
+	psoc = wlan_pdev_get_psoc(pdev);
+
+	afc_priv = afc_psoc_priv(psoc);
+	if (!afc_priv) {
+		afc_err("psoc AFC private null");
+		return;
+	}
+
+	if (afc_priv->cbs.afc_updated_func)
+		afc_priv->cbs.afc_updated_func(pdev, afc_pwr_evt);
+}
+
+int wlan_afc_data_send(struct wlan_objmgr_psoc *psoc,
+		       struct wlan_objmgr_pdev *pdev,
+		       struct wlan_afc_host_resp *data,
+		       uint32_t len)
+{
+	struct wlan_afc_psoc_priv *afc_priv;
+
+	afc_priv = afc_psoc_priv(psoc);
+	if (!afc_priv) {
+		afc_err("psoc AFC private null");
+		return -EINVAL;
+	}
+
+	if (afc_priv->cbs.afc_rsp_cp_func)
+		return afc_priv->cbs.afc_rsp_cp_func(psoc, pdev, data, len);
+
+	return -EINVAL;
+}
+
+QDF_STATUS wlan_afc_register_data_send_cb(struct wlan_objmgr_psoc *psoc,
+					  send_response_to_afcmem func)
+{
+	struct wlan_afc_psoc_priv *afc_priv;
+
+	afc_priv = afc_psoc_priv(psoc);
+	if (!afc_priv) {
+		afc_err("psoc AFC private null");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	afc_priv->cbs.afc_rsp_cp_func = func;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS
+wlan_afc_psoc_created_notification(struct wlan_objmgr_psoc *psoc,
+				   void *arg)
+{
+	QDF_STATUS status;
+	struct wlan_afc_psoc_priv *afc_priv;
+
+	afc_priv = qdf_mem_malloc(sizeof(*afc_priv));
+	if (!afc_priv)
+		return QDF_STATUS_E_NOMEM;
+
+	status = wlan_objmgr_psoc_component_obj_attach(psoc,
+						       WLAN_UMAC_COMP_AFC,
+						       afc_priv,
+						       QDF_STATUS_SUCCESS);
+
+	if (QDF_IS_STATUS_ERROR(status)) {
+		qdf_mem_free(afc_priv);
+		afc_err("Failed to attach psoc AFC component");
+		return status;
+	}
+
+	afc_priv->psoc = psoc;
+	afc_priv->cbs.afc_req_func = wlan_cfg80211_afc_send_request;
+	afc_priv->cbs.afc_updated_func = wlan_cfg80211_afc_send_update_complete;
+
+	return status;
+}
+
+QDF_STATUS
+wlan_afc_psoc_destroyed_notification(struct wlan_objmgr_psoc *psoc,
+				     void *arg)
+{
+	QDF_STATUS status;
+	void *afc_priv;
+
+	afc_priv = afc_psoc_priv(psoc);
+	if (!afc_priv) {
+		afc_err("Failed to get psoc AFC component private");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	status = wlan_objmgr_psoc_component_obj_detach(psoc,
+						       WLAN_UMAC_COMP_AFC,
+						       afc_priv);
+	if (QDF_IS_STATUS_ERROR(status))
+		afc_err("Failed to detach AFC component");
+
+	qdf_mem_free(afc_priv);
+
+	return status;
+}
+
+QDF_STATUS
+wlan_afc_pdev_obj_create_handler(struct wlan_objmgr_pdev *pdev,
+				 void *arg)
+{
+	QDF_STATUS status;
+
+	status = ucfg_reg_register_afc_req_rx_callback(pdev,
+						       wlan_send_afc_request,
+						       NULL);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		afc_err("Failed to register AFC request callback");
+		return status;
+	}
+
+	status = ucfg_reg_register_afc_power_event_callback(pdev,
+							    wlan_send_afc_power_event,
+							    NULL);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		afc_err("Failed to register AFC power callback");
+		ucfg_reg_unregister_afc_req_rx_callback(pdev,
+							wlan_send_afc_request);
+		return status;
+	}
+
+	return status;
+}
+
+QDF_STATUS
+wlan_afc_pdev_obj_destroy_handler(struct wlan_objmgr_pdev *pdev, void *arg)
+{
+	ucfg_reg_unregister_afc_req_rx_callback(pdev,
+						wlan_send_afc_request);
+	ucfg_reg_unregister_afc_power_event_callback(pdev,
+						     wlan_send_afc_power_event);
+
+	return QDF_STATUS_SUCCESS;
+}

+ 49 - 0
umac/afc/core/src/wlan_afc_priv.h

@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_afc_priv.h
+ *
+ * Declare various struct, macros which are used private to AFC component.
+ */
+
+#ifndef __WLAN_AFC_PRIV_H__
+#define __WLAN_AFC_PRIV_H__
+
+#include "wlan_afc_main.h"
+
+/**
+ * struct wlan_afc_callbacks - AFC PSOC private callbacks
+ * @afc_rsp_cp_func: AFC callback function to pass AFC response data to target
+ * @afc_req_func: AFC callback function to send AFC request
+ * @afc_updated_func: AFC callback function to send AFC update complete event
+ */
+struct wlan_afc_callbacks {
+	send_response_to_afcmem afc_rsp_cp_func;
+	osif_send_afc_request afc_req_func;
+	osif_send_afc_power_update_complete afc_updated_func;
+};
+
+/**
+ * struct wlan_afc_psoc_priv - AFC PSOC private data and callbacks
+ * @psoc: Pointer to PSOC object
+ * @cbs: AFC PSOC callbacks
+ */
+struct wlan_afc_psoc_priv {
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_afc_callbacks cbs;
+};
+#endif

+ 77 - 0
umac/afc/dispatcher/inc/wlan_afc_ucfg_api.h

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_afc_ucfg_api.h
+ *
+ * This file has the prototypes of AFC dispatcher API which is exposed
+ * to outside of AFC component.
+ */
+
+#ifndef __WLAN_AFC_UCFG_API_H__
+#define __WLAN_AFC_UCFG_API_H__
+
+#include <wlan_afc_main.h>
+
+#ifdef CONFIG_AFC_SUPPORT
+/**
+ * ucfg_afc_register_data_send_cb() - UCFG API to register AFC data send
+ * callback function to pass AFC response data to target.
+ * @psoc: Pointer to PSOC object
+ * @func: Pointer to PLD AFC function to pass AFC response data to target
+ *
+ * Return: QDF STATUS
+ */
+QDF_STATUS ucfg_afc_register_data_send_cb(struct wlan_objmgr_psoc *psoc,
+					  send_response_to_afcmem func);
+/**
+ * ucfg_afc_data_send() - UCFG API to send AFC response data to target
+ * @psoc: Pointer to PSOC object
+ * @pdev: Pointer to PDEV object
+ * @data: Pointer to AFC response data which pass to target
+ * @len: Length of AFC response data
+ *
+ * Return: 0 if success, otherwise error code
+ */
+int ucfg_afc_data_send(struct wlan_objmgr_psoc *psoc,
+		       struct wlan_objmgr_pdev *pdev,
+		       struct wlan_afc_host_resp *data,
+		       uint32_t len);
+/**
+ * ucfg_afc_init() - UCFG API to initialize AFC component
+ *
+ * Return: QDF STATUS
+ */
+QDF_STATUS ucfg_afc_init(void);
+
+/**
+ * ucfg_afc_deinit() - UCFG API to deinitialize AFC component
+ *
+ * Return: QDF STATUS
+ */
+QDF_STATUS ucfg_afc_deinit(void);
+#else
+static inline QDF_STATUS ucfg_afc_init(void)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline QDF_STATUS ucfg_afc_deinit(void)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif
+#endif

+ 125 - 0
umac/afc/dispatcher/src/wlan_afc_ucfg_api.c

@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_afc_ucfg_api.c
+ *
+ * This file has the AFC dispatcher API implementation which is exposed
+ * to outside of AFC component.
+ */
+
+#include <wlan_afc_main.h>
+#include <wlan_afc_ucfg_api.h>
+#include <wlan_objmgr_global_obj.h>
+
+QDF_STATUS ucfg_afc_register_data_send_cb(struct wlan_objmgr_psoc *psoc,
+					  send_response_to_afcmem func)
+{
+	return wlan_afc_register_data_send_cb(psoc, func);
+}
+
+int ucfg_afc_data_send(struct wlan_objmgr_psoc *psoc,
+		       struct wlan_objmgr_pdev *pdev,
+		       struct wlan_afc_host_resp *data,
+		       uint32_t len)
+{
+	return wlan_afc_data_send(psoc, pdev, data, len);
+}
+
+QDF_STATUS ucfg_afc_init(void)
+{
+	QDF_STATUS status;
+
+	status = wlan_objmgr_register_psoc_create_handler(WLAN_UMAC_COMP_AFC,
+							  wlan_afc_psoc_created_notification,
+							  NULL);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		afc_err("Failed to register psoc create handler");
+		goto fail_create_psoc;
+	}
+
+	status = wlan_objmgr_register_psoc_destroy_handler(WLAN_UMAC_COMP_AFC,
+							   wlan_afc_psoc_destroyed_notification,
+							   NULL);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		afc_err("Failed to register psoc delete handler");
+		goto fail_psoc_destroy;
+	}
+
+	status = wlan_objmgr_register_pdev_create_handler(WLAN_UMAC_COMP_AFC,
+							  wlan_afc_pdev_obj_create_handler,
+							  NULL);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		afc_err("Failed to register pdev create handler");
+		goto fail_create_pdev;
+	}
+
+	status = wlan_objmgr_register_pdev_destroy_handler(WLAN_UMAC_COMP_AFC,
+							   wlan_afc_pdev_obj_destroy_handler,
+							   NULL);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		afc_err("Failed to register pdev delete handler");
+		goto fail_pdev_destroy;
+	}
+
+	return status;
+
+fail_pdev_destroy:
+	wlan_objmgr_unregister_pdev_create_handler(WLAN_UMAC_COMP_AFC,
+						   wlan_afc_pdev_obj_create_handler,
+						   NULL);
+fail_create_pdev:
+	wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_AFC,
+						    wlan_afc_psoc_destroyed_notification,
+						    NULL);
+fail_psoc_destroy:
+	wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_AFC,
+						   wlan_afc_psoc_created_notification,
+						   NULL);
+fail_create_psoc:
+	return status;
+}
+
+QDF_STATUS ucfg_afc_deinit(void)
+{
+	QDF_STATUS status;
+
+	status = wlan_objmgr_unregister_pdev_destroy_handler(WLAN_UMAC_COMP_AFC,
+							     wlan_afc_pdev_obj_destroy_handler,
+							     NULL);
+	if (QDF_IS_STATUS_ERROR(status))
+		afc_err("Failed to unregister pdev destroy handler");
+
+	status = wlan_objmgr_unregister_pdev_create_handler(WLAN_UMAC_COMP_AFC,
+							    wlan_afc_pdev_obj_create_handler,
+							    NULL);
+	if (QDF_IS_STATUS_ERROR(status))
+		afc_err("Failed to unregister pdev create handler");
+
+	status = wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_AFC,
+							     wlan_afc_psoc_destroyed_notification,
+							     NULL);
+	if (QDF_IS_STATUS_ERROR(status))
+		afc_err("Failed to unregister psoc destroy handler");
+
+	status = wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_AFC,
+							    wlan_afc_psoc_created_notification,
+							    NULL);
+	if (QDF_IS_STATUS_ERROR(status))
+		afc_err("Failed to unregister psoc create handler");
+
+	return status;
+}