1 /* 2 * Copyright (c) 2012-2017 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 #include <net/cnss_nl.h> 35 #include <wlan_cfg80211.h> 36 #else 37 38 /** ptt Process ID */ 39 static int32_t ptt_pid = INVALID_PID; 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 == NULL) { 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 (NULL == 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) 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 #ifndef CNSS_GENL 173 /* 174 * Process tregisteration request and send registration response messages 175 * to the PTT Socket App in user space 176 */ 177 static void ptt_sock_proc_reg_req(tAniHdr *wmsg, int radio) 178 { 179 struct sAniAppRegReq *reg_req; 180 struct sAniNlAppRegRsp rspmsg; 181 182 reg_req = (struct sAniAppRegReq *) (wmsg + 1); 183 memset((char *)&rspmsg, 0, sizeof(rspmsg)); 184 /* send reg response message to the application */ 185 rspmsg.ret = ANI_NL_MSG_OK; 186 rspmsg.regReq.type = reg_req->type; 187 /*Save the pid */ 188 ptt_pid = reg_req->pid; 189 rspmsg.regReq.pid = reg_req->pid; 190 rspmsg.wniHdr.type = cpu_to_be16(ANI_MSG_APP_REG_RSP); 191 rspmsg.wniHdr.length = cpu_to_be16(sizeof(rspmsg)); 192 if (ptt_sock_send_msg_to_app((tAniHdr *) &rspmsg.wniHdr, radio, 193 ANI_NL_MSG_PUMAC, ptt_pid) < 0) { 194 PTT_TRACE(QDF_TRACE_LEVEL_INFO, 195 "%s: Error sending ANI_MSG_APP_REG_RSP to pid[%d]\n", 196 __func__, ptt_pid); 197 } 198 } 199 200 /* 201 * Process all the messages from the PTT Socket App in user space 202 */ 203 static void ptt_proc_pumac_msg(struct sk_buff *skb, tAniHdr *wmsg, int radio) 204 { 205 u16 ani_msg_type = be16_to_cpu(wmsg->type); 206 207 switch (ani_msg_type) { 208 case ANI_MSG_APP_REG_REQ: 209 PTT_TRACE(QDF_TRACE_LEVEL_INFO, 210 "%s: Received ANI_MSG_APP_REG_REQ [0x%X]\n", __func__, 211 ani_msg_type); 212 ptt_sock_proc_reg_req(wmsg, radio); 213 break; 214 default: 215 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, 216 "%s: Received Unknown Msg Type[0x%X]\n", __func__, 217 ani_msg_type); 218 break; 219 } 220 } 221 222 /* 223 * Process all the Netlink messages from PTT Socket app in user space 224 */ 225 static int ptt_sock_rx_nlink_msg(struct sk_buff *skb) 226 { 227 tAniNlHdr *wnl; 228 int radio; 229 int type; 230 231 wnl = (tAniNlHdr *) skb->data; 232 radio = wnl->radio; 233 type = wnl->nlh.nlmsg_type; 234 switch (type) { 235 case ANI_NL_MSG_PUMAC: /* Message from the PTT socket APP */ 236 PTT_TRACE(QDF_TRACE_LEVEL_INFO, 237 "%s: Received ANI_NL_MSG_PUMAC Msg [0x%X]\n", 238 __func__, type); 239 ptt_proc_pumac_msg(skb, &wnl->wmsg, radio); 240 break; 241 default: 242 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "%s: Unknown NL Msg [0x%X]\n", 243 __func__, type); 244 break; 245 } 246 return 0; 247 } 248 #endif 249 250 #ifdef CNSS_GENL 251 /** 252 * ptt_cmd_handler() - Handler function for PTT commands 253 * @data: Data to be parsed 254 * @data_len: Length of the data received 255 * @ctx: Registered context reference 256 * @pid: Process id of the user space application 257 * 258 * This function handles the command from PTT user space application 259 * 260 * Return: None 261 */ 262 static void ptt_cmd_handler(const void *data, int data_len, void *ctx, int pid) 263 { 264 uint16_t length; 265 struct sptt_app_reg_req *payload; 266 struct nlattr *tb[CLD80211_ATTR_MAX + 1]; 267 268 /* 269 * audit note: it is ok to pass a NULL policy here since a 270 * length check on the data is added later already 271 */ 272 if (wlan_cfg80211_nla_parse(tb, CLD80211_ATTR_MAX, 273 data, data_len, NULL)) { 274 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "Invalid ATTR"); 275 return; 276 } 277 278 if (!tb[CLD80211_ATTR_DATA]) { 279 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "attr ATTR_DATA failed"); 280 return; 281 } 282 283 if (nla_len(tb[CLD80211_ATTR_DATA]) < sizeof(struct sptt_app_reg_req)) { 284 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "%s:attr length check fails\n", 285 __func__); 286 return; 287 } 288 289 payload = (struct sptt_app_reg_req *)(nla_data(tb[CLD80211_ATTR_DATA])); 290 length = be16_to_cpu(payload->wmsg.length); 291 if ((USHRT_MAX - length) < (sizeof(payload->radio) + sizeof(tAniHdr))) { 292 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, 293 "u16 overflow length %d %zu %zu", 294 length, 295 sizeof(payload->radio), 296 sizeof(tAniHdr)); 297 return; 298 } 299 300 if (nla_len(tb[CLD80211_ATTR_DATA]) < (length + 301 sizeof(payload->radio) + 302 sizeof(tAniHdr))) { 303 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "ATTR_DATA len check failed"); 304 return; 305 } 306 307 switch (payload->wmsg.type) { 308 case ANI_MSG_APP_REG_REQ: 309 ptt_sock_send_msg_to_app(&payload->wmsg, payload->radio, 310 ANI_NL_MSG_PUMAC, pid); 311 break; 312 default: 313 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "Unknown msg type %d", 314 payload->wmsg.type); 315 break; 316 } 317 } 318 319 /** 320 * ptt_sock_activate_svc() - API to register PTT/PUMAC command handler 321 * 322 * API to register the PTT/PUMAC command handlers. Argument @pAdapter 323 * is sent for prototype compatibility between new genl and legacy 324 * implementation 325 * 326 * Return: 0 327 */ 328 int ptt_sock_activate_svc(void) 329 { 330 register_cld_cmd_cb(ANI_NL_MSG_PUMAC, ptt_cmd_handler, NULL); 331 register_cld_cmd_cb(ANI_NL_MSG_PTT, ptt_cmd_handler, NULL); 332 return 0; 333 } 334 335 /** 336 * ptt_sock_deactivate_svc() - Dummy API to deactivate PTT service 337 * 338 * Return: Void 339 */ 340 void ptt_sock_deactivate_svc(void) 341 { 342 } 343 #else 344 345 /** 346 * ptt_sock_activate_svc() - activate PTT service 347 * 348 * Return: 0 349 */ 350 int ptt_sock_activate_svc(void) 351 { 352 ptt_pid = INVALID_PID; 353 nl_srv_register(ANI_NL_MSG_PUMAC, ptt_sock_rx_nlink_msg); 354 nl_srv_register(ANI_NL_MSG_PTT, ptt_sock_rx_nlink_msg); 355 return 0; 356 } 357 358 /** 359 * ptt_sock_deactivate_svc() - deactivate PTT service 360 * 361 * Return: Void 362 */ 363 void ptt_sock_deactivate_svc(void) 364 { 365 ptt_pid = INVALID_PID; 366 } 367 #endif 368 #endif /* PTT_SOCK_SVC_ENABLE */ 369