Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162// 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 bool vt_switch_done;
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. */int pm_vt_switch_required(struct device *dev, bool required){ struct pm_vt_switch *entry, *tmp; int ret = 0;
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_obj(*entry); if (!entry) { ret = -ENOMEM; goto out; }
entry->required = required; entry->dev = dev;
list_add(&entry->head, &pm_vt_switch_list);out: mutex_unlock(&vt_switch_mutex); return ret;}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;
vt_switch_done = true;
orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE); return;}
void pm_restore_console(void){ if (!pm_vt_switch() && !vt_switch_done) return;
if (orig_fgconsole >= 0) { vt_move_to_console(orig_fgconsole, 0); vt_kmsg_redirect(orig_kmsg); }
vt_switch_done = false;}