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