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