Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Functions for saving/restoring console.
0004  *
0005  * Originally from swsusp.
0006  */
0007 
0008 #include <linux/console.h>
0009 #include <linux/vt_kern.h>
0010 #include <linux/kbd_kern.h>
0011 #include <linux/vt.h>
0012 #include <linux/module.h>
0013 #include <linux/slab.h>
0014 #include "power.h"
0015 
0016 #define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1)
0017 
0018 static int orig_fgconsole, orig_kmsg;
0019 
0020 static DEFINE_MUTEX(vt_switch_mutex);
0021 
0022 struct pm_vt_switch {
0023     struct list_head head;
0024     struct device *dev;
0025     bool required;
0026 };
0027 
0028 static LIST_HEAD(pm_vt_switch_list);
0029 
0030 
0031 /**
0032  * pm_vt_switch_required - indicate VT switch at suspend requirements
0033  * @dev: device
0034  * @required: if true, caller needs VT switch at suspend/resume time
0035  *
0036  * The different console drivers may or may not require VT switches across
0037  * suspend/resume, depending on how they handle restoring video state and
0038  * what may be running.
0039  *
0040  * Drivers can indicate support for switchless suspend/resume, which can
0041  * save time and flicker, by using this routine and passing 'false' as
0042  * the argument.  If any loaded driver needs VT switching, or the
0043  * no_console_suspend argument has been passed on the command line, VT
0044  * switches will occur.
0045  */
0046 void pm_vt_switch_required(struct device *dev, bool required)
0047 {
0048     struct pm_vt_switch *entry, *tmp;
0049 
0050     mutex_lock(&vt_switch_mutex);
0051     list_for_each_entry(tmp, &pm_vt_switch_list, head) {
0052         if (tmp->dev == dev) {
0053             /* already registered, update requirement */
0054             tmp->required = required;
0055             goto out;
0056         }
0057     }
0058 
0059     entry = kmalloc(sizeof(*entry), GFP_KERNEL);
0060     if (!entry)
0061         goto out;
0062 
0063     entry->required = required;
0064     entry->dev = dev;
0065 
0066     list_add(&entry->head, &pm_vt_switch_list);
0067 out:
0068     mutex_unlock(&vt_switch_mutex);
0069 }
0070 EXPORT_SYMBOL(pm_vt_switch_required);
0071 
0072 /**
0073  * pm_vt_switch_unregister - stop tracking a device's VT switching needs
0074  * @dev: device
0075  *
0076  * Remove @dev from the vt switch list.
0077  */
0078 void pm_vt_switch_unregister(struct device *dev)
0079 {
0080     struct pm_vt_switch *tmp;
0081 
0082     mutex_lock(&vt_switch_mutex);
0083     list_for_each_entry(tmp, &pm_vt_switch_list, head) {
0084         if (tmp->dev == dev) {
0085             list_del(&tmp->head);
0086             kfree(tmp);
0087             break;
0088         }
0089     }
0090     mutex_unlock(&vt_switch_mutex);
0091 }
0092 EXPORT_SYMBOL(pm_vt_switch_unregister);
0093 
0094 /*
0095  * There are three cases when a VT switch on suspend/resume are required:
0096  *   1) no driver has indicated a requirement one way or another, so preserve
0097  *      the old behavior
0098  *   2) console suspend is disabled, we want to see debug messages across
0099  *      suspend/resume
0100  *   3) any registered driver indicates it needs a VT switch
0101  *
0102  * If none of these conditions is present, meaning we have at least one driver
0103  * that doesn't need the switch, and none that do, we can avoid it to make
0104  * resume look a little prettier (and suspend too, but that's usually hidden,
0105  * e.g. when closing the lid on a laptop).
0106  */
0107 static bool pm_vt_switch(void)
0108 {
0109     struct pm_vt_switch *entry;
0110     bool ret = true;
0111 
0112     mutex_lock(&vt_switch_mutex);
0113     if (list_empty(&pm_vt_switch_list))
0114         goto out;
0115 
0116     if (!console_suspend_enabled)
0117         goto out;
0118 
0119     list_for_each_entry(entry, &pm_vt_switch_list, head) {
0120         if (entry->required)
0121             goto out;
0122     }
0123 
0124     ret = false;
0125 out:
0126     mutex_unlock(&vt_switch_mutex);
0127     return ret;
0128 }
0129 
0130 void pm_prepare_console(void)
0131 {
0132     if (!pm_vt_switch())
0133         return;
0134 
0135     orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1);
0136     if (orig_fgconsole < 0)
0137         return;
0138 
0139     orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
0140     return;
0141 }
0142 
0143 void pm_restore_console(void)
0144 {
0145     if (!pm_vt_switch())
0146         return;
0147 
0148     if (orig_fgconsole >= 0) {
0149         vt_move_to_console(orig_fgconsole, 0);
0150         vt_kmsg_redirect(orig_kmsg);
0151     }
0152 }