?? apm.c
字號:
*/ 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;}/** * 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);}#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 dont 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; 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) || (!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);}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); cli(); /* set the clock to 100 Hz */ outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ udelay(10); outb_p(LATCH & 0xff , 0x40); /* LSB */ udelay(10); outb(LATCH >> 8 , 0x40); /* MSB */ udelay(10); restore_flags(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) apm_set_power_state(APM_STATE_REJECT); err = -EBUSY; waiting_for_resume = 0; printk(KERN_WARNING "apm: suspend was vetoed.\n"); goto out; } printk(KERN_CRIT "apm: suspend was vetoed, but suspending anyway.\n"); } get_time_diff(); cli(); err = apm_set_power_state(APM_STATE_SUSPEND); reinit_timer(); set_time(); sti(); if (err == APM_NO_ERROR) err = APM_SUCCESS; if (err != APM_SUCCESS) apm_error("suspend", err); err = (err == APM_SUCCESS) ? 0 : -EIO; pm_send_all(PM_RESUME, (void *)0); queue_event(APM_NORMAL_RESUME, NULL); ignore_normal_resume = 1; out: for (as = user_list; as != NULL; as = as->next) { as->suspend_wait = 0; as->suspend_result = err; } ignore_normal_resume = 1; wake_up_interruptible(&apm_suspend_waitqueue); return err;}static void standby(void){ int err; /* If needed, notify drivers here */ get_time_diff(); err = apm_set_power_state(APM_STATE_STANDBY); if ((err != APM_SUCCESS) && (err != APM_NO_ERROR)) apm_error("standby", err);}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; if (ignore_normal_resume && (event != APM_NORMAL_RESUME)) ignore_normal_resume = 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) apm_set_power_state(APM_STATE_REJECT); break;#endif case APM_SYS_SUSPEND: if (ignore_bounce) { if (apm_info.connection_version > 0x100) apm_set_power_state(APM_STATE_REJECT); break; } /* * If we are already processing a SUSPEND, * then further SUSPEND events from the BIOS * will be ignored. We also return here to * cope with the fact that the Thinkpads keep * sending a SUSPEND event until something else * happens! */ if (waiting_for_resume) return; waiting_for_resume = 1; queue_event(event, NULL); if (suspends_pending <= 0) (void) suspend(1); break; case APM_NORMAL_RESUME: case APM_CRITICAL_RESUME: case APM_STANDBY_RESUME: waiting_for_resume = 0; last_resume = jiffies; ignore_bounce = 1; if ((event != APM_NORMAL_RESUME) || (ignore_normal_resume == 0)) { set_time(); pm_send_all(PM_RESUME, (void *)0); queue_event(event, NULL); } ignore_normal_resume = 0; break; case APM_CAPABILITY_CHANGE: case APM_LOW_BATTERY: case APM_POWER_STATUS_CHANGE: queue_event(event, NULL); /* If needed, notify drivers here */ break; case APM_UPDATE_TIME: set_time(); break; case APM_CRITICAL_SUSPEND: /* * We are not allowed to reject a critical suspend. */ (void) suspend(0); break; } }}static void apm_event_handler(void){ static int pending_count = 4; int err; if ((standbys_pending > 0) || (suspends_pending > 0)) { if ((apm_info.connection_version > 0x100) && (pending_count-- <= 0)) { pending_count = 4; if (debug) printk(KERN_DEBUG "apm: setting state busy\n"); err = apm_set_power_state(APM_STATE_BUSY); if (err) apm_error("busy", err); } } else pending_count = 4; check_events();}/* * This is the APM thread main loop. */static void apm_mainloop(void){ DECLARE_WAITQUEUE(wait, current); add_wait_queue(&apm_waitqueue, &wait); set_current_state(TASK_INTERRUPTIBLE); for (;;) { schedule_timeout(APM_CHECK_TIMEOUT); if (exit_kapmd) break; /* * Ok, check all events, check for idle (and mark us sleeping * so as not to count towards the load average).. */ set_current_state(TASK_INTERRUPTIBLE); apm_event_handler(); } remove_wait_queue(&apm_waitqueue, &wait);}static int check_apm_user(struct apm_user *as, const char *func){ if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) { printk(KERN_ERR "apm: %s passed bad filp\n", func); return 1; } return 0;}static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos){ struct apm_user * as; int i; apm_event_t event; as = fp->private_data; if (check_apm_user(as, "read")) return -EIO; if (count < sizeof(apm_event_t)) return -EINVAL; if ((queue_empty(as)) && (fp->f_flags & O_NONBLOCK)) return -EAGAIN; wait_event_interruptible(apm_waitqueue, !queue_empty(as)); i = count; while ((i >= sizeof(event)) && !queue_empty(as)) { event = get_queued_event(as); if (copy_to_user(buf, &event, sizeof(event))) { if (i < count) break; return -EFAULT; } switch (event) { case APM_SYS_SUSPEND: case APM_USER_SUSPEND: as->suspends_read++; break; case APM_SYS_STANDBY: case APM_USER_STANDBY: as->standbys_read++; break; } buf += sizeof(event); i -= sizeof(event); } if (i < count) return count - i; if (signal_pending(current)) return -ERESTARTSYS; return 0;}static unsigned int do_poll(struct file *fp, poll_table * wait){ struct apm_user * as; as = fp->private_data; if (check_apm_user(as, "poll")) return 0; poll_wait(fp, &apm_waitqueue, wait); if (!queue_empty(as)) return POLLIN | POLLRDNORM; return 0;}static int do_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg){ struct apm_user * as; as = filp->private_data; if (check_apm_user(as, "ioctl")) return -EIO; if ((!as->suser) || (!as->writer)) return -EPERM; switch (cmd) { case APM_IOC_STANDBY: if (as->standbys_read > 0) { as->standbys_read--; as->standbys_pending--; standbys_pending--; } else queue_event(APM_USER_STANDBY, as); if (standbys_pending <= 0) standby(); break; case APM_IOC_SUSPEND: if (as->suspends_read > 0) { as->suspends_read--; as->suspends_pending--; suspends_pending--; } else queue_event(APM_USER_SUSPEND, as); if (suspends_pending <= 0) { return suspend(1); } else { as->suspend_wait = 1; wait_event_interruptible(apm_suspend_waitqueue, as->suspend_wait == 0); return as->suspend_result; } break; default: return -EINVAL; } return 0;}static int do_release(struct inode * inode, struct file * filp){ struct apm_user * as; as = filp->private_data; if (check_apm_user(as, "release")) return 0; filp->private_data = NULL; lock_kernel(); if (as->standbys_pending > 0) { standbys_pending -= as->standbys_pending; if (standbys_pending <= 0) standby(); } if (as->suspends_pending > 0) { suspends_pending -= as->suspends_pending; if (suspends_pending <= 0) (void) suspend(1); } if (user_list == as) user_list = as->next;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -