⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 apm.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
#define NR_APM_EVENT_NAME ARRAY_SIZE(apm_event_name)typedef struct lookup_t {	int	key;	char *	msg;} lookup_t;/* *	The BIOS returns a set of standard error codes in AX when the *	carry flag is set. */ static const lookup_t error_table[] = {/* N/A	{ APM_SUCCESS,		"Operation succeeded" }, */	{ APM_DISABLED,		"Power management disabled" },	{ APM_CONNECTED,	"Real mode interface already connected" },	{ APM_NOT_CONNECTED,	"Interface not connected" },	{ APM_16_CONNECTED,	"16 bit interface already connected" },/* N/A	{ APM_16_UNSUPPORTED,	"16 bit interface not supported" }, */	{ APM_32_CONNECTED,	"32 bit interface already connected" },	{ APM_32_UNSUPPORTED,	"32 bit interface not supported" },	{ APM_BAD_DEVICE,	"Unrecognized device ID" },	{ APM_BAD_PARAM,	"Parameter out of range" },	{ APM_NOT_ENGAGED,	"Interface not engaged" },	{ APM_BAD_FUNCTION,     "Function not supported" },	{ APM_RESUME_DISABLED,	"Resume timer disabled" },	{ APM_BAD_STATE,	"Unable to enter requested state" },/* N/A	{ APM_NO_EVENTS,	"No events pending" }, */	{ APM_NO_ERROR,		"BIOS did not set a return code" },	{ APM_NOT_PRESENT,	"No APM present" }};#define ERROR_COUNT	ARRAY_SIZE(error_table)/** *	apm_error	-	display an APM error *	@str: information string *	@err: APM BIOS return code * *	Write a meaningful log entry to the kernel log in the event of *	an APM error. */ static void apm_error(char *str, int err){	int	i;	for (i = 0; i < ERROR_COUNT; i++)		if (error_table[i].key == err) break;	if (i < ERROR_COUNT)		printk(KERN_NOTICE "apm: %s: %s\n", str, error_table[i].msg);	else		printk(KERN_NOTICE "apm: %s: unknown error code %#2.2x\n",			str, err);}/* * Lock APM functionality to physical CPU 0 */ #ifdef CONFIG_SMPstatic cpumask_t apm_save_cpus(void){	cpumask_t x = current->cpus_allowed;	/* Some bioses don't like being called from CPU != 0 */	set_cpus_allowed(current, cpumask_of_cpu(0));	BUG_ON(smp_processor_id() != 0);	return x;}static inline void apm_restore_cpus(cpumask_t mask){	set_cpus_allowed(current, mask);}#else/* *	No CPU lockdown needed on a uniprocessor */ #define apm_save_cpus()		(current->cpus_allowed)#define apm_restore_cpus(x)	(void)(x)#endif/* * These are the actual BIOS calls.  Depending on APM_ZERO_SEGS and * apm_info.allow_ints, we are being really paranoid here!  Not only * are interrupts disabled, but all the segment registers (except SS) * are saved and zeroed this means that if the BIOS tries to reference * any data without explicitly loading the segment registers, the kernel * will fault immediately rather than have some unforeseen circumstances * for the rest of the kernel.  And it will be very obvious!  :-) Doing * this depends on CS referring to the same physical memory as DS so that * DS can be zeroed before the call. Unfortunately, we can't do anything * about the stack segment/pointer.  Also, we tell the compiler that * everything could change. * * Also, we KNOW that for the non error case of apm_bios_call, there * is no useful data returned in the low order 8 bits of eax. */#define APM_DO_CLI	\	if (apm_info.allow_ints) \		local_irq_enable(); \	else \		local_irq_disable();#ifdef APM_ZERO_SEGS#	define APM_DECL_SEGS \		unsigned int saved_fs; unsigned int saved_gs;#	define APM_DO_SAVE_SEGS \		savesegment(fs, saved_fs); savesegment(gs, saved_gs)#	define APM_DO_RESTORE_SEGS \		loadsegment(fs, saved_fs); loadsegment(gs, saved_gs)#else#	define APM_DECL_SEGS#	define APM_DO_SAVE_SEGS#	define APM_DO_RESTORE_SEGS#endif/** *	apm_bios_call	-	Make an APM BIOS 32bit call *	@func: APM function to execute *	@ebx_in: EBX register for call entry *	@ecx_in: ECX register for call entry *	@eax: EAX register return *	@ebx: EBX register return *	@ecx: ECX register return *	@edx: EDX register return *	@esi: ESI register return * *	Make an APM call using the 32bit protected mode interface. The *	caller is responsible for knowing if APM BIOS is configured and *	enabled. This call can disable interrupts for a long period of *	time on some laptops.  The return value is in AH and the carry *	flag is loaded into AL.  If there is an error, then the error *	code is returned in AH (bits 8-15 of eax) and this function *	returns non-zero. */ static u8 apm_bios_call(u32 func, u32 ebx_in, u32 ecx_in,	u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi){	APM_DECL_SEGS	unsigned long		flags;	cpumask_t		cpus;	int			cpu;	struct desc_struct	save_desc_40;	struct desc_struct	*gdt;	cpus = apm_save_cpus();		cpu = get_cpu();	gdt = get_cpu_gdt_table(cpu);	save_desc_40 = gdt[0x40 / 8];	gdt[0x40 / 8] = bad_bios_desc;	local_save_flags(flags);	APM_DO_CLI;	APM_DO_SAVE_SEGS;	apm_bios_call_asm(func, ebx_in, ecx_in, eax, ebx, ecx, edx, esi);	APM_DO_RESTORE_SEGS;	local_irq_restore(flags);	gdt[0x40 / 8] = save_desc_40;	put_cpu();	apm_restore_cpus(cpus);		return *eax & 0xff;}/** *	apm_bios_call_simple	-	make a simple APM BIOS 32bit call *	@func: APM function to invoke *	@ebx_in: EBX register value for BIOS call *	@ecx_in: ECX register value for BIOS call *	@eax: EAX register on return from the BIOS call * *	Make a BIOS call that does only returns one value, or just status. *	If there is an error, then the error code is returned in AH *	(bits 8-15 of eax) and this function returns non-zero. This is *	used for simpler BIOS operations. This call may hold interrupts *	off for a long time on some laptops. */static u8 apm_bios_call_simple(u32 func, u32 ebx_in, u32 ecx_in, u32 *eax){	u8			error;	APM_DECL_SEGS	unsigned long		flags;	cpumask_t		cpus;	int			cpu;	struct desc_struct	save_desc_40;	struct desc_struct	*gdt;	cpus = apm_save_cpus();		cpu = get_cpu();	gdt = get_cpu_gdt_table(cpu);	save_desc_40 = gdt[0x40 / 8];	gdt[0x40 / 8] = bad_bios_desc;	local_save_flags(flags);	APM_DO_CLI;	APM_DO_SAVE_SEGS;	error = apm_bios_call_simple_asm(func, ebx_in, ecx_in, eax);	APM_DO_RESTORE_SEGS;	local_irq_restore(flags);	gdt[0x40 / 8] = save_desc_40;	put_cpu();	apm_restore_cpus(cpus);	return error;}/** *	apm_driver_version	-	APM driver version *	@val:	loaded with the APM version on return * *	Retrieve the APM version supported by the BIOS. This is only *	supported for APM 1.1 or higher. An error indicates APM 1.0 is *	probably present. * *	On entry val should point to a value indicating the APM driver *	version with the high byte being the major and the low byte the *	minor number both in BCD * *	On return it will hold the BIOS revision supported in the *	same format. */static int apm_driver_version(u_short *val){	u32	eax;	if (apm_bios_call_simple(APM_FUNC_VERSION, 0, *val, &eax))		return (eax >> 8) & 0xff;	*val = eax;	return APM_SUCCESS;}/** *	apm_get_event	-	get an APM event from the BIOS *	@event: pointer to the event *	@info: point to the event information * *	The APM BIOS provides a polled information for event *	reporting. The BIOS expects to be polled at least every second *	when events are pending. When a message is found the caller should *	poll until no more messages are present.  However, this causes *	problems on some laptops where a suspend event notification is *	not cleared until it is acknowledged. * *	Additional information is returned in the info pointer, providing *	that APM 1.2 is in use. If no messges are pending the value 0x80 *	is returned (No power management events pending). */ static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info){	u32	eax;	u32	ebx;	u32	ecx;	u32	dummy;	if (apm_bios_call(APM_FUNC_GET_EVENT, 0, 0, &eax, &ebx, &ecx,			&dummy, &dummy))		return (eax >> 8) & 0xff;	*event = ebx;	if (apm_info.connection_version < 0x0102)		*info = ~0; /* indicate info not valid */	else		*info = ecx;	return APM_SUCCESS;}/** *	set_power_state	-	set the power management state *	@what: which items to transition *	@state: state to transition to * *	Request an APM change of state for one or more system devices. The *	processor state must be transitioned last of all. what holds the *	class of device in the upper byte and the device number (0xFF for *	all) for the object to be transitioned. * *	The state holds the state to transition to, which may in fact *	be an acceptance of a BIOS requested state change. */ static int set_power_state(u_short what, u_short state){	u32	eax;	if (apm_bios_call_simple(APM_FUNC_SET_STATE, what, state, &eax))		return (eax >> 8) & 0xff;	return APM_SUCCESS;}/** *	set_system_power_state - set system wide power state *	@state: which state to enter * *	Transition the entire system into a new APM power state. */ static int set_system_power_state(u_short state){	return set_power_state(APM_DEVICE_ALL, state);}/** *	apm_do_idle	-	perform power saving * *	This function notifies the BIOS that the processor is (in the view *	of the OS) idle. It returns -1 in the event that the BIOS refuses *	to handle the idle request. On a success the function returns 1 *	if the BIOS did clock slowing or 0 otherwise. */ static int apm_do_idle(void){	u32	eax;	u8	ret = 0;	int	idled = 0;	int	polling;	polling = test_thread_flag(TIF_POLLING_NRFLAG);	if (polling) {		clear_thread_flag(TIF_POLLING_NRFLAG);		smp_mb__after_clear_bit();	}	if (!need_resched()) {		idled = 1;		ret = apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &eax);	}	if (polling)		set_thread_flag(TIF_POLLING_NRFLAG);	if (!idled)		return 0;	if (ret) {		static unsigned long t;		/* This always fails on some SMP boards running UP kernels.		 * Only report the failure the first 5 times.		 */		if (++t < 5)		{			printk(KERN_DEBUG "apm_do_idle failed (%d)\n",					(eax >> 8) & 0xff);			t = jiffies;		}		return -1;	}	clock_slowed = (apm_info.bios.flags & APM_IDLE_SLOWS_CLOCK) != 0;	return clock_slowed;}/** *	apm_do_busy	-	inform the BIOS the CPU is busy * *	Request that the BIOS brings the CPU back to full performance.  */ static void apm_do_busy(void){	u32	dummy;	if (clock_slowed || ALWAYS_CALL_BUSY) {		(void) apm_bios_call_simple(APM_FUNC_BUSY, 0, 0, &dummy);		clock_slowed = 0;	}}/* * If no process has really been interested in * the CPU for some time, we want to call BIOS * power management - we probably want * to conserve power. */#define IDLE_CALC_LIMIT   (HZ * 100)#define IDLE_LEAKY_MAX    16static void (*original_pm_idle)(void);extern void default_idle(void);/** * apm_cpu_idle		-	cpu idling for APM capable Linux * * This is the idling function the kernel executes when APM is available. It  * tries to do BIOS powermanagement based on the average system idle time. * Furthermore it calls the system default idle routine. */static void apm_cpu_idle(void){	static int use_apm_idle; /* = 0 */	static unsigned int last_jiffies; /* = 0 */	static unsigned int last_stime; /* = 0 */	int apm_idle_done = 0;	unsigned int jiffies_since_last_check = jiffies - last_jiffies;	unsigned int bucket;recalc:	if (jiffies_since_last_check > IDLE_CALC_LIMIT) {		use_apm_idle = 0;		last_jiffies = jiffies;		last_stime = current->stime;	} else if (jiffies_since_last_check > idle_period) {		unsigned int idle_percentage;		idle_percentage = current->stime - last_stime;		idle_percentage *= 100;		idle_percentage /= jiffies_since_last_check;		use_apm_idle = (idle_percentage > idle_threshold);		if (apm_info.forbid_idle)			use_apm_idle = 0;		last_jiffies = jiffies;		last_stime = current->stime;	}	bucket = IDLE_LEAKY_MAX;	while (!need_resched()) {		if (use_apm_idle) {			unsigned int t;			t = jiffies;			switch (apm_do_idle()) {			case 0: apm_idle_done = 1;				if (t != jiffies) {					if (bucket) {						bucket = IDLE_LEAKY_MAX;						continue;					}				} else if (bucket) {					bucket--;					continue;				}				break;			case 1: apm_idle_done = 1;				break;			default: /* BIOS refused */				break;			}		}		if (original_pm_idle)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -