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 17static int orig_fgconsole, orig_kmsg; 18 19static DEFINE_MUTEX(vt_switch_mutex); 20 21struct pm_vt_switch { 22 struct list_head head; 23 struct device *dev; 24 bool required; 25}; 26 27static 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 */ 45void 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); 66out: 67 mutex_unlock(&vt_switch_mutex); 68} 69EXPORT_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 */ 77void 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} 91EXPORT_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 */ 106static 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; 124out: 125 mutex_unlock(&vt_switch_mutex); 126 return ret; 127} 128 129int pm_prepare_console(void) 130{ 131 if (!pm_vt_switch()) 132 return 0; 133 134 orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1); 135 if (orig_fgconsole < 0) 136 return 1; 137 138 orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE); 139 return 0; 140} 141 142void 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