rmnet_ctl_client.c 4.9 KB

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