sysfs “wakeup%d“节点的创建

device_init_wakeup(struct device *dev, bool enable) - Device wakeup initialization.device_set_wakeup_capable(dev, true); - Set/reset device wakeup capability flag.
→ → wakeup_sysfs_add(dev);
→ → → sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group);
→ → → kobject_uevent(&dev->kobj, KOBJ_CHANGE);device_wakeup_enable(dev);
→ → wakeup_source_register(dev, dev_name(dev));
→ → → wakeup_source_create(name); - Create a struct wakeup_source object.
→ → → wakeup_source_sysfs_add(dev, ws); - Add wakeup_source attributes to sysfs.
→ → → → wakeup_source_device_create(parent, ws);
→ → → → → dev_set_name(dev, "wakeup%d", ws->id);
→ → → wakeup_source_add(ws); - Add given object to the list of wakeup sources.
→ → device_wakeup_attach(dev, ws);- Attach a wakeup source object to a device object.

device结构体,里面有一个成员struct dev_pm_info power,函数device_init_wakeup围绕can_wakeup和wakeup_source这两个概念进行一系列的逻辑处理。

can_wakeup,标识本设备是否具有唤醒能力。只有具备唤醒能力的设备,才会在sysfs中有一个power目录,用于提供所有的wakeup信息,这些信息是以struct wakeup_source的形式组织起来的。

struct dev_pm_info {unsigned int		can_wakeup:1;#ifdef CONFIG_PM_SLEEPstruct wakeup_source	*wakeup;#else
	unsigned int		should_wakeup:1;
#endif};
/**
 * struct wakeup_source - Representation of wakeup sources
 *
 * @name: Name of the wakeup source
 * @id: Wakeup source id
 * @entry: Wakeup source list entry
 * @lock: Wakeup source lock
 * @wakeirq: Optional device specific wakeirq
 * @timer: Wakeup timer list
 * @timer_expires: Wakeup timer expiration
 * @total_time: Total time this wakeup source has been active.
 * @max_time: Maximum time this wakeup source has been continuously active.
 * @last_time: Monotonic clock when the wakeup source's was touched last time.
 * @prevent_sleep_time: Total time this source has been preventing autosleep.
 * @event_count: Number of signaled wakeup events.
 * @active_count: Number of times the wakeup source was activated.
 * @relax_count: Number of times the wakeup source was deactivated.
 * @expire_count: Number of times the wakeup source's timeout has expired.
 * @wakeup_count: Number of times the wakeup source might abort suspend.
 * @dev: Struct device for sysfs statistics about the wakeup source.
 * @active: Status of the wakeup source.
 * @autosleep_enabled: Autosleep is active, so update @prevent_sleep_time.
 */
struct wakeup_source { //代表了一个具有唤醒能力的设备,也称该设备为一个wakeup source。
	const char 		*name;
	int			id;
	struct list_head	entry;
	spinlock_t		lock;
	struct wake_irq		*wakeirq;
	struct timer_list	timer;
	unsigned long		timer_expires;
	ktime_t total_time;
	ktime_t max_time;
	ktime_t last_time;
	ktime_t start_prevent_time;
	ktime_t prevent_sleep_time;
	unsigned long		event_count;
	unsigned long		active_count;
	unsigned long		relax_count;
	unsigned long		expire_count;
	unsigned long		wakeup_count;
	struct device		*dev;
	bool			active:1;
	bool			autosleep_enabled:1;
};

struct device {
	struct kobject kobj;
	struct device		*parent;struct dev_pm_info	power;}
/**
 * device_init_wakeup - Device wakeup initialization.
 * @dev: Device to handle.
 * @enable: Whether or not to enable @dev as a wakeup device.
 *
 * By default, most devices should leave wakeup disabled.  The exceptions are
 * devices that everyone expects to be wakeup sources: keyboards, power buttons,
 * possibly network interfaces, etc.  Also, devices that don't generate their
 * own wakeup requests but merely forward requests from one bus to another
 * (like PCI bridges) should have wakeup enabled by default.
 */
static inline int device_init_wakeup(struct device *dev, bool enable)
{
	if (enable) {
		device_set_wakeup_capable(dev, true);
		return device_wakeup_enable(dev);
	} else {
		device_wakeup_disable(dev);
		device_set_wakeup_capable(dev, false);
		return 0;
	}
}

/**
 * device_set_wakeup_capable - Set/reset device wakeup capability flag.
 * @dev: Device to handle.
 * @capable: Whether or not @dev is capable of waking up the system from sleep.
 *
 * If @capable is set, set the @dev's power.can_wakeup flag and add its
 * wakeup-related attributes to sysfs.  Otherwise, unset the @dev's
 * power.can_wakeup flag and remove its wakeup-related attributes from sysfs.
 *
 * This function may sleep and it can't be called from any context where
 * sleeping is not allowed.
 */
void device_set_wakeup_capable(struct device *dev, bool capable)
{
	if (!!dev->power.can_wakeup == !!capable)
		return;

	dev->power.can_wakeup = capable;
	if (device_is_registered(dev) && !list_empty(&dev->power.entry)) {
		if (capable) {
			int ret = wakeup_sysfs_add(dev);

			if (ret)
				dev_info(dev, "Wakeup sysfs attributes not added\n");
		} else {
			wakeup_sysfs_remove(dev);
		}
	}
}

/**
 * device_wakeup_enable - Enable given device to be a wakeup source.
 * @dev: Device to handle.
 *
 * Create a wakeup source object, register it and attach it to @dev.
 */
int device_wakeup_enable(struct device *dev)
{
	struct wakeup_source *ws;
	int ret;

	if (!dev || !dev->power.can_wakeup)
		return -EINVAL;

	if (pm_suspend_target_state != PM_SUSPEND_ON)
		dev_dbg(dev, "Suspicious %s() during system transition!\n", __func__);

	ws = wakeup_source_register(dev, dev_name(dev));
	if (!ws)
		return -ENOMEM;

	ret = device_wakeup_attach(dev, ws);
	if (ret)
		wakeup_source_unregister(ws);

	return ret;
}

/**
 * wakeup_source_register - Create wakeup source and add it to the list.
 * @dev: Device this wakeup source is associated with (or NULL if virtual).
 * @name: Name of the wakeup source to register.
 */
struct wakeup_source *wakeup_source_register(struct device *dev,
					     const char *name)
{
	struct wakeup_source *ws;
	int ret;

	ws = wakeup_source_create(name);
	if (ws) {
		if (!dev || device_is_registered(dev)) {
			ret = wakeup_source_sysfs_add(dev, ws);
			if (ret) {
				wakeup_source_free(ws);
				return NULL;
			}
		}
		wakeup_source_add(ws);
	}
	return ws;
}

/**
 * wakeup_source_create - Create a struct wakeup_source object.
 * @name: Name of the new wakeup source.
 */
struct wakeup_source *wakeup_source_create(const char *name)
{
	struct wakeup_source *ws;
	const char *ws_name;
	int id;

