1 /*
2  * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /******************************************************************************
21 * wlan_ptt_sock_svc.c
22 *
23 ******************************************************************************/
24 #ifdef PTT_SOCK_SVC_ENABLE
25 #include <wlan_nlink_srv.h>
26 #include <qdf_types.h>
27 #include <qdf_status.h>
28 #include <qdf_trace.h>
29 #include <wlan_nlink_common.h>
30 #include <wlan_ptt_sock_svc.h>
31 #include <qdf_types.h>
32 #include <qdf_trace.h>
33 
34 #ifdef CNSS_GENL
35 #ifdef CONFIG_CNSS_OUT_OF_TREE
36 #include "cnss_nl.h"
37 #else
38 #include <net/cnss_nl.h>
39 #endif
40 #include <wlan_cfg80211.h>
41 #endif
42 
43 #define PTT_SOCK_DEBUG
44 #ifdef PTT_SOCK_DEBUG
45 #define PTT_TRACE(level, args ...) QDF_TRACE(QDF_MODULE_ID_QDF, level, ## args)
46 #else
47 #define PTT_TRACE(level, args ...)
48 #endif
49 
50 #ifdef PTT_SOCK_DEBUG_VERBOSE
51 /* Utility function to perform a hex dump */
ptt_sock_dump_buf(const unsigned char * pbuf,int cnt)52 static void ptt_sock_dump_buf(const unsigned char *pbuf, int cnt)
53 {
54 	int i;
55 
56 	for (i = 0; i < cnt; i++) {
57 		if ((i % 16) == 0)
58 			QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO,
59 				  "\n%pK:", pbuf);
60 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO, " %02X",
61 			  *pbuf);
62 		pbuf++;
63 	}
64 	QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO, "\n");
65 }
66 #endif
67 
68 /**
69  * nl_srv_ucast_ptt() - Wrapper function to send ucast msgs to PTT
70  * @skb: sk buffer pointer
71  * @dst_pid: Destination PID
72  * @flag: flags
73  *
74  * Sends the ucast message to PTT with generic nl socket if CNSS_GENL
75  * is enabled. Else, use the legacy netlink socket to send.
76  *
77  * Return: zero on success, error code otherwise
78  */
nl_srv_ucast_ptt(struct sk_buff * skb,int dst_pid,int flag)79 static int nl_srv_ucast_ptt(struct sk_buff *skb, int dst_pid, int flag)
80 {
81 #ifdef CNSS_GENL
82 	return nl_srv_ucast(skb, dst_pid, flag, ANI_NL_MSG_PUMAC,
83 				CLD80211_MCGRP_DIAG_EVENTS);
84 #else
85 	return nl_srv_ucast(skb, dst_pid, flag);
86 #endif
87 }
88 
89 /**
90  * nl_srv_bcast_ptt() - Wrapper function to send bcast msgs to DIAG mcast group
91  * @skb: sk buffer pointer
92  *
93  * Sends the bcast message to DIAG multicast group with generic nl socket
94  * if CNSS_GENL is enabled. Else, use the legacy netlink socket to send.
95  *
96  * Return: zero on success, error code otherwise
97  */
nl_srv_bcast_ptt(struct sk_buff * skb)98 static int nl_srv_bcast_ptt(struct sk_buff *skb)
99 {
100 #ifdef CNSS_GENL
101 	return nl_srv_bcast(skb, CLD80211_MCGRP_DIAG_EVENTS, ANI_NL_MSG_PUMAC);
102 #else
103 	return nl_srv_bcast(skb);
104 #endif
105 }
106 
107 /**
108  * ptt_sock_send_msg_to_app() - Send nl message to user space
109  * wmsg: Message header
110  * radio: Unit number of the radio
111  * src_mod: Message type
112  * pid: Process ID to which message will be unicast. Message
113  * will be broadcast when PID is INVALID_PID
114  *
115  * Utility function to send a netlink message to an application in user space
116  *
117  * Return: 0 on success and negative value on failure
118  */
ptt_sock_send_msg_to_app(tAniHdr * wmsg,int radio,int src_mod,int pid)119 int ptt_sock_send_msg_to_app(tAniHdr *wmsg, int radio, int src_mod, int pid)
120 {
121 	int err = -1;
122 	int payload_len;
123 	int tot_msg_len;
124 	tAniNlHdr *wnl;
125 	struct sk_buff *skb;
126 	struct nlmsghdr *nlh;
127 	int wmsg_length = be16_to_cpu(wmsg->length);
128 	static int nlmsg_seq;
129 
130 	if (radio < 0 || radio > ANI_MAX_RADIOS) {
131 		PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "%s: invalid radio id [%d]\n",
132 			  __func__, radio);
133 		return -EINVAL;
134 	}
135 	payload_len = wmsg_length + sizeof(wnl->radio) + sizeof(*wmsg);
136 	tot_msg_len = NLMSG_SPACE(payload_len);
137 	skb = dev_alloc_skb(tot_msg_len);
138 	if (!skb) {
139 		PTT_TRACE(QDF_TRACE_LEVEL_ERROR,
140 			  "%s: dev_alloc_skb() failed for msg size[%d]\n",
141 			  __func__, tot_msg_len);
142 		return -ENOMEM;
143 	}
144 	nlh =
145 		nlmsg_put(skb, pid, nlmsg_seq++, src_mod, payload_len,
146 			  NLM_F_REQUEST);
147 	if (!nlh) {
148 		PTT_TRACE(QDF_TRACE_LEVEL_ERROR,
149 			  "%s: nlmsg_put() failed for msg size[%d]\n", __func__,
150 			  tot_msg_len);
151 		kfree_skb(skb);
152 		return -ENOMEM;
153 	}
154 	wnl = (tAniNlHdr *) nlh;
155 	wnl->radio = radio;
156 
157 	/* Offset of data buffer from nlmsg_hdr + sizeof(int) radio */
158 	memcpy(nlmsg_data(nlh) + sizeof(wnl->radio), wmsg, wmsg_length);
159 #ifdef PTT_SOCK_DEBUG_VERBOSE
160 	ptt_sock_dump_buf((const unsigned char *)skb->data, skb->len);
161 #endif
162 
163 	if (pid != INVALID_PID)
164 		err = nl_srv_ucast_ptt(skb, pid, MSG_DONTWAIT);
165 	else
166 		err = nl_srv_bcast_ptt(skb);
167 
168 	if ((err < 0) && (err != -ESRCH))
169 		PTT_TRACE(QDF_TRACE_LEVEL_INFO,
170 			  "%s:Failed sending Msg Type [0x%X] to pid[%d]\n",
171 			  __func__, be16_to_cpu(wmsg->type), pid);
172 	return err;
173 }
174 
175 #ifdef CNSS_GENL
176 /**
177  * ptt_cmd_handler() - Handler function for PTT commands
178  * @data: Data to be parsed
179  * @data_len: Length of the data received
180  * @ctx: Registered context reference
181  * @pid: Process id of the user space application
182  *
183  * This function handles the command from PTT user space application
184  *
185  * Return: None
186  */
ptt_cmd_handler(const void * data,int data_len,void * ctx,int pid)187 static void ptt_cmd_handler(const void *data, int data_len, void *ctx, int pid)
188 {
189 	uint16_t length;
190 	struct sptt_app_reg_req *payload;
191 	struct nlattr *tb[CLD80211_ATTR_MAX + 1];
192 
193 	/*
194 	 * audit note: it is ok to pass a NULL policy here since a
195 	 * length check on the data is added later already
196 	 */
197 	if (wlan_cfg80211_nla_parse(tb, CLD80211_ATTR_MAX,
198 				    data, data_len, NULL)) {
199 		PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "Invalid ATTR");
200 		return;
201 	}
202 
203 	if (!tb[CLD80211_ATTR_DATA]) {
204 		PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "attr ATTR_DATA failed");
205 		return;
206 	}
207 
208 	if (nla_len(tb[CLD80211_ATTR_DATA]) < sizeof(struct sptt_app_reg_req)) {
209 		PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "%s:attr length check fails\n",
210 			__func__);
211 		return;
212 	}
213 
214 	payload = (struct sptt_app_reg_req *)(nla_data(tb[CLD80211_ATTR_DATA]));
215 	length = be16_to_cpu(payload->wmsg.length);
216 	if ((USHRT_MAX - length) < (sizeof(payload->radio) + sizeof(tAniHdr))) {
217 		PTT_TRACE(QDF_TRACE_LEVEL_ERROR,
218 			"u16 overflow length %d %zu %zu",
219 			length,
220 			sizeof(payload->radio),
221 			sizeof(tAniHdr));
222 		return;
223 	}
224 
225 	if (nla_len(tb[CLD80211_ATTR_DATA]) <  (length +
226 						sizeof(payload->radio) +
227 						sizeof(tAniHdr))) {
228 		PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "ATTR_DATA len check failed");
229 		return;
230 	}
231 
232 	switch (payload->wmsg.type) {
233 	case ANI_MSG_APP_REG_REQ:
234 		ptt_sock_send_msg_to_app(&payload->wmsg, payload->radio,
235 							ANI_NL_MSG_PUMAC, pid);
236 		break;
237 	default:
238 		PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "Unknown msg type %d",
239 							payload->wmsg.type);
240 		break;
241 	}
242 }
243 
ptt_sock_activate_svc(void)244 void ptt_sock_activate_svc(void)
245 {
246 	register_cld_cmd_cb(ANI_NL_MSG_PUMAC, ptt_cmd_handler, NULL);
247 	register_cld_cmd_cb(ANI_NL_MSG_PTT, ptt_cmd_handler, NULL);
248 }
249 
ptt_sock_deactivate_svc(void)250 void ptt_sock_deactivate_svc(void)
251 {
252 	deregister_cld_cmd_cb(ANI_NL_MSG_PTT);
253 	deregister_cld_cmd_cb(ANI_NL_MSG_PUMAC);
254 }
255 #endif
256 #endif /* PTT_SOCK_SVC_ENABLE */
257