rmnet_ctl_client.c 4.3 KB

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