apm-emulation driver
在suspend_prepare()里会call error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls);
因为apm-emulation有注册关于此的callback func,所以会call到apm_suspend_notifier,在此函数里,将会发一个apm_event_t类型的event APM_USER_SUSPEND(通过queue_add_event),然后wakeup apm_waitqueue,userspace如果有进程来read此event,将会读到此event,然后返回,userspace read系统调用会走到apm_read(),这是一个阻塞系统调用,没有event将会阻塞,有event读到event后返回。
drivers/char/apm-emulation.c
static int apm_suspend_notifier(struct notifier_block *nb, unsigned long event, void *dummy) { struct apm_user *as; int err; unsigned long apm_event; /* short-cut emergency suspends */ if (atomic_read(&userspace_notification_inhibit)) return NOTIFY_DONE; switch (event) { case PM_SUSPEND_PREPARE: case PM_HIBERNATION_PREPARE: apm_event = (event == PM_SUSPEND_PREPARE) ? APM_USER_SUSPEND : APM_USER_HIBERNATION; /* * Queue an event to all "writer" users that we want * to suspend and need their ack. */ mutex_lock(&state_lock); down_read(&user_list_lock); list_for_each_entry(as, &apm_user_list, list) { if (as->suspend_state != SUSPEND_WAIT && as->reader && as->writer && as->suser) { as->suspend_state = SUSPEND_PENDING; atomic_inc(&suspend_acks_pending); queue_add_event(&as->queue, apm_event); } } up_read(&user_list_lock); mutex_unlock(&state_lock); wake_up_interruptible(&apm_waitqueue); /* * Wait for the the suspend_acks_pending variable to drop to * zero, meaning everybody acked the suspend event (or the * process was killed.) * * If the app won't answer within a short while we assume it * locked up and ignore it. */ err = wait_event_interruptible_timeout( apm_suspend_waitqueue, atomic_read(&suspend_acks_pending) == 0, 5*HZ);