subsystem_notif.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /* Copyright (c) 2011, 2013, The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. *
  13. * Subsystem Notifier -- Provides notifications
  14. * of subsys events.
  15. *
  16. * Use subsys_notif_register_notifier to register for notifications
  17. * and subsys_notif_queue_notification to send notifications.
  18. *
  19. */
  20. #include <linux/notifier.h>
  21. #include <linux/init.h>
  22. #include <linux/debugfs.h>
  23. #include <linux/module.h>
  24. #include <linux/workqueue.h>
  25. #include <linux/stringify.h>
  26. #include <linux/delay.h>
  27. #include <linux/slab.h>
  28. #include <mach/subsystem_notif.h>
  29. struct subsys_notif_info {
  30. char name[50];
  31. struct srcu_notifier_head subsys_notif_rcvr_list;
  32. struct list_head list;
  33. };
  34. static LIST_HEAD(subsystem_list);
  35. static DEFINE_MUTEX(notif_lock);
  36. static DEFINE_MUTEX(notif_add_lock);
  37. #if defined(SUBSYS_RESTART_DEBUG)
  38. static void subsys_notif_reg_test_notifier(const char *);
  39. #endif
  40. static struct subsys_notif_info *_notif_find_subsys(const char *subsys_name)
  41. {
  42. struct subsys_notif_info *subsys;
  43. mutex_lock(&notif_lock);
  44. list_for_each_entry(subsys, &subsystem_list, list)
  45. if (!strncmp(subsys->name, subsys_name,
  46. ARRAY_SIZE(subsys->name))) {
  47. mutex_unlock(&notif_lock);
  48. return subsys;
  49. }
  50. mutex_unlock(&notif_lock);
  51. return NULL;
  52. }
  53. void *subsys_notif_register_notifier(
  54. const char *subsys_name, struct notifier_block *nb)
  55. {
  56. int ret;
  57. struct subsys_notif_info *subsys = _notif_find_subsys(subsys_name);
  58. if (!subsys) {
  59. /* Possible first time reference to this subsystem. Add it. */
  60. subsys = (struct subsys_notif_info *)
  61. subsys_notif_add_subsys(subsys_name);
  62. if (!subsys)
  63. return ERR_PTR(-EINVAL);
  64. }
  65. ret = srcu_notifier_chain_register(
  66. &subsys->subsys_notif_rcvr_list, nb);
  67. if (ret < 0)
  68. return ERR_PTR(ret);
  69. return subsys;
  70. }
  71. EXPORT_SYMBOL(subsys_notif_register_notifier);
  72. int subsys_notif_unregister_notifier(void *subsys_handle,
  73. struct notifier_block *nb)
  74. {
  75. int ret;
  76. struct subsys_notif_info *subsys =
  77. (struct subsys_notif_info *)subsys_handle;
  78. if (!subsys)
  79. return -EINVAL;
  80. ret = srcu_notifier_chain_unregister(
  81. &subsys->subsys_notif_rcvr_list, nb);
  82. return ret;
  83. }
  84. EXPORT_SYMBOL(subsys_notif_unregister_notifier);
  85. void *subsys_notif_add_subsys(const char *subsys_name)
  86. {
  87. struct subsys_notif_info *subsys = NULL;
  88. if (!subsys_name)
  89. goto done;
  90. mutex_lock(&notif_add_lock);
  91. subsys = _notif_find_subsys(subsys_name);
  92. if (subsys) {
  93. mutex_unlock(&notif_add_lock);
  94. goto done;
  95. }
  96. subsys = kmalloc(sizeof(struct subsys_notif_info), GFP_KERNEL);
  97. if (!subsys) {
  98. mutex_unlock(&notif_add_lock);
  99. return ERR_PTR(-EINVAL);
  100. }
  101. strlcpy(subsys->name, subsys_name, ARRAY_SIZE(subsys->name));
  102. srcu_init_notifier_head(&subsys->subsys_notif_rcvr_list);
  103. INIT_LIST_HEAD(&subsys->list);
  104. mutex_lock(&notif_lock);
  105. list_add_tail(&subsys->list, &subsystem_list);
  106. mutex_unlock(&notif_lock);
  107. #if defined(SUBSYS_RESTART_DEBUG)
  108. subsys_notif_reg_test_notifier(subsys->name);
  109. #endif
  110. mutex_unlock(&notif_add_lock);
  111. done:
  112. return subsys;
  113. }
  114. EXPORT_SYMBOL(subsys_notif_add_subsys);
  115. int subsys_notif_queue_notification(void *subsys_handle,
  116. enum subsys_notif_type notif_type,
  117. void *data)
  118. {
  119. int ret = 0;
  120. struct subsys_notif_info *subsys =
  121. (struct subsys_notif_info *) subsys_handle;
  122. if (!subsys)
  123. return -EINVAL;
  124. if (notif_type < 0 || notif_type >= SUBSYS_NOTIF_TYPE_COUNT)
  125. return -EINVAL;
  126. ret = srcu_notifier_call_chain(
  127. &subsys->subsys_notif_rcvr_list, notif_type,
  128. data);
  129. return ret;
  130. }
  131. EXPORT_SYMBOL(subsys_notif_queue_notification);
  132. #if defined(SUBSYS_RESTART_DEBUG)
  133. static const char *notif_to_string(enum subsys_notif_type notif_type)
  134. {
  135. switch (notif_type) {
  136. case SUBSYS_BEFORE_SHUTDOWN:
  137. return __stringify(SUBSYS_BEFORE_SHUTDOWN);
  138. case SUBSYS_AFTER_SHUTDOWN:
  139. return __stringify(SUBSYS_AFTER_SHUTDOWN);
  140. case SUBSYS_BEFORE_POWERUP:
  141. return __stringify(SUBSYS_BEFORE_POWERUP);
  142. case SUBSYS_AFTER_POWERUP:
  143. return __stringify(SUBSYS_AFTER_POWERUP);
  144. default:
  145. return "unknown";
  146. }
  147. }
  148. static int subsys_notifier_test_call(struct notifier_block *this,
  149. unsigned long code,
  150. void *data)
  151. {
  152. switch (code) {
  153. default:
  154. printk(KERN_WARNING "%s: Notification %s from subsystem %p\n",
  155. __func__, notif_to_string(code), data);
  156. break;
  157. }
  158. return NOTIFY_DONE;
  159. }
  160. static struct notifier_block nb = {
  161. .notifier_call = subsys_notifier_test_call,
  162. };
  163. static void subsys_notif_reg_test_notifier(const char *subsys_name)
  164. {
  165. void *handle = subsys_notif_register_notifier(subsys_name, &nb);
  166. printk(KERN_WARNING "%s: Registered test notifier, handle=%p",
  167. __func__, handle);
  168. }
  169. #endif
  170. MODULE_DESCRIPTION("Subsystem Restart Notifier");
  171. MODULE_VERSION("1.0");
  172. MODULE_LICENSE("GPL v2");