📄 apm.c
字号:
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 send_event(apm_event_t event){ switch (event) { case APM_SYS_SUSPEND: case APM_CRITICAL_SUSPEND: case APM_USER_SUSPEND: /* map all suspends to ACPI D3 */ if (pm_send_all(PM_SUSPEND, (void *)3)) { if (event == APM_CRITICAL_SUSPEND) { printk(KERN_CRIT "apm: Critical suspend was vetoed, expect armageddon\n" ); return 0; } if (apm_info.connection_version > 0x100) apm_set_power_state(APM_STATE_REJECT); return 0; } break; case APM_NORMAL_RESUME: case APM_CRITICAL_RESUME: /* map all resumes to ACPI D0 */ (void) pm_send_all(PM_RESUME, (void *)0); break; } return 1;}static int suspend(void){ int err; struct apm_user *as; get_time_diff(); cli(); err = apm_set_power_state(APM_STATE_SUSPEND); reinit_timer(); set_time(); if (err == APM_NO_ERROR) err = APM_SUCCESS; if (err != APM_SUCCESS) apm_error("suspend", err); send_event(APM_NORMAL_RESUME); sti(); queue_event(APM_NORMAL_RESUME, NULL); for (as = user_list; as != NULL; as = as->next) { as->suspend_wait = 0; as->suspend_result = ((err == APM_SUCCESS) ? 0 : -EIO); } ignore_normal_resume = 1; wake_up_interruptible(&apm_suspend_waitqueue); return err;}static void standby(void){ int err; 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: if (send_event(event)) { 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; if (send_event(event)) { queue_event(event, NULL); waiting_for_resume = 1; if (suspends_pending <= 0) (void) suspend(); } 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(); send_event(event); queue_event(event, NULL); } break; case APM_CAPABILITY_CHANGE: case APM_LOW_BATTERY: case APM_POWER_STATUS_CHANGE: send_event(event); queue_event(event, NULL); break; case APM_UPDATE_TIME: set_time(); break; case APM_CRITICAL_SUSPEND: send_event(event); /* * We can only hope it worked - we are not allowed * to reject a critical suspend. */ (void) suspend(); 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. * * Check whether we're the only running process to * decide if we should just power down. * */#define system_idle() (nr_running == 1)static void apm_mainloop(void){ int timeout = HZ; DECLARE_WAITQUEUE(wait, current); add_wait_queue(&apm_waitqueue, &wait); set_current_state(TASK_INTERRUPTIBLE); for (;;) { /* Nothing to do, just sleep for the timeout */ timeout = 2*timeout; if (timeout > APM_CHECK_TIMEOUT) timeout = APM_CHECK_TIMEOUT; schedule_timeout(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();#ifdef CONFIG_APM_CPU_IDLE if (!system_idle()) continue; if (apm_do_idle()) { unsigned long start = jiffies; while ((!exit_kapmd) && system_idle()) { apm_do_idle(); if ((jiffies - start) > APM_CHECK_TIMEOUT) { apm_event_handler(); start = jiffies; } } apm_do_busy(); apm_event_handler(); timeout = 1; }#endif } 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; DECLARE_WAITQUEUE(wait, current); as = fp->private_data; if (check_apm_user(as, "read")) return -EIO; if (count < sizeof(apm_event_t)) return -EINVAL; if (queue_empty(as)) { if (fp->f_flags & O_NONBLOCK) return -EAGAIN; add_wait_queue(&apm_waitqueue, &wait);repeat: set_current_state(TASK_INTERRUPTIBLE); if (queue_empty(as) && !signal_pending(current)) { schedule(); goto repeat; } set_current_state(TASK_RUNNING); remove_wait_queue(&apm_waitqueue, &wait); } 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; DECLARE_WAITQUEUE(wait, current); as = filp->private_data; if (check_apm_user(as, "ioctl")) return -EIO; if (!as->suser) return -EPERM; switch (cmd) { case APM_IOC_STANDBY: if (as->standbys_read > 0) { as->standbys_read--; as->standbys_pending--; standbys_pending--; } else if (!send_event(APM_USER_STANDBY)) return -EAGAIN; 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 if (!send_event(APM_USER_SUSPEND)) return -EAGAIN; else queue_event(APM_USER_SUSPEND, as); if (suspends_pending <= 0) { if (suspend() != APM_SUCCESS) return -EIO; } else { as->suspend_wait = 1; add_wait_queue(&apm_suspend_waitqueue, &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); if ((as->suspend_wait == 0) || signal_pending(current)) break; schedule(); } set_current_state(TASK_RUNNING); remove_wait_queue(&apm_suspend_waitqueue, &wait); 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(); } if (user_list == as) user_list = as->next; else { struct apm_user * as1; for (as1 = user_list; (as1 != NULL) && (as1->next != as); as1 = as1->next) ; if (as1 == NULL) printk(KERN_ERR "apm: filp not in user list\n"); else as1->next = as->next; } unlock_kernel(); kfree(as); return 0;}static int do_open(struct inode * inode, struct file * filp){ struct apm_user * as;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -