📄 apm.c
字号:
# define APM_DECL_SEGS# define APM_DO_SAVE_SEGS# define APM_DO_ZERO_SEGS# define APM_DO_POP_SEGS# define APM_DO_RESTORE_SEGS#endifstatic 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; __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); return *eax & 0xff;}/* * This version only returns one value (usually an error code) */static u8 apm_bios_call_simple(u32 func, u32 ebx_in, u32 ecx_in, u32 *eax){ u8 error; APM_DECL_SEGS unsigned long flags; __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); return error;}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;}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;}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;}static int apm_set_power_state(u_short state){ return set_power_state(APM_DEVICE_ALL, state);}#ifdef CONFIG_APM_CPU_IDLEstatic int apm_do_idle(void){ u32 dummy; if (apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &dummy)) return 0;#ifdef ALWAYS_CALL_BUSY clock_slowed = 1;#else clock_slowed = (apm_info.bios.flags & APM_IDLE_SLOWS_CLOCK) != 0;#endif return 1;}static void apm_do_busy(void){ u32 dummy; if (clock_slowed) { (void) apm_bios_call_simple(APM_FUNC_BUSY, 0, 0, &dummy); clock_slowed = 0; }}#if 0extern int hlt_counter;/* * If no process has been interested in this * CPU for some time, we want to wake up the * power management thread - we probably want * to conserve power. */#define HARD_IDLE_TIMEOUT (HZ/3)/* This should wake up kapmd and ask it to slow the CPU */#define powermanagement_idle() do { } while (0)/* * This is the idle thing. */static void apm_cpu_idle(void){ unsigned int start_idle; start_idle = jiffies; while (1) { if (!current->need_resched) { if (jiffies - start_idle < HARD_IDLE_TIMEOUT) { if (!current_cpu_data.hlt_works_ok) continue; if (hlt_counter) continue; __cli(); if (!current->need_resched) safe_halt(); else __sti(); continue; } /* * Ok, do some power management - we've been idle for too long */ powermanagement_idle(); } schedule(); check_pgt_cache(); start_idle = jiffies; }}#endif#endif#ifdef CONFIG_SMPstatic int apm_magic(void * unused){ while (1) schedule();}#endifstatic void apm_power_off(void){#ifdef CONFIG_APM_REAL_MODE_POWER_OFF 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 */ };#endif /* * This may be called on an SMP machine. */#ifdef CONFIG_SMP /* Some bioses don't like being called from CPU != 0 */ while (cpu_number_map(smp_processor_id()) != 0) { kernel_thread(apm_magic, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD); schedule(); }#endif#ifdef CONFIG_APM_REAL_MODE_POWER_OFF machine_real_restart(po_bios_call, sizeof(po_bios_call));#else (void) apm_set_power_state(APM_STATE_OFF);#endif}#ifdef CONFIG_APM_DO_ENABLEstatic 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;}#endifstatic 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; *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)) return (eax >> 8) & 0xff; *status = ebx; *bat = ecx; *life = edx; *nbat = esi; return APM_SUCCESS;}#endifstatic int apm_engage_power_management(u_short device, int enable){ u32 eax; if ((enable == 0) && (device == APM_DEVICE_ALL) && (apm_info.bios.flags & APM_BIOS_DISABLED)) return APM_DISABLED; if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, enable, &eax)) return (eax >> 8) & 0xff; if (device == APM_DEVICE_ALL) { if (enable) apm_info.bios.flags &= ~APM_BIOS_DISENGAGED; else apm_info.bios.flags |= APM_BIOS_DISENGAGED; } return APM_SUCCESS;}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);}#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)static int apm_console_blank(int blank){ int error; u_short state; state = blank ? APM_STATE_STANDBY : APM_STATE_READY; /* Blank the first display device */ error = set_power_state(0x100, state); if ((error != APM_SUCCESS) && (error != APM_NO_ERROR)) { /* try to blank them all instead */ error = set_power_state(0x1ff, state); if ((error != APM_SUCCESS) && (error != APM_NO_ERROR)) /* try to blank device one instead */ error = set_power_state(0x101, state); } if ((error == APM_SUCCESS) || (error == APM_NO_ERROR)) return 1; apm_error("set display", error); return 0;}#endifstatic int queue_empty(struct apm_user *as){ return as->event_head == as->event_tail;}static apm_event_t get_queued_event(struct apm_user *as){ as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS; return as->events[as->event_tail];}static void queue_event(apm_event_t event, struct apm_user *sender){ struct apm_user * as; if (user_list == NULL) return; for (as = user_list; as != NULL; as = as->next) { if (as == sender) continue; as->event_head = (as->event_head + 1) % APM_MAX_EVENTS; if (as->event_head == as->event_tail) { static int notified; if (notified++ == 0) printk(KERN_ERR "apm: an event queue overflowed\n"); as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS; } as->events[as->event_head] = event; if (!as->suser) continue; switch (event) { case APM_SYS_SUSPEND: case APM_USER_SUSPEND: as->suspends_pending++; suspends_pending++; break; case APM_SYS_STANDBY: case APM_USER_STANDBY: as->standbys_pending++; standbys_pending++; break; } } wake_up_interruptible(&apm_waitqueue);}static void set_time(void){ unsigned long flags; if (got_clock_diff) { /* Must know time zone in order to set clock */ save_flags(flags); cli(); CURRENT_TIME = get_cmos_time() + clock_cmos_diff; restore_flags(flags); }}static void get_time_diff(void){#ifndef CONFIG_APM_RTC_IS_GMT unsigned long flags; /* * Estimate time zone so that set_time can update the clock */ save_flags(flags); clock_cmos_diff = -get_cmos_time(); cli(); clock_cmos_diff += CURRENT_TIME; got_clock_diff = 1; restore_flags(flags);#endif}static void reinit_timer(void){#ifdef INIT_TIMER_AFTER_SUSPEND unsigned long flags; save_flags(flags);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -