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