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 kfree(tmp); 85 break; 86 } 87 } 88 mutex_unlock(&vt_switch_mutex); 89 } 90 EXPORT_SYMBOL(pm_vt_switch_unregister); 91 92 /* 93 * There are three cases when a VT switch on suspend/resume are required: 94 * 1) no driver has indicated a requirement one way or another, so preserve 95 * the old behavior 96 * 2) console suspend is disabled, we want to see debug messages across 97 * suspend/resume 98 * 3) any registered driver indicates it needs a VT switch 99 * 100 * If none of these conditions is present, meaning we have at least one driver 101 * that doesn't need the switch, and none that do, we can avoid it to make 102 * resume look a little prettier (and suspend too, but that's usually hidden, 103 * e.g. when closing the lid on a laptop). 104 */ 105 static bool pm_vt_switch(void) 106 { 107 struct pm_vt_switch *entry; 108 bool ret = true; 109 110 mutex_lock(&vt_switch_mutex); 111 if (list_empty(&pm_vt_switch_list)) 112 goto out; 113 114 if (!console_suspend_enabled) 115 goto out; 116 117 list_for_each_entry(entry, &pm_vt_switch_list, head) { 118 if (entry->required) 119 goto out; 120 } 121 122 ret = false; 123 out: 124 mutex_unlock(&vt_switch_mutex); 125 return ret; 126 } 127 128 int pm_prepare_console(void) 129 { 130 if (!pm_vt_switch()) 131 return 0; 132 133 orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1); 134 if (orig_fgconsole < 0) 135 return 1; 136 137 orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE); 138 return 0; 139 } 140 141 void pm_restore_console(void) 142 { 143 if (!pm_vt_switch()) 144 return; 145 146 if (orig_fgconsole >= 0) { 147 vt_move_to_console(orig_fgconsole, 0); 148 vt_kmsg_redirect(orig_kmsg); 149 } 150 } 151