您的位置:首页 > 运维架构 > Linux

Linux 电源管理在ARM上的实现原理

2012-10-15 17:07 483 查看
http://blog.chinaunix.net/uid-12461657-id-3148498.html

内核版本号:linux2.6.32

由于arm系统中没有bios设备, 所以只能为arm系统创建一个虚拟的字符设备与用户空间进行通讯. 即在apm中实现一个misc设备,实质上也是一个字符设备, misc设备的主设备号是10, 而apm_bios作为个misc设备, 次设备号是134。 Linux2.6.30.10内核的/drivers/char/apm-emulation.c提供了apm_bios的驱动模型,也就是系统进入睡眠的入口函数,更早的版本的接口文件为:arch/arm/kernel/apm.c

在apm-emulation.c中:

#define APM_MINOR_DEV 134

这个apm_bios设备通过ioctl系统调用和用户空间进行通讯, 即当用户进程通过ioctl发来suspend命令时, 它就传给内核, 使系统进入suspend状态.

1,初始化

static int __init apm_init(void)

{

int ret;

if (apm_disabled) {

printk(KERN_NOTICE "apm: disabled on user request.\n");

return -ENODEV;

}

//创建一个线程, 用于处理事件队列, 工作函数是kapmd

kapmd_tsk = kthread_create(kapmd, NULL, "kapmd");

if (IS_ERR(kapmd_tsk)) {

ret = PTR_ERR(kapmd_tsk);

kapmd_tsk = NULL;

goto out;

}

wake_up_process(kapmd_tsk);

//通过proc,向用户空间输出apm信息

#ifdef CONFIG_PROC_FS

proc_create("apm", 0, NULL, &apm_proc_fops);

#endif

//注册misc设备

ret = misc_register(&apm_device);

if (ret)

goto out_stop;

ret = register_pm_notifier(&apm_notif_block);

if (ret)

goto out_unregister;

return 0;

out_unregister:

misc_deregister(&apm_device);

out_stop:

remove_proc_entry("apm", NULL);

kthread_stop(kapmd_tsk);

out:

return ret;

}

//注册结构为:

static struct file_operations apm_bios_fops = {

.owner = THIS_MODULE,

.read = apm_read,

.poll = apm_poll,

.ioctl = apm_ioctl,

.open = apm_open,

.release = apm_release,

};

static struct miscdevice apm_device = {

.minor = APM_MINOR_DEV,

.name = "apm_bios",

.fops = &apm_bios_fops

};

这样就我们就可以像对一般的设备文件一样,读取apm_bios的相关信息了。

2,结构中函数实现

当一个用户进程打开apm_bios设备时, 它就会调用这个函数

static int apm_open(struct inode * inode, struct file * filp)

{

//这个关键是apm_user结构变量as,它是用户和apm内核部分沟通的桥梁,当有apm事件发生时,就把event挂到apm_user的queue上,这样当用户读时就会读到相关事件然后处理。

struct apm_user *as;

lock_kernel();

//分配一个apm_user结构, 来表示一个用户进程

as = kzalloc(sizeof(*as), GFP_KERNEL);

//读写等权限设置

if (as) {

as->suser = capable(CAP_SYS_ADMIN);

as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;

as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;

//将这个用户加入用户队列

down_write(&user_list_lock);

list_add(&as->list, &apm_user_list);

up_write(&user_list_lock);

//这是一个传递私有数据的一个通用方式

filp->private_data = as;

}

unlock_kernel();

return as ? 0 : -ENOMEM;

}

当用户空间进程去读这个设备时, 这个函数就会被调用. 这个函数的主要作用是将事件读出到用户空间.

static ssize_t apm_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)

{

struct apm_user *as = fp->private_data;

apm_event_t event;

int i = count, ret = 0;

if (count < sizeof(apm_event_t))

return -EINVAL;

//队列空, 且进程非阻塞读, 立刻返回

if (queue_empty(&as->queue) && fp->f_flags & O_NONBLOCK)

return -EAGAIN;

//否则等待到队列非空为止,

wait_event_interruptible(apm_waitqueue, !queue_empty(&as->queue));

//将队列中的事件复制给用户空间

while ((i >= sizeof(event)) && !queue_empty(&as->queue)) {

event = queue_get_event(&as->queue);

ret = -EFAULT;

if (copy_to_user(buf, &event, sizeof(event)))

break;

//设置状态

mutex_lock(&state_lock);

if (as->suspend_state == SUSPEND_PENDING &&

(event == APM_SYS_SUSPEND || event == APM_USER_SUSPEND))

as->suspend_state = SUSPEND_READ;

mutex_unlock(&state_lock);

buf += sizeof(event);

i -= sizeof(event);

}

if (i < count)

ret = count - i;

return ret;

}

