| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2011-09-21 22:47:55 +02:00
										 |  |  |  * Functions for saving/restoring console. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Originally from swsusp. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-04 13:37:20 +00:00
										 |  |  | #include <linux/console.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <linux/vt_kern.h>
 | 
					
						
							|  |  |  | #include <linux/kbd_kern.h>
 | 
					
						
							| 
									
										
										
										
											2009-12-14 18:00:43 -08:00
										 |  |  | #include <linux/vt.h>
 | 
					
						
							| 
									
										
										
										
											2008-04-28 02:15:03 -07:00
										 |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2014-01-28 18:10:37 -05:00
										 |  |  | #include <linux/slab.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include "power.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-07 12:58:50 -08:00
										 |  |  | #define SUSPEND_CONSOLE	(MAX_NR_CONSOLES-1)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static int orig_fgconsole, orig_kmsg; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-04 13:37:20 +00:00
										 |  |  | 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); | 
					
						
							| 
									
										
										
										
											2013-12-19 20:00:47 +09:00
										 |  |  | 			kfree(tmp); | 
					
						
							| 
									
										
										
										
											2013-02-04 13:37:20 +00:00
										 |  |  | 			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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | int pm_prepare_console(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-02-04 13:37:20 +00:00
										 |  |  | 	if (!pm_vt_switch()) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-19 13:13:25 -07:00
										 |  |  | 	orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1); | 
					
						
							|  |  |  | 	if (orig_fgconsole < 0) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-14 18:00:43 -08:00
										 |  |  | 	orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void pm_restore_console(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-02-04 13:37:20 +00:00
										 |  |  | 	if (!pm_vt_switch()) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-19 13:13:25 -07:00
										 |  |  | 	if (orig_fgconsole >= 0) { | 
					
						
							|  |  |  | 		vt_move_to_console(orig_fgconsole, 0); | 
					
						
							| 
									
										
										
										
											2009-12-14 18:00:43 -08:00
										 |  |  | 		vt_kmsg_redirect(orig_kmsg); | 
					
						
							| 
									
										
										
										
											2009-02-14 02:06:17 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } |