Linux APM在ARM上的实现原理.docx
- 文档编号:4695420
- 上传时间:2022-12-07
- 格式:DOCX
- 页数:15
- 大小:19.50KB
Linux APM在ARM上的实现原理.docx
《Linux APM在ARM上的实现原理.docx》由会员分享,可在线阅读,更多相关《Linux APM在ARM上的实现原理.docx(15页珍藏版)》请在冰豆网上搜索。
LinuxAPM在ARM上的实现原理
Linux2.6.30.10内核的/drivers/char/apm-emulation.c提供了apm_bios的驱动模型,也就是系统进入睡眠的入口函数,更早的版本的接口文件为:
arch/arm/kernel/apm.c
在apm-emulation.c中:
/*
*Theapm_biosdeviceisoneofthemiscchardevices.
*Thisisitsminornumber.
*/
#defineAPM_MINOR_DEV134
这个apm_bios设备通过ioctl系统调用和用户空间进行通讯,即当用户进程通过ioctl发来suspend命令时,它就传给内核,使系统进入suspend状态.
1,初始化
staticint__initapm_init(void)
{
intret;
if(apm_disabled){
printk(KERN_NOTICE"apm:
disabledonuserrequest.\n");
return-ENODEV;
}
//创建一个线程,用于处理事件队列,工作函数是kapmd
kapmd_tsk=kthread_create(kapmd,NULL,"kapmd");
if(IS_ERR(kapmd_tsk)){
ret=PTR_ERR(kapmd_tsk);
kapmd_tsk=NULL;
gotoout;
}
wake_up_process(kapmd_tsk);
//通过proc,向用户空间输出apm信息
#ifdefCONFIG_PROC_FS
proc_create("apm",0,NULL,&apm_proc_fops);
#endif
//注册misc设备
ret=misc_register(&apm_device);
if(ret)
gotoout_stop;
ret=register_pm_notifier(&apm_notif_block);
if(ret)
gotoout_unregister;
return0;
out_unregister:
misc_deregister(&apm_device);
out_stop:
remove_proc_entry("apm",NULL);
kthread_stop(kapmd_tsk);
out:
returnret;
}
//注册结构为:
staticstructfile_operationsapm_bios_fops={
.owner=THIS_MODULE,
.read=apm_read,
.poll=apm_poll,
.ioctl=apm_ioctl,
.open=apm_open,
.release=apm_release,
};
staticstructmiscdeviceapm_device={
.minor=APM_MINOR_DEV,
.name="apm_bios",
.fops=&apm_bios_fops
};
这样就我们就可以像对一般的设备文件一样,读取apm_bios的相关信息了。
2,结构中函数实现
当一个用户进程打开apm_bios设备时,它就会调用这个函数
staticintapm_open(structinode*inode,structfile*filp)
{
//这个关键是apm_user结构变量as,它是用户和apm内核部分沟通的桥梁,当有apm事件发生时,就把event挂到apm_user的queue上,这样当用户读时就会读到相关事件然后处理。
structapm_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();
returnas?
0:
-ENOMEM;
}
当用户空间进程去读这个设备时,这个函数就会被调用.这个函数的主要作用是将事件读出到用户空间.
staticssize_tapm_read(structfile*fp,char__user*buf,size_tcount,loff_t*ppos)
{
structapm_user*as=fp->private_data;
apm_event_tevent;
inti=count,ret=0;
if(count 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 ret=count-i; returnret; } //这个poll/select的后端实现,用于查询有没有数据可读 staticunsignedintapm_poll(structfile*fp,poll_table*wait) { structapm_user*as=fp->private_data; poll_wait(fp,&apm_waitqueue,wait); returnqueue_empty(&as->queue)? 0: POLLIN|POLLRDNORM; } //这个是这个设备的核心函数,用于内核与用户空间交互 staticintapm_ioctl(structinode*inode,structfile*filp,u_intcmd,u_longarg) { structapm_user*as=filp->private_data; interr=-EINVAL; //只有超级用户才能执行回复 if(! as->suser||! as->writer) return-EPERM; switch(cmd){ caseAPM_IOC_SUSPEND: mutex_lock(&state_lock); as->suspend_result=-EINTR; switch(as->suspend_state){ //这个就是当user读取到event时的状态,这是发送这个事件,意味着这是回应ack caseSUSPEND_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; caseSUSPEND_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; } returnerr; } 3,事件队列函数 staticvoidqueue_event(apm_event_tevent) { structapm_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); } staticvoidqueue_add_event(structapm_queue*q,apm_event_tevent) { q->event_head=(q->event_head+1)%APM_MAX_EVENTS; if(q->event_head==q->event_tail){ staticintnotified; if(notified++==0) printk(KERN_ERR"apm: aneventqueueoverflowed\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中 intpm_suspend(suspend_state_tstate) { if(state>PM_SUSPEND_ON&&state<=PM_SUSPEND_MAX) returnenter_state(state); return-EINVAL; } 调用enter_state(),同样在supend.c中 intenter_state(suspend_state_tstate) { interror; if(! valid_state(state)) return-ENODEV; //获得锁,参见注释 if(! mutex_trylock(&pm_mutex)) return-EBUSY; printk(KERN_INFO"PM: Syncingfilesystems..."); sys_sync(); printk("done.\n"); //prepare阶段 pr_debug("PM: Preparingsystemfor%ssleep\n",pm_states[state]); error=suspend_prepare(); if(error) gotoUnlock; if(suspend_test(TEST_FREEZER)) gotoFinish; //进入阶段 pr_debug("PM: Entering%ssleep\n",pm_states[state]); error=suspend_devices_and_enter(state); //完成挂起,恢复状态 Finish: pr_debug("PM: Finishingwakeup.\n"); suspend_finish(); Unlock: mutex_unlock(&pm_mutex); returnerror; } 4.1准备阶段,为进入supend状态做准备 staticintsuspend_prepare(void) { interror; if(! suspend_ops||! suspend_ops->enter) return-EPERM; //allocateaconsole pm_prepare_console(); //Runsuspendnotifiers error=pm_notifier_call_chain(PM_SUSPEND_PREPARE); if(error) gotoFinish; error=usermodehelper_disable(); if(error) gotoFinish; //进程处理 error=suspend_freeze_processes(); if(! error) return0; suspend_thaw_processes(); usermodehelper_enable(); Finish: pm_notifier_call_chain(PM_POST_SUSPEND); pm_restore_console(); returnerror; } 4.2进入阶段,挂起设备。 intsuspend_devices_and_enter(suspend_state_tstate) { interror; if(! suspend_ops) return-ENOSYS; if(suspend_ops->begin){ error=suspend_ops->begin(state); if(error) gotoClose; } suspend_console(); suspend_test_start(); //挂起设备 error=dpm_suspend_start(PMSG_SUSPEND); if(error){ printk(KERN_ERR"PM: Somedevicesfailedtosuspend\n"); gotoRecover_platform; } suspend_test_finish("suspenddevices"); if(suspend_test(TEST_DEVICES)) gotoRecover_platform; suspend_enter(state); Resume_devices: suspend_test_start(); dpm_resume_end(PMSG_RESUME); suspend_test_finish("resumedevices"); resume_console(); Close: if(suspend_ops->end) suspend_ops->end(); returnerror; Recover_platform: if(suspend_ops->recover) suspend_ops->recover(); gotoResume_devices; } 4.2.1挂起设备 intdpm_suspend_start(pm_message_tstate) { interror; might_sleep(); error=dpm_prepare(state); if(! error) error=dpm_suspend(state); returnerror; } 函数dpm_suspend_start()最后会调用dpm_suspend函数来挂起每个设备。 staticintdpm_suspend(pm_message_tstate) { structlist_headlist; interror=0; INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); //遍历设备链表,当一个设备被注册进系统时,它同时会被加入到这个dpm_list队列中 while(! list_empty(&dpm_list)){ structdevice*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); returnerror; } 此函数又会调用suspend_device()函数挂起相应得设备,这个函数调用相应设备的suspend实现设备挂起.所以说,系统挂起时,设备也应该做相应的工作,由于设备的特殊性,这些就是留在设备里面来实现了. staticintsuspend_device(structdevice*dev,pm_message_tstate) { interror=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); }elseif(dev->class->suspend){ pm_dev_dbg(dev,state,"legacyclass"); error=dev->class->suspend(dev,state); suspend_report_result(dev->class->suspend,error); } if(error) gotoEnd; } if(dev->type){ if(dev->type->pm){ pm_dev_dbg(dev,state,"type"); error=pm_op(dev,dev->type->pm,state); }elseif(dev->type->suspend){ pm_dev_dbg(dev,state,"legacytype"); error=dev->type->suspend(dev,state); suspend_report_result(dev->type->suspend,error); } if(error) gotoEnd; } if(dev->bus){ if(dev->bus->pm){ pm_dev_dbg(dev,state,""); error=pm_op(dev,dev->bus->pm,state); }elseif(dev
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux APM在ARM上的实现原理 APM ARM 实现 原理
![提示](https://static.bdocx.com/images/bang_tan.gif)