//这个poll/select的后端实现, 用于查询有没有数据可读

static unsigned int apm_poll(struct file *fp, poll_table * wait)

{

struct apm_user *as = fp->private_data;

poll_wait(fp, &apm_waitqueue, wait);

return queue_empty(&as->queue) ? 0 : POLLIN | POLLRDNORM;

}

//这个是这个设备的核心函数, 用于内核与用户空间交互

static int apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg)

{

struct apm_user *as = filp->private_data;

int err = -EINVAL;

//只有超级用户才能执行回复

if (!as->suser || !as->writer)

return -EPERM;

switch (cmd) {

case APM_IOC_SUSPEND:

mutex_lock(&state_lock);

as->suspend_result = -EINTR;

switch (as->suspend_state) {

//这个就是当user读取到event时的状态,这是发送这个事件,意味着这是回应ack

case SUSPEND_READ:

as->suspend_state = SUSPEND_ACKED;

atomic_dec(&suspend_acks_pending);

mutex_unlock(&state_lock);

wake_up(&apm_suspend_waitqueue);

freezer_do_not_count();

wait_event(apm_suspend_waitqueue, as->suspend_state == SUSPEND_DONE);

freezer_count();

break;

case SUSPEND_ACKTO:

as->suspend_result = -ETIMEDOUT;

mutex_unlock(&state_lock);

break;

default:

as->suspend_state = SUSPEND_WAIT;

mutex_unlock(&state_lock);

as->suspend_result = pm_suspend(PM_SUSPEND_MEM);

}

mutex_lock(&state_lock);

err = as->suspend_result;

as->suspend_state = SUSPEND_NONE;

mutex_unlock(&state_lock);

break;

}

return err;

}

3, 事件队列函数

static void queue_event(apm_event_t event)

{

struct apm_user *as;

down_read(&user_list_lock);

list_for_each_entry(as, &apm_user_list, list) {

if (as->reader)

//这个是将这个事件发给每个需要知道事件的apm_user

queue_add_event(&as->queue, event);

}

up_read(&user_list_lock);

//唤醒等待读的进程

wake_up_interruptible(&apm_waitqueue);

}

static void queue_add_event(struct apm_queue *q, apm_event_t event)

{

q->event_head = (q->event_head + 1) % APM_MAX_EVENTS;

if (q->event_head == q->event_tail) {

static int notified;

if (notified++ == 0)

printk(KERN_ERR "apm: an event queue overflowed\n");

q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS;

}

q->events[q->event_head] = event;

}

4,所有用户回复了,可以执行ioctl中的pm_suspend了这部分说明kernel里面的电源管理的核心函数,这部分的代码在/kernel/power/suspend.c中

int pm_suspend(suspend_state_t state)

{

if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)

return enter_state(state);

return -EINVAL;

}

调用enter_state(),同样在supend.c中

int enter_state(suspend_state_t state)

{

int error;

if (!valid_state(state))

return -ENODEV;

//获得锁, 参见注释

if (!mutex_trylock(&pm_mutex))

return -EBUSY;

printk(KERN_INFO "PM: Syncing filesystems ... ");

sys_sync();

printk("done.\n");

//prepare阶段

pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);

error = suspend_prepare();

if (error)

goto Unlock;

if (suspend_test(TEST_FREEZER))

goto Finish;

//进入阶段

pr_debug("PM: Entering %s sleep\n", pm_states[state]);

error = suspend_devices_and_enter(state);

//完成挂起, 恢复状态

Finish:

pr_debug("PM: Finishing wakeup.\n");

suspend_finish();

Unlock:

mutex_unlock(&pm_mutex);

return error;

}

4.1准备阶段,为进入supend状态做准备

static int suspend_prepare(void)

{

int error;

if (!suspend_ops || !suspend_ops->enter)

return -EPERM;

//allocate a console

pm_prepare_console();

//Run suspend notifiers

error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);

if (error)

goto Finish;

error = usermodehelper_disable();

if (error)

goto Finish;

//进程处理

