cnss_nl.c 6.3 KB

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