Linux APM在ARM上的实现原理.docx

上传人:b****4 文档编号:4695420 上传时间:2022-12-07 格式:DOCX 页数:15 大小:19.50KB
下载 相关 举报
Linux APM在ARM上的实现原理.docx_第1页
第1页 / 共15页
Linux APM在ARM上的实现原理.docx_第2页
第2页 / 共15页
Linux APM在ARM上的实现原理.docx_第3页
第3页 / 共15页
Linux APM在ARM上的实现原理.docx_第4页
第4页 / 共15页
Linux APM在ARM上的实现原理.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

Linux APM在ARM上的实现原理.docx

《Linux APM在ARM上的实现原理.docx》由会员分享,可在线阅读,更多相关《Linux APM在ARM上的实现原理.docx(15页珍藏版)》请在冰豆网上搜索。

Linux APM在ARM上的实现原理.docx

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

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 理化生

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1