User Space Entry:
1 : sys file system
Path: /sys/power
2 : Find out what power management state the system support
cat /sys/power/state
3 : Made system enter into the state you specified
echo disk > /sys/power/state
What does kernel do:
1 :
File : /kernel/power/main.c
Function : pm_init
Created the sys file system interface to give ability to user to control the system's state
2 :
File : /kernel/power/main.c
Function : state_store
: state_show
is the function which executed the command from userspace
state_show handle the read request like cat /sys/power/state and
state_store handle the write request like echo disk > /sys/power/state
3 : Enter standy mode's function call sequence
enter_state -> suspend_devices_and_enter -> dpm_suspend_start -> dpm_suspend -> device_suspend
drivers/base/power:
static int device_suspend(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 = legacy_suspend(dev, state, dev->class->suspend);
}
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);
}
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 = legacy_suspend(dev, state, dev->bus->suspend);
}
}
End:
up(&dev->sem);
return error;
}
In this function, kernel try to call the device's suspend callback function to complete the suspend task. And class , type
and bus are the driver model used by Linux.
I will explain the sequence of device suspend in the USB subsystem later.
Global Var: dpm_list
The driver model core calls device_pm_add() when a device is registered.
This will intialize the embedded device_pm_info object in the device and
add it to the list of power-controlled devices. sysfs entries for controlling device
power management will also be added
device_add -> device_pm_add
USB system suspend process:
1 : What happened when one USB device plugs in
USB subsystem starts a kernel thread which is called hub_thread to detect the USB device plug/unplug event.
usb/core/hub.c
int usb_hub_init(void)
{
if (usb_register(&hub_driver) < 0) {
printk(KERN_ERR "%s: can't register hub driver/n",
usbcore_name);
return -1;
}
khubd_task = kthread_run(hub_thread, NULL, "khubd");
if (!IS_ERR(khubd_task))
return 0;
/* Fall through if kernel_thread failed */
usb_deregister(&hub_driver);
printk(KERN_ERR "%s: can't start khubd/n", usbcore_name);
return -1;
}
USB subsystem handle USB port status change event in the hub_events, and call routine
hub_port_connect_change if there is an plug/unplug event happeded.
In hub_port_connect_change function , we should allocate a usb_device structure before
we start to acutally find a device. This need us call function usb_alloc_dev, and we can see following 3 lines
in this function :
dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;
dev->dev.groups = usb_device_groups;
The definition of usb_device type is :
struct device_type usb_device_type = {
.name = "usb_device",
.release = usb_release_dev,
.uevent = usb_dev_uevent,
.devnode = usb_devnode,
.pm = &usb_device_pm_ops,
};
and the definiton of the usb_device bus is :
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};
so regarding to the USB subsystem , the really worker in the device_supsend is :
if (dev->type) {
if (dev->type->pm) {
pm_dev_dbg(dev, state, "type ");
error = pm_op(dev, dev->type->pm, state);
}
if (error)
goto End;
}
According to the power management event PM_EVENT_SUSPEND, the pm_op handle it like this :
if (ops->suspend) {
error = ops->suspend(dev);
suspend_report_result(ops->suspend, error);
}
That is to say, USB subsystem will call usb_dev_suspend, the sequence is as following :
usb_dev_suspend -> usb_suspend -> usb_external_suspend_device -> usb_suspend_both -> usb_suspend_interface.
And finally, in the routine usb_suspend_interface, USB subsystem will call your own usb device's suspend function.
static int usb_suspend_interface(struct usb_device *udev,
struct usb_interface *intf, pm_message_t msg)
{
struct usb_driver *driver;
int status = 0;
/* with no hardware, USB interfaces only use FREEZE and ON states */
if (udev->state == USB_STATE_NOTATTACHED || !is_active(intf))
goto done;
/* This can happen; see usb_driver_release_interface() */
if (intf->condition == USB_INTERFACE_UNBOUND)
goto done;
driver = to_usb_driver(intf->dev.driver);
if (driver->suspend) {
status = driver->suspend(intf, msg);
if (status == 0)
mark_quiesced(intf);
else if (!(msg.event & PM_EVENT_AUTO))
dev_err(&intf->dev, "%s error %d/n",
"suspend", status);
} else {
/* Later we will unbind the driver and reprobe */
intf->needs_binding = 1;
dev_warn(&intf->dev, "no %s for driver %s?/n",
"suspend", driver->name);
mark_quiesced(intf);
}
done:
dev_vdbg(&intf->dev, "%s: status %d/n", __func__, status);
return status;
}


被折叠的 条评论
为什么被折叠?



