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