1 /* 2 * Copyright (c) 2012-2020 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 #endif 37 38 #define PTT_SOCK_DEBUG 39 #ifdef PTT_SOCK_DEBUG 40 #define PTT_TRACE(level, args ...) QDF_TRACE(QDF_MODULE_ID_QDF, level, ## args) 41 #else 42 #define PTT_TRACE(level, args ...) 43 #endif 44 45 #ifdef PTT_SOCK_DEBUG_VERBOSE 46 /* Utility function to perform a hex dump */ 47 static void ptt_sock_dump_buf(const unsigned char *pbuf, int cnt) 48 { 49 int i; 50 51 for (i = 0; i < cnt; i++) { 52 if ((i % 16) == 0) 53 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO, 54 "\n%pK:", pbuf); 55 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO, " %02X", 56 *pbuf); 57 pbuf++; 58 } 59 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO, "\n"); 60 } 61 #endif 62 63 /** 64 * nl_srv_ucast_ptt() - Wrapper function to send ucast msgs to PTT 65 * @skb: sk buffer pointer 66 * @dst_pid: Destination PID 67 * @flag: flags 68 * 69 * Sends the ucast message to PTT with generic nl socket if CNSS_GENL 70 * is enabled. Else, use the legacy netlink socket to send. 71 * 72 * Return: zero on success, error code otherwise 73 */ 74 static int nl_srv_ucast_ptt(struct sk_buff *skb, int dst_pid, int flag) 75 { 76 #ifdef CNSS_GENL 77 return nl_srv_ucast(skb, dst_pid, flag, ANI_NL_MSG_PUMAC, 78 CLD80211_MCGRP_DIAG_EVENTS); 79 #else 80 return nl_srv_ucast(skb, dst_pid, flag); 81 #endif 82 } 83 84 /** 85 * nl_srv_bcast_ptt() - Wrapper function to send bcast msgs to DIAG mcast group 86 * @skb: sk buffer pointer 87 * 88 * Sends the bcast message to DIAG multicast group with generic nl socket 89 * if CNSS_GENL is enabled. Else, use the legacy netlink socket to send. 90 * 91 * Return: zero on success, error code otherwise 92 */ 93 static int nl_srv_bcast_ptt(struct sk_buff *skb) 94 { 95 #ifdef CNSS_GENL 96 return nl_srv_bcast(skb, CLD80211_MCGRP_DIAG_EVENTS, ANI_NL_MSG_PUMAC); 97 #else 98 return nl_srv_bcast(skb); 99 #endif 100 } 101 102 /** 103 * ptt_sock_send_msg_to_app() - Send nl message to user space 104 * wmsg: Message header 105 * radio: Unit number of the radio 106 * src_mod: Message type 107 * pid: Process ID to which message will be unicast. Message 108 * will be broadcast when PID is INVALID_PID 109 * 110 * Utility function to send a netlink message to an application in user space 111 * 112 * Return: 0 on success and negative value on failure 113 */ 114 int ptt_sock_send_msg_to_app(tAniHdr *wmsg, int radio, int src_mod, int pid) 115 { 116 int err = -1; 117 int payload_len; 118 int tot_msg_len; 119 tAniNlHdr *wnl; 120 struct sk_buff *skb; 121 struct nlmsghdr *nlh; 122 int wmsg_length = be16_to_cpu(wmsg->length); 123 static int nlmsg_seq; 124 125 if (radio < 0 || radio > ANI_MAX_RADIOS) { 126 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "%s: invalid radio id [%d]\n", 127 __func__, radio); 128 return -EINVAL; 129 } 130 payload_len = wmsg_length + sizeof(wnl->radio) + sizeof(*wmsg); 131 tot_msg_len = NLMSG_SPACE(payload_len); 132 skb = dev_alloc_skb(tot_msg_len); 133 if (!skb) { 134 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, 135 "%s: dev_alloc_skb() failed for msg size[%d]\n", 136 __func__, tot_msg_len); 137 return -ENOMEM; 138 } 139 nlh = 140 nlmsg_put(skb, pid, nlmsg_seq++, src_mod, payload_len, 141 NLM_F_REQUEST); 142 if (!nlh) { 143 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, 144 "%s: nlmsg_put() failed for msg size[%d]\n", __func__, 145 tot_msg_len); 146 kfree_skb(skb); 147 return -ENOMEM; 148 } 149 wnl = (tAniNlHdr *) nlh; 150 wnl->radio = radio; 151 memcpy(&wnl->wmsg, wmsg, wmsg_length); 152 #ifdef PTT_SOCK_DEBUG_VERBOSE 153 ptt_sock_dump_buf((const unsigned char *)skb->data, skb->len); 154 #endif 155 156 if (pid != INVALID_PID) 157 err = nl_srv_ucast_ptt(skb, pid, MSG_DONTWAIT); 158 else 159 err = nl_srv_bcast_ptt(skb); 160 161 if ((err < 0) && (err != -ESRCH)) 162 PTT_TRACE(QDF_TRACE_LEVEL_INFO, 163 "%s:Failed sending Msg Type [0x%X] to pid[%d]\n", 164 __func__, be16_to_cpu(wmsg->type), pid); 165 return err; 166 } 167 168 #ifdef CNSS_GENL 169 /** 170 * ptt_cmd_handler() - Handler function for PTT commands 171 * @data: Data to be parsed 172 * @data_len: Length of the data received 173 * @ctx: Registered context reference 174 * @pid: Process id of the user space application 175 * 176 * This function handles the command from PTT user space application 177 * 178 * Return: None 179 */ 180 static void ptt_cmd_handler(const void *data, int data_len, void *ctx, int pid) 181 { 182 uint16_t length; 183 struct sptt_app_reg_req *payload; 184 struct nlattr *tb[CLD80211_ATTR_MAX + 1]; 185 186 /* 187 * audit note: it is ok to pass a NULL policy here since a 188 * length check on the data is added later already 189 */ 190 if (wlan_cfg80211_nla_parse(tb, CLD80211_ATTR_MAX, 191 data, data_len, NULL)) { 192 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "Invalid ATTR"); 193 return; 194 } 195 196 if (!tb[CLD80211_ATTR_DATA]) { 197 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "attr ATTR_DATA failed"); 198 return; 199 } 200 201 if (nla_len(tb[CLD80211_ATTR_DATA]) < sizeof(struct sptt_app_reg_req)) { 202 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "%s:attr length check fails\n", 203 __func__); 204 return; 205 } 206 207 payload = (struct sptt_app_reg_req *)(nla_data(tb[CLD80211_ATTR_DATA])); 208 length = be16_to_cpu(payload->wmsg.length); 209 if ((USHRT_MAX - length) < (sizeof(payload->radio) + sizeof(tAniHdr))) { 210 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, 211 "u16 overflow length %d %zu %zu", 212 length, 213 sizeof(payload->radio), 214 sizeof(tAniHdr)); 215 return; 216 } 217 218 if (nla_len(tb[CLD80211_ATTR_DATA]) < (length + 219 sizeof(payload->radio) + 220 sizeof(tAniHdr))) { 221 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "ATTR_DATA len check failed"); 222 return; 223 } 224 225 switch (payload->wmsg.type) { 226 case ANI_MSG_APP_REG_REQ: 227 ptt_sock_send_msg_to_app(&payload->wmsg, payload->radio, 228 ANI_NL_MSG_PUMAC, pid); 229 break; 230 default: 231 PTT_TRACE(QDF_TRACE_LEVEL_ERROR, "Unknown msg type %d", 232 payload->wmsg.type); 233 break; 234 } 235 } 236 237 void ptt_sock_activate_svc(void) 238 { 239 register_cld_cmd_cb(ANI_NL_MSG_PUMAC, ptt_cmd_handler, NULL); 240 register_cld_cmd_cb(ANI_NL_MSG_PTT, ptt_cmd_handler, NULL); 241 } 242 243 void ptt_sock_deactivate_svc(void) 244 { 245 deregister_cld_cmd_cb(ANI_NL_MSG_PTT); 246 deregister_cld_cmd_cb(ANI_NL_MSG_PUMAC); 247 } 248 #endif 249 #endif /* PTT_SOCK_SVC_ENABLE */ 250