?? apm.c
字號:
/* * 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 (ignore_sys_suspend) return; ignore_sys_suspend = 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: ignore_sys_suspend = 0; last_resume = jiffies; ignore_bounce = 1; if ((event != APM_NORMAL_RESUME) || (ignore_normal_resume == 0)) { write_seqlock_irq(&xtime_lock); set_time(); write_sequnlock_irq(&xtime_lock); device_resume(); 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: write_seqlock_irq(&xtime_lock); set_time(); write_sequnlock_irq(&xtime_lock); 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 = set_system_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 __user *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 ((int)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; 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); } spin_lock(&user_list_lock); 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; } spin_unlock(&user_list_lock); kfree(as); return 0;}static int do_open(struct inode * inode, struct file * filp){ struct apm_user * as; as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL); if (as == NULL) { printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n", sizeof(*as)); return -ENOMEM; } as->magic = APM_BIOS_MAGIC; as->event_tail = as->event_head = 0; as->suspends_pending = as->standbys_pending = 0; as->suspends_read = as->standbys_read = 0; /* * XXX - this is a tiny bit broken, when we consider BSD * process accounting. If the device is opened by root, we * instantly flag that we used superuser privs. Who knows, * we might close the device immediately without doing a * privileged operation -- cevans */ as->suser = capable(CAP_SYS_ADMIN); as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE; as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ; spin_lock(&user_list_lock); as->next = user_list; user_list = as; spin_unlock(&user_list_lock); filp->private_data = as; return 0;}static int apm_get_info(char *buf, char **start, off_t fpos, int length){ char * p; unsigned short bx; unsigned short cx; unsigned short dx; int error; unsigned short ac_line_status = 0xff; unsigned short battery_status = 0xff; unsigned short battery_flag = 0xff; int percentage = -1; int time_units = -1; char *units = "?"; p = buf; if ((num_online_cpus() == 1) && !(error = apm_get_power_status(&bx, &cx, &dx))) { ac_line_status = (bx >> 8) & 0xff; battery_status = bx & 0xff; if ((cx & 0xff) != 0xff) percentage = cx & 0xff; if (apm_info.connection_version > 0x100) { battery_flag = (cx >> 8) & 0xff; if (dx != 0xffff) { units = (dx & 0x8000) ? "min" : "sec"; time_units = dx & 0x7fff; } } } /* Arguments, with symbols from linux/apm_bios.h. Information is from the Get Power Status (0x0a) call unless otherwise noted. 0) Linux driver version (this will change if format changes) 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2. 2) APM flags from APM Installation Check (0x00): bit 0: APM_16_BIT_SUPPORT bit 1: APM_32_BIT_SUPPORT bit 2: APM_IDLE_SLOWS_CLOCK bit 3: APM_BIOS_DISABLED bit 4: APM_BIOS_DISENGAGED 3) AC line status 0x00: Off-line 0x01: On-line 0x02: On backup power (BIOS >= 1.1 only) 0xff: Unknown 4) Battery status 0x00: High 0x01: Low 0x02: Critical 0x03: Charging 0x04: Selected battery not present (BIOS >= 1.2 only) 0xff: Unknown 5) Battery flag bit 0: High bit 1: Low bit 2: Critical bit 3: Charging bit 7: No system battery 0xff: Unknown 6) Remaining battery life (percentage of charge): 0-100: valid -1: Unknown 7) Remaining battery life (time units): Number of remaining minutes or seconds -1: Unknown 8) min = minutes; sec = seconds */ p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n", driver_version, (apm_info.bios.version >> 8) & 0xff, apm_info.bios.version & 0xff, apm_info.bios.flags, ac_line_status, battery_status, battery_flag, percentage, time_units, units); return p - buf;}static int apm(void *unused){ unsigned short bx; unsigned short cx; unsigned short dx; int error; char * power_stat; char * bat_stat; kapmd_running = 1; daemonize("kapmd"); current->flags |= PF_NOFREEZE;#ifdef CONFIG_SMP /* 2002/08/01 - WT * This is to avoid random crashes at boot time during initialization * on SMP systems in case of "apm=power-off" mode. Seen on ASUS A7M266D. * Some bioses don't like being called from CPU != 0. * Method suggested by Ingo Molnar. */ set_cpus_allowed(current, cpumask_of_cpu(0)); BUG_ON(smp_processor_id() != 0);#endif if (apm_info.connection_version == 0) { apm_info.connection_version = apm_info.bios.version; if (apm_info.connection_version > 0x100) { /* * We only support BIOSs up to version 1.2 */ if (apm_info.connection_version > 0x0102) apm_info.connection_version = 0x0102; error = apm_driver_version(&apm_info.connection_version); if (error != APM_SUCCESS) { apm_error("driver version", error); /* Fall back to an APM 1.0 connection. */ apm_info.connection_version = 0x100; } } } if (debug) printk(KERN_INFO "apm: Connection version %d.%d\n", (apm_info.connection_version >> 8) & 0xff, apm_info.connection_version & 0xff);#ifdef CONFIG_APM_DO_ENABLE if (apm_info.bios.flags & APM_BIOS_DISABLED) { /* * This call causes my NEC UltraLite Versa 33/C to hang if it * is booted with PM disabled but not in the docking station. * Unfortunate ... */ error = apm_enable_power_management(1); if (error) { apm_error("enable power management", error); return -1; } }#endif if ((apm_info.bios.flags & APM_BIOS_DISENGAGED) && (apm_info.connection_version > 0x0100)) { error = apm_engage_power_management(APM_DEVICE_ALL, 1); if (error) { apm_error("engage power management", error); return -1; } } if (debug && (num_online_cpus() == 1 || smp )) { error = apm_get_power_status(&bx, &cx, &dx); if (error) printk(KERN_INFO "apm: power status not available\n"); else { switch ((bx >> 8) & 0xff) { case 0: power_stat = "off line"; break; case 1: power_stat = "on line"; break; case 2: power_stat = "on backup power"; break; default: power_stat = "unknown"; break; } switch (bx & 0xff) { case 0: bat_stat = "high"; break; case 1: bat_stat = "low"; break; case 2: bat_stat = "critical"; break;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -