rmnet_ctl_client.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
  3. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  4. *
  5. * RMNET_CTL client handlers
  6. *
  7. */
  8. #include <linux/debugfs.h>
  9. #include <linux/ipc_logging.h>
  10. #include <linux/version.h>
  11. #include "rmnet_ctl.h"
  12. #include "rmnet_ctl_client.h"
  13. #define RMNET_CTL_LOG_PAGE 10
  14. #define RMNET_CTL_LOG_NAME "rmnet_ctl"
  15. #define RMNET_CTL_LOG_LVL "ipc_log_lvl"
  16. struct rmnet_ctl_client {
  17. struct rmnet_ctl_client_hooks hooks;
  18. };
  19. struct rmnet_ctl_endpoint {
  20. struct rmnet_ctl_dev __rcu *dev;
  21. struct rmnet_ctl_client __rcu *client;
  22. struct dentry *dbgfs_dir;
  23. struct dentry *dbgfs_loglvl;
  24. void *ipc_log;
  25. };
  26. #if defined(CONFIG_IPA_DEBUG) || defined(CONFIG_MHI_DEBUG)
  27. #define CONFIG_RMNET_CTL_DEBUG 1
  28. #endif
  29. #ifdef CONFIG_RMNET_CTL_DEBUG
  30. static u8 ipc_log_lvl = RMNET_CTL_LOG_DEBUG;
  31. #else
  32. static u8 ipc_log_lvl = RMNET_CTL_LOG_ERR;
  33. #endif
  34. static DEFINE_SPINLOCK(client_lock);
  35. static struct rmnet_ctl_endpoint ctl_ep;
  36. void rmnet_ctl_set_dbgfs(bool enable)
  37. {
  38. if (enable) {
  39. if (IS_ERR_OR_NULL(ctl_ep.dbgfs_dir))
  40. ctl_ep.dbgfs_dir = debugfs_create_dir(
  41. RMNET_CTL_LOG_NAME, NULL);
  42. if (!IS_ERR_OR_NULL(ctl_ep.dbgfs_dir))
  43. #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
  44. ctl_ep.dbgfs_loglvl = debugfs_create_u8(
  45. RMNET_CTL_LOG_LVL, 0644, ctl_ep.dbgfs_dir,
  46. &ipc_log_lvl);
  47. #else
  48. debugfs_create_u8((const char *) RMNET_CTL_LOG_LVL,
  49. (umode_t) 0644,
  50. (struct dentry *) ctl_ep.dbgfs_dir,
  51. (u8 *) &ipc_log_lvl);
  52. #endif
  53. if (!ctl_ep.ipc_log)
  54. ctl_ep.ipc_log = ipc_log_context_create(
  55. RMNET_CTL_LOG_PAGE, RMNET_CTL_LOG_NAME, 0);
  56. } else {
  57. debugfs_remove_recursive(ctl_ep.dbgfs_dir);
  58. ipc_log_context_destroy(ctl_ep.ipc_log);
  59. ctl_ep.dbgfs_dir = NULL;
  60. ctl_ep.dbgfs_loglvl = NULL;
  61. ctl_ep.ipc_log = NULL;
  62. }
  63. }
  64. void rmnet_ctl_endpoint_setdev(const struct rmnet_ctl_dev *dev)
  65. {
  66. rcu_assign_pointer(ctl_ep.dev, dev);
  67. }
  68. void rmnet_ctl_endpoint_post(const void *data, size_t len)
  69. {
  70. struct rmnet_ctl_client *client;
  71. struct sk_buff *skb;
  72. if (unlikely(!data || !len))
  73. return;
  74. if (len == 0xFFFFFFFF) {
  75. skb = (struct sk_buff *)data;
  76. rmnet_ctl_log_info("RX", skb->data, skb->len);
  77. rcu_read_lock();
  78. client = rcu_dereference(ctl_ep.client);
  79. if (client && client->hooks.ctl_dl_client_hook) {
  80. skb->protocol = htons(ETH_P_MAP);
  81. client->hooks.ctl_dl_client_hook(skb);
  82. } else {
  83. kfree(skb);
  84. }
  85. rcu_read_unlock();
  86. } else {
  87. rmnet_ctl_log_info("RX", data, len);
  88. rcu_read_lock();
  89. client = rcu_dereference(ctl_ep.client);
  90. if (client && client->hooks.ctl_dl_client_hook) {
  91. skb = alloc_skb(len, GFP_ATOMIC);
  92. if (skb) {
  93. skb_put_data(skb, data, len);
  94. skb->protocol = htons(ETH_P_MAP);
  95. client->hooks.ctl_dl_client_hook(skb);
  96. }
  97. }
  98. rcu_read_unlock();
  99. }
  100. }
  101. void *rmnet_ctl_register_client(struct rmnet_ctl_client_hooks *hook)
  102. {
  103. struct rmnet_ctl_client *client;
  104. if (!hook)
  105. return NULL;
  106. client = kzalloc(sizeof(*client), GFP_KERNEL);
  107. if (!client)
  108. return NULL;
  109. client->hooks = *hook;
  110. spin_lock(&client_lock);
  111. /* Only support one client for now */
  112. if (rcu_dereference(ctl_ep.client)) {
  113. spin_unlock(&client_lock);
  114. kfree(client);
  115. return NULL;
  116. }
  117. rcu_assign_pointer(ctl_ep.client, client);
  118. spin_unlock(&client_lock);
  119. rmnet_ctl_set_dbgfs(true);
  120. return client;
  121. }
  122. EXPORT_SYMBOL(rmnet_ctl_register_client);
  123. int rmnet_ctl_unregister_client(void *handle)
  124. {
  125. struct rmnet_ctl_client *client = (struct rmnet_ctl_client *)handle;
  126. spin_lock(&client_lock);
  127. if (rcu_dereference(ctl_ep.client) != client) {
  128. spin_unlock(&client_lock);
  129. return -EINVAL;
  130. }
  131. RCU_INIT_POINTER(ctl_ep.client, NULL);
  132. spin_unlock(&client_lock);
  133. synchronize_rcu();
  134. kfree(client);
  135. rmnet_ctl_set_dbgfs(false);
  136. return 0;
  137. }
  138. EXPORT_SYMBOL(rmnet_ctl_unregister_client);
  139. int rmnet_ctl_send_client(void *handle, struct sk_buff *skb)
  140. {
  141. struct rmnet_ctl_client *client = (struct rmnet_ctl_client *)handle;
  142. struct rmnet_ctl_dev *dev;
  143. int rc = -EINVAL;
  144. if (client != rcu_dereference(ctl_ep.client)) {
  145. kfree_skb(skb);
  146. return rc;
  147. }
  148. rmnet_ctl_log_info("TX", skb->data, skb->len);
  149. rcu_read_lock();
  150. dev = rcu_dereference(ctl_ep.dev);
  151. if (dev && dev->xmit)
  152. rc = dev->xmit(dev, skb);
  153. else
  154. kfree_skb(skb);
  155. rcu_read_unlock();
  156. if (rc)
  157. rmnet_ctl_log_err("TXE", rc, NULL, 0);
  158. return rc;
  159. }
  160. EXPORT_SYMBOL(rmnet_ctl_send_client);
  161. void rmnet_ctl_log(enum rmnet_ctl_log_lvl lvl, const char *msg,
  162. int rc, const void *data, unsigned int len)
  163. {
  164. if (lvl <= ipc_log_lvl && ctl_ep.ipc_log) {
  165. if (data == NULL || len == 0)
  166. ipc_log_string(ctl_ep.ipc_log, "%3s(%d): (null)\n",
  167. msg, rc);
  168. else
  169. ipc_log_string(ctl_ep.ipc_log, "%3s(%d): %*ph\n",
  170. msg, rc, len > 32 ? 32 : len, data);
  171. }
  172. }
  173. EXPORT_SYMBOL(rmnet_ctl_log);
  174. static struct rmnet_ctl_client_if client_if = {
  175. .reg = rmnet_ctl_register_client,
  176. .dereg = rmnet_ctl_unregister_client,
  177. .send = rmnet_ctl_send_client,
  178. .log = rmnet_ctl_log,
  179. };
  180. struct rmnet_ctl_client_if *rmnet_ctl_if(void)
  181. {
  182. return &client_if;
  183. }
  184. EXPORT_SYMBOL(rmnet_ctl_if);
  185. int rmnet_ctl_get_stats(u64 *s, int n)
  186. {
  187. struct rmnet_ctl_dev *dev;
  188. rcu_read_lock();
  189. dev = rcu_dereference(ctl_ep.dev);
  190. if (dev && n > 0) {
  191. n = min(n, (int)(sizeof(dev->stats) / sizeof(u64)));
  192. memcpy(s, &dev->stats, n * sizeof(u64));
  193. }
  194. rcu_read_unlock();
  195. return n;
  196. }
  197. EXPORT_SYMBOL(rmnet_ctl_get_stats);