xref: /wlan-driver/qca-wifi-host-cmn/os_if/linux/afc/src/wlan_cfg80211_afc.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1*5113495bSYour Name /*
2*5113495bSYour Name  * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
3*5113495bSYour Name  *
4*5113495bSYour Name  * Permission to use, copy, modify, and/or distribute this software for any
5*5113495bSYour Name  * purpose with or without fee is hereby granted, provided that the above
6*5113495bSYour Name  * copyright notice and this permission notice appear in all copies.
7*5113495bSYour Name  *
8*5113495bSYour Name  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9*5113495bSYour Name  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10*5113495bSYour Name  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11*5113495bSYour Name  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12*5113495bSYour Name  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13*5113495bSYour Name  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14*5113495bSYour Name  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15*5113495bSYour Name  */
16*5113495bSYour Name 
17*5113495bSYour Name /**
18*5113495bSYour Name  * DOC: wlan_cfg80211_afc.c
19*5113495bSYour Name  *
20*5113495bSYour Name  * Defines AFC cfg80211 vendor command interface handles
21*5113495bSYour Name  */
22*5113495bSYour Name 
23*5113495bSYour Name #include <wlan_cfg80211.h>
24*5113495bSYour Name #include <wlan_cfg80211_afc.h>
25*5113495bSYour Name #include <wlan_reg_ucfg_api.h>
26*5113495bSYour Name #include <wlan_objmgr_pdev_obj.h>
27*5113495bSYour Name #include <wlan_osif_priv.h>
28*5113495bSYour Name #include <wlan_afc_ucfg_api.h>
29*5113495bSYour Name 
30*5113495bSYour Name /* Maximum AFC data length can pass to target limited by platform driver */
31*5113495bSYour Name #define IF_AFC_RESPONSE_MAX_LEN  4096
32*5113495bSYour Name 
33*5113495bSYour Name /*
34*5113495bSYour Name  * JSON format AFC response data maximum length, limited by interface,
35*5113495bSYour Name  * struct wlan_afc_host_resp is AFC response format pass to target.
36*5113495bSYour Name  */
37*5113495bSYour Name #define QCA_NL80211_AFC_RESP_DATA_MAX_SIZE  \
38*5113495bSYour Name 	(IF_AFC_RESPONSE_MAX_LEN - sizeof(struct wlan_afc_host_resp))
39*5113495bSYour Name 
40*5113495bSYour Name /**
41*5113495bSYour Name  * struct frange_obj - Structure of channel frequency range with psd
42*5113495bSYour Name  * @freq_start: Frequency range start in MHz
43*5113495bSYour Name  * @freq_end: Frequency range end in MHz
44*5113495bSYour Name  * @psd: The PSD power info (dBm/MHz) multiplied by a factor of 100 to
45*5113495bSYour Name  * preserve granularity up to 2 decimal places
46*5113495bSYour Name  */
47*5113495bSYour Name struct frange_obj {
48*5113495bSYour Name 	qdf_freq_t freq_start;
49*5113495bSYour Name 	qdf_freq_t freq_end;
50*5113495bSYour Name 	uint32_t psd;
51*5113495bSYour Name };
52*5113495bSYour Name 
53*5113495bSYour Name /**
54*5113495bSYour Name  * struct channel_eirp - Structure of channel with eirp
55*5113495bSYour Name  * @channel_cfi: Channel center frequency index
56*5113495bSYour Name  * @eirp: The EIRP power info (dBm) multiplied by a factor of 100 to
57*5113495bSYour Name  * preserve granularity up to 2 decimal places
58*5113495bSYour Name  */
59*5113495bSYour Name struct channel_eirp {
60*5113495bSYour Name 	uint8_t channel_cfi;
61*5113495bSYour Name 	uint32_t eirp;
62*5113495bSYour Name };
63*5113495bSYour Name 
64*5113495bSYour Name /**
65*5113495bSYour Name  * struct opclass_eirp_obj - Structure of operation class eirp object
66*5113495bSYour Name  * @opclass: Operation class number
67*5113495bSYour Name  * @num_channel: Number of channels belongs to this opclass
68*5113495bSYour Name  * @chan_eirp: Channel eirp structure list
69*5113495bSYour Name  */
70*5113495bSYour Name struct opclass_eirp_obj {
71*5113495bSYour Name 	uint8_t opclass;
72*5113495bSYour Name 	uint8_t num_channel;
73*5113495bSYour Name 	struct channel_eirp chan_eirp[REG_MAX_CHANNELS_PER_OPERATING_CLASS];
74*5113495bSYour Name };
75*5113495bSYour Name 
76*5113495bSYour Name /**
77*5113495bSYour Name  * struct afc_resp_extracted - Structure of AFC response extracted from
78*5113495bSYour Name  * AFC vendor response
79*5113495bSYour Name  * @json_data: Pointer to JSON data buffer
80*5113495bSYour Name  * @json_len: JSON data length
81*5113495bSYour Name  * @time_to_live: Time to live of AFC response in seconds
82*5113495bSYour Name  * @request_id: Request ID
83*5113495bSYour Name  * @avail_exp_date: Expire date
84*5113495bSYour Name  * Date format: bits 7:0   - DD (Day 1-31)
85*5113495bSYour Name  *              bits 15:8  - MM (Month 1-12)
86*5113495bSYour Name  *              bits 31:16 - YYYY (Year)
87*5113495bSYour Name  * @avail_exp_time: Expire time
88*5113495bSYour Name  * Time format: bits 7:0   - SS (Seconds 0-59)
89*5113495bSYour Name  *              bits 15:8  - MM (Minutes 0-59)
90*5113495bSYour Name  *              bits 23:16 - HH (Hours 0-23)
91*5113495bSYour Name  *              bits 31:24 - Reserved
92*5113495bSYour Name  * @afc_serv_resp_code: AFC server respond code
93*5113495bSYour Name  * -1: General Failure.
94*5113495bSYour Name  * 0: Success.
95*5113495bSYour Name  * 100 - 199: General errors related to protocol.
96*5113495bSYour Name  * 300 - 399: Error events specific to message exchange
97*5113495bSYour Name  *            for the Available Spectrum Inquiry.
98*5113495bSYour Name  * @num_frange_obj: Number of frequency range objects
99*5113495bSYour Name  * @frange: Array of frequency range object
100*5113495bSYour Name  * @num_opclass: Number of operation class channel eirp objects
101*5113495bSYour Name  * @op_obj: Array of operation class channel eirp objects
102*5113495bSYour Name  */
103*5113495bSYour Name struct afc_resp_extracted {
104*5113495bSYour Name 	uint8_t *json_data;
105*5113495bSYour Name 	uint32_t json_len;
106*5113495bSYour Name 	uint32_t time_to_live;
107*5113495bSYour Name 	uint32_t request_id;
108*5113495bSYour Name 	uint32_t avail_exp_date;
109*5113495bSYour Name 	uint32_t avail_exp_time;
110*5113495bSYour Name 	int32_t  afc_serv_resp_code;
111*5113495bSYour Name 	uint32_t num_frange_obj;
112*5113495bSYour Name 	struct frange_obj frange[NUM_6GHZ_CHANNELS];
113*5113495bSYour Name 	uint32_t num_opclass;
114*5113495bSYour Name 	struct opclass_eirp_obj op_obj[REG_MAX_SUPP_OPER_CLASSES];
115*5113495bSYour Name };
116*5113495bSYour Name 
117*5113495bSYour Name const struct nla_policy
118*5113495bSYour Name wlan_cfg80211_afc_response_policy[QCA_WLAN_VENDOR_ATTR_AFC_RESP_MAX + 1] = {
119*5113495bSYour Name 	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_DATA] = { .type = NLA_STRING,
120*5113495bSYour Name 				.len = QCA_NL80211_AFC_RESP_DATA_MAX_SIZE },
121*5113495bSYour Name 	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_TIME_TO_LIVE] = { .type = NLA_U32 },
122*5113495bSYour Name 	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID] = { .type = NLA_U32 },
123*5113495bSYour Name 	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_DATE] = { .type = NLA_U32 },
124*5113495bSYour Name 	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_TIME] = { .type = NLA_U32 },
125*5113495bSYour Name 	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFC_SERVER_RESP_CODE] = {
126*5113495bSYour Name 							.type = NLA_S32 },
127*5113495bSYour Name 	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO] = { .type = NLA_NESTED },
128*5113495bSYour Name 	[QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO] = {
129*5113495bSYour Name 							.type = NLA_NESTED },
130*5113495bSYour Name };
131*5113495bSYour Name 
132*5113495bSYour Name #define nla_nest_end_checked(skb, start) do {		\
133*5113495bSYour Name 	if ((skb) && (start))				\
134*5113495bSYour Name 		nla_nest_end(skb, start);		\
135*5113495bSYour Name } while (0)
136*5113495bSYour Name 
137*5113495bSYour Name /**
138*5113495bSYour Name  * afc_expiry_event_update_or_get_len() - Function to fill vendor event buffer
139*5113495bSYour Name  * with info extracted from AFC request, or get required vendor buffer length.
140*5113495bSYour Name  * @vendor_event: Pointer to vendor event SK buffer structure
141*5113495bSYour Name  * @afc_req: Pointer to AFC request from regulatory component
142*5113495bSYour Name  *
143*5113495bSYour Name  * If vendor_event is NULL, to get vendor buffer length, otherwise
144*5113495bSYour Name  * to fill vendor event buffer with info
145*5113495bSYour Name  *
146*5113495bSYour Name  * Return: If get vendor buffer length, return positive value as length,
147*5113495bSYour Name  * If fill vendor event  0 if success, otherwise negative error code
148*5113495bSYour Name  */
149*5113495bSYour Name static int
afc_expiry_event_update_or_get_len(struct sk_buff * vendor_event,struct wlan_afc_host_request * afc_req)150*5113495bSYour Name afc_expiry_event_update_or_get_len(struct sk_buff *vendor_event,
151*5113495bSYour Name 				   struct wlan_afc_host_request *afc_req)
152*5113495bSYour Name {
153*5113495bSYour Name 	struct nlattr *nla_attr;
154*5113495bSYour Name 	struct nlattr *freq_info;
155*5113495bSYour Name 	struct nlattr *opclass_info = NULL;
156*5113495bSYour Name 	struct nlattr *chan_list = NULL;
157*5113495bSYour Name 	struct nlattr *chan_info = NULL;
158*5113495bSYour Name 	int i, j, len = NLMSG_HDRLEN;
159*5113495bSYour Name 	struct wlan_afc_opclass_obj *afc_opclass_obj;
160*5113495bSYour Name 
161*5113495bSYour Name 	if (vendor_event &&
162*5113495bSYour Name 	    nla_put_u8(vendor_event,
163*5113495bSYour Name 		       QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE,
164*5113495bSYour Name 		       QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY)) {
165*5113495bSYour Name 		osif_err("QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY put fail");
166*5113495bSYour Name 		goto fail;
167*5113495bSYour Name 	} else {
168*5113495bSYour Name 		len += nla_total_size(sizeof(u8));
169*5113495bSYour Name 	}
170*5113495bSYour Name 
171*5113495bSYour Name 	if (vendor_event &&
172*5113495bSYour Name 	    nla_put_u32(vendor_event,
173*5113495bSYour Name 			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID,
174*5113495bSYour Name 			afc_req->req_id)) {
175*5113495bSYour Name 		osif_err("QCA_WLAN_VENDOR_ATTR_AFC_REQ_ID put fail");
176*5113495bSYour Name 		goto fail;
177*5113495bSYour Name 	} else {
178*5113495bSYour Name 		len += nla_total_size(sizeof(u32));
179*5113495bSYour Name 	}
180*5113495bSYour Name 
181*5113495bSYour Name 	if (vendor_event &&
182*5113495bSYour Name 	    nla_put_u32(vendor_event,
183*5113495bSYour Name 			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFC_WFA_VERSION,
184*5113495bSYour Name 			(afc_req->version_major << 16) |
185*5113495bSYour Name 			afc_req->version_minor)) {
186*5113495bSYour Name 		osif_err("AFC EVENT WFA version put fail");
187*5113495bSYour Name 		goto fail;
188*5113495bSYour Name 	} else {
189*5113495bSYour Name 		len += nla_total_size(sizeof(u32));
190*5113495bSYour Name 	}
191*5113495bSYour Name 
192*5113495bSYour Name 	if (vendor_event &&
193*5113495bSYour Name 	    nla_put_u16(vendor_event,
194*5113495bSYour Name 			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_MIN_DES_POWER,
195*5113495bSYour Name 			afc_req->min_des_power)) {
196*5113495bSYour Name 		osif_err("QCA_WLAN_VENDOR_ATTR_AFC_REQ_MIN_DES_PWR put fail");
197*5113495bSYour Name 		goto fail;
198*5113495bSYour Name 	} else {
199*5113495bSYour Name 		len += nla_total_size(sizeof(u16));
200*5113495bSYour Name 	}
201*5113495bSYour Name 
202*5113495bSYour Name 	if (vendor_event &&
203*5113495bSYour Name 	    nla_put_u8(vendor_event,
204*5113495bSYour Name 		       QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AP_DEPLOYMENT,
205*5113495bSYour Name 		       afc_req->afc_location->deployment_type)) {
206*5113495bSYour Name 		osif_err("AFC EVENT AP deployment put fail");
207*5113495bSYour Name 		goto fail;
208*5113495bSYour Name 	} else {
209*5113495bSYour Name 		len += nla_total_size(sizeof(u8));
210*5113495bSYour Name 	}
211*5113495bSYour Name 
212*5113495bSYour Name 	if (vendor_event) {
213*5113495bSYour Name 		/* Update the frequency range list from the Expiry event */
214*5113495bSYour Name 		nla_attr = nla_nest_start(vendor_event,
215*5113495bSYour Name 					  QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST);
216*5113495bSYour Name 		if (!nla_attr) {
217*5113495bSYour Name 			osif_err("AFC FREQ RANGE LIST start put fail");
218*5113495bSYour Name 			goto fail;
219*5113495bSYour Name 		}
220*5113495bSYour Name 	} else {
221*5113495bSYour Name 		len += nla_total_size(0);
222*5113495bSYour Name 	}
223*5113495bSYour Name 
224*5113495bSYour Name 	for (i = 0; i < afc_req->freq_lst->num_ranges; i++) {
225*5113495bSYour Name 		if (vendor_event) {
226*5113495bSYour Name 			freq_info = nla_nest_start(vendor_event, i);
227*5113495bSYour Name 			if (!freq_info) {
228*5113495bSYour Name 				osif_err("Fail to put freq list nest %d", i);
229*5113495bSYour Name 				goto fail;
230*5113495bSYour Name 			}
231*5113495bSYour Name 		} else {
232*5113495bSYour Name 			len += nla_total_size(0);
233*5113495bSYour Name 		}
234*5113495bSYour Name 
235*5113495bSYour Name 		if (vendor_event &&
236*5113495bSYour Name 		    (nla_put_u32(vendor_event,
237*5113495bSYour Name 				 QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START,
238*5113495bSYour Name 				 afc_req->freq_lst->range_objs[i].lowfreq) ||
239*5113495bSYour Name 		    nla_put_u32(vendor_event,
240*5113495bSYour Name 				QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END,
241*5113495bSYour Name 				afc_req->freq_lst->range_objs[i].highfreq))) {
242*5113495bSYour Name 			osif_err("AFC REQ FREQ RANGE LIST put fail, num %d",
243*5113495bSYour Name 				 afc_req->freq_lst->num_ranges);
244*5113495bSYour Name 			goto fail;
245*5113495bSYour Name 		} else {
246*5113495bSYour Name 			len += nla_total_size(sizeof(u32)) * 2;
247*5113495bSYour Name 		}
248*5113495bSYour Name 		nla_nest_end_checked(vendor_event, freq_info);
249*5113495bSYour Name 	}
250*5113495bSYour Name 	nla_nest_end_checked(vendor_event, nla_attr);
251*5113495bSYour Name 
252*5113495bSYour Name 	if (vendor_event) {
253*5113495bSYour Name 		/* Update the Operating class and channel list */
254*5113495bSYour Name 		nla_attr = nla_nest_start(vendor_event,
255*5113495bSYour Name 					  QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_LIST);
256*5113495bSYour Name 		if (!nla_attr) {
257*5113495bSYour Name 			osif_err("AFC OPCLASS CHAN LIST start put fail");
258*5113495bSYour Name 			goto fail;
259*5113495bSYour Name 		}
260*5113495bSYour Name 	} else {
261*5113495bSYour Name 		len += nla_total_size(0);
262*5113495bSYour Name 	}
263*5113495bSYour Name 
264*5113495bSYour Name 	for (i = 0; i < afc_req->opclass_obj_lst->num_opclass_objs; i++) {
265*5113495bSYour Name 		if (vendor_event) {
266*5113495bSYour Name 			opclass_info = nla_nest_start(vendor_event, i);
267*5113495bSYour Name 			if (!opclass_info) {
268*5113495bSYour Name 				osif_err("Fail to put opclass nest %d", i);
269*5113495bSYour Name 				goto fail;
270*5113495bSYour Name 			}
271*5113495bSYour Name 		} else {
272*5113495bSYour Name 			len += nla_total_size(0);
273*5113495bSYour Name 		}
274*5113495bSYour Name 
275*5113495bSYour Name 		afc_opclass_obj = &afc_req->opclass_obj_lst->opclass_objs[i];
276*5113495bSYour Name 
277*5113495bSYour Name 		if (vendor_event &&
278*5113495bSYour Name 		    nla_put_u8(vendor_event,
279*5113495bSYour Name 			       QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS,
280*5113495bSYour Name 			       afc_opclass_obj->opclass)) {
281*5113495bSYour Name 			osif_err("AFC OPCLASS INFO OPCLASS put fail, num %d",
282*5113495bSYour Name 				 afc_req->opclass_obj_lst->num_opclass_objs);
283*5113495bSYour Name 			goto fail;
284*5113495bSYour Name 		} else {
285*5113495bSYour Name 			len += nla_total_size(sizeof(u8));
286*5113495bSYour Name 		}
287*5113495bSYour Name 
288*5113495bSYour Name 		if (vendor_event) {
289*5113495bSYour Name 			chan_list = nla_nest_start(vendor_event,
290*5113495bSYour Name 						   QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST);
291*5113495bSYour Name 			if (!chan_list) {
292*5113495bSYour Name 				osif_err("AFC OPCLASS INFO CHAN LIST start put fail");
293*5113495bSYour Name 				goto fail;
294*5113495bSYour Name 			}
295*5113495bSYour Name 		} else {
296*5113495bSYour Name 			len += nla_total_size(0);
297*5113495bSYour Name 		}
298*5113495bSYour Name 
299*5113495bSYour Name 		for (j = 0; j < afc_opclass_obj->opclass_num_cfis; j++) {
300*5113495bSYour Name 			if (vendor_event) {
301*5113495bSYour Name 				chan_info = nla_nest_start(vendor_event, j);
302*5113495bSYour Name 				if (!chan_info) {
303*5113495bSYour Name 					osif_err("Fail to put opclass cfis nest %d", j);
304*5113495bSYour Name 					goto fail;
305*5113495bSYour Name 				}
306*5113495bSYour Name 			} else {
307*5113495bSYour Name 				len += nla_total_size(0);
308*5113495bSYour Name 			}
309*5113495bSYour Name 
310*5113495bSYour Name 			if (vendor_event &&
311*5113495bSYour Name 			    nla_put_u8(vendor_event,
312*5113495bSYour Name 				       QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM,
313*5113495bSYour Name 				       afc_opclass_obj->cfis[j])) {
314*5113495bSYour Name 				osif_err("AFC EIRP INFO CHAN NUM put fail, num %d",
315*5113495bSYour Name 					 afc_opclass_obj->opclass_num_cfis);
316*5113495bSYour Name 				goto fail;
317*5113495bSYour Name 			} else {
318*5113495bSYour Name 				len += nla_total_size(sizeof(u8));
319*5113495bSYour Name 			}
320*5113495bSYour Name 			nla_nest_end_checked(vendor_event, chan_info);
321*5113495bSYour Name 		}
322*5113495bSYour Name 		nla_nest_end_checked(vendor_event, chan_list);
323*5113495bSYour Name 		nla_nest_end_checked(vendor_event, opclass_info);
324*5113495bSYour Name 	}
325*5113495bSYour Name 	nla_nest_end_checked(vendor_event, nla_attr);
326*5113495bSYour Name 
327*5113495bSYour Name 	return vendor_event ? 0 : len;
328*5113495bSYour Name 
329*5113495bSYour Name fail:
330*5113495bSYour Name 	return -EINVAL;
331*5113495bSYour Name }
332*5113495bSYour Name 
333*5113495bSYour Name /**
334*5113495bSYour Name  * afc_power_event_update_or_get_len() - Function to fill vendor event buffer
335*5113495bSYour Name  * with AFC power update event or get required vendor buffer length
336*5113495bSYour Name  * @vendor_event: Pointer to vendor event SK buffer
337*5113495bSYour Name  * @pwr_evt: Pointer to AFC power event
338*5113495bSYour Name  *
339*5113495bSYour Name  * If vendor_event is NULL, to get vendor buffer length, otherwise
340*5113495bSYour Name  * to fill vendor event buffer with info
341*5113495bSYour Name  *
342*5113495bSYour Name  * Return: If get vendor buffer length, return positive value as length,
343*5113495bSYour Name  * If fill vendor event, 0 if success, otherwise negative error code
344*5113495bSYour Name  */
345*5113495bSYour Name static int
afc_power_event_update_or_get_len(struct sk_buff * vendor_event,struct reg_fw_afc_power_event * pwr_evt)346*5113495bSYour Name afc_power_event_update_or_get_len(struct sk_buff *vendor_event,
347*5113495bSYour Name 				  struct reg_fw_afc_power_event *pwr_evt)
348*5113495bSYour Name {
349*5113495bSYour Name 	struct afc_chan_obj *pow_evt_chan_info = NULL;
350*5113495bSYour Name 	struct chan_eirp_obj *pow_evt_eirp_info = NULL;
351*5113495bSYour Name 	struct nlattr *nla_attr;
352*5113495bSYour Name 	struct nlattr *freq_info;
353*5113495bSYour Name 	struct nlattr *opclass_info;
354*5113495bSYour Name 	struct nlattr *chan_list;
355*5113495bSYour Name 	struct nlattr *chan_info = NULL;
356*5113495bSYour Name 	int i, j, len = NLMSG_HDRLEN;
357*5113495bSYour Name 
358*5113495bSYour Name 	if (vendor_event &&
359*5113495bSYour Name 	    nla_put_u8(vendor_event,
360*5113495bSYour Name 		       QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE,
361*5113495bSYour Name 		       QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE)) {
362*5113495bSYour Name 		osif_err("AFC power update complete event type put fail");
363*5113495bSYour Name 		goto fail;
364*5113495bSYour Name 	} else {
365*5113495bSYour Name 		len += nla_total_size(sizeof(u8));
366*5113495bSYour Name 	}
367*5113495bSYour Name 
368*5113495bSYour Name 	if (vendor_event &&
369*5113495bSYour Name 	    nla_put_u32(vendor_event,
370*5113495bSYour Name 			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID,
371*5113495bSYour Name 			pwr_evt->resp_id)) {
372*5113495bSYour Name 		osif_err("QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID put fail");
373*5113495bSYour Name 		goto fail;
374*5113495bSYour Name 	} else {
375*5113495bSYour Name 		len += nla_total_size(sizeof(u32));
376*5113495bSYour Name 	}
377*5113495bSYour Name 
378*5113495bSYour Name 	if (vendor_event &&
379*5113495bSYour Name 	    nla_put_u8(vendor_event,
380*5113495bSYour Name 		       QCA_WLAN_VENDOR_ATTR_AFC_EVENT_STATUS_CODE,
381*5113495bSYour Name 		       pwr_evt->fw_status_code)) {
382*5113495bSYour Name 		osif_err("AFC EVENT STATUS CODE put fail");
383*5113495bSYour Name 		goto fail;
384*5113495bSYour Name 	} else {
385*5113495bSYour Name 		len += nla_total_size(sizeof(u8));
386*5113495bSYour Name 	}
387*5113495bSYour Name 
388*5113495bSYour Name 	if (vendor_event &&
389*5113495bSYour Name 	    nla_put_s32(vendor_event,
390*5113495bSYour Name 			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_SERVER_RESP_CODE,
391*5113495bSYour Name 			pwr_evt->serv_resp_code)) {
392*5113495bSYour Name 		osif_err("AFC EVENT SERVER RESP CODE put fail");
393*5113495bSYour Name 		goto fail;
394*5113495bSYour Name 	} else {
395*5113495bSYour Name 		len += nla_total_size(sizeof(s32));
396*5113495bSYour Name 	}
397*5113495bSYour Name 
398*5113495bSYour Name 	if (vendor_event &&
399*5113495bSYour Name 	    nla_put_u32(vendor_event,
400*5113495bSYour Name 			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_DATE,
401*5113495bSYour Name 			pwr_evt->avail_exp_time_d)) {
402*5113495bSYour Name 		osif_err("AFC EVENT EXPIRE DATE put fail");
403*5113495bSYour Name 		goto fail;
404*5113495bSYour Name 	} else {
405*5113495bSYour Name 		len += nla_total_size(sizeof(u32));
406*5113495bSYour Name 	}
407*5113495bSYour Name 
408*5113495bSYour Name 	if (vendor_event &&
409*5113495bSYour Name 	    nla_put_u32(vendor_event,
410*5113495bSYour Name 			QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_TIME,
411*5113495bSYour Name 			pwr_evt->avail_exp_time_t)) {
412*5113495bSYour Name 		osif_err("AFC EVENT EXPIRE TIME put fail");
413*5113495bSYour Name 		goto fail;
414*5113495bSYour Name 	} else {
415*5113495bSYour Name 		len += nla_total_size(sizeof(u32));
416*5113495bSYour Name 	}
417*5113495bSYour Name 
418*5113495bSYour Name 	if (vendor_event) {
419*5113495bSYour Name 		/* Update the Frequency and corresponding PSD info */
420*5113495bSYour Name 		nla_attr = nla_nest_start(vendor_event,
421*5113495bSYour Name 					  QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST);
422*5113495bSYour Name 		if (!nla_attr)
423*5113495bSYour Name 			goto fail;
424*5113495bSYour Name 	} else {
425*5113495bSYour Name 		len += nla_total_size(0);
426*5113495bSYour Name 	}
427*5113495bSYour Name 
428*5113495bSYour Name 	for (i = 0; i < pwr_evt->num_freq_objs; i++) {
429*5113495bSYour Name 		if (vendor_event) {
430*5113495bSYour Name 			freq_info = nla_nest_start(vendor_event, i);
431*5113495bSYour Name 			if (!freq_info)
432*5113495bSYour Name 				goto fail;
433*5113495bSYour Name 		} else {
434*5113495bSYour Name 			len += nla_total_size(0);
435*5113495bSYour Name 		}
436*5113495bSYour Name 
437*5113495bSYour Name 		if (vendor_event &&
438*5113495bSYour Name 		    (nla_put_u32(vendor_event,
439*5113495bSYour Name 				 QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START,
440*5113495bSYour Name 				 pwr_evt->afc_freq_info[i].low_freq) ||
441*5113495bSYour Name 		    nla_put_u32(vendor_event,
442*5113495bSYour Name 				QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END,
443*5113495bSYour Name 				pwr_evt->afc_freq_info[i].high_freq) ||
444*5113495bSYour Name 		    nla_put_u32(vendor_event,
445*5113495bSYour Name 				QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_PSD,
446*5113495bSYour Name 				pwr_evt->afc_freq_info[i].max_psd))) {
447*5113495bSYour Name 			osif_err("AFC FREQUENCY PSD INFO put failed, num %d",
448*5113495bSYour Name 				 pwr_evt->num_freq_objs);
449*5113495bSYour Name 			goto fail;
450*5113495bSYour Name 		} else {
451*5113495bSYour Name 			len += nla_total_size(sizeof(u32)) * 3;
452*5113495bSYour Name 		}
453*5113495bSYour Name 		nla_nest_end_checked(vendor_event, freq_info);
454*5113495bSYour Name 	}
455*5113495bSYour Name 	nla_nest_end_checked(vendor_event, nla_attr);
456*5113495bSYour Name 
457*5113495bSYour Name 	if (vendor_event) {
458*5113495bSYour Name 		/* Update the Operating class, channel list and EIRP info */
459*5113495bSYour Name 		nla_attr = nla_nest_start(vendor_event,
460*5113495bSYour Name 					  QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_LIST);
461*5113495bSYour Name 		if (!nla_attr)
462*5113495bSYour Name 			goto fail;
463*5113495bSYour Name 	} else {
464*5113495bSYour Name 		len += nla_total_size(0);
465*5113495bSYour Name 	}
466*5113495bSYour Name 
467*5113495bSYour Name 	pow_evt_chan_info = pwr_evt->afc_chan_info;
468*5113495bSYour Name 
469*5113495bSYour Name 	for (i = 0; i < pwr_evt->num_chan_objs; i++) {
470*5113495bSYour Name 		if (vendor_event) {
471*5113495bSYour Name 			opclass_info = nla_nest_start(vendor_event, i);
472*5113495bSYour Name 			if (!opclass_info)
473*5113495bSYour Name 				goto fail;
474*5113495bSYour Name 		} else {
475*5113495bSYour Name 			len += nla_total_size(0);
476*5113495bSYour Name 		}
477*5113495bSYour Name 
478*5113495bSYour Name 		if (vendor_event &&
479*5113495bSYour Name 		    nla_put_u8(vendor_event,
480*5113495bSYour Name 			       QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS,
481*5113495bSYour Name 			       pow_evt_chan_info[i].global_opclass)) {
482*5113495bSYour Name 			osif_err("AFC OPCLASS INFO put fail, num %d",
483*5113495bSYour Name 				 pwr_evt->num_chan_objs);
484*5113495bSYour Name 			goto fail;
485*5113495bSYour Name 		} else {
486*5113495bSYour Name 			len += nla_total_size(sizeof(u8));
487*5113495bSYour Name 		}
488*5113495bSYour Name 
489*5113495bSYour Name 		if (vendor_event) {
490*5113495bSYour Name 			chan_list = nla_nest_start(vendor_event,
491*5113495bSYour Name 						   QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST);
492*5113495bSYour Name 			if (!chan_list)
493*5113495bSYour Name 				goto fail;
494*5113495bSYour Name 		} else {
495*5113495bSYour Name 			len += nla_total_size(0);
496*5113495bSYour Name 		}
497*5113495bSYour Name 
498*5113495bSYour Name 		pow_evt_eirp_info = pow_evt_chan_info[i].chan_eirp_info;
499*5113495bSYour Name 
500*5113495bSYour Name 		for (j = 0; j < pow_evt_chan_info[i].num_chans; j++) {
501*5113495bSYour Name 			if (vendor_event) {
502*5113495bSYour Name 				chan_info = nla_nest_start(vendor_event, j);
503*5113495bSYour Name 				if (!chan_info)
504*5113495bSYour Name 					goto fail;
505*5113495bSYour Name 			} else {
506*5113495bSYour Name 				len += nla_total_size(0);
507*5113495bSYour Name 			}
508*5113495bSYour Name 
509*5113495bSYour Name 			if (vendor_event &&
510*5113495bSYour Name 			    (nla_put_u8(vendor_event,
511*5113495bSYour Name 					QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM,
512*5113495bSYour Name 					pow_evt_eirp_info[j].cfi) ||
513*5113495bSYour Name 			    nla_put_u32(vendor_event,
514*5113495bSYour Name 					QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP,
515*5113495bSYour Name 					pow_evt_eirp_info[j].eirp_power))) {
516*5113495bSYour Name 				osif_err("AFC CHAN EIRP_INFO put fail, num %d",
517*5113495bSYour Name 					 pow_evt_chan_info[i].num_chans);
518*5113495bSYour Name 				goto fail;
519*5113495bSYour Name 			} else {
520*5113495bSYour Name 				len += nla_total_size(sizeof(u8));
521*5113495bSYour Name 				len += nla_total_size(sizeof(u32));
522*5113495bSYour Name 			}
523*5113495bSYour Name 			nla_nest_end_checked(vendor_event, chan_info);
524*5113495bSYour Name 		}
525*5113495bSYour Name 		nla_nest_end_checked(vendor_event, chan_list);
526*5113495bSYour Name 		nla_nest_end_checked(vendor_event, opclass_info);
527*5113495bSYour Name 	}
528*5113495bSYour Name 
529*5113495bSYour Name 	nla_nest_end_checked(vendor_event, nla_attr);
530*5113495bSYour Name 
531*5113495bSYour Name 	return vendor_event ? 0 : len;
532*5113495bSYour Name 
533*5113495bSYour Name fail:
534*5113495bSYour Name 	return -EINVAL;
535*5113495bSYour Name }
536*5113495bSYour Name 
wlan_cfg80211_afc_send_request(struct wlan_objmgr_pdev * pdev,struct wlan_afc_host_request * afc_req)537*5113495bSYour Name int wlan_cfg80211_afc_send_request(struct wlan_objmgr_pdev *pdev,
538*5113495bSYour Name 				   struct wlan_afc_host_request *afc_req)
539*5113495bSYour Name {
540*5113495bSYour Name 	struct sk_buff *vendor_event;
541*5113495bSYour Name 	struct pdev_osif_priv *osif_priv;
542*5113495bSYour Name 	int ret, vendor_buffer_len;
543*5113495bSYour Name 
544*5113495bSYour Name 	osif_priv = wlan_pdev_get_ospriv(pdev);
545*5113495bSYour Name 	if (!osif_priv) {
546*5113495bSYour Name 		osif_err("PDEV OS private structure is NULL");
547*5113495bSYour Name 		return -EINVAL;
548*5113495bSYour Name 	}
549*5113495bSYour Name 
550*5113495bSYour Name 	if (!afc_req) {
551*5113495bSYour Name 		osif_err("afc host request is NULL");
552*5113495bSYour Name 		return -EINVAL;
553*5113495bSYour Name 	}
554*5113495bSYour Name 
555*5113495bSYour Name 	vendor_buffer_len = afc_expiry_event_update_or_get_len(NULL, afc_req);
556*5113495bSYour Name 
557*5113495bSYour Name 	vendor_event = wlan_cfg80211_vendor_event_alloc(osif_priv->wiphy,
558*5113495bSYour Name 							NULL,
559*5113495bSYour Name 							vendor_buffer_len,
560*5113495bSYour Name 							QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT_INDEX,
561*5113495bSYour Name 							GFP_ATOMIC);
562*5113495bSYour Name 	if (!vendor_event) {
563*5113495bSYour Name 		osif_err("cfg80211 vendor event alloc failed");
564*5113495bSYour Name 		return -ENOMEM;
565*5113495bSYour Name 	}
566*5113495bSYour Name 
567*5113495bSYour Name 	ret = afc_expiry_event_update_or_get_len(vendor_event, afc_req);
568*5113495bSYour Name 
569*5113495bSYour Name 	if (ret) {
570*5113495bSYour Name 		osif_err("Failed to update AFC request vendor event");
571*5113495bSYour Name 		goto fail;
572*5113495bSYour Name 	}
573*5113495bSYour Name 
574*5113495bSYour Name 	osif_debug("Sending AFC expiry event to user application");
575*5113495bSYour Name 	wlan_cfg80211_vendor_event(vendor_event, GFP_ATOMIC);
576*5113495bSYour Name 
577*5113495bSYour Name 	return 0;
578*5113495bSYour Name 
579*5113495bSYour Name fail:
580*5113495bSYour Name 	wlan_cfg80211_vendor_free_skb(vendor_event);
581*5113495bSYour Name 	return -EINVAL;
582*5113495bSYour Name }
583*5113495bSYour Name 
584*5113495bSYour Name int
wlan_cfg80211_afc_send_update_complete(struct wlan_objmgr_pdev * pdev,struct reg_fw_afc_power_event * afc_evt)585*5113495bSYour Name wlan_cfg80211_afc_send_update_complete(struct wlan_objmgr_pdev *pdev,
586*5113495bSYour Name 				       struct reg_fw_afc_power_event *afc_evt)
587*5113495bSYour Name {
588*5113495bSYour Name 	struct sk_buff *vendor_event;
589*5113495bSYour Name 	struct pdev_osif_priv *osif_priv;
590*5113495bSYour Name 	int vendor_buffer_len;
591*5113495bSYour Name 
592*5113495bSYour Name 	osif_priv = wlan_pdev_get_ospriv(pdev);
593*5113495bSYour Name 	if (!osif_priv) {
594*5113495bSYour Name 		osif_err("PDEV OS private structure is NULL");
595*5113495bSYour Name 		return -EINVAL;
596*5113495bSYour Name 	}
597*5113495bSYour Name 
598*5113495bSYour Name 	if (!afc_evt) {
599*5113495bSYour Name 		osif_err("afc power event is NULL");
600*5113495bSYour Name 		return -EINVAL;
601*5113495bSYour Name 	}
602*5113495bSYour Name 
603*5113495bSYour Name 	vendor_buffer_len = afc_power_event_update_or_get_len(NULL, afc_evt);
604*5113495bSYour Name 
605*5113495bSYour Name 	vendor_event = wlan_cfg80211_vendor_event_alloc(osif_priv->wiphy,
606*5113495bSYour Name 							NULL,
607*5113495bSYour Name 							vendor_buffer_len,
608*5113495bSYour Name 							QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT_INDEX,
609*5113495bSYour Name 							GFP_ATOMIC);
610*5113495bSYour Name 	if (!vendor_event) {
611*5113495bSYour Name 		osif_err("cfg80211 vendor event alloc failed");
612*5113495bSYour Name 		return -ENOMEM;
613*5113495bSYour Name 	}
614*5113495bSYour Name 
615*5113495bSYour Name 	if (afc_power_event_update_or_get_len(vendor_event, afc_evt)) {
616*5113495bSYour Name 		osif_err("Failed to update AFC power vendor event");
617*5113495bSYour Name 		goto fail;
618*5113495bSYour Name 	}
619*5113495bSYour Name 
620*5113495bSYour Name 	osif_debug("Sending AFC update complete event to user application");
621*5113495bSYour Name 	wlan_cfg80211_vendor_event(vendor_event, GFP_ATOMIC);
622*5113495bSYour Name 
623*5113495bSYour Name 	return 0;
624*5113495bSYour Name 
625*5113495bSYour Name fail:
626*5113495bSYour Name 	wlan_cfg80211_vendor_free_skb(vendor_event);
627*5113495bSYour Name 	return -EINVAL;
628*5113495bSYour Name }
629*5113495bSYour Name 
630*5113495bSYour Name /**
631*5113495bSYour Name  * afc_response_display() - Function to display AFC response information
632*5113495bSYour Name  * @rsp: Pointer to AFC response structure which is extracted from vendor
633*5113495bSYour Name  * command
634*5113495bSYour Name  *
635*5113495bSYour Name  * Return: None
636*5113495bSYour Name  */
afc_response_display(struct afc_resp_extracted * rsp)637*5113495bSYour Name static void afc_response_display(struct afc_resp_extracted *rsp)
638*5113495bSYour Name {
639*5113495bSYour Name 	int iter, j;
640*5113495bSYour Name 
641*5113495bSYour Name 	if (rsp->json_data)
642*5113495bSYour Name 		return;
643*5113495bSYour Name 
644*5113495bSYour Name 	osif_debug("Req ID: %u TTL: %u Date: 0x%x Time: 0x%x Resp code: %u Freq objs: %u Opclass objs: %u",
645*5113495bSYour Name 		   rsp->request_id,
646*5113495bSYour Name 		   rsp->time_to_live,
647*5113495bSYour Name 		   rsp->avail_exp_date,
648*5113495bSYour Name 		   rsp->avail_exp_time,
649*5113495bSYour Name 		   rsp->afc_serv_resp_code,
650*5113495bSYour Name 		   rsp->num_frange_obj,
651*5113495bSYour Name 		   rsp->num_opclass);
652*5113495bSYour Name 
653*5113495bSYour Name 	for (iter = 0; iter < rsp->num_frange_obj; iter++)
654*5113495bSYour Name 		osif_debug("Freq Info[%d]: start %u end %u PSD %u",
655*5113495bSYour Name 			   iter,
656*5113495bSYour Name 			   rsp->frange[iter].freq_start,
657*5113495bSYour Name 			   rsp->frange[iter].freq_end,
658*5113495bSYour Name 			   rsp->frange[iter].psd);
659*5113495bSYour Name 
660*5113495bSYour Name 	for (iter = 0; iter < rsp->num_opclass; iter++) {
661*5113495bSYour Name 		osif_debug("Opclass[%d]: %u Num channels: %u",
662*5113495bSYour Name 			   iter,
663*5113495bSYour Name 			   rsp->op_obj[iter].opclass,
664*5113495bSYour Name 			   rsp->op_obj[iter].num_channel);
665*5113495bSYour Name 
666*5113495bSYour Name 		for (j = 0; j < rsp->op_obj[iter].num_channel; j++)
667*5113495bSYour Name 			osif_debug("Channel Info[%d]:CFI: %u EIRP: %u",
668*5113495bSYour Name 				   j,
669*5113495bSYour Name 				   rsp->op_obj[iter].chan_eirp[j].channel_cfi,
670*5113495bSYour Name 				   rsp->op_obj[iter].chan_eirp[j].eirp);
671*5113495bSYour Name 	}
672*5113495bSYour Name }
673*5113495bSYour Name 
674*5113495bSYour Name /**
675*5113495bSYour Name  * wlan_parse_afc_rsp_freq_psd() - Function to parse AFC response channel
676*5113495bSYour Name  * frequency range PSD information from NL attribute.
677*5113495bSYour Name  * @attr: Pointer to NL AFC frequency PSD attributes
678*5113495bSYour Name  * @rsp: Pointer to AFC extracted response
679*5113495bSYour Name  *
680*5113495bSYour Name  * Return: Negative error number if failed, otherwise success
681*5113495bSYour Name  */
682*5113495bSYour Name static int
wlan_parse_afc_rsp_freq_psd(struct nlattr * attr,struct afc_resp_extracted * rsp)683*5113495bSYour Name wlan_parse_afc_rsp_freq_psd(struct nlattr *attr, struct afc_resp_extracted *rsp)
684*5113495bSYour Name {
685*5113495bSYour Name 	int ret = -EINVAL;
686*5113495bSYour Name 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_MAX + 1];
687*5113495bSYour Name 	struct nlattr *cur_attr = NULL, *tb2;
688*5113495bSYour Name 	uint32_t rem;
689*5113495bSYour Name 	qdf_size_t i = 0;
690*5113495bSYour Name 
691*5113495bSYour Name 	nla_for_each_nested(cur_attr, attr, rem) {
692*5113495bSYour Name 		if (i >= NUM_6GHZ_CHANNELS) {
693*5113495bSYour Name 			osif_err("Ignore exceed");
694*5113495bSYour Name 			break;
695*5113495bSYour Name 		}
696*5113495bSYour Name 		if (wlan_cfg80211_nla_parse(tb,
697*5113495bSYour Name 					    QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_MAX,
698*5113495bSYour Name 					    nla_data(cur_attr),
699*5113495bSYour Name 					    nla_len(cur_attr),
700*5113495bSYour Name 					    NULL)) {
701*5113495bSYour Name 			osif_err("Invalid ATTR");
702*5113495bSYour Name 			return ret;
703*5113495bSYour Name 		}
704*5113495bSYour Name 
705*5113495bSYour Name 		tb2 = tb[QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START];
706*5113495bSYour Name 		if (tb2)
707*5113495bSYour Name 			rsp->frange[i].freq_start = nla_get_u32(tb2);
708*5113495bSYour Name 
709*5113495bSYour Name 		tb2 = tb[QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END];
710*5113495bSYour Name 		if (tb2)
711*5113495bSYour Name 			rsp->frange[i].freq_end = nla_get_u32(tb2);
712*5113495bSYour Name 
713*5113495bSYour Name 		tb2 = tb[QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_PSD];
714*5113495bSYour Name 		if (tb2)
715*5113495bSYour Name 			rsp->frange[i].psd = nla_get_u32(tb2);
716*5113495bSYour Name 
717*5113495bSYour Name 		i++;
718*5113495bSYour Name 	}
719*5113495bSYour Name 
720*5113495bSYour Name 	rsp->num_frange_obj = i;
721*5113495bSYour Name 	return i;
722*5113495bSYour Name }
723*5113495bSYour Name 
724*5113495bSYour Name /**
725*5113495bSYour Name  * wlan_parse_afc_rsp_opclass_eirp() - Function to parse AFC response operation
726*5113495bSYour Name  * class EIRP information from NL attributes.
727*5113495bSYour Name  * @attr: Pointer to NL AFC operation class EIRP attributes
728*5113495bSYour Name  * @rsp: Pointer to AFC extracted response
729*5113495bSYour Name  *
730*5113495bSYour Name  * Return: Negative error number if failed, otherwise success
731*5113495bSYour Name  */
732*5113495bSYour Name static int
wlan_parse_afc_rsp_opclass_eirp(struct nlattr * attr,struct afc_resp_extracted * rsp)733*5113495bSYour Name wlan_parse_afc_rsp_opclass_eirp(struct nlattr *attr,
734*5113495bSYour Name 				struct afc_resp_extracted *rsp)
735*5113495bSYour Name {
736*5113495bSYour Name 	int ret = -EINVAL;
737*5113495bSYour Name 	struct nlattr *tb1[QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_MAX + 1];
738*5113495bSYour Name 	struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_MAX + 1];
739*5113495bSYour Name 	struct nlattr *cur_attr = NULL, *sub_attr = NULL, *tb;
740*5113495bSYour Name 	uint32_t rem, sub_rem;
741*5113495bSYour Name 	int i = 0, ch_idx;
742*5113495bSYour Name 
743*5113495bSYour Name 	nla_for_each_nested(cur_attr, attr, rem) {
744*5113495bSYour Name 		if (i >= REG_MAX_SUPP_OPER_CLASSES) {
745*5113495bSYour Name 			osif_err("Ignore opclass list exceed");
746*5113495bSYour Name 			break;
747*5113495bSYour Name 		}
748*5113495bSYour Name 		if (wlan_cfg80211_nla_parse(tb1,
749*5113495bSYour Name 					    QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_MAX,
750*5113495bSYour Name 					    nla_data(cur_attr),
751*5113495bSYour Name 					    nla_len(cur_attr),
752*5113495bSYour Name 					    NULL)) {
753*5113495bSYour Name 			osif_err("Invalid ATTR");
754*5113495bSYour Name 			return ret;
755*5113495bSYour Name 		}
756*5113495bSYour Name 		tb = tb1[QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS];
757*5113495bSYour Name 		if (tb)
758*5113495bSYour Name 			rsp->op_obj[i].opclass = nla_get_u8(tb);
759*5113495bSYour Name 
760*5113495bSYour Name 		tb = tb1[QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST];
761*5113495bSYour Name 		if (!tb) {
762*5113495bSYour Name 			osif_err("No opclass channel list");
763*5113495bSYour Name 			return ret;
764*5113495bSYour Name 		}
765*5113495bSYour Name 
766*5113495bSYour Name 		ch_idx = 0;
767*5113495bSYour Name 
768*5113495bSYour Name 		nla_for_each_nested(sub_attr, tb, sub_rem) {
769*5113495bSYour Name 			if (ch_idx >= NUM_6GHZ_CHANNELS) {
770*5113495bSYour Name 				osif_err("Ignore eirp list exceed");
771*5113495bSYour Name 				break;
772*5113495bSYour Name 			}
773*5113495bSYour Name 			if (wlan_cfg80211_nla_parse(tb2,
774*5113495bSYour Name 						    QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_MAX,
775*5113495bSYour Name 						    nla_data(sub_attr),
776*5113495bSYour Name 						    nla_len(sub_attr),
777*5113495bSYour Name 						    NULL)) {
778*5113495bSYour Name 				osif_err("Invalid ATTR");
779*5113495bSYour Name 				return ret;
780*5113495bSYour Name 			}
781*5113495bSYour Name 			tb = tb2[QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM];
782*5113495bSYour Name 			if (tb)
783*5113495bSYour Name 				rsp->op_obj[i].chan_eirp[ch_idx].channel_cfi =
784*5113495bSYour Name 						nla_get_u8(tb);
785*5113495bSYour Name 			tb = tb2[QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP];
786*5113495bSYour Name 			if (tb)
787*5113495bSYour Name 				rsp->op_obj[i].chan_eirp[ch_idx].eirp =
788*5113495bSYour Name 						nla_get_u32(tb);
789*5113495bSYour Name 			ch_idx++;
790*5113495bSYour Name 		}
791*5113495bSYour Name 		rsp->op_obj[i].num_channel = ch_idx;
792*5113495bSYour Name 
793*5113495bSYour Name 		i++;
794*5113495bSYour Name 	}
795*5113495bSYour Name 	rsp->num_opclass = i;
796*5113495bSYour Name 	return i;
797*5113495bSYour Name }
798*5113495bSYour Name 
799*5113495bSYour Name /**
800*5113495bSYour Name  * free_extract_afc_rsp() - Function to free AFC extracted response
801*5113495bSYour Name  * @rsp: Pointer to AFC extracted response
802*5113495bSYour Name  *
803*5113495bSYour Name  * Return: None
804*5113495bSYour Name  */
free_extract_afc_rsp(struct afc_resp_extracted * rsp)805*5113495bSYour Name static inline void free_extract_afc_rsp(struct afc_resp_extracted *rsp)
806*5113495bSYour Name {
807*5113495bSYour Name 	if (!rsp)
808*5113495bSYour Name 		return;
809*5113495bSYour Name 
810*5113495bSYour Name 	qdf_mem_free(rsp->json_data);
811*5113495bSYour Name 	qdf_mem_free(rsp);
812*5113495bSYour Name }
813*5113495bSYour Name 
814*5113495bSYour Name /**
815*5113495bSYour Name  * extract_afc_resp() - Function to extract AFC response
816*5113495bSYour Name  * @attr: Pointer to NL attribute array
817*5113495bSYour Name  *
818*5113495bSYour Name  * Return: Pointer to AFC response axtracted
819*5113495bSYour Name  */
extract_afc_resp(struct nlattr ** attr)820*5113495bSYour Name static struct afc_resp_extracted *extract_afc_resp(struct nlattr **attr)
821*5113495bSYour Name {
822*5113495bSYour Name 	struct afc_resp_extracted *afc_rsp;
823*5113495bSYour Name 	struct nlattr *nl;
824*5113495bSYour Name 
825*5113495bSYour Name 	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_TIME_TO_LIVE]) {
826*5113495bSYour Name 		osif_err("ATTR AFC RESP TIME TO LIVE is required");
827*5113495bSYour Name 		return NULL;
828*5113495bSYour Name 	}
829*5113495bSYour Name 
830*5113495bSYour Name 	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID]) {
831*5113495bSYour Name 		osif_err("ATTR AFC RESP REQ ID is required");
832*5113495bSYour Name 		return NULL;
833*5113495bSYour Name 	}
834*5113495bSYour Name 
835*5113495bSYour Name 	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_DATE]) {
836*5113495bSYour Name 		osif_err("ATTR AFC RESP EXP DATE is required");
837*5113495bSYour Name 		return NULL;
838*5113495bSYour Name 	}
839*5113495bSYour Name 
840*5113495bSYour Name 	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_TIME]) {
841*5113495bSYour Name 		osif_err("ATTR AFC RESP EXP TIME is required");
842*5113495bSYour Name 		return NULL;
843*5113495bSYour Name 	}
844*5113495bSYour Name 
845*5113495bSYour Name 	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFC_SERVER_RESP_CODE]) {
846*5113495bSYour Name 		osif_err("ATTR AFC RESP SERVER RESP CODE is required");
847*5113495bSYour Name 		return NULL;
848*5113495bSYour Name 	}
849*5113495bSYour Name 
850*5113495bSYour Name 	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO]) {
851*5113495bSYour Name 		osif_err("ATTR AFC RESP FREQ PSD INFO is required");
852*5113495bSYour Name 		return NULL;
853*5113495bSYour Name 	}
854*5113495bSYour Name 
855*5113495bSYour Name 	if (!attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO]) {
856*5113495bSYour Name 		osif_err("ATTR AFC RESP OPCLASS CHAN EIRP INFO is required");
857*5113495bSYour Name 		return NULL;
858*5113495bSYour Name 	}
859*5113495bSYour Name 
860*5113495bSYour Name 	afc_rsp = qdf_mem_malloc(sizeof(*afc_rsp));
861*5113495bSYour Name 	if (!afc_rsp)
862*5113495bSYour Name 		return NULL;
863*5113495bSYour Name 
864*5113495bSYour Name 	if (attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_DATA]) {
865*5113495bSYour Name 		nl = attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_DATA];
866*5113495bSYour Name 		afc_rsp->json_data = qdf_mem_malloc(nla_len(nl));
867*5113495bSYour Name 		if (!afc_rsp->json_data)
868*5113495bSYour Name 			goto fail;
869*5113495bSYour Name 
870*5113495bSYour Name 		afc_rsp->json_len = nla_len(nl);
871*5113495bSYour Name 		nla_memcpy(afc_rsp->json_data, nl, afc_rsp->json_len);
872*5113495bSYour Name 	}
873*5113495bSYour Name 
874*5113495bSYour Name 	afc_rsp->time_to_live =
875*5113495bSYour Name 		nla_get_u32(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_TIME_TO_LIVE]);
876*5113495bSYour Name 
877*5113495bSYour Name 	afc_rsp->request_id =
878*5113495bSYour Name 		nla_get_u32(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID]);
879*5113495bSYour Name 
880*5113495bSYour Name 	afc_rsp->avail_exp_date =
881*5113495bSYour Name 		nla_get_u32(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_DATE]);
882*5113495bSYour Name 
883*5113495bSYour Name 	afc_rsp->avail_exp_time =
884*5113495bSYour Name 		nla_get_u32(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_TIME]);
885*5113495bSYour Name 
886*5113495bSYour Name 	afc_rsp->afc_serv_resp_code =
887*5113495bSYour Name 		nla_get_s32(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFC_SERVER_RESP_CODE]);
888*5113495bSYour Name 
889*5113495bSYour Name 	if (wlan_parse_afc_rsp_freq_psd(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO],
890*5113495bSYour Name 					afc_rsp) <= 0) {
891*5113495bSYour Name 		osif_err("parse freq psd err");
892*5113495bSYour Name 		goto fail;
893*5113495bSYour Name 	}
894*5113495bSYour Name 
895*5113495bSYour Name 	if (wlan_parse_afc_rsp_opclass_eirp(attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO],
896*5113495bSYour Name 					    afc_rsp) <= 0) {
897*5113495bSYour Name 		osif_err("parse opclass eirp err");
898*5113495bSYour Name 		goto fail;
899*5113495bSYour Name 	}
900*5113495bSYour Name 
901*5113495bSYour Name 	return afc_rsp;
902*5113495bSYour Name fail:
903*5113495bSYour Name 	osif_err("Error parsing the AFC response from application");
904*5113495bSYour Name 	free_extract_afc_rsp(afc_rsp);
905*5113495bSYour Name 	return NULL;
906*5113495bSYour Name }
907*5113495bSYour Name 
908*5113495bSYour Name /**
909*5113495bSYour Name  * is_target_support_json_format() - API to get whether target support JSON
910*5113495bSYour Name  * format AFC response.
911*5113495bSYour Name  * @psoc: Pointer to PSOC object
912*5113495bSYour Name  *
913*5113495bSYour Name  * Return: Boolean
914*5113495bSYour Name  */
915*5113495bSYour Name 
is_target_support_json_format(struct wlan_objmgr_psoc * psoc)916*5113495bSYour Name static inline bool is_target_support_json_format(struct wlan_objmgr_psoc *psoc)
917*5113495bSYour Name {
918*5113495bSYour Name 	return false;
919*5113495bSYour Name }
920*5113495bSYour Name 
921*5113495bSYour Name /**
922*5113495bSYour Name  * fill_host_afc_response_buffer() - Function to fill AFC response buffer which
923*5113495bSYour Name  * pass to target.
924*5113495bSYour Name  * @psoc: Pointer to PSOC object
925*5113495bSYour Name  * @afc_rsp: Pointer to AFC extracted response
926*5113495bSYour Name  * @host_afc: Pointer to interface AFC response buffer with target
927*5113495bSYour Name  *
928*5113495bSYour Name  * Return: Negative error number if failed, otherwise success
929*5113495bSYour Name  */
930*5113495bSYour Name 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)931*5113495bSYour Name fill_host_afc_response_buffer(struct wlan_objmgr_psoc *psoc,
932*5113495bSYour Name 			      struct afc_resp_extracted *afc_rsp,
933*5113495bSYour Name 			      struct wlan_afc_host_resp *host_afc)
934*5113495bSYour Name {
935*5113495bSYour Name 	int ret = -EINVAL;
936*5113495bSYour Name 	uint32_t bin_len, tmp_len;
937*5113495bSYour Name 	struct wlan_afc_bin_resp_data *afc_bin;
938*5113495bSYour Name 	struct wlan_afc_resp_freq_psd_info *freq_psd;
939*5113495bSYour Name 	struct wlan_afc_resp_opclass_info *op;
940*5113495bSYour Name 	struct wlan_afc_resp_eirp_info *chan_eirp;
941*5113495bSYour Name 	int i, j;
942*5113495bSYour Name 
943*5113495bSYour Name 	if (!afc_rsp || !host_afc)
944*5113495bSYour Name 		return ret;
945*5113495bSYour Name 
946*5113495bSYour Name 	host_afc->time_to_live = afc_rsp->time_to_live;
947*5113495bSYour Name 	if (is_target_support_json_format(psoc)) {
948*5113495bSYour Name 		if (!afc_rsp->json_data) {
949*5113495bSYour Name 			osif_err("No JSON data");
950*5113495bSYour Name 			return ret;
951*5113495bSYour Name 		}
952*5113495bSYour Name 		if (afc_rsp->json_len >
953*5113495bSYour Name 		    IF_AFC_RESPONSE_MAX_LEN - sizeof(*host_afc)) {
954*5113495bSYour Name 			osif_err("Invalid JSON data len %d", afc_rsp->json_len);
955*5113495bSYour Name 			return ret;
956*5113495bSYour Name 		}
957*5113495bSYour Name 		host_afc->resp_format = REG_AFC_SERV_RESP_FORMAT_JSON;
958*5113495bSYour Name 		host_afc->length = sizeof(*host_afc) + afc_rsp->json_len;
959*5113495bSYour Name 		qdf_mem_copy(host_afc->afc_resp,
960*5113495bSYour Name 			     afc_rsp->json_data,
961*5113495bSYour Name 			     afc_rsp->json_len);
962*5113495bSYour Name 		return host_afc->length;
963*5113495bSYour Name 	}
964*5113495bSYour Name 	host_afc->resp_format = REG_AFC_SERV_RESP_FORMAT_BINARY;
965*5113495bSYour Name 	afc_bin = (struct wlan_afc_bin_resp_data *)host_afc->afc_resp;
966*5113495bSYour Name 	afc_bin->request_id = afc_rsp->request_id;
967*5113495bSYour Name 	afc_bin->avail_exp_time_d = afc_rsp->avail_exp_date;
968*5113495bSYour Name 	afc_bin->avail_exp_time_t = afc_rsp->avail_exp_time;
969*5113495bSYour Name 	afc_bin->afc_serv_resp_code = afc_rsp->afc_serv_resp_code;
970*5113495bSYour Name 	afc_bin->num_frequency_obj = afc_rsp->num_frange_obj;
971*5113495bSYour Name 	afc_bin->num_channel_obj = afc_rsp->num_opclass;
972*5113495bSYour Name 	bin_len = sizeof(*host_afc) + sizeof(*afc_bin);
973*5113495bSYour Name 
974*5113495bSYour Name 	if (bin_len + sizeof(*freq_psd) * afc_bin->num_frequency_obj >
975*5113495bSYour Name 	    IF_AFC_RESPONSE_MAX_LEN) {
976*5113495bSYour Name 		osif_err("Invalid number frequency obj %d",
977*5113495bSYour Name 			 afc_bin->num_frequency_obj);
978*5113495bSYour Name 		return ret;
979*5113495bSYour Name 	}
980*5113495bSYour Name 	freq_psd = (struct wlan_afc_resp_freq_psd_info *)
981*5113495bSYour Name 		   ((uint8_t *)host_afc + bin_len);
982*5113495bSYour Name 	for (i = 0; i < afc_bin->num_frequency_obj; i++) {
983*5113495bSYour Name 		freq_psd->freq_info =
984*5113495bSYour Name 			(afc_rsp->frange[i].freq_start & 0x0000FFFF) |
985*5113495bSYour Name 			(afc_rsp->frange[i].freq_end << 16);
986*5113495bSYour Name 		freq_psd->max_psd = afc_rsp->frange[i].psd;
987*5113495bSYour Name 		freq_psd++;
988*5113495bSYour Name 	}
989*5113495bSYour Name 	bin_len += sizeof(*freq_psd) * afc_bin->num_frequency_obj;
990*5113495bSYour Name 
991*5113495bSYour Name 	tmp_len = bin_len;
992*5113495bSYour Name 	for (i = 0; i < afc_rsp->num_opclass; i++) {
993*5113495bSYour Name 		tmp_len += sizeof(*op) +
994*5113495bSYour Name 			   sizeof(*chan_eirp) * afc_rsp->op_obj[i].num_channel;
995*5113495bSYour Name 	}
996*5113495bSYour Name 	if (tmp_len > IF_AFC_RESPONSE_MAX_LEN) {
997*5113495bSYour Name 		osif_err("Invalid opclass channel eirp info");
998*5113495bSYour Name 		return ret;
999*5113495bSYour Name 	}
1000*5113495bSYour Name 
1001*5113495bSYour Name 	op = (struct wlan_afc_resp_opclass_info *)
1002*5113495bSYour Name 	     ((uint8_t *)host_afc + bin_len);
1003*5113495bSYour Name 	for (i = 0; i < afc_rsp->num_opclass; i++) {
1004*5113495bSYour Name 		op->opclass = afc_rsp->op_obj[i].opclass;
1005*5113495bSYour Name 		op->num_channels = afc_rsp->op_obj[i].num_channel;
1006*5113495bSYour Name 		chan_eirp = (struct wlan_afc_resp_eirp_info *)
1007*5113495bSYour Name 			    ((uint8_t *)op + sizeof(*op));
1008*5113495bSYour Name 		for (j = 0; j < afc_rsp->op_obj[i].num_channel; j++) {
1009*5113495bSYour Name 			chan_eirp->channel_cfi =
1010*5113495bSYour Name 				afc_rsp->op_obj[i].chan_eirp[j].channel_cfi;
1011*5113495bSYour Name 			chan_eirp->max_eirp_pwr =
1012*5113495bSYour Name 				afc_rsp->op_obj[i].chan_eirp[j].eirp;
1013*5113495bSYour Name 			chan_eirp++;
1014*5113495bSYour Name 		}
1015*5113495bSYour Name 		op = (struct wlan_afc_resp_opclass_info *)chan_eirp;
1016*5113495bSYour Name 	}
1017*5113495bSYour Name 
1018*5113495bSYour Name 	host_afc->length = tmp_len;
1019*5113495bSYour Name 
1020*5113495bSYour Name 	return tmp_len;
1021*5113495bSYour Name }
1022*5113495bSYour Name 
wlan_cfg80211_vendor_afc_response(struct wlan_objmgr_psoc * psoc,struct wlan_objmgr_pdev * pdev,const void * data,int data_len)1023*5113495bSYour Name int wlan_cfg80211_vendor_afc_response(struct wlan_objmgr_psoc *psoc,
1024*5113495bSYour Name 				      struct wlan_objmgr_pdev *pdev,
1025*5113495bSYour Name 				      const void *data,
1026*5113495bSYour Name 				      int data_len)
1027*5113495bSYour Name {
1028*5113495bSYour Name 	int ret = -EINVAL;
1029*5113495bSYour Name 	struct nlattr *attr[QCA_WLAN_VENDOR_ATTR_AFC_RESP_MAX + 1];
1030*5113495bSYour Name 	struct afc_resp_extracted *afc_rsp;
1031*5113495bSYour Name 	struct wlan_afc_host_resp *host_afc;
1032*5113495bSYour Name 	struct reg_afc_resp_rx_ind_info afc_ind_obj;
1033*5113495bSYour Name 	bool is_json = is_target_support_json_format(psoc);
1034*5113495bSYour Name 
1035*5113495bSYour Name 	if (wlan_cfg80211_nla_parse(attr, QCA_WLAN_VENDOR_ATTR_AFC_RESP_MAX,
1036*5113495bSYour Name 				    data, data_len,
1037*5113495bSYour Name 				    wlan_cfg80211_afc_response_policy)) {
1038*5113495bSYour Name 		osif_err("Invalid AFC RESP ATTR");
1039*5113495bSYour Name 		return ret;
1040*5113495bSYour Name 	}
1041*5113495bSYour Name 
1042*5113495bSYour Name 	afc_rsp = extract_afc_resp(attr);
1043*5113495bSYour Name 	if (!afc_rsp)
1044*5113495bSYour Name 		return ret;
1045*5113495bSYour Name 
1046*5113495bSYour Name 	afc_response_display(afc_rsp);
1047*5113495bSYour Name 
1048*5113495bSYour Name 	host_afc = qdf_mem_malloc(IF_AFC_RESPONSE_MAX_LEN);
1049*5113495bSYour Name 	if (!host_afc)
1050*5113495bSYour Name 		goto fail;
1051*5113495bSYour Name 
1052*5113495bSYour Name 	ret = fill_host_afc_response_buffer(psoc, afc_rsp, host_afc);
1053*5113495bSYour Name 	if (ret <= 0)
1054*5113495bSYour Name 		goto fail;
1055*5113495bSYour Name 
1056*5113495bSYour Name 	ret = ucfg_afc_data_send(psoc, pdev, host_afc, ret);
1057*5113495bSYour Name 	if (ret) {
1058*5113495bSYour Name 		osif_err("Failed to send afc data");
1059*5113495bSYour Name 		goto fail;
1060*5113495bSYour Name 	}
1061*5113495bSYour Name 
1062*5113495bSYour Name 	afc_ind_obj.cmd_type = REG_AFC_CMD_SERV_RESP_READY;
1063*5113495bSYour Name 	afc_ind_obj.serv_resp_format =
1064*5113495bSYour Name 				is_json ? REG_AFC_SERV_RESP_FORMAT_JSON :
1065*5113495bSYour Name 				REG_AFC_SERV_RESP_FORMAT_BINARY;
1066*5113495bSYour Name 	if (ucfg_reg_send_afc_resp_rx_ind(pdev, &afc_ind_obj) !=
1067*5113495bSYour Name 	    QDF_STATUS_SUCCESS) {
1068*5113495bSYour Name 		osif_err("Failed to send afc rx indication");
1069*5113495bSYour Name 		ret = -EINVAL;
1070*5113495bSYour Name 	}
1071*5113495bSYour Name 
1072*5113495bSYour Name fail:
1073*5113495bSYour Name 	free_extract_afc_rsp(afc_rsp);
1074*5113495bSYour Name 	qdf_mem_free(host_afc);
1075*5113495bSYour Name 	return ret;
1076*5113495bSYour Name }
1077