console.c (3581B)
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; 19 20static DEFINE_MUTEX(vt_switch_mutex); 21 22struct pm_vt_switch { 23 struct list_head head; 24 struct device *dev; 25 bool required; 26}; 27 28static 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 */ 46void 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); 67out: 68 mutex_unlock(&vt_switch_mutex); 69} 70EXPORT_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 */ 78void 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); 86 kfree(tmp); 87 break; 88 } 89 } 90 mutex_unlock(&vt_switch_mutex); 91} 92EXPORT_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 */ 107static 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; 125out: 126 mutex_unlock(&vt_switch_mutex); 127 return ret; 128} 129 130void pm_prepare_console(void) 131{ 132 if (!pm_vt_switch()) 133 return; 134 135 orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1); 136 if (orig_fgconsole < 0) 137 return; 138 139 orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE); 140 return; 141} 142 143void pm_restore_console(void) 144{ 145 if (!pm_vt_switch()) 146 return; 147 148 if (orig_fgconsole >= 0) { 149 vt_move_to_console(orig_fgconsole, 0); 150 vt_kmsg_redirect(orig_kmsg); 151 } 152}