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

📄 apm.c

📁 这个linux源代码是很全面的~基本完整了~使用c编译的~由于时间问题我没有亲自测试~但就算用来做参考资料也是非常好的
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * 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) \		__sti(); \	else \		__cli();#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_ZERO_SEGS \		"pushl %%ds\n\t" \		"pushl %%es\n\t" \		"xorl %%edx, %%edx\n\t" \		"mov %%dx, %%ds\n\t" \		"mov %%dx, %%es\n\t" \		"mov %%dx, %%fs\n\t" \		"mov %%dx, %%gs\n\t"#	define APM_DO_POP_SEGS \		"popl %%es\n\t" \		"popl %%ds\n\t"#	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_ZERO_SEGS#	define APM_DO_POP_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;	unsigned long cpus = apm_save_cpus();	struct desc_struct	save_desc_40;		__save_flags(flags);	APM_DO_CLI;	APM_DO_SAVE_SEGS;	/*	 * N.B. We do NOT need a cld after the BIOS call	 * because we always save and restore the flags.	 */	__asm__ __volatile__(APM_DO_ZERO_SEGS		"pushl %%edi\n\t"		"pushl %%ebp\n\t"		"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"		"setc %%al\n\t"		"popl %%ebp\n\t"		"popl %%edi\n\t"		APM_DO_POP_SEGS		: "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx),		  "=S" (*esi)		: "a" (func), "b" (ebx_in), "c" (ecx_in)		: "memory", "cc");	APM_DO_RESTORE_SEGS;	__restore_flags(flags);		apm_restore_cpus(cpus);		cpu_gdt_table[smp_processor_id()][0x40 / 8] = save_desc_40;		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;	int			cpu = smp_processor_id();	struct desc_struct	save_desc_40;	unsigned long cpus = apm_save_cpus();	save_desc_40 = cpu_gdt_table[cpu][0x40 / 8];	cpu_gdt_table[cpu][0x40 / 8] = bad_bios_desc;		__save_flags(flags);	APM_DO_CLI;	APM_DO_SAVE_SEGS;	{		int	cx, dx, si;		/*		 * N.B. We do NOT need a cld after the BIOS call		 * because we always save and restore the flags.		 */		__asm__ __volatile__(APM_DO_ZERO_SEGS			"pushl %%edi\n\t"			"pushl %%ebp\n\t"			"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"			"setc %%bl\n\t"			"popl %%ebp\n\t"			"popl %%edi\n\t"			APM_DO_POP_SEGS			: "=a" (*eax), "=b" (error), "=c" (cx), "=d" (dx),			  "=S" (si)			: "a" (func), "b" (ebx_in), "c" (ecx_in)			: "memory", "cc");	}	APM_DO_RESTORE_SEGS;	__restore_flags(flags);	apm_restore_cpus(cpus);		cpu_gdt_table[smp_processor_id()][0x40 / 8] = save_desc_40;		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 __init 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;	if (apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &eax)) {		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);		}		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->times.tms_stime;	} else if (jiffies_since_last_check > idle_period) {		unsigned int idle_percentage;		idle_percentage = current->times.tms_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->times.tms_stime;	}	bucket = IDLE_LEAKY_MAX;	while (!current->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)			original_pm_idle();		else			default_idle();		jiffies_since_last_check = jiffies - last_jiffies;		if (jiffies_since_last_check > idle_period)			goto recalc;	}	if (apm_idle_done)		apm_do_busy();}/** *	apm_power_off	-	ask the BIOS to power off * *	Handle the power off sequence. This is the one piece of code we *	will execute even on SMP machines. In order to deal with BIOS *	bugs we support real mode APM BIOS power off calls. We also make *	the SMP call on CPU0 as some systems will only honour this call *	on their first cpu. */ static void apm_power_off(void){	unsigned char	po_bios_call[] = {		0xb8, 0x00, 0x10,	/* movw  $0x1000,ax  */		0x8e, 0xd0,		/* movw  ax,ss       */		0xbc, 0x00, 0xf0,	/* movw  $0xf000,sp  */		0xb8, 0x07, 0x53,	/* movw  $0x5307,ax  */		0xbb, 0x01, 0x00,	/* movw  $0x0001,bx  */		0xb9, 0x03, 0x00,	/* movw  $0x0003,cx  */		0xcd, 0x15		/* int   $0x15       */	};	/*	 * This may be called on an SMP machine.	 */	if (apm_info.realmode_power_off)	{		(void)apm_save_cpus();		machine_real_restart(po_bios_call, sizeof(po_bios_call));		/* Never returns */	}	else		(void) set_system_power_state(APM_STATE_OFF);}/** * handle_poweroff	-	sysrq callback for power down * @key: key pressed (unused) * @pt_regs: register state (unused) * @kbd: keyboard state (unused) * @tty: tty involved (unused) * * When the user hits Sys-Rq o to power down the machine this is the * callback we use. */void handle_poweroff (int key, struct pt_regs *pt_regs,		struct kbd_struct *kbd, struct tty_struct *tty) {        apm_power_off();}struct sysrq_key_op	sysrq_poweroff_op = {	handler:        handle_poweroff,	help_msg:       "Off",	action_msg:     "Power Off\n"};#ifdef CONFIG_APM_DO_ENABLE/** *	apm_enable_power_management - enable BIOS APM power management *	@enable: enable yes/no * *	Enable or disable the APM BIOS power services.  */ static int apm_enable_power_management(int enable){	u32	eax;	if ((enable == 0) && (apm_info.bios.flags & APM_BIOS_DISENGAGED))		return APM_NOT_ENGAGED;	if (apm_bios_call_simple(APM_FUNC_ENABLE_PM, APM_DEVICE_BALL,			enable, &eax))		return (eax >> 8) & 0xff;	if (enable)		apm_info.bios.flags &= ~APM_BIOS_DISABLED;	else		apm_info.bios.flags |= APM_BIOS_DISABLED;	return APM_SUCCESS;}#endif/** *	apm_get_power_status	-	get current power state *	@status: returned status *	@bat: battery info *	@life: estimated life * *	Obtain the current power status from the APM BIOS. We return a *	status which gives the rough battery status, and current power *	source. The bat value returned give an estimate as a percentage *	of life and a status value for the battery. The estimated life *	if reported is a lifetime in secodnds/minutes at current powwer *	consumption. */ static int apm_get_power_status(u_short *status, u_short *bat, u_short *life){	u32	eax;	u32	ebx;	u32	ecx;	u32	edx;	u32	dummy;	if (apm_info.get_power_status_broken)		return APM_32_UNSUPPORTED;	if (apm_bios_call(APM_FUNC_GET_STATUS, APM_DEVICE_ALL, 0,			&eax, &ebx, &ecx, &edx, &dummy))		return (eax >> 8) & 0xff;	*status = ebx;	*bat = ecx;	if (apm_info.get_power_status_swabinminutes) {		*life = swab16((u16)edx);		*life |= 0x8000;	} else		*life = edx;	return APM_SUCCESS;}#if 0static int apm_get_battery_status(u_short which, u_short *status,				  u_short *bat, u_short *life, u_short *nbat){	u32	eax;	u32	ebx;	u32	ecx;	u32	edx;	u32	esi;	if (apm_info.connection_version < 0x0102) {		/* pretend we only have one battery. */		if (which != 1)			return APM_BAD_DEVICE;		*nbat = 1;		return apm_get_power_status(status, bat, life);	}	if (apm_bios_call(APM_FUNC_GET_STATUS, (0x8000 | (which)), 0, &eax,			&ebx, &ecx, &edx, &esi))

⌨️ 快捷键说明

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