console.c 3.6 KB

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