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