Blame view

kernel/power/console.c 3.46 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
37cce26b3   H Hartley Sweeten   PM / VT: Cleanup ...
2
   * Functions for saving/restoring console.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
   *
   * Originally from swsusp.
   */
f43f627d2   Jesse Barnes   PM: make VT switc...
6
  #include <linux/console.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
  #include <linux/vt_kern.h>
  #include <linux/kbd_kern.h>
5ada918b8   Bernhard Walle   vt: introduce and...
9
  #include <linux/vt.h>
b6f448e99   Andres Salomon   PM/gxfb: add hook...
10
  #include <linux/module.h>
1ff6bbfd1   Tejun Heo   arm, pm, vmpressu...
11
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
  #include "power.h"
46cd2f32b   Rafael J. Wysocki   [PATCH] Fix build...
13
  #define SUSPEND_CONSOLE	(MAX_NR_CONSOLES-1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
  static int orig_fgconsole, orig_kmsg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15

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

8d233558c   Alan Cox   vt: remove power ...
130
131
  	orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1);
  	if (orig_fgconsole < 0)
ca5f2b4c4   Borislav Petkov   PM / sleep: Make ...
132
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133

5ada918b8   Bernhard Walle   vt: introduce and...
134
  	orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
ca5f2b4c4   Borislav Petkov   PM / sleep: Make ...
135
  	return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
  }
  
  void pm_restore_console(void)
  {
f43f627d2   Jesse Barnes   PM: make VT switc...
140
141
  	if (!pm_vt_switch())
  		return;
8d233558c   Alan Cox   vt: remove power ...
142
143
  	if (orig_fgconsole >= 0) {
  		vt_move_to_console(orig_fgconsole, 0);
5ada918b8   Bernhard Walle   vt: introduce and...
144
  		vt_kmsg_redirect(orig_kmsg);
b090f9fa5   Arve Hjønnevåg   PM: Wait for cons...
145
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
  }