console.c 3.5 KB

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