PageRenderTime 24ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/kernel/power/powersuspend.c

https://gitlab.com/DroidThug-2/delta_msm8916
C | 324 lines | 233 code | 56 blank | 35 comment | 43 complexity | 8812e5956a095c851a78b842a68134a6 MD5 | raw file
  1. /* kernel/power/powersuspend.c
  2. *
  3. * Copyright (C) 2005-2008 Google, Inc.
  4. * Copyright (C) 2013 Paul Reioux
  5. *
  6. * Modified by Jean-Pierre Rasquin <yank555.lu@gmail.com>
  7. *
  8. * v1.1 - make powersuspend not depend on a userspace initiator anymore,
  9. * but use a hook in autosleep instead.
  10. *
  11. * v1.2 - make kernel / userspace mode switchable
  12. *
  13. * v1.3 - add a hook in display panel driver as alternative kernel trigger
  14. *
  15. * v1.4 - add a hybrid-kernel mode, accepting both kernel hooks (first wins)
  16. *
  17. * This software is licensed under the terms of the GNU General Public
  18. * License version 2, as published by the Free Software Foundation, and
  19. * may be copied, distributed, and modified under those terms.
  20. *
  21. * This program is distributed in the hope that it will be useful,
  22. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. * GNU General Public License for more details.
  25. *
  26. */
  27. #include <linux/powersuspend.h>
  28. #include <linux/module.h>
  29. #include <linux/mutex.h>
  30. #include <linux/workqueue.h>
  31. #define MAJOR_VERSION 1
  32. #define MINOR_VERSION 5
  33. //#define POWER_SUSPEND_DEBUG // Add debugging prints in dmesg
  34. struct workqueue_struct *suspend_work_queue;
  35. static DEFINE_MUTEX(power_suspend_lock);
  36. static LIST_HEAD(power_suspend_handlers);
  37. static void power_suspend(struct work_struct *work);
  38. static void power_resume(struct work_struct *work);
  39. static DECLARE_WORK(power_suspend_work, power_suspend);
  40. static DECLARE_WORK(power_resume_work, power_resume);
  41. static DEFINE_SPINLOCK(state_lock);
  42. static int state; // Yank555.lu : Current powersave state (screen on / off)
  43. static int mode; // Yank555.lu : Current powersave mode (kernel / userspace / panel / hybrid)
  44. void register_power_suspend(struct power_suspend *handler)
  45. {
  46. struct list_head *pos;
  47. mutex_lock(&power_suspend_lock);
  48. list_for_each(pos, &power_suspend_handlers) {
  49. struct power_suspend *p;
  50. p = list_entry(pos, struct power_suspend, link);
  51. }
  52. list_add_tail(&handler->link, pos);
  53. mutex_unlock(&power_suspend_lock);
  54. }
  55. EXPORT_SYMBOL(register_power_suspend);
  56. void unregister_power_suspend(struct power_suspend *handler)
  57. {
  58. mutex_lock(&power_suspend_lock);
  59. list_del(&handler->link);
  60. mutex_unlock(&power_suspend_lock);
  61. }
  62. EXPORT_SYMBOL(unregister_power_suspend);
  63. static void power_suspend(struct work_struct *work)
  64. {
  65. struct power_suspend *pos;
  66. unsigned long irqflags;
  67. int abort = 0;
  68. #ifdef POWER_SUSPEND_DEBUG
  69. pr_info("[POWERSUSPEND] entering suspend...\n");
  70. #endif
  71. mutex_lock(&power_suspend_lock);
  72. spin_lock_irqsave(&state_lock, irqflags);
  73. if (state == POWER_SUSPEND_INACTIVE)
  74. abort = 1;
  75. spin_unlock_irqrestore(&state_lock, irqflags);
  76. if (abort)
  77. goto abort_suspend;
  78. #ifdef POWER_SUSPEND_DEBUG
  79. pr_info("[POWERSUSPEND] suspending...\n");
  80. #endif
  81. list_for_each_entry(pos, &power_suspend_handlers, link) {
  82. if (pos->suspend != NULL) {
  83. pos->suspend(pos);
  84. }
  85. }
  86. #ifdef POWER_SUSPEND_DEBUG
  87. pr_info("[POWERSUSPEND] suspend completed.\n");
  88. #endif
  89. abort_suspend:
  90. mutex_unlock(&power_suspend_lock);
  91. }
  92. static void power_resume(struct work_struct *work)
  93. {
  94. struct power_suspend *pos;
  95. unsigned long irqflags;
  96. int abort = 0;
  97. #ifdef POWER_SUSPEND_DEBUG
  98. pr_info("[POWERSUSPEND] entering resume...\n");
  99. #endif
  100. mutex_lock(&power_suspend_lock);
  101. spin_lock_irqsave(&state_lock, irqflags);
  102. if (state == POWER_SUSPEND_ACTIVE)
  103. abort = 1;
  104. spin_unlock_irqrestore(&state_lock, irqflags);
  105. if (abort)
  106. goto abort_resume;
  107. #ifdef POWER_SUSPEND_DEBUG
  108. pr_info("[POWERSUSPEND] resuming...\n");
  109. #endif
  110. list_for_each_entry_reverse(pos, &power_suspend_handlers, link) {
  111. if (pos->resume != NULL) {
  112. pos->resume(pos);
  113. }
  114. }
  115. #ifdef POWER_SUSPEND_DEBUG
  116. pr_info("[POWERSUSPEND] resume completed.\n");
  117. #endif
  118. abort_resume:
  119. mutex_unlock(&power_suspend_lock);
  120. }
  121. void set_power_suspend_state(int new_state)
  122. {
  123. unsigned long irqflags;
  124. spin_lock_irqsave(&state_lock, irqflags);
  125. if (state == POWER_SUSPEND_INACTIVE && new_state == POWER_SUSPEND_ACTIVE) {
  126. #ifdef POWER_SUSPEND_DEBUG
  127. pr_info("[POWERSUSPEND] state activated.\n");
  128. #endif
  129. state = new_state;
  130. queue_work(suspend_work_queue, &power_suspend_work);
  131. } else if (state == POWER_SUSPEND_ACTIVE && new_state == POWER_SUSPEND_INACTIVE) {
  132. #ifdef POWER_SUSPEND_DEBUG
  133. pr_info("[POWERSUSPEND] state deactivated.\n");
  134. #endif
  135. state = new_state;
  136. queue_work(suspend_work_queue, &power_resume_work);
  137. }
  138. spin_unlock_irqrestore(&state_lock, irqflags);
  139. }
  140. void set_power_suspend_state_autosleep_hook(int new_state)
  141. {
  142. #ifdef POWER_SUSPEND_DEBUG
  143. pr_info("[POWERSUSPEND] autosleep resquests %s.\n", new_state == POWER_SUSPEND_ACTIVE ? "sleep" : "wakeup");
  144. #endif
  145. // Yank555.lu : Only allow autosleep hook changes in autosleep & hybrid mode
  146. if (mode == POWER_SUSPEND_AUTOSLEEP || mode == POWER_SUSPEND_HYBRID)
  147. set_power_suspend_state(new_state);
  148. }
  149. EXPORT_SYMBOL(set_power_suspend_state_autosleep_hook);
  150. void set_power_suspend_state_panel_hook(int new_state)
  151. {
  152. #ifdef POWER_SUSPEND_DEBUG
  153. pr_info("[POWERSUSPEND] panel resquests %s.\n", new_state == POWER_SUSPEND_ACTIVE ? "sleep" : "wakeup");
  154. #endif
  155. // Yank555.lu : Only allow autosleep hook changes in autosleep & hybrid mode
  156. if (mode == POWER_SUSPEND_PANEL || mode == POWER_SUSPEND_HYBRID)
  157. set_power_suspend_state(new_state);
  158. }
  159. EXPORT_SYMBOL(set_power_suspend_state_panel_hook);
  160. // ------------------------------------------ sysfs interface ------------------------------------------
  161. static ssize_t power_suspend_state_show(struct kobject *kobj,
  162. struct kobj_attribute *attr, char *buf)
  163. {
  164. return sprintf(buf, "%u\n", state);
  165. }
  166. static ssize_t power_suspend_state_store(struct kobject *kobj,
  167. struct kobj_attribute *attr, const char *buf, size_t count)
  168. {
  169. int new_state = 0;
  170. // Yank555.lu : Only allow sysfs changes from userspace mode
  171. if (mode != POWER_SUSPEND_USERSPACE)
  172. return -EINVAL;
  173. sscanf(buf, "%d\n", &new_state);
  174. #ifdef POWER_SUSPEND_DEBUG
  175. pr_info("[POWERSUSPEND] userspace resquests %s.\n", new_state == POWER_SUSPEND_ACTIVE ? "sleep" : "wakeup");
  176. #endif
  177. if(new_state == POWER_SUSPEND_ACTIVE || new_state == POWER_SUSPEND_INACTIVE)
  178. set_power_suspend_state(new_state);
  179. return count;
  180. }
  181. static struct kobj_attribute power_suspend_state_attribute =
  182. __ATTR(power_suspend_state, 0666,
  183. power_suspend_state_show,
  184. power_suspend_state_store);
  185. static ssize_t power_suspend_mode_show(struct kobject *kobj,
  186. struct kobj_attribute *attr, char *buf)
  187. {
  188. return sprintf(buf, "%u\n", mode);
  189. }
  190. static ssize_t power_suspend_mode_store(struct kobject *kobj,
  191. struct kobj_attribute *attr, const char *buf, size_t count)
  192. {
  193. int data = 0;
  194. sscanf(buf, "%d\n", &data);
  195. switch (data) {
  196. case POWER_SUSPEND_AUTOSLEEP:
  197. case POWER_SUSPEND_PANEL:
  198. case POWER_SUSPEND_USERSPACE:
  199. case POWER_SUSPEND_HYBRID: mode = data;
  200. return count;
  201. default:
  202. return -EINVAL;
  203. }
  204. }
  205. static struct kobj_attribute power_suspend_mode_attribute =
  206. __ATTR(power_suspend_mode, 0666,
  207. power_suspend_mode_show,
  208. power_suspend_mode_store);
  209. static ssize_t power_suspend_version_show(struct kobject *kobj,
  210. struct kobj_attribute *attr, char *buf)
  211. {
  212. return sprintf(buf, "version: %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
  213. }
  214. static struct kobj_attribute power_suspend_version_attribute =
  215. __ATTR(power_suspend_version, 0444,
  216. power_suspend_version_show,
  217. NULL);
  218. static struct attribute *power_suspend_attrs[] =
  219. {
  220. &power_suspend_state_attribute.attr,
  221. &power_suspend_mode_attribute.attr,
  222. &power_suspend_version_attribute.attr,
  223. NULL,
  224. };
  225. static struct attribute_group power_suspend_attr_group =
  226. {
  227. .attrs = power_suspend_attrs,
  228. };
  229. static struct kobject *power_suspend_kobj;
  230. // ------------------ sysfs interface -----------------------
  231. static int __init power_suspend_init(void)
  232. {
  233. int sysfs_result;
  234. power_suspend_kobj = kobject_create_and_add("power_suspend",
  235. kernel_kobj);
  236. if (!power_suspend_kobj) {
  237. pr_err("%s kobject create failed!\n", __FUNCTION__);
  238. return -ENOMEM;
  239. }
  240. sysfs_result = sysfs_create_group(power_suspend_kobj,
  241. &power_suspend_attr_group);
  242. if (sysfs_result) {
  243. pr_info("%s group create failed!\n", __FUNCTION__);
  244. kobject_put(power_suspend_kobj);
  245. return -ENOMEM;
  246. }
  247. suspend_work_queue = create_singlethread_workqueue("p-suspend");
  248. if (suspend_work_queue == NULL) {
  249. return -ENOMEM;
  250. }
  251. // mode = POWER_SUSPEND_AUTOSLEEP; // Yank555.lu : Default to autosleep mode
  252. // mode = POWER_SUSPEND_USERSPACE; // Yank555.lu : Default to userspace mode
  253. // mode = POWER_SUSPEND_PANEL; // Yank555.lu : Default to display panel mode
  254. mode = POWER_SUSPEND_HYBRID; // Yank555.lu : Default to display panel / autosleep hybrid mode
  255. return 0;
  256. }
  257. static void __exit power_suspend_exit(void)
  258. {
  259. if (power_suspend_kobj != NULL)
  260. kobject_put(power_suspend_kobj);
  261. destroy_workqueue(suspend_work_queue);
  262. }
  263. core_initcall(power_suspend_init);
  264. module_exit(power_suspend_exit);
  265. MODULE_AUTHOR("Paul Reioux <reioux@gmail.com> / Jean-Pierre Rasquin <yank555.lu@gmail.com>");
  266. MODULE_DESCRIPTION("power_suspend - A replacement kernel PM driver for"
  267. "Android's deprecated early_suspend/late_resume PM driver!");
  268. MODULE_LICENSE("GPL v2");