error = suspend_freeze_processes();

if (!error)

return 0;

suspend_thaw_processes();

usermodehelper_enable();

Finish:

pm_notifier_call_chain(PM_POST_SUSPEND);

pm_restore_console();

return error;

}

4.2 进入阶段,挂起设备。

int suspend_devices_and_enter(suspend_state_t state)

{

int error;

if (!suspend_ops)

return -ENOSYS;

if (suspend_ops->begin) {

error = suspend_ops->begin(state);

if (error)

goto Close;

}

suspend_console();

suspend_test_start();

//挂起设备

error = dpm_suspend_start(PMSG_SUSPEND);

if (error) {

printk(KERN_ERR "PM: Some devices failed to suspend\n");

goto Recover_platform;

}

suspend_test_finish("suspend devices");

if (suspend_test(TEST_DEVICES))

goto Recover_platform;

suspend_enter(state);

Resume_devices:

suspend_test_start();

dpm_resume_end(PMSG_RESUME);

suspend_test_finish("resume devices");

resume_console();

Close:

if (suspend_ops->end)

suspend_ops->end();

return error;

Recover_platform:

if (suspend_ops->recover)

suspend_ops->recover();

goto Resume_devices;

}

4.2.1挂起设备

int dpm_suspend_start(pm_message_t state)

{

int error;

might_sleep();

error = dpm_prepare(state);

if (!error)

error = dpm_suspend(state);

return error;

}

函数dpm_suspend_start()最后会调用dpm_suspend函数来挂起每个设备。

static int dpm_suspend(pm_message_t state)

{

struct list_head list;

int error = 0;

INIT_LIST_HEAD(&list);

mutex_lock(&dpm_list_mtx);

//遍历设备链表, 当一个设备被注册进系统时, 它同时会被加入到这个dpm_list队列中

while (!list_empty(&dpm_list)) {

struct device *dev = to_device(dpm_list.prev);

get_device(dev);

mutex_unlock(&dpm_list_mtx);

//挂起这个设备

error = suspend_device(dev, state);

mutex_lock(&dpm_list_mtx);

if (error) {

pm_dev_err(dev, state, "", error);

put_device(dev);

break;

}

dev->power.status = DPM_OFF;

//加入list队列, 用于以后唤醒

if (!list_empty(&dev->power.entry))

list_move(&dev->power.entry, &list);

put_device(dev);

}

list_splice(&list, dpm_list.prev);

mutex_unlock(&dpm_list_mtx);

return error;

}

此函数又会调用 suspend_device()函数挂起相应得设备,这个函数调用相应设备的suspend实现设备挂起. 所以说, 系统挂起时, 设备也应该做相应的工作, 由于设备的特殊性, 这些就是留在设备里面来实现了.

static int suspend_device(struct device *dev, pm_message_t state)

{

int error = 0;

down(&dev->sem);

if (dev->class) {

if (dev->class->pm) {

pm_dev_dbg(dev, state, "class ");

error = pm_op(dev, dev->class->pm, state);

} else if (dev->class->suspend) {

pm_dev_dbg(dev, state, "legacy class ");

error = dev->class->suspend(dev, state);

suspend_report_result(dev->class->suspend, error);

}

if (error)

goto End;

}

if (dev->type) {

if (dev->type->pm) {

pm_dev_dbg(dev, state, "type ");

error = pm_op(dev, dev->type->pm, state);

} else if (dev->type->suspend) {

pm_dev_dbg(dev, state, "legacy type ");

error = dev->type->suspend(dev, state);

suspend_report_result(dev->type->suspend, error);

}

if (error)

goto End;

}

if (dev->bus) {

if (dev->bus->pm) {

pm_dev_dbg(dev, state, "");

error = pm_op(dev, dev->bus->pm, state);

} else if (dev->bus->suspend) {

pm_dev_dbg(dev, state, "legacy ");

error = dev->bus->suspend(dev, state);

suspend_report_result(dev->bus->suspend, error);

}

}

End:

up(&dev->sem);

return error;

}

4.2.2 进入阶段

回到suspend_devices_and_enter()再看suspend_enter(state)函数

static int suspend_enter(suspend_state_t state)

