projects/linux·blob master · refs

161 lines · 3.6 KiB

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