1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. 4 * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. 5 */ 6 7 #include <net/genetlink.h> 8 #ifdef CONFIG_CNSS_OUT_OF_TREE 9 #include "cnss_nl.h" 10 #else 11 #include <net/cnss_nl.h> 12 #endif 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/version.h> 16 17 #define CLD80211_GENL_NAME "cld80211" 18 19 #define CLD80211_MULTICAST_GROUP_SVC_MSGS "svc_msgs" 20 #define CLD80211_MULTICAST_GROUP_HOST_LOGS "host_logs" 21 #define CLD80211_MULTICAST_GROUP_FW_LOGS "fw_logs" 22 #define CLD80211_MULTICAST_GROUP_PER_PKT_STATS "per_pkt_stats" 23 #define CLD80211_MULTICAST_GROUP_DIAG_EVENTS "diag_events" 24 #define CLD80211_MULTICAST_GROUP_FATAL_EVENTS "fatal_events" 25 #define CLD80211_MULTICAST_GROUP_OEM_MSGS "oem_msgs" 26 27 static const struct genl_multicast_group nl_mcgrps[] = { 28 [CLD80211_MCGRP_SVC_MSGS] = { .name = 29 CLD80211_MULTICAST_GROUP_SVC_MSGS}, 30 [CLD80211_MCGRP_HOST_LOGS] = { .name = 31 CLD80211_MULTICAST_GROUP_HOST_LOGS}, 32 [CLD80211_MCGRP_FW_LOGS] = { .name = 33 CLD80211_MULTICAST_GROUP_FW_LOGS}, 34 [CLD80211_MCGRP_PER_PKT_STATS] = { .name = 35 CLD80211_MULTICAST_GROUP_PER_PKT_STATS}, 36 [CLD80211_MCGRP_DIAG_EVENTS] = { .name = 37 CLD80211_MULTICAST_GROUP_DIAG_EVENTS}, 38 [CLD80211_MCGRP_FATAL_EVENTS] = { .name = 39 CLD80211_MULTICAST_GROUP_FATAL_EVENTS}, 40 [CLD80211_MCGRP_OEM_MSGS] = { .name = 41 CLD80211_MULTICAST_GROUP_OEM_MSGS}, 42 }; 43 44 struct cld_ops { 45 cld80211_cb cb; 46 void *cb_ctx; 47 }; 48 49 struct cld80211_nl_data { 50 struct cld_ops cld_ops[CLD80211_MAX_COMMANDS]; 51 }; 52 53 static struct cld80211_nl_data nl_data; 54 55 static inline struct cld80211_nl_data *get_local_ctx(void) 56 { 57 return &nl_data; 58 } 59 60 static struct genl_ops nl_ops[CLD80211_MAX_COMMANDS]; 61 62 /* policy for the attributes */ 63 static const struct nla_policy cld80211_policy[CLD80211_ATTR_MAX + 1] = { 64 [CLD80211_ATTR_VENDOR_DATA] = { .type = NLA_NESTED }, 65 [CLD80211_ATTR_DATA] = { .type = NLA_BINARY, 66 .len = CLD80211_MAX_NL_DATA }, 67 [CLD80211_ATTR_META_DATA] = { .type = NLA_BINARY, 68 .len = CLD80211_MAX_NL_DATA }, 69 [CLD80211_ATTR_CMD] = { .type = NLA_U32 }, 70 [CLD80211_ATTR_CMD_TAG_DATA] = { .type = NLA_NESTED }, 71 }; 72 73 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0)) 74 static int cld80211_pre_doit(const struct genl_split_ops *ops, 75 struct sk_buff *skb, 76 struct genl_info *info) 77 { 78 u8 cmd_id = ops->cmd; 79 struct cld80211_nl_data *nl = get_local_ctx(); 80 81 if (cmd_id < 1 || cmd_id > CLD80211_MAX_COMMANDS) { 82 pr_err("CLD80211: Command Not supported: %u\n", cmd_id); 83 return -EOPNOTSUPP; 84 } 85 info->user_ptr[0] = nl->cld_ops[cmd_id - 1].cb; 86 info->user_ptr[1] = nl->cld_ops[cmd_id - 1].cb_ctx; 87 88 return 0; 89 } 90 #else 91 static int cld80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, 92 struct genl_info *info) 93 { 94 u8 cmd_id = ops->cmd; 95 struct cld80211_nl_data *nl = get_local_ctx(); 96 97 if (cmd_id < 1 || cmd_id > CLD80211_MAX_COMMANDS) { 98 pr_err("CLD80211: Command Not supported: %u\n", cmd_id); 99 return -EOPNOTSUPP; 100 } 101 info->user_ptr[0] = nl->cld_ops[cmd_id - 1].cb; 102 info->user_ptr[1] = nl->cld_ops[cmd_id - 1].cb_ctx; 103 104 return 0; 105 } 106 #endif 107 /* The netlink family */ 108 static struct genl_family cld80211_fam __ro_after_init = { 109 .name = CLD80211_GENL_NAME, 110 .hdrsize = 0, /* no private header */ 111 .version = 1, /* no particular meaning now */ 112 .maxattr = CLD80211_ATTR_MAX, 113 .policy = cld80211_policy, 114 .netnsok = true, 115 .pre_doit = cld80211_pre_doit, 116 .post_doit = NULL, 117 .module = THIS_MODULE, 118 .ops = nl_ops, 119 .n_ops = ARRAY_SIZE(nl_ops), 120 .mcgrps = nl_mcgrps, 121 .n_mcgrps = ARRAY_SIZE(nl_mcgrps), 122 }; 123 124 int register_cld_cmd_cb(u8 cmd_id, cld80211_cb func, void *cb_ctx) 125 { 126 struct cld80211_nl_data *nl = get_local_ctx(); 127 128 pr_debug("CLD80211: Registering command: %d\n", cmd_id); 129 if (!cmd_id || cmd_id > CLD80211_MAX_COMMANDS) { 130 pr_debug("CLD80211: invalid command: %d\n", cmd_id); 131 return -EINVAL; 132 } 133 134 nl->cld_ops[cmd_id - 1].cb = func; 135 nl->cld_ops[cmd_id - 1].cb_ctx = cb_ctx; 136 137 return 0; 138 } 139 EXPORT_SYMBOL(register_cld_cmd_cb); 140 141 int deregister_cld_cmd_cb(u8 cmd_id) 142 { 143 struct cld80211_nl_data *nl = get_local_ctx(); 144 145 pr_debug("CLD80211: De-registering command: %d\n", cmd_id); 146 if (!cmd_id || cmd_id > CLD80211_MAX_COMMANDS) { 147 pr_debug("CLD80211: invalid command: %d\n", cmd_id); 148 return -EINVAL; 149 } 150 151 nl->cld_ops[cmd_id - 1].cb = NULL; 152 nl->cld_ops[cmd_id - 1].cb_ctx = NULL; 153 154 return 0; 155 } 156 EXPORT_SYMBOL(deregister_cld_cmd_cb); 157 158 struct genl_family *cld80211_get_genl_family(void) 159 { 160 return &cld80211_fam; 161 } 162 EXPORT_SYMBOL(cld80211_get_genl_family); 163 164 static int cld80211_doit(struct sk_buff *skb, struct genl_info *info) 165 { 166 cld80211_cb cld_cb; 167 void *cld_ctx; 168 169 cld_cb = info->user_ptr[0]; 170 171 if (!cld_cb) { 172 pr_err("CLD80211: Not supported\n"); 173 return -EOPNOTSUPP; 174 } 175 cld_ctx = info->user_ptr[1]; 176 177 if (info->attrs[CLD80211_ATTR_VENDOR_DATA]) { 178 cld_cb(nla_data(info->attrs[CLD80211_ATTR_VENDOR_DATA]), 179 nla_len(info->attrs[CLD80211_ATTR_VENDOR_DATA]), 180 cld_ctx, info->snd_portid); 181 } else { 182 pr_err("CLD80211: No CLD80211_ATTR_VENDOR_DATA\n"); 183 return -EINVAL; 184 } 185 return 0; 186 } 187 188 static int __cld80211_init(void) 189 { 190 int err, i; 191 192 memset(&nl_ops[0], 0, sizeof(nl_ops)); 193 194 pr_info("CLD80211: Initializing\n"); 195 for (i = 0; i < CLD80211_MAX_COMMANDS; i++) { 196 nl_ops[i].cmd = i + 1; 197 nl_ops[i].doit = cld80211_doit; 198 nl_ops[i].flags = GENL_ADMIN_PERM; 199 } 200 201 err = genl_register_family(&cld80211_fam); 202 if (err) { 203 pr_err("CLD80211: Failed to register cld80211 family: %d\n", 204 err); 205 } 206 207 return err; 208 } 209 210 static void __cld80211_exit(void) 211 { 212 genl_unregister_family(&cld80211_fam); 213 } 214 215 /** 216 * cld80211_is_valid_dt_node_found - Check if valid device tree node present 217 * 218 * Valid device tree node means a node with "qcom,wlan" property present and 219 * "status" property not disabled. 220 * 221 * Return: true if valid device tree node found, false if not found 222 */ 223 static bool cld80211_is_valid_dt_node_found(void) 224 { 225 struct device_node *dn = NULL; 226 227 for_each_node_with_property(dn, "qcom,wlan") { 228 if (of_device_is_available(dn)) 229 break; 230 } 231 232 if (dn) 233 return true; 234 235 return false; 236 } 237 238 static int __init cld80211_init(void) 239 { 240 if (!cld80211_is_valid_dt_node_found()) 241 return -ENODEV; 242 243 return __cld80211_init(); 244 } 245 246 static void __exit cld80211_exit(void) 247 { 248 __cld80211_exit(); 249 } 250 251 module_init(cld80211_init); 252 module_exit(cld80211_exit); 253 254 MODULE_LICENSE("GPL v2"); 255 MODULE_DESCRIPTION("CNSS generic netlink module"); 256