{

int error;

//调用体系结构相关的函数, 这是在系统初始化的时候注册的

if (suspend_ops->prepare) {

error = suspend_ops->prepare();

if (error)

return error;

}

error = device_power_down(PMSG_SUSPEND);

if (error) {

printk(KERN_ERR "PM: Some devices failed to power down\n");

goto Platfrom_finish;

}

if (suspend_ops->prepare_late) {

error = suspend_ops->prepare_late();

if (error)

goto Power_up_devices;

}

if (suspend_test(TEST_PLATFORM))

goto Platform_wake;

error = disable_nonboot_cpus();

if (error || suspend_test(TEST_CPUS))

goto Enable_cpus;

arch_suspend_disable_irqs();

BUG_ON(!irqs_disabled());

error = sysdev_suspend(PMSG_SUSPEND);

if (!error) {

if (!suspend_test(TEST_CORE))

//调用体系结构相关的函数, 这是在系统初始化的时候注册的

error = suspend_ops->enter(state);

sysdev_resume();

}

arch_suspend_enable_irqs();

BUG_ON(irqs_disabled());

Enable_cpus:

enable_nonboot_cpus();

Platform_wake:

if (suspend_ops->wake)

suspend_ops->wake();

Power_up_devices:

device_power_up(PMSG_RESUME);

Platfrom_finish:

if (suspend_ops->finish)

//调用体系结构相关的函数, 这是在系统初始化的时候注册的

suspend_ops->finish();

return error;

}

这个suspend_ops就是在体系结构初始化的时候注册进来的,

接着看arch/arm/plat-s3c/pm.c

int __init s3c_pm_init(void)

{

printk("S3C Power Management, Copyright 2004 Simtec Electronics\n");

suspend_set_ops(&s3c_pm_ops);

return 0;

}

static struct platform_suspend_ops s3c_pm_ops = {

.enter = s3c_pm_enter,

.prepare = s3c_pm_prepare,

.finish = s3c_pm_finish,

.valid = suspend_valid_only_mem,

};

这就是实现三个状态转换的三个钩子函数.

这个函数较为简单, 只是将/kerenel/power/main.c里的全局变量suspend_ops设置成

s3c_pm_ops而已了.

这就完成了这个全局变量的初始化.后续对suspend_ops的访问实质上都是访问s3c_pm_ops.

void suspend_set_ops(struct platform_suspend_ops *ops)

{

mutex_lock(&pm_mutex);

suspend_ops = ops;

mutex_unlock(&pm_mutex);

}

最后看看函数实现:

static int s3c_pm_prepare(void)

{

s3c_pm_check_prepare();

return 0;

}

static void s3c_pm_finish(void)

{

s3c_pm_check_cleanup();

}

这里主要是s3c_pm_nter函数:

static int s3c_pm_enter(suspend_state_t state)

{

//用于保存16个通用寄存器的栈

static unsigned long regs_save[16];

s3c_pm_debug_init();

S3C_PMDBG("%s(%d)\n", __func__, state);

if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) {

printk(KERN_ERR "%s: error: no cpu sleep function\n", __func__);

return -EINVAL;

}

//检查允许的唤醒中断

if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) &&

!any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) {

printk(KERN_ERR "%s: No wake-up sources!\n", __func__);

printk(KERN_ERR "%s: Aborting sleep\n", __func__);

return -EINVAL;

}

//寄存器的物理地址

s3c_sleep_save_phys = virt_to_phys(regs_save);

S3C_PMDBG("s3c_sleep_save_phys=0xlx\n", s3c_sleep_save_phys);

//保存不属于driver的核心寄存器, driver的各自保存

s3c_pm_save_gpios();

s3c_pm_save_uarts();

s3c_pm_save_core();

//设置外部中断用于唤醒

s3c_pm_configure_extint();

S3C_PMDBG("sleep: irq wakeup masks: lx,lx\n",

s3c_irqwake_intmask, s3c_irqwake_eintmask);

s3c_pm_arch_prepare_irqs();

pm_cpu_prep();

flush_cache_all();

s3c_pm_check_store();

s3c_pm_arch_stop_clocks();

s3c_cpu_save(regs_save);

//当接收到一个外部中断时,系统开始恢复

cpu_init();

s3c_pm_restore_core();

s3c_pm_restore_uarts();

s3c_pm_restore_gpios();

s3c_pm_debug_init();

s3c_pm_arch_show_resume_irqs();

S3C_PMDBG("%s: post sleep, preparing to return\n", __func__);

s3c_pm_check_restore();

S3C_PMDBG("S3C PM Resume (post-restore)\n");

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: