123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Functions for saving/restoring console.
- *
- * Originally from swsusp.
- */
- #include <linux/console.h>
- #include <linux/vt_kern.h>
- #include <linux/kbd_kern.h>
- #include <linux/vt.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include "power.h"
- #define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1)
- static int orig_fgconsole, orig_kmsg;
- static DEFINE_MUTEX(vt_switch_mutex);
- struct pm_vt_switch {
- struct list_head head;
- struct device *dev;
- bool required;
- };
- static LIST_HEAD(pm_vt_switch_list);
- /**
- * pm_vt_switch_required - indicate VT switch at suspend requirements
- * @dev: device
- * @required: if true, caller needs VT switch at suspend/resume time
- *
- * The different console drivers may or may not require VT switches across
- * suspend/resume, depending on how they handle restoring video state and
- * what may be running.
- *
- * Drivers can indicate support for switchless suspend/resume, which can
- * save time and flicker, by using this routine and passing 'false' as
- * the argument. If any loaded driver needs VT switching, or the
- * no_console_suspend argument has been passed on the command line, VT
- * switches will occur.
- */
- void pm_vt_switch_required(struct device *dev, bool required)
- {
- struct pm_vt_switch *entry, *tmp;
- mutex_lock(&vt_switch_mutex);
- list_for_each_entry(tmp, &pm_vt_switch_list, head) {
- if (tmp->dev == dev) {
- /* already registered, update requirement */
- tmp->required = required;
- goto out;
- }
- }
- entry = kmalloc(sizeof(*entry), GFP_KERNEL);
- if (!entry)
- goto out;
- entry->required = required;
- entry->dev = dev;
- list_add(&entry->head, &pm_vt_switch_list);
- out:
- mutex_unlock(&vt_switch_mutex);
- }
- EXPORT_SYMBOL(pm_vt_switch_required);
- /**
- * pm_vt_switch_unregister - stop tracking a device's VT switching needs
- * @dev: device
- *
- * Remove @dev from the vt switch list.
- */
- void pm_vt_switch_unregister(struct device *dev)
- {
- struct pm_vt_switch *tmp;
- mutex_lock(&vt_switch_mutex);
- list_for_each_entry(tmp, &pm_vt_switch_list, head) {
- if (tmp->dev == dev) {
- list_del(&tmp->head);
- kfree(tmp);
- break;
- }
- }
- mutex_unlock(&vt_switch_mutex);
- }
- EXPORT_SYMBOL(pm_vt_switch_unregister);
- /*
- * There are three cases when a VT switch on suspend/resume are required:
- * 1) no driver has indicated a requirement one way or another, so preserve
- * the old behavior
- * 2) console suspend is disabled, we want to see debug messages across
- * suspend/resume
- * 3) any registered driver indicates it needs a VT switch
- *
- * If none of these conditions is present, meaning we have at least one driver
- * that doesn't need the switch, and none that do, we can avoid it to make
- * resume look a little prettier (and suspend too, but that's usually hidden,
- * e.g. when closing the lid on a laptop).
- */
- static bool pm_vt_switch(void)
- {
- struct pm_vt_switch *entry;
- bool ret = true;
- mutex_lock(&vt_switch_mutex);
- if (list_empty(&pm_vt_switch_list))
- goto out;
- if (!console_suspend_enabled)
- goto out;
- list_for_each_entry(entry, &pm_vt_switch_list, head) {
- if (entry->required)
- goto out;
- }
- ret = false;
- out:
- mutex_unlock(&vt_switch_mutex);
- return ret;
- }
- void pm_prepare_console(void)
- {
- if (!pm_vt_switch())
- return;
- orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1);
- if (orig_fgconsole < 0)
- return;
- orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
- return;
- }
- void pm_restore_console(void)
- {
- if (!pm_vt_switch())
- return;
- if (orig_fgconsole >= 0) {
- vt_move_to_console(orig_fgconsole, 0);
- vt_kmsg_redirect(orig_kmsg);
- }
- }
|