161 lines · 3.6 KB · C
RawDownload
// 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;}