	ws = kzalloc(sizeof(*ws), GFP_KERNEL);
	if (!ws)
		goto err_ws;

	ws_name = kstrdup_const(name, GFP_KERNEL);
	if (!ws_name)
		goto err_name;
	ws->name = ws_name;

	id = ida_alloc(&wakeup_ida, GFP_KERNEL);
	if (id < 0)
		goto err_id;
	ws->id = id;

	return ws;

err_id:
	kfree_const(ws->name);
err_name:
	kfree(ws);
err_ws:
	return NULL;
}

/**
 * wakeup_source_sysfs_add - Add wakeup_source attributes to sysfs.
 * @parent: Device given wakeup source is associated with (or NULL if virtual).
 * @ws: Wakeup source to be added in sysfs.
 */
int wakeup_source_sysfs_add(struct device *parent, struct wakeup_source *ws)
{
	struct device *dev;

	dev = wakeup_source_device_create(parent, ws);
	if (IS_ERR(dev))
		return PTR_ERR(dev);
	ws->dev = dev;

	return 0;
}

static struct device *wakeup_source_device_create(struct device *parent,
						  struct wakeup_source *ws)
{
	struct device *dev = NULL;
	int retval;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
		retval = -ENOMEM;
		goto error;
	}

	device_initialize(dev);
	dev->devt = MKDEV(0, 0);
	dev->class = wakeup_class;
	dev->parent = parent;
	dev->groups = wakeup_source_groups;
	dev->release = device_create_release;
	dev_set_drvdata(dev, ws);
	device_set_pm_not_required(dev);

	retval = dev_set_name(dev, "wakeup%d", ws->id);
	if (retval)
		goto error;

	retval = device_add(dev);
	if (retval)
		goto error;

	return dev;

error:
	put_device(dev);
	return ERR_PTR(retval);
}

int wakeup_sysfs_add(struct device *dev)
{
	int ret = sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group);

	if (!ret)
		kobject_uevent(&dev->kobj, KOBJ_CHANGE);

	return ret;
}

/**
 * wakeup_source_add - Add given object to the list of wakeup sources.
 * @ws: Wakeup source object to add to the list.
 */
void wakeup_source_add(struct wakeup_source *ws)
{
	unsigned long flags;

	if (WARN_ON(!ws))
		return;

	spin_lock_init(&ws->lock);
	timer_setup(&ws->timer, pm_wakeup_timer_fn, 0);
	ws->active = false;

	raw_spin_lock_irqsave(&events_lock, flags);
	list_add_rcu(&ws->entry, &wakeup_sources);
	raw_spin_unlock_irqrestore(&events_lock, flags);
}

/**
 * device_wakeup_attach - Attach a wakeup source object to a device object.
 * @dev: Device to handle.
 * @ws: Wakeup source object to attach to @dev.
 *
 * This causes @dev to be treated as a wakeup device.
 */
static int device_wakeup_attach(struct device *dev, struct wakeup_source *ws)
{
	spin_lock_irq(&dev->power.lock);
	if (dev->power.wakeup) {
		spin_unlock_irq(&dev->power.lock);
		return -EEXIST;
	}
	dev->power.wakeup = ws;
	if (dev->power.wakeirq)
		device_wakeup_attach_irq(dev, dev->power.wakeirq);
	spin_unlock_irq(&dev->power.lock);
	return 0;
}

你可能感兴趣的:(Linux内核,linux)