?? apm.c
字號:
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 */ }; /* Some bioses don't like being called from CPU != 0 */ if (apm_info.realmode_power_off) { (void)apm_save_cpus(); machine_real_restart(po_bios_call, sizeof(po_bios_call)); } else (void) set_system_power_state(APM_STATE_OFF);}#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)) return (eax >> 8) & 0xff; *status = ebx; *bat = ecx; *life = edx; *nbat = esi; return APM_SUCCESS;}#endif/** * apm_engage_power_management - enable PM on a device * @device: identity of device * @enable: on/off * * Activate or deactive power management on either a specific device * or the entire system (%APM_DEVICE_ALL). */ static 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;}#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)/** * apm_console_blank - blank the display * @blank: on/off * * Attempt to blank the console, firstly by blanking just video device * zero, and if that fails (some BIOSes don't support it) then it blanks * all video devices. Typically the BIOS will do laptop backlight and * monitor powerdown for us. */ 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; if (error == APM_NOT_ENGAGED) { static int tried; int eng_error; if (tried++ == 0) { eng_error = apm_engage_power_management(APM_DEVICE_ALL, 1); if (eng_error) { apm_error("set display", error); apm_error("engage interface", eng_error); return 0; } else return apm_console_blank(blank); } } 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; spin_lock(&user_list_lock); if (user_list == NULL) goto out; for (as = user_list; as != NULL; as = as->next) { if ((as == sender) || (!as->reader)) 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) || (!as->writer)) 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);out: spin_unlock(&user_list_lock);}static void set_time(void){ if (got_clock_diff) { /* Must know time zone in order to set clock */ xtime.tv_sec = get_cmos_time() + clock_cmos_diff; xtime.tv_nsec = 0; } }static void get_time_diff(void){#ifndef CONFIG_APM_RTC_IS_GMT /* * Estimate time zone so that set_time can update the clock */ clock_cmos_diff = -get_cmos_time(); clock_cmos_diff += get_seconds(); got_clock_diff = 1;#endif}static void reinit_timer(void){#ifdef INIT_TIMER_AFTER_SUSPEND unsigned long flags; spin_lock_irqsave(&i8253_lock, flags); /* set the clock to 100 Hz */ outb_p(0x34, PIT_MODE); /* binary, mode 2, LSB/MSB, ch 0 */ udelay(10); outb_p(LATCH & 0xff, PIT_CH0); /* LSB */ udelay(10); outb(LATCH >> 8, PIT_CH0); /* MSB */ udelay(10); spin_unlock_irqrestore(&i8253_lock, flags);#endif}static int suspend(int vetoable){ int err; struct apm_user *as; if (pm_send_all(PM_SUSPEND, (void *)3)) { /* Vetoed */ if (vetoable) { if (apm_info.connection_version > 0x100) set_system_power_state(APM_STATE_REJECT); err = -EBUSY; ignore_sys_suspend = 0; printk(KERN_WARNING "apm: suspend was vetoed.\n"); goto out; } printk(KERN_CRIT "apm: suspend was vetoed, but suspending anyway.\n"); } device_suspend(PMSG_SUSPEND); local_irq_disable(); device_power_down(PMSG_SUSPEND); /* serialize with the timer interrupt */ write_seqlock(&xtime_lock); /* protect against access to timer chip registers */ spin_lock(&i8253_lock); get_time_diff(); /* * Irq spinlock must be dropped around set_system_power_state. * We'll undo any timer changes due to interrupts below. */ spin_unlock(&i8253_lock); write_sequnlock(&xtime_lock); local_irq_enable(); save_processor_state(); err = set_system_power_state(APM_STATE_SUSPEND); ignore_normal_resume = 1; restore_processor_state(); local_irq_disable(); write_seqlock(&xtime_lock); spin_lock(&i8253_lock); reinit_timer(); set_time(); spin_unlock(&i8253_lock); write_sequnlock(&xtime_lock); if (err == APM_NO_ERROR) err = APM_SUCCESS; if (err != APM_SUCCESS) apm_error("suspend", err); err = (err == APM_SUCCESS) ? 0 : -EIO; device_power_up(); local_irq_enable(); device_resume(); pm_send_all(PM_RESUME, (void *)0); queue_event(APM_NORMAL_RESUME, NULL); out: spin_lock(&user_list_lock); for (as = user_list; as != NULL; as = as->next) { as->suspend_wait = 0; as->suspend_result = err; } spin_unlock(&user_list_lock); wake_up_interruptible(&apm_suspend_waitqueue); return err;}static void standby(void){ int err; local_irq_disable(); device_power_down(PMSG_SUSPEND); /* serialize with the timer interrupt */ write_seqlock(&xtime_lock); /* If needed, notify drivers here */ get_time_diff(); write_sequnlock(&xtime_lock); local_irq_enable(); err = set_system_power_state(APM_STATE_STANDBY); if ((err != APM_SUCCESS) && (err != APM_NO_ERROR)) apm_error("standby", err); local_irq_disable(); device_power_up(); local_irq_enable();}static apm_event_t get_event(void){ int error; apm_event_t event; apm_eventinfo_t info; static int notified; /* we don't use the eventinfo */ error = apm_get_event(&event, &info); if (error == APM_SUCCESS) return event; if ((error != APM_NO_EVENTS) && (notified++ == 0)) apm_error("get_event", error); return 0;}static void check_events(void){ apm_event_t event; static unsigned long last_resume; static int ignore_bounce; while ((event = get_event()) != 0) { if (debug) { if (event <= NR_APM_EVENT_NAME) printk(KERN_DEBUG "apm: received %s notify\n", apm_event_name[event - 1]); else printk(KERN_DEBUG "apm: received unknown " "event 0x%02x\n", event); } if (ignore_bounce && ((jiffies - last_resume) > bounce_interval)) ignore_bounce = 0; switch (event) { case APM_SYS_STANDBY: case APM_USER_STANDBY: queue_event(event, NULL); if (standbys_pending <= 0) standby(); break; case APM_USER_SUSPEND:#ifdef CONFIG_APM_IGNORE_USER_SUSPEND if (apm_info.connection_version > 0x100) set_system_power_state(APM_STATE_REJECT); break;#endif case APM_SYS_SUSPEND: if (ignore_bounce) { if (apm_info.connection_version > 0x100) set_system_power_state(APM_STATE_REJECT); break; }
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -