Branch data Line data Source code
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 : : 18 : : static int orig_fgconsole, orig_kmsg; 19 : : 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 : 0 : void pm_vt_switch_required(struct device *dev, bool required) 47 : : { 48 : 0 : struct pm_vt_switch *entry, *tmp; 49 : : 50 : 0 : mutex_lock(&vt_switch_mutex); 51 [ # # ]: 0 : list_for_each_entry(tmp, &pm_vt_switch_list, head) { 52 [ # # ]: 0 : if (tmp->dev == dev) { 53 : : /* already registered, update requirement */ 54 : 0 : tmp->required = required; 55 : 0 : goto out; 56 : : } 57 : : } 58 : : 59 : 0 : entry = kmalloc(sizeof(*entry), GFP_KERNEL); 60 [ # # ]: 0 : if (!entry) 61 : 0 : goto out; 62 : : 63 : 0 : entry->required = required; 64 : 0 : entry->dev = dev; 65 : : 66 : 0 : list_add(&entry->head, &pm_vt_switch_list); 67 : 0 : out: 68 : 0 : mutex_unlock(&vt_switch_mutex); 69 : 0 : } 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 : 0 : void pm_vt_switch_unregister(struct device *dev) 79 : : { 80 : 0 : struct pm_vt_switch *tmp; 81 : : 82 : 0 : mutex_lock(&vt_switch_mutex); 83 [ # # ]: 0 : list_for_each_entry(tmp, &pm_vt_switch_list, head) { 84 [ # # ]: 0 : if (tmp->dev == dev) { 85 : 0 : list_del(&tmp->head); 86 : 0 : kfree(tmp); 87 : 0 : break; 88 : : } 89 : : } 90 : 0 : mutex_unlock(&vt_switch_mutex); 91 : 0 : } 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 : 0 : static bool pm_vt_switch(void) 108 : : { 109 : 0 : struct pm_vt_switch *entry; 110 : 0 : bool ret = true; 111 : : 112 : 0 : mutex_lock(&vt_switch_mutex); 113 [ # # ]: 0 : if (list_empty(&pm_vt_switch_list)) 114 : 0 : goto out; 115 : : 116 [ # # ]: 0 : if (!console_suspend_enabled) 117 : 0 : goto out; 118 : : 119 [ # # ]: 0 : list_for_each_entry(entry, &pm_vt_switch_list, head) { 120 [ # # ]: 0 : if (entry->required) 121 : 0 : goto out; 122 : : } 123 : : 124 : : ret = false; 125 : 0 : out: 126 : 0 : mutex_unlock(&vt_switch_mutex); 127 : 0 : return ret; 128 : : } 129 : : 130 : 0 : void pm_prepare_console(void) 131 : : { 132 [ # # ]: 0 : if (!pm_vt_switch()) 133 : : return; 134 : : 135 : 0 : orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1); 136 [ # # ]: 0 : if (orig_fgconsole < 0) 137 : : return; 138 : : 139 : 0 : orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE); 140 : 0 : return; 141 : : } 142 : : 143 : 0 : void pm_restore_console(void) 144 : : { 145 [ # # ]: 0 : if (!pm_vt_switch()) 146 : : return; 147 : : 148 [ # # ]: 0 : if (orig_fgconsole >= 0) { 149 : 0 : vt_move_to_console(orig_fgconsole, 0); 150 : 0 : vt_kmsg_redirect(orig_kmsg); 151 : : } 152 : : }