console.c 3.4 KB

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