linux新旧电源管理

2.4内核的电源管理:
asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void * arg)
{
char buffer[256];
/* We only trust the superuser with rebooting the system. */
if (!capable(CAP_SYS_BOOT))
return -EPERM;
/* For safety, we require "magic" arguments. */
if (magic1 != LINUX_REBOOT_MAGIC1 ||
(magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A &&
magic2 != LINUX_REBOOT_MAGIC2B))
return -EINVAL;
lock_kernel();
switch (cmd) {
case LINUX_REBOOT_CMD_RESTART:
notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL);//直接在这里调用回调
printk(KERN_EMERG "Restarting system./n");
machine_restart(NULL);
break;

}
unlock_kernel();
return 0;
}
void ctrl_alt_del(void)
{
if (C_A_D) {
notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL);
machine_restart(NULL);
} else
kill_proc(1, SIGINT, 1);
}
void machine_restart(char * __unused)
{
#if CONFIG_SMP
smp_send_stop();
disable_IO_APIC();
#endif
if(!reboot_thru_bios) {
/* rebooting needs to touch the page at absolute addr 0 */
*((unsigned short *)__va(0x472)) = reboot_mode;
for (;;) {
int i;
for (i=0; i kb_wait();
udelay(50);
outb(0xfe,0x64); /* pulse reset low */
udelay(50);
}
/* That didn't work - force a triple fault.. */
__asm__ __volatile__("lidt %0": :"m" (no_idt));
__asm__ __volatile__("int3");
}
}
machine_real_restart(jump_to_bios, sizeof(jump_to_bios));
}
就这就完了,而2.6内核就比较完善了:
asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user * arg)
{
char buffer[256];
/* We only trust the superuser with rebooting the system. */
if (!capable(CAP_SYS_BOOT))
return -EPERM;
/* For safety, we require "magic" arguments. */
if (magic1 != LINUX_REBOOT_MAGIC1 ||
(magic2 != LINUX_REBOOT_MAGIC2 &&
magic2 != LINUX_REBOOT_MAGIC2A &&
magic2 != LINUX_REBOOT_MAGIC2B &&
magic2 != LINUX_REBOOT_MAGIC2C))
return -EINVAL;
if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
cmd = LINUX_REBOOT_CMD_HALT;
lock_kernel();
switch (cmd) {
case LINUX_REBOOT_CMD_RESTART:
kernel_restart(NULL);
break;
…//以下类似

}
unlock_kernel();
return 0;
}
void kernel_restart(char *cmd)
{
kernel_restart_prepare(cmd);
if (!cmd) {
printk(KERN_EMERG "Restarting system./n");
} else {
printk(KERN_EMERG "Restarting system with command '%s'./n", cmd);
}
printk("./n");
machine_restart(cmd);
}
void kernel_restart_prepare(char *cmd)
{
blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);
system_state = SYSTEM_RESTART;
device_shutdown();
}
void machine_shutdown(void)
{
#ifdef CONFIG_SMP
int reboot_cpu_id;
reboot_cpu_id = 0;
if ((reboot_cpu != -1) && (reboot_cpu cpu_isset(reboot_cpu, cpu_online_map)) {
reboot_cpu_id = reboot_cpu;
}
if (!cpu_isset(reboot_cpu_id, cpu_online_map)) {
reboot_cpu_id = smp_processor_id();
}
set_cpus_allowed(current, cpumask_of_cpu(reboot_cpu_id));
smp_send_stop();
#endif /* CONFIG_SMP */
lapic_shutdown();
#ifdef CONFIG_X86_IO_APIC
disable_IO_APIC();
#endif
}
void machine_restart(char * __unused)
{
machine_shutdown();
machine_emergency_restart();
}
void machine_emergency_restart(void)
{
if (!reboot_thru_bios) {
if (efi_enabled) {
efi.reset_system(EFI_RESET_COLD, EFI_SUCCESS, 0, NULL);
load_idt(&no_idt);
__asm__ __volatile__("int3");
}
*((unsigned short *)__va(0x472)) = reboot_mode;
for (;;) {
mach_reboot_fixups(); /* for board specific fixups */
mach_reboot();
/* That didn't work - force a triple fault.. */
load_idt(&no_idt);
__asm__ __volatile__("int3");
}
}
if (efi_enabled)
efi.reset_system(EFI_RESET_WARM, EFI_SUCCESS, 0, NULL);
machine_real_restart(jump_to_bios, sizeof(jump_to_bios));
}
以上可以看得出新内核中添加了很多操作,而且将很多调用互换了位置,封装性增强了。再一个就是新内核将apm.c中的send_event取消了,因为有了统一的设备模型,当然send_event封装的pm_send_all还是存在的,我想可能有些地方还要用吧,还可能是因为兼容。
int pm_send_all(pm_request_t rqst, void *data)
{
struct list_head *entry = pm_devs.next;
while (entry != &pm_devs) {
struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
if (dev->callback) {
int status = pm_send(dev, rqst, data);
if (status) {
if (rqst == PM_SUSPEND)
pm_undo_all(dev);
return status;
}
}
entry = entry->next;
}
return 0;
}
看一下新内核的suspend函数
static int suspend(int vetoable)
{
int err;
struct apm_user *as;
if (pm_send_all(PM_SUSPEND, (void *)3)) {
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;
}

}
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();
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:

}
体现统一设备模型好处的就是电源管理,以下是上面函数中的调用的一个函数的代码:
int device_power_down(pm_message_t state)
{
int error = 0;
struct device * dev;
list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) {
if ((error = suspend_device(dev, state)))
break;
}
if (error)
goto Error;
if ((error = sysdev_suspend(state)))
goto Error;

}
int sysdev_suspend(pm_message_t state)
{
struct sysdev_class * cls;
struct sys_device *sysdev, *err_dev;
struct sysdev_driver *drv, *err_drv;
int ret;
list_for_each_entry_reverse(cls, &system_subsys.kset.list,
kset.kobj.entry) {
list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
list_for_each_entry(drv, &sysdev_drivers, entry) {
if (drv->suspend) {
ret = drv->suspend(sysdev, state);
if (ret)
goto gbl_driver;
}
}
/* Call auxillary drivers next. */
list_for_each_entry(drv, &cls->drivers, entry) {
if (drv->suspend) {
ret = drv->suspend(sysdev, state);
if (ret)
goto aux_driver;
}
}
/* Now call the generic one */
if (cls->suspend) {
ret = cls->suspend(sysdev, state);
if (ret)
goto cls_driver;
}
}
}
return 0;
/* resume current sysdev */
cls_driver:
drv = NULL;
printk(KERN_ERR "Class suspend failed for %s/n",
kobject_name(&sysdev->kobj));
aux_driver:
list_for_each_entry(err_drv, &cls->drivers, entry) {
if (err_drv == drv)
break;
if (err_drv->resume)
err_drv->resume(sysdev);
}
drv = NULL;
gbl_driver:
list_for_each_entry(err_drv, &sysdev_drivers, entry) {
if (err_drv == drv)
break;
if (err_drv->resume)
err_drv->resume(sysdev);
}
list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
if (err_dev == sysdev)
break;
__sysdev_resume(err_dev);
}
list_for_each_entry_continue(cls, &system_subsys.kset.list, kset.kobj.entry) {
list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
__sysdev_resume(err_dev);
}
}
return ret;
}
内核中有一个el3_suspend函数,但是它在2.4和2.6中的调用路径是不一样的,2.4中是这样的:
int __init el3_probe(struct net_device *dev, int card_idx)
{
struct el3_private *lp;
short lrs_state = 0xff, i;
dev->watchdog_timeo = TX_TIMEOUT;
dev->do_ioctl = netdev_ioctl;
#ifdef CONFIG_PM
lp->pmdev = pm_register(PM_ISA_DEV, card_idx, el3_pm_callback);
if (lp->pmdev) {
struct pm_dev *p;
p = lp->pmdev;
p->data = (struct net_device *)dev;
}
......
}
static int el3_pm_callback(struct pm_dev *pdev, pm_request_t rqst, void *data)
{
switch (rqst) {
case PM_SUSPEND:
return el3_suspend(pdev);
case PM_RESUME:
return el3_resume(pdev);
}
return 0;
}
static int el3_suspend(struct pm_dev *pdev)
{
unsigned long flags;
struct net_device *dev;
struct el3_private *lp;
if (!pdev && !pdev->data)
return -EINVAL;
dev = (struct net_device *)pdev->data;
lp = (struct el3_private *)dev->priv;
spin_lock_irqsave(&lp->lock, flags);
if (netif_running(dev))
netif_device_detach(dev);
el3_down(dev);
spin_unlock_irqrestore(&lp->lock, flags);
return 0;
}
然后在pm_send_all当中遍历链表就可以调用到,那么在2.6内核当中就不是在pm_send_all中被调用了,pm_send_all,的职能已经单一化了。2.6内核中是在arch/i386/kernel/apm.c中的suspend函数中的device_suspend中被调用的
int device_suspend(pm_message_t state)
{
int error = 0;
down(&dpm_sem);
down(&dpm_list_sem);
while (!list_empty(&dpm_active) && error == 0) {
struct list_head * entry = dpm_active.prev;
struct device * dev = to_device(entry);
get_device(dev);
up(&dpm_list_sem);
error = suspend_device(dev, state);
down(&dpm_list_sem);
if (!list_empty(&dev->power.entry)) {
if (!error) {
list_del(&dev->power.entry);
list_add(&dev->power.entry, &dpm_off);
} else if (error == -EAGAIN) {
list_del(&dev->power.entry);
list_add(&dev->power.entry, &dpm_off_irq);
error = 0;
}
}

put_device(dev);
}
up(&dpm_list_sem);
if (error) {
while (!list_empty(&dpm_off_irq)) {
struct list_head * entry = dpm_off_irq.next;
list_del(entry);
list_add(entry, &dpm_off);
}
dpm_resume();
}
up(&dpm_sem);
return error;
}
其中suspend_device被调用:
int suspend_device(struct device * dev, pm_message_t state)
{
int error = 0;
down(&dev->sem);
if (dev->power.power_state.event) {
dev_dbg(dev, "PM: suspend %d-->%d/n",
dev->power.power_state.event, state.event);
}
if (dev->power.pm_parent&& dev->power.pm_parent->power.power_state.event) {

}
dev->power.prev_state = dev->power.power_state;
if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
dev_dbg(dev, "suspending/n");
error = dev->bus->suspend(dev, state);
suspend_report_result(dev->bus->suspend, error);
}
up(&dev->sem);
return error;
}
从dev->bus->suspend(dev, state)可以看出,实际调用的是bus_type的suspend方法,那么就得得到el3的bus_type了,仍然分析/drivers/net/3c509.c文件:
static struct mca_driver el3_mca_driver = {
.id_table = el3_mca_adapter_ids,
.driver = {
.name = "3c529",
.bus = &eisa_bus_type,//原来不是这个,这里往下的代码来自一个补丁
.probe = el3_mca_probe,
.remove = __devexit_p(el3_device_remove),
.suspend = el3_suspend,
.resume = el3_resume,
},
};
struct bus_type eisa_bus_type = {
.name = "eisa",
.match = eisa_bus_match,
+ .suspend = eisa_bus_suspend,//补丁中添加
+ .resume = eisa_bus_resume, //补丁中添加
};
static int eisa_bus_suspend(struct device * dev, pm_message_t state)
{
int ret = 0;
if (dev->driver && dev->driver->suspend)
ret = dev->driver->suspend(dev, state);
return ret;
}
上面函数的dev->driver->suspend实际就是el3_suspend。
就这样,在新内核当中,device_suspend被单独作为一个功能封装成了一个函数,而在老的内核中却被统一纳入了apm的管理范围。最后看一下apm的管理前提,就是将设备注册进apm可以管理的链表
struct pm_dev *pm_register(pm_dev_t type, unsigned long id, pm_callback callback)
{
struct pm_dev *dev = kzalloc(sizeof(struct pm_dev), GFP_KERNEL);
if (dev) {
dev->type = type;
dev->id = id;
dev->callback = callback;
mutex_lock(&pm_devs_lock);
list_add(&dev->entry, &pm_devs);
mutex_unlock(&pm_devs_lock);
}
return dev;
}
这个函数很简单,仅仅在链表中创建并且添加了一个项,但是怎么将之和具体设备联系起来呢,那就是调用pm_register的函数下面要做的事情,看个例子:
static int ali_ircc_open(int i, chipio_t *info)
{
struct net_device *dev;
struct ali_ircc_cb *self;
struct pm_dev *pmdev;
int dongle_id;
int err;
dev = alloc_irdadev(sizeof(*self));
......
self = dev->priv;
self->netdev = dev;
dev_self[i] = self;
self->index = i;
......
if (!request_region(self->io.fir_base, self->io.fir_ext, driver_name)) {
.....
dev->hard_start_xmit = ali_ircc_sir_hard_xmit;
dev->open = ali_ircc_net_open;
dev->stop = ali_ircc_net_close;
dev->do_ioctl = ali_ircc_net_ioctl;
dev->get_stats = ali_ircc_net_get_stats;
......
err = register_netdev(dev);
......
pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, ali_ircc_pmproc);
if (pmdev)
pmdev->data = self;
......
}
注意pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, ali_ircc_pmproc);
if (pmdev)
pmdev->data = self;
这两行,这样就可以在需要apm管理的地方找到原来的设备了。

你可能感兴趣的:(linux)