forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
152 lines
3.5 KiB
152 lines
3.5 KiB
// 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); |
|
} |
|
}
|
|
|