console.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /*
  2. * Functions for saving/restoring console.
  3. *
  4. * Originally from swsusp.
  5. */
  6. #include <linux/console.h>
  7. #include <linux/vt_kern.h>
  8. #include <linux/kbd_kern.h>
  9. #include <linux/vt.h>
  10. #include <linux/module.h>
  11. #include <linux/slab.h>
  12. #include "power.h"
  13. #define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1)
  14. static int orig_fgconsole, orig_kmsg;
  15. static DEFINE_MUTEX(vt_switch_mutex);
  16. struct pm_vt_switch {
  17. struct list_head head;
  18. struct device *dev;
  19. bool required;
  20. };
  21. static LIST_HEAD(pm_vt_switch_list);
  22. /**
  23. * pm_vt_switch_required - indicate VT switch at suspend requirements
  24. * @dev: device
  25. * @required: if true, caller needs VT switch at suspend/resume time
  26. *
  27. * The different console drivers may or may not require VT switches across
  28. * suspend/resume, depending on how they handle restoring video state and
  29. * what may be running.
  30. *
  31. * Drivers can indicate support for switchless suspend/resume, which can
  32. * save time and flicker, by using this routine and passing 'false' as
  33. * the argument. If any loaded driver needs VT switching, or the
  34. * no_console_suspend argument has been passed on the command line, VT
  35. * switches will occur.
  36. */
  37. void pm_vt_switch_required(struct device *dev, bool required)
  38. {
  39. struct pm_vt_switch *entry, *tmp;
  40. mutex_lock(&vt_switch_mutex);
  41. list_for_each_entry(tmp, &pm_vt_switch_list, head) {
  42. if (tmp->dev == dev) {
  43. /* already registered, update requirement */
  44. tmp->required = required;
  45. goto out;
  46. }
  47. }
  48. entry = kmalloc(sizeof(*entry), GFP_KERNEL);
  49. if (!entry)
  50. goto out;
  51. entry->required = required;
  52. entry->dev = dev;
  53. list_add(&entry->head, &pm_vt_switch_list);
  54. out:
  55. mutex_unlock(&vt_switch_mutex);
  56. }
  57. EXPORT_SYMBOL(pm_vt_switch_required);
  58. /**
  59. * pm_vt_switch_unregister - stop tracking a device's VT switching needs
  60. * @dev: device
  61. *
  62. * Remove @dev from the vt switch list.
  63. */
  64. void pm_vt_switch_unregister(struct device *dev)
  65. {
  66. struct pm_vt_switch *tmp;
  67. mutex_lock(&vt_switch_mutex);
  68. list_for_each_entry(tmp, &pm_vt_switch_list, head) {
  69. if (tmp->dev == dev) {
  70. list_del(&tmp->head);
  71. kfree(tmp);
  72. break;
  73. }
  74. }
  75. mutex_unlock(&vt_switch_mutex);
  76. }
  77. EXPORT_SYMBOL(pm_vt_switch_unregister);
  78. /*
  79. * There are three cases when a VT switch on suspend/resume are required:
  80. * 1) no driver has indicated a requirement one way or another, so preserve
  81. * the old behavior
  82. * 2) console suspend is disabled, we want to see debug messages across
  83. * suspend/resume
  84. * 3) any registered driver indicates it needs a VT switch
  85. *
  86. * If none of these conditions is present, meaning we have at least one driver
  87. * that doesn't need the switch, and none that do, we can avoid it to make
  88. * resume look a little prettier (and suspend too, but that's usually hidden,
  89. * e.g. when closing the lid on a laptop).
  90. */
  91. static bool pm_vt_switch(void)
  92. {
  93. struct pm_vt_switch *entry;
  94. bool ret = true;
  95. mutex_lock(&vt_switch_mutex);
  96. if (list_empty(&pm_vt_switch_list))
  97. goto out;
  98. if (!console_suspend_enabled)
  99. goto out;
  100. list_for_each_entry(entry, &pm_vt_switch_list, head) {
  101. if (entry->required)
  102. goto out;
  103. }
  104. ret = false;
  105. out:
  106. mutex_unlock(&vt_switch_mutex);
  107. return ret;
  108. }
  109. int pm_prepare_console(void)
  110. {
  111. if (!pm_vt_switch())
  112. return 0;
  113. orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1);
  114. if (orig_fgconsole < 0)
  115. return 1;
  116. orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
  117. return 0;
  118. }
  119. void pm_restore_console(void)
  120. {
  121. if (!pm_vt_switch())
  122. return;
  123. if (orig_fgconsole >= 0) {
  124. vt_move_to_console(orig_fgconsole, 0);
  125. vt_kmsg_redirect(orig_kmsg);
  126. }
  127. }