You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/driver-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/driver-2.6: (61 commits) sysfs: add parameter "struct bin_attribute *" in .read/.write methods for sysfs binary attributes sysfs: make directory dentries and inodes reclaimable sysfs: implement sysfs_get_dentry() sysfs: move sysfs_drop_dentry() to dir.c and make it static sysfs: restructure add/remove paths and fix inode update sysfs: use sysfs_mutex to protect the sysfs_dirent tree sysfs: consolidate sysfs spinlocks sysfs: make kobj point to sysfs_dirent instead of dentry sysfs: implement sysfs_find_dirent() and sysfs_get_dirent() sysfs: implement SYSFS_FLAG_REMOVED flag sysfs: rename sysfs_dirent->s_type to s_flags and make room for flags sysfs: make sysfs_drop_dentry() access inodes using ilookup() sysfs: Fix oops in sysfs_drop_dentry on x86_64 sysfs: use singly-linked list for sysfs_dirent tree sysfs: slim down sysfs_dirent->s_active sysfs: move s_active functions to fs/sysfs/dir.c sysfs: fix root sysfs_dirent -> root dentry association sysfs: use iget_locked() instead of new_inode() sysfs: reorganize sysfs_new_indoe() and sysfs_create() sysfs: fix parent refcounting during rename and move ...
This commit is contained in:
@@ -78,6 +78,7 @@ static CLASS_DEVICE_ATTR(loading, 0644,
|
|||||||
firmware_loading_show, firmware_loading_store);
|
firmware_loading_show, firmware_loading_store);
|
||||||
|
|
||||||
static ssize_t firmware_data_read(struct kobject *kobj,
|
static ssize_t firmware_data_read(struct kobject *kobj,
|
||||||
|
struct bin_attribute *bin_attr,
|
||||||
char *buffer, loff_t offset, size_t count)
|
char *buffer, loff_t offset, size_t count)
|
||||||
{
|
{
|
||||||
struct class_device *class_dev = to_class_dev(kobj);
|
struct class_device *class_dev = to_class_dev(kobj);
|
||||||
@@ -88,6 +89,7 @@ static ssize_t firmware_data_read(struct kobject *kobj,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
static ssize_t firmware_data_write(struct kobject *kobj,
|
static ssize_t firmware_data_write(struct kobject *kobj,
|
||||||
|
struct bin_attribute *bin_attr,
|
||||||
char *buffer, loff_t offset, size_t count)
|
char *buffer, loff_t offset, size_t count)
|
||||||
{
|
{
|
||||||
struct class_device *class_dev = to_class_dev(kobj);
|
struct class_device *class_dev = to_class_dev(kobj);
|
||||||
|
|||||||
@@ -0,0 +1,166 @@
|
|||||||
|
Rules on how to access information in the Linux kernel sysfs
|
||||||
|
|
||||||
|
The kernel exported sysfs exports internal kernel implementation-details
|
||||||
|
and depends on internal kernel structures and layout. It is agreed upon
|
||||||
|
by the kernel developers that the Linux kernel does not provide a stable
|
||||||
|
internal API. As sysfs is a direct export of kernel internal
|
||||||
|
structures, the sysfs interface can not provide a stable interface eighter,
|
||||||
|
it may always change along with internal kernel changes.
|
||||||
|
|
||||||
|
To minimize the risk of breaking users of sysfs, which are in most cases
|
||||||
|
low-level userspace applications, with a new kernel release, the users
|
||||||
|
of sysfs must follow some rules to use an as abstract-as-possible way to
|
||||||
|
access this filesystem. The current udev and HAL programs already
|
||||||
|
implement this and users are encouraged to plug, if possible, into the
|
||||||
|
abstractions these programs provide instead of accessing sysfs
|
||||||
|
directly.
|
||||||
|
|
||||||
|
But if you really do want or need to access sysfs directly, please follow
|
||||||
|
the following rules and then your programs should work with future
|
||||||
|
versions of the sysfs interface.
|
||||||
|
|
||||||
|
- Do not use libsysfs
|
||||||
|
It makes assumptions about sysfs which are not true. Its API does not
|
||||||
|
offer any abstraction, it exposes all the kernel driver-core
|
||||||
|
implementation details in its own API. Therefore it is not better than
|
||||||
|
reading directories and opening the files yourself.
|
||||||
|
Also, it is not actively maintained, in the sense of reflecting the
|
||||||
|
current kernel-development. The goal of providing a stable interface
|
||||||
|
to sysfs has failed, it causes more problems, than it solves. It
|
||||||
|
violates many of the rules in this document.
|
||||||
|
|
||||||
|
- sysfs is always at /sys
|
||||||
|
Parsing /proc/mounts is a waste of time. Other mount points are a
|
||||||
|
system configuration bug you should not try to solve. For test cases,
|
||||||
|
possibly support a SYSFS_PATH environment variable to overwrite the
|
||||||
|
applications behavior, but never try to search for sysfs. Never try
|
||||||
|
to mount it, if you are not an early boot script.
|
||||||
|
|
||||||
|
- devices are only "devices"
|
||||||
|
There is no such thing like class-, bus-, physical devices,
|
||||||
|
interfaces, and such that you can rely on in userspace. Everything is
|
||||||
|
just simply a "device". Class-, bus-, physical, ... types are just
|
||||||
|
kernel implementation details, which should not be expected by
|
||||||
|
applications that look for devices in sysfs.
|
||||||
|
|
||||||
|
The properties of a device are:
|
||||||
|
o devpath (/devices/pci0000:00/0000:00:1d.1/usb2/2-2/2-2:1.0)
|
||||||
|
- identical to the DEVPATH value in the event sent from the kernel
|
||||||
|
at device creation and removal
|
||||||
|
- the unique key to the device at that point in time
|
||||||
|
- the kernels path to the device-directory without the leading
|
||||||
|
/sys, and always starting with with a slash
|
||||||
|
- all elements of a devpath must be real directories. Symlinks
|
||||||
|
pointing to /sys/devices must always be resolved to their real
|
||||||
|
target, and the target path must be used to access the device.
|
||||||
|
That way the devpath to the device matches the devpath of the
|
||||||
|
kernel used at event time.
|
||||||
|
- using or exposing symlink values as elements in a devpath string
|
||||||
|
is a bug in the application
|
||||||
|
|
||||||
|
o kernel name (sda, tty, 0000:00:1f.2, ...)
|
||||||
|
- a directory name, identical to the last element of the devpath
|
||||||
|
- applications need to handle spaces and characters like '!' in
|
||||||
|
the name
|
||||||
|
|
||||||
|
o subsystem (block, tty, pci, ...)
|
||||||
|
- simple string, never a path or a link
|
||||||
|
- retrieved by reading the "subsystem"-link and using only the
|
||||||
|
last element of the target path
|
||||||
|
|
||||||
|
o driver (tg3, ata_piix, uhci_hcd)
|
||||||
|
- a simple string, which may contain spaces, never a path or a
|
||||||
|
link
|
||||||
|
- it is retrieved by reading the "driver"-link and using only the
|
||||||
|
last element of the target path
|
||||||
|
- devices which do not have "driver"-link, just do not have a
|
||||||
|
driver; copying the driver value in a child device context, is a
|
||||||
|
bug in the application
|
||||||
|
|
||||||
|
o attributes
|
||||||
|
- the files in the device directory or files below a subdirectories
|
||||||
|
of the same device directory
|
||||||
|
- accessing attributes reached by a symlink pointing to another device,
|
||||||
|
like the "device"-link, is a bug in the application
|
||||||
|
|
||||||
|
Everything else is just a kernel driver-core implementation detail,
|
||||||
|
that should not be assumed to be stable across kernel releases.
|
||||||
|
|
||||||
|
- Properties of parent devices never belong into a child device.
|
||||||
|
Always look at the parent devices themselves for determining device
|
||||||
|
context properties. If the device 'eth0' or 'sda' does not have a
|
||||||
|
"driver"-link, then this device does not have a driver. Its value is empty.
|
||||||
|
Never copy any property of the parent-device into a child-device. Parent
|
||||||
|
device-properties may change dynamically without any notice to the
|
||||||
|
child device.
|
||||||
|
|
||||||
|
- Hierarchy in a single device-tree
|
||||||
|
There is only one valid place in sysfs where hierarchy can be examined
|
||||||
|
and this is below: /sys/devices.
|
||||||
|
It is planned, that all device directories will end up in the tree
|
||||||
|
below this directory.
|
||||||
|
|
||||||
|
- Classification by subsystem
|
||||||
|
There are currently three places for classification of devices:
|
||||||
|
/sys/block, /sys/class and /sys/bus. It is planned that these will
|
||||||
|
not contain any device-directories themselves, but only flat lists of
|
||||||
|
symlinks pointing to the unified /sys/devices tree.
|
||||||
|
All three places have completely different rules on how to access
|
||||||
|
device information. It is planned to merge all three
|
||||||
|
classification-directories into one place at /sys/subsystem,
|
||||||
|
following the layout of the bus-directories. All buses and
|
||||||
|
classes, including the converted block-subsystem, will show up
|
||||||
|
there.
|
||||||
|
The devices belonging to a subsystem will create a symlink in the
|
||||||
|
"devices" directory at /sys/subsystem/<name>/devices.
|
||||||
|
|
||||||
|
If /sys/subsystem exists, /sys/bus, /sys/class and /sys/block can be
|
||||||
|
ignored. If it does not exist, you have always to scan all three
|
||||||
|
places, as the kernel is free to move a subsystem from one place to
|
||||||
|
the other, as long as the devices are still reachable by the same
|
||||||
|
subsystem name.
|
||||||
|
|
||||||
|
Assuming /sys/class/<subsystem> and /sys/bus/<subsystem>, or
|
||||||
|
/sys/block and /sys/class/block are not interchangeable, is a bug in
|
||||||
|
the application.
|
||||||
|
|
||||||
|
- Block
|
||||||
|
The converted block-subsystem at /sys/class/block, or
|
||||||
|
/sys/subsystem/block will contain the links for disks and partitions
|
||||||
|
at the same level, never in a hierarchy. Assuming the block-subsytem to
|
||||||
|
contain only disks and not partition-devices in the same flat list is
|
||||||
|
a bug in the application.
|
||||||
|
|
||||||
|
- "device"-link and <subsystem>:<kernel name>-links
|
||||||
|
Never depend on the "device"-link. The "device"-link is a workaround
|
||||||
|
for the old layout, where class-devices are not created in
|
||||||
|
/sys/devices/ like the bus-devices. If the link-resolving of a
|
||||||
|
device-directory does not end in /sys/devices/, you can use the
|
||||||
|
"device"-link to find the parent devices in /sys/devices/. That is the
|
||||||
|
single valid use of the "device"-link, it must never appear in any
|
||||||
|
path as an element. Assuming the existence of the "device"-link for
|
||||||
|
a device in /sys/devices/ is a bug in the application.
|
||||||
|
Accessing /sys/class/net/eth0/device is a bug in the application.
|
||||||
|
|
||||||
|
Never depend on the class-specific links back to the /sys/class
|
||||||
|
directory. These links are also a workaround for the design mistake
|
||||||
|
that class-devices are not created in /sys/devices. If a device
|
||||||
|
directory does not contain directories for child devices, these links
|
||||||
|
may be used to find the child devices in /sys/class. That is the single
|
||||||
|
valid use of these links, they must never appear in any path as an
|
||||||
|
element. Assuming the existence of these links for devices which are
|
||||||
|
real child device directories in the /sys/devices tree, is a bug in
|
||||||
|
the application.
|
||||||
|
|
||||||
|
It is planned to remove all these links when when all class-device
|
||||||
|
directories live in /sys/devices.
|
||||||
|
|
||||||
|
- Position of devices along device chain can change.
|
||||||
|
Never depend on a specific parent device position in the devpath,
|
||||||
|
or the chain of parent devices. The kernel is free to insert devices into
|
||||||
|
the chain. You must always request the parent device you are looking for
|
||||||
|
by its subsystem value. You need to walk up the chain until you find
|
||||||
|
the device that matches the expected subsystem. Depending on a specific
|
||||||
|
position of a parent device, or exposing relative paths, using "../" to
|
||||||
|
access the chain of parents, is a bug in the application.
|
||||||
|
|
||||||
@@ -60,6 +60,9 @@ struct locomo {
|
|||||||
unsigned int irq;
|
unsigned int irq;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
void *saved_state;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct locomo_dev_info {
|
struct locomo_dev_info {
|
||||||
@@ -565,7 +568,7 @@ static int locomo_suspend(struct platform_device *dev, pm_message_t state)
|
|||||||
if (!save)
|
if (!save)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
dev->dev.power.saved_state = (void *) save;
|
lchip->saved_state = save;
|
||||||
|
|
||||||
spin_lock_irqsave(&lchip->lock, flags);
|
spin_lock_irqsave(&lchip->lock, flags);
|
||||||
|
|
||||||
@@ -605,8 +608,8 @@ static int locomo_resume(struct platform_device *dev)
|
|||||||
struct locomo_save_data *save;
|
struct locomo_save_data *save;
|
||||||
unsigned long r;
|
unsigned long r;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
save = (struct locomo_save_data *) dev->dev.power.saved_state;
|
save = lchip->saved_state;
|
||||||
if (!save)
|
if (!save)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -628,6 +631,8 @@ static int locomo_resume(struct platform_device *dev)
|
|||||||
locomo_writel(0x1, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KCMD);
|
locomo_writel(0x1, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KCMD);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&lchip->lock, flags);
|
spin_unlock_irqrestore(&lchip->lock, flags);
|
||||||
|
|
||||||
|
lchip->saved_state = NULL;
|
||||||
kfree(save);
|
kfree(save);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ struct sa1111 {
|
|||||||
int irq;
|
int irq;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
void *saved_state;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -822,7 +825,7 @@ static int sa1111_suspend(struct platform_device *dev, pm_message_t state)
|
|||||||
save = kmalloc(sizeof(struct sa1111_save_data), GFP_KERNEL);
|
save = kmalloc(sizeof(struct sa1111_save_data), GFP_KERNEL);
|
||||||
if (!save)
|
if (!save)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
dev->dev.power.saved_state = save;
|
sachip->saved_state = save;
|
||||||
|
|
||||||
spin_lock_irqsave(&sachip->lock, flags);
|
spin_lock_irqsave(&sachip->lock, flags);
|
||||||
|
|
||||||
@@ -878,7 +881,7 @@ static int sa1111_resume(struct platform_device *dev)
|
|||||||
unsigned long flags, id;
|
unsigned long flags, id;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
|
||||||
save = (struct sa1111_save_data *)dev->dev.power.saved_state;
|
save = sachip->saved_state;
|
||||||
if (!save)
|
if (!save)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -923,7 +926,7 @@ static int sa1111_resume(struct platform_device *dev)
|
|||||||
|
|
||||||
spin_unlock_irqrestore(&sachip->lock, flags);
|
spin_unlock_irqrestore(&sachip->lock, flags);
|
||||||
|
|
||||||
dev->dev.power.saved_state = NULL;
|
sachip->saved_state = NULL;
|
||||||
kfree(save);
|
kfree(save);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -958,8 +961,8 @@ static int sa1111_remove(struct platform_device *pdev)
|
|||||||
platform_set_drvdata(pdev, NULL);
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
kfree(pdev->dev.power.saved_state);
|
kfree(sachip->saved_state);
|
||||||
pdev->dev.power.saved_state = NULL;
|
sachip->saved_state = NULL;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -185,28 +185,21 @@ static int __devinit neponset_probe(struct platform_device *dev)
|
|||||||
/*
|
/*
|
||||||
* LDM power management.
|
* LDM power management.
|
||||||
*/
|
*/
|
||||||
|
static unsigned int neponset_saved_state;
|
||||||
|
|
||||||
static int neponset_suspend(struct platform_device *dev, pm_message_t state)
|
static int neponset_suspend(struct platform_device *dev, pm_message_t state)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Save state.
|
* Save state.
|
||||||
*/
|
*/
|
||||||
if (!dev->dev.power.saved_state)
|
neponset_saved_state = NCR_0;
|
||||||
dev->dev.power.saved_state = kmalloc(sizeof(unsigned int), GFP_KERNEL);
|
|
||||||
if (!dev->dev.power.saved_state)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*(unsigned int *)dev->dev.power.saved_state = NCR_0;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int neponset_resume(struct platform_device *dev)
|
static int neponset_resume(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
if (dev->dev.power.saved_state) {
|
NCR_0 = neponset_saved_state;
|
||||||
NCR_0 = *(unsigned int *)dev->dev.power.saved_state;
|
|
||||||
kfree(dev->dev.power.saved_state);
|
|
||||||
dev->dev.power.saved_state = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2415,7 +2415,6 @@ static struct bin_attribute mv64xxx_hs_reg_attr = { /* Hotswap register */
|
|||||||
.attr = {
|
.attr = {
|
||||||
.name = "hs_reg",
|
.name = "hs_reg",
|
||||||
.mode = S_IRUGO | S_IWUSR,
|
.mode = S_IRUGO | S_IWUSR,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.size = VAL_LEN_MAX,
|
.size = VAL_LEN_MAX,
|
||||||
.read = mv64xxx_hs_reg_read,
|
.read = mv64xxx_hs_reg_read,
|
||||||
|
|||||||
@@ -312,7 +312,6 @@ static struct bin_attribute ipl_parameter_attr = {
|
|||||||
.attr = {
|
.attr = {
|
||||||
.name = "binary_parameter",
|
.name = "binary_parameter",
|
||||||
.mode = S_IRUGO,
|
.mode = S_IRUGO,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.size = PAGE_SIZE,
|
.size = PAGE_SIZE,
|
||||||
.read = &ipl_parameter_read,
|
.read = &ipl_parameter_read,
|
||||||
@@ -336,7 +335,6 @@ static struct bin_attribute ipl_scp_data_attr = {
|
|||||||
.attr = {
|
.attr = {
|
||||||
.name = "scp_data",
|
.name = "scp_data",
|
||||||
.mode = S_IRUGO,
|
.mode = S_IRUGO,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.size = PAGE_SIZE,
|
.size = PAGE_SIZE,
|
||||||
.read = &ipl_scp_data_read,
|
.read = &ipl_scp_data_read,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -44,6 +44,6 @@ struct class_device_attribute *to_class_dev_attr(struct attribute *_attr)
|
|||||||
|
|
||||||
extern char *make_class_name(const char *name, struct kobject *kobj);
|
extern char *make_class_name(const char *name, struct kobject *kobj);
|
||||||
|
|
||||||
extern void devres_release_all(struct device *dev);
|
extern int devres_release_all(struct device *dev);
|
||||||
|
|
||||||
extern struct kset devices_subsys;
|
extern struct kset devices_subsys;
|
||||||
|
|||||||
+19
-7
@@ -138,12 +138,24 @@ void bus_remove_file(struct bus_type * bus, struct bus_attribute * attr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct kobj_type ktype_bus = {
|
static struct kobj_type bus_ktype = {
|
||||||
.sysfs_ops = &bus_sysfs_ops,
|
.sysfs_ops = &bus_sysfs_ops,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static decl_subsys(bus, &ktype_bus, NULL);
|
static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
|
||||||
|
{
|
||||||
|
struct kobj_type *ktype = get_ktype(kobj);
|
||||||
|
|
||||||
|
if (ktype == &bus_ktype)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kset_uevent_ops bus_uevent_ops = {
|
||||||
|
.filter = bus_uevent_filter,
|
||||||
|
};
|
||||||
|
|
||||||
|
static decl_subsys(bus, &bus_ktype, &bus_uevent_ops);
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_HOTPLUG
|
#ifdef CONFIG_HOTPLUG
|
||||||
@@ -562,7 +574,6 @@ static int add_probe_files(struct bus_type *bus)
|
|||||||
|
|
||||||
bus->drivers_probe_attr.attr.name = "drivers_probe";
|
bus->drivers_probe_attr.attr.name = "drivers_probe";
|
||||||
bus->drivers_probe_attr.attr.mode = S_IWUSR;
|
bus->drivers_probe_attr.attr.mode = S_IWUSR;
|
||||||
bus->drivers_probe_attr.attr.owner = bus->owner;
|
|
||||||
bus->drivers_probe_attr.store = store_drivers_probe;
|
bus->drivers_probe_attr.store = store_drivers_probe;
|
||||||
retval = bus_create_file(bus, &bus->drivers_probe_attr);
|
retval = bus_create_file(bus, &bus->drivers_probe_attr);
|
||||||
if (retval)
|
if (retval)
|
||||||
@@ -570,7 +581,6 @@ static int add_probe_files(struct bus_type *bus)
|
|||||||
|
|
||||||
bus->drivers_autoprobe_attr.attr.name = "drivers_autoprobe";
|
bus->drivers_autoprobe_attr.attr.name = "drivers_autoprobe";
|
||||||
bus->drivers_autoprobe_attr.attr.mode = S_IWUSR | S_IRUGO;
|
bus->drivers_autoprobe_attr.attr.mode = S_IWUSR | S_IRUGO;
|
||||||
bus->drivers_autoprobe_attr.attr.owner = bus->owner;
|
|
||||||
bus->drivers_autoprobe_attr.show = show_drivers_autoprobe;
|
bus->drivers_autoprobe_attr.show = show_drivers_autoprobe;
|
||||||
bus->drivers_autoprobe_attr.store = store_drivers_autoprobe;
|
bus->drivers_autoprobe_attr.store = store_drivers_autoprobe;
|
||||||
retval = bus_create_file(bus, &bus->drivers_autoprobe_attr);
|
retval = bus_create_file(bus, &bus->drivers_autoprobe_attr);
|
||||||
@@ -610,7 +620,8 @@ int bus_add_driver(struct device_driver *drv)
|
|||||||
if (error)
|
if (error)
|
||||||
goto out_put_bus;
|
goto out_put_bus;
|
||||||
drv->kobj.kset = &bus->drivers;
|
drv->kobj.kset = &bus->drivers;
|
||||||
if ((error = kobject_register(&drv->kobj)))
|
error = kobject_register(&drv->kobj);
|
||||||
|
if (error)
|
||||||
goto out_put_bus;
|
goto out_put_bus;
|
||||||
|
|
||||||
if (drv->bus->drivers_autoprobe) {
|
if (drv->bus->drivers_autoprobe) {
|
||||||
@@ -760,7 +771,8 @@ static int bus_add_attrs(struct bus_type * bus)
|
|||||||
|
|
||||||
if (bus->bus_attrs) {
|
if (bus->bus_attrs) {
|
||||||
for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
|
for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
|
||||||
if ((error = bus_create_file(bus,&bus->bus_attrs[i])))
|
error = bus_create_file(bus,&bus->bus_attrs[i]);
|
||||||
|
if (error)
|
||||||
goto Err;
|
goto Err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-30
@@ -312,9 +312,6 @@ static void class_dev_release(struct kobject * kobj)
|
|||||||
|
|
||||||
pr_debug("device class '%s': release.\n", cd->class_id);
|
pr_debug("device class '%s': release.\n", cd->class_id);
|
||||||
|
|
||||||
kfree(cd->devt_attr);
|
|
||||||
cd->devt_attr = NULL;
|
|
||||||
|
|
||||||
if (cd->release)
|
if (cd->release)
|
||||||
cd->release(cd);
|
cd->release(cd);
|
||||||
else if (cls->release)
|
else if (cls->release)
|
||||||
@@ -547,6 +544,9 @@ static ssize_t show_dev(struct class_device *class_dev, char *buf)
|
|||||||
return print_dev_t(buf, class_dev->devt);
|
return print_dev_t(buf, class_dev->devt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct class_device_attribute class_devt_attr =
|
||||||
|
__ATTR(dev, S_IRUGO, show_dev, NULL);
|
||||||
|
|
||||||
static ssize_t store_uevent(struct class_device *class_dev,
|
static ssize_t store_uevent(struct class_device *class_dev,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
@@ -554,6 +554,9 @@ static ssize_t store_uevent(struct class_device *class_dev,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct class_device_attribute class_uevent_attr =
|
||||||
|
__ATTR(uevent, S_IWUSR, NULL, store_uevent);
|
||||||
|
|
||||||
void class_device_initialize(struct class_device *class_dev)
|
void class_device_initialize(struct class_device *class_dev)
|
||||||
{
|
{
|
||||||
kobj_set_kset_s(class_dev, class_obj_subsys);
|
kobj_set_kset_s(class_dev, class_obj_subsys);
|
||||||
@@ -603,32 +606,15 @@ int class_device_add(struct class_device *class_dev)
|
|||||||
&parent_class->subsys.kobj, "subsystem");
|
&parent_class->subsys.kobj, "subsystem");
|
||||||
if (error)
|
if (error)
|
||||||
goto out3;
|
goto out3;
|
||||||
class_dev->uevent_attr.attr.name = "uevent";
|
|
||||||
class_dev->uevent_attr.attr.mode = S_IWUSR;
|
error = class_device_create_file(class_dev, &class_uevent_attr);
|
||||||
class_dev->uevent_attr.attr.owner = parent_class->owner;
|
|
||||||
class_dev->uevent_attr.store = store_uevent;
|
|
||||||
error = class_device_create_file(class_dev, &class_dev->uevent_attr);
|
|
||||||
if (error)
|
if (error)
|
||||||
goto out3;
|
goto out3;
|
||||||
|
|
||||||
if (MAJOR(class_dev->devt)) {
|
if (MAJOR(class_dev->devt)) {
|
||||||
struct class_device_attribute *attr;
|
error = class_device_create_file(class_dev, &class_devt_attr);
|
||||||
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
|
if (error)
|
||||||
if (!attr) {
|
|
||||||
error = -ENOMEM;
|
|
||||||
goto out4;
|
goto out4;
|
||||||
}
|
|
||||||
attr->attr.name = "dev";
|
|
||||||
attr->attr.mode = S_IRUGO;
|
|
||||||
attr->attr.owner = parent_class->owner;
|
|
||||||
attr->show = show_dev;
|
|
||||||
error = class_device_create_file(class_dev, attr);
|
|
||||||
if (error) {
|
|
||||||
kfree(attr);
|
|
||||||
goto out4;
|
|
||||||
}
|
|
||||||
|
|
||||||
class_dev->devt_attr = attr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error = class_device_add_attrs(class_dev);
|
error = class_device_add_attrs(class_dev);
|
||||||
@@ -671,10 +657,10 @@ int class_device_add(struct class_device *class_dev)
|
|||||||
out6:
|
out6:
|
||||||
class_device_remove_attrs(class_dev);
|
class_device_remove_attrs(class_dev);
|
||||||
out5:
|
out5:
|
||||||
if (class_dev->devt_attr)
|
if (MAJOR(class_dev->devt))
|
||||||
class_device_remove_file(class_dev, class_dev->devt_attr);
|
class_device_remove_file(class_dev, &class_devt_attr);
|
||||||
out4:
|
out4:
|
||||||
class_device_remove_file(class_dev, &class_dev->uevent_attr);
|
class_device_remove_file(class_dev, &class_uevent_attr);
|
||||||
out3:
|
out3:
|
||||||
kobject_del(&class_dev->kobj);
|
kobject_del(&class_dev->kobj);
|
||||||
out2:
|
out2:
|
||||||
@@ -774,9 +760,9 @@ void class_device_del(struct class_device *class_dev)
|
|||||||
sysfs_remove_link(&class_dev->kobj, "device");
|
sysfs_remove_link(&class_dev->kobj, "device");
|
||||||
}
|
}
|
||||||
sysfs_remove_link(&class_dev->kobj, "subsystem");
|
sysfs_remove_link(&class_dev->kobj, "subsystem");
|
||||||
class_device_remove_file(class_dev, &class_dev->uevent_attr);
|
class_device_remove_file(class_dev, &class_uevent_attr);
|
||||||
if (class_dev->devt_attr)
|
if (MAJOR(class_dev->devt))
|
||||||
class_device_remove_file(class_dev, class_dev->devt_attr);
|
class_device_remove_file(class_dev, &class_devt_attr);
|
||||||
class_device_remove_attrs(class_dev);
|
class_device_remove_attrs(class_dev);
|
||||||
class_device_remove_groups(class_dev);
|
class_device_remove_groups(class_dev);
|
||||||
|
|
||||||
|
|||||||
+21
-37
@@ -310,6 +310,9 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct device_attribute uevent_attr =
|
||||||
|
__ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent);
|
||||||
|
|
||||||
static int device_add_attributes(struct device *dev,
|
static int device_add_attributes(struct device *dev,
|
||||||
struct device_attribute *attrs)
|
struct device_attribute *attrs)
|
||||||
{
|
{
|
||||||
@@ -423,6 +426,9 @@ static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
|
|||||||
return print_dev_t(buf, dev->devt);
|
return print_dev_t(buf, dev->devt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct device_attribute devt_attr =
|
||||||
|
__ATTR(dev, S_IRUGO, show_dev, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* devices_subsys - structure to be registered with kobject core.
|
* devices_subsys - structure to be registered with kobject core.
|
||||||
*/
|
*/
|
||||||
@@ -681,35 +687,14 @@ int device_add(struct device *dev)
|
|||||||
blocking_notifier_call_chain(&dev->bus->bus_notifier,
|
blocking_notifier_call_chain(&dev->bus->bus_notifier,
|
||||||
BUS_NOTIFY_ADD_DEVICE, dev);
|
BUS_NOTIFY_ADD_DEVICE, dev);
|
||||||
|
|
||||||
dev->uevent_attr.attr.name = "uevent";
|
error = device_create_file(dev, &uevent_attr);
|
||||||
dev->uevent_attr.attr.mode = S_IRUGO | S_IWUSR;
|
|
||||||
if (dev->driver)
|
|
||||||
dev->uevent_attr.attr.owner = dev->driver->owner;
|
|
||||||
dev->uevent_attr.store = store_uevent;
|
|
||||||
dev->uevent_attr.show = show_uevent;
|
|
||||||
error = device_create_file(dev, &dev->uevent_attr);
|
|
||||||
if (error)
|
if (error)
|
||||||
goto attrError;
|
goto attrError;
|
||||||
|
|
||||||
if (MAJOR(dev->devt)) {
|
if (MAJOR(dev->devt)) {
|
||||||
struct device_attribute *attr;
|
error = device_create_file(dev, &devt_attr);
|
||||||
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
|
if (error)
|
||||||
if (!attr) {
|
|
||||||
error = -ENOMEM;
|
|
||||||
goto ueventattrError;
|
goto ueventattrError;
|
||||||
}
|
|
||||||
attr->attr.name = "dev";
|
|
||||||
attr->attr.mode = S_IRUGO;
|
|
||||||
if (dev->driver)
|
|
||||||
attr->attr.owner = dev->driver->owner;
|
|
||||||
attr->show = show_dev;
|
|
||||||
error = device_create_file(dev, attr);
|
|
||||||
if (error) {
|
|
||||||
kfree(attr);
|
|
||||||
goto ueventattrError;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->devt_attr = attr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev->class) {
|
if (dev->class) {
|
||||||
@@ -733,11 +718,14 @@ int device_add(struct device *dev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error = device_add_attrs(dev)))
|
error = device_add_attrs(dev);
|
||||||
|
if (error)
|
||||||
goto AttrsError;
|
goto AttrsError;
|
||||||
if ((error = device_pm_add(dev)))
|
error = device_pm_add(dev);
|
||||||
|
if (error)
|
||||||
goto PMError;
|
goto PMError;
|
||||||
if ((error = bus_add_device(dev)))
|
error = bus_add_device(dev);
|
||||||
|
if (error)
|
||||||
goto BusError;
|
goto BusError;
|
||||||
kobject_uevent(&dev->kobj, KOBJ_ADD);
|
kobject_uevent(&dev->kobj, KOBJ_ADD);
|
||||||
bus_attach_device(dev);
|
bus_attach_device(dev);
|
||||||
@@ -767,10 +755,8 @@ int device_add(struct device *dev)
|
|||||||
BUS_NOTIFY_DEL_DEVICE, dev);
|
BUS_NOTIFY_DEL_DEVICE, dev);
|
||||||
device_remove_attrs(dev);
|
device_remove_attrs(dev);
|
||||||
AttrsError:
|
AttrsError:
|
||||||
if (dev->devt_attr) {
|
if (MAJOR(dev->devt))
|
||||||
device_remove_file(dev, dev->devt_attr);
|
device_remove_file(dev, &devt_attr);
|
||||||
kfree(dev->devt_attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev->class) {
|
if (dev->class) {
|
||||||
sysfs_remove_link(&dev->kobj, "subsystem");
|
sysfs_remove_link(&dev->kobj, "subsystem");
|
||||||
@@ -792,7 +778,7 @@ int device_add(struct device *dev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ueventattrError:
|
ueventattrError:
|
||||||
device_remove_file(dev, &dev->uevent_attr);
|
device_remove_file(dev, &uevent_attr);
|
||||||
attrError:
|
attrError:
|
||||||
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
|
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
|
||||||
kobject_del(&dev->kobj);
|
kobject_del(&dev->kobj);
|
||||||
@@ -869,10 +855,8 @@ void device_del(struct device * dev)
|
|||||||
|
|
||||||
if (parent)
|
if (parent)
|
||||||
klist_del(&dev->knode_parent);
|
klist_del(&dev->knode_parent);
|
||||||
if (dev->devt_attr) {
|
if (MAJOR(dev->devt))
|
||||||
device_remove_file(dev, dev->devt_attr);
|
device_remove_file(dev, &devt_attr);
|
||||||
kfree(dev->devt_attr);
|
|
||||||
}
|
|
||||||
if (dev->class) {
|
if (dev->class) {
|
||||||
sysfs_remove_link(&dev->kobj, "subsystem");
|
sysfs_remove_link(&dev->kobj, "subsystem");
|
||||||
/* If this is not a "fake" compatible device, remove the
|
/* If this is not a "fake" compatible device, remove the
|
||||||
@@ -926,7 +910,7 @@ void device_del(struct device * dev)
|
|||||||
up(&dev->class->sem);
|
up(&dev->class->sem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
device_remove_file(dev, &dev->uevent_attr);
|
device_remove_file(dev, &uevent_attr);
|
||||||
device_remove_attrs(dev);
|
device_remove_attrs(dev);
|
||||||
bus_remove_device(dev);
|
bus_remove_device(dev);
|
||||||
|
|
||||||
|
|||||||
+10
-11
@@ -281,24 +281,16 @@ int driver_attach(struct device_driver * drv)
|
|||||||
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
|
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* device_release_driver - manually detach device from driver.
|
|
||||||
* @dev: device.
|
|
||||||
*
|
|
||||||
* Manually detach device from driver.
|
|
||||||
*
|
|
||||||
* __device_release_driver() must be called with @dev->sem held.
|
* __device_release_driver() must be called with @dev->sem held.
|
||||||
* When called for a USB interface, @dev->parent->sem must be held
|
* When called for a USB interface, @dev->parent->sem must be held as well.
|
||||||
* as well.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void __device_release_driver(struct device * dev)
|
static void __device_release_driver(struct device * dev)
|
||||||
{
|
{
|
||||||
struct device_driver * drv;
|
struct device_driver * drv;
|
||||||
|
|
||||||
drv = dev->driver;
|
drv = get_driver(dev->driver);
|
||||||
if (drv) {
|
if (drv) {
|
||||||
get_driver(drv);
|
|
||||||
driver_sysfs_remove(dev);
|
driver_sysfs_remove(dev);
|
||||||
sysfs_remove_link(&dev->kobj, "driver");
|
sysfs_remove_link(&dev->kobj, "driver");
|
||||||
klist_remove(&dev->knode_driver);
|
klist_remove(&dev->knode_driver);
|
||||||
@@ -318,6 +310,13 @@ static void __device_release_driver(struct device * dev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* device_release_driver - manually detach device from driver.
|
||||||
|
* @dev: device.
|
||||||
|
*
|
||||||
|
* Manually detach device from driver.
|
||||||
|
* When called for a USB interface, @dev->parent->sem must be held.
|
||||||
|
*/
|
||||||
void device_release_driver(struct device * dev)
|
void device_release_driver(struct device * dev)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
||||||
|
#include "base.h"
|
||||||
|
|
||||||
struct devres_node {
|
struct devres_node {
|
||||||
struct list_head entry;
|
struct list_head entry;
|
||||||
dr_release_t release;
|
dr_release_t release;
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ static ssize_t firmware_loading_store(struct device *dev,
|
|||||||
static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
|
static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
firmware_data_read(struct kobject *kobj,
|
firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr,
|
||||||
char *buffer, loff_t offset, size_t count)
|
char *buffer, loff_t offset, size_t count)
|
||||||
{
|
{
|
||||||
struct device *dev = to_dev(kobj);
|
struct device *dev = to_dev(kobj);
|
||||||
@@ -240,7 +240,7 @@ fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
|
|||||||
* the driver as a firmware image.
|
* the driver as a firmware image.
|
||||||
**/
|
**/
|
||||||
static ssize_t
|
static ssize_t
|
||||||
firmware_data_write(struct kobject *kobj,
|
firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr,
|
||||||
char *buffer, loff_t offset, size_t count)
|
char *buffer, loff_t offset, size_t count)
|
||||||
{
|
{
|
||||||
struct device *dev = to_dev(kobj);
|
struct device *dev = to_dev(kobj);
|
||||||
@@ -271,7 +271,7 @@ out:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct bin_attribute firmware_attr_data_tmpl = {
|
static struct bin_attribute firmware_attr_data_tmpl = {
|
||||||
.attr = {.name = "data", .mode = 0644, .owner = THIS_MODULE},
|
.attr = {.name = "data", .mode = 0644},
|
||||||
.size = 0,
|
.size = 0,
|
||||||
.read = firmware_data_read,
|
.read = firmware_data_read,
|
||||||
.write = firmware_data_write,
|
.write = firmware_data_write,
|
||||||
|
|||||||
+12
-32
@@ -20,64 +20,44 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
|
|
||||||
LIST_HEAD(dpm_active);
|
LIST_HEAD(dpm_active);
|
||||||
LIST_HEAD(dpm_off);
|
LIST_HEAD(dpm_off);
|
||||||
LIST_HEAD(dpm_off_irq);
|
LIST_HEAD(dpm_off_irq);
|
||||||
|
|
||||||
DECLARE_MUTEX(dpm_sem);
|
DEFINE_MUTEX(dpm_mtx);
|
||||||
DECLARE_MUTEX(dpm_list_sem);
|
DEFINE_MUTEX(dpm_list_mtx);
|
||||||
|
|
||||||
int (*platform_enable_wakeup)(struct device *dev, int is_on);
|
int (*platform_enable_wakeup)(struct device *dev, int is_on);
|
||||||
|
|
||||||
|
int device_pm_add(struct device *dev)
|
||||||
/**
|
|
||||||
* device_pm_set_parent - Specify power dependency.
|
|
||||||
* @dev: Device who needs power.
|
|
||||||
* @parent: Device that supplies power.
|
|
||||||
*
|
|
||||||
* This function is used to manually describe a power-dependency
|
|
||||||
* relationship. It may be used to specify a transversal relationship
|
|
||||||
* (where the power supplier is not the physical (or electrical)
|
|
||||||
* ancestor of a specific device.
|
|
||||||
* The effect of this is that the supplier will not be powered down
|
|
||||||
* before the power dependent.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void device_pm_set_parent(struct device * dev, struct device * parent)
|
|
||||||
{
|
|
||||||
put_device(dev->power.pm_parent);
|
|
||||||
dev->power.pm_parent = get_device(parent);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(device_pm_set_parent);
|
|
||||||
|
|
||||||
int device_pm_add(struct device * dev)
|
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
pr_debug("PM: Adding info for %s:%s\n",
|
pr_debug("PM: Adding info for %s:%s\n",
|
||||||
dev->bus ? dev->bus->name : "No Bus",
|
dev->bus ? dev->bus->name : "No Bus",
|
||||||
kobject_name(&dev->kobj));
|
kobject_name(&dev->kobj));
|
||||||
down(&dpm_list_sem);
|
mutex_lock(&dpm_list_mtx);
|
||||||
list_add_tail(&dev->power.entry, &dpm_active);
|
list_add_tail(&dev->power.entry, &dpm_active);
|
||||||
device_pm_set_parent(dev, dev->parent);
|
error = dpm_sysfs_add(dev);
|
||||||
if ((error = dpm_sysfs_add(dev)))
|
if (error)
|
||||||
list_del(&dev->power.entry);
|
list_del(&dev->power.entry);
|
||||||
up(&dpm_list_sem);
|
mutex_unlock(&dpm_list_mtx);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
void device_pm_remove(struct device * dev)
|
void device_pm_remove(struct device *dev)
|
||||||
{
|
{
|
||||||
pr_debug("PM: Removing info for %s:%s\n",
|
pr_debug("PM: Removing info for %s:%s\n",
|
||||||
dev->bus ? dev->bus->name : "No Bus",
|
dev->bus ? dev->bus->name : "No Bus",
|
||||||
kobject_name(&dev->kobj));
|
kobject_name(&dev->kobj));
|
||||||
down(&dpm_list_sem);
|
mutex_lock(&dpm_list_mtx);
|
||||||
dpm_sysfs_remove(dev);
|
dpm_sysfs_remove(dev);
|
||||||
put_device(dev->power.pm_parent);
|
|
||||||
list_del_init(&dev->power.entry);
|
list_del_init(&dev->power.entry);
|
||||||
up(&dpm_list_sem);
|
mutex_unlock(&dpm_list_mtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ extern void device_shutdown(void);
|
|||||||
/*
|
/*
|
||||||
* Used to synchronize global power management operations.
|
* Used to synchronize global power management operations.
|
||||||
*/
|
*/
|
||||||
extern struct semaphore dpm_sem;
|
extern struct mutex dpm_mtx;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used to serialize changes to the dpm_* lists.
|
* Used to serialize changes to the dpm_* lists.
|
||||||
*/
|
*/
|
||||||
extern struct semaphore dpm_list_sem;
|
extern struct mutex dpm_list_mtx;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The PM lists.
|
* The PM lists.
|
||||||
|
|||||||
@@ -29,14 +29,6 @@ int resume_device(struct device * dev)
|
|||||||
|
|
||||||
down(&dev->sem);
|
down(&dev->sem);
|
||||||
|
|
||||||
if (dev->power.pm_parent
|
|
||||||
&& dev->power.pm_parent->power.power_state.event) {
|
|
||||||
dev_err(dev, "PM: resume from %d, parent %s still %d\n",
|
|
||||||
dev->power.power_state.event,
|
|
||||||
dev->power.pm_parent->bus_id,
|
|
||||||
dev->power.pm_parent->power.power_state.event);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev->bus && dev->bus->resume) {
|
if (dev->bus && dev->bus->resume) {
|
||||||
dev_dbg(dev,"resuming\n");
|
dev_dbg(dev,"resuming\n");
|
||||||
error = dev->bus->resume(dev);
|
error = dev->bus->resume(dev);
|
||||||
@@ -80,7 +72,7 @@ static int resume_device_early(struct device * dev)
|
|||||||
*/
|
*/
|
||||||
void dpm_resume(void)
|
void dpm_resume(void)
|
||||||
{
|
{
|
||||||
down(&dpm_list_sem);
|
mutex_lock(&dpm_list_mtx);
|
||||||
while(!list_empty(&dpm_off)) {
|
while(!list_empty(&dpm_off)) {
|
||||||
struct list_head * entry = dpm_off.next;
|
struct list_head * entry = dpm_off.next;
|
||||||
struct device * dev = to_device(entry);
|
struct device * dev = to_device(entry);
|
||||||
@@ -88,13 +80,12 @@ void dpm_resume(void)
|
|||||||
get_device(dev);
|
get_device(dev);
|
||||||
list_move_tail(entry, &dpm_active);
|
list_move_tail(entry, &dpm_active);
|
||||||
|
|
||||||
up(&dpm_list_sem);
|
mutex_unlock(&dpm_list_mtx);
|
||||||
if (!dev->power.prev_state.event)
|
resume_device(dev);
|
||||||
resume_device(dev);
|
mutex_lock(&dpm_list_mtx);
|
||||||
down(&dpm_list_sem);
|
|
||||||
put_device(dev);
|
put_device(dev);
|
||||||
}
|
}
|
||||||
up(&dpm_list_sem);
|
mutex_unlock(&dpm_list_mtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -108,9 +99,9 @@ void dpm_resume(void)
|
|||||||
void device_resume(void)
|
void device_resume(void)
|
||||||
{
|
{
|
||||||
might_sleep();
|
might_sleep();
|
||||||
down(&dpm_sem);
|
mutex_lock(&dpm_mtx);
|
||||||
dpm_resume();
|
dpm_resume();
|
||||||
up(&dpm_sem);
|
mutex_unlock(&dpm_mtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(device_resume);
|
EXPORT_SYMBOL_GPL(device_resume);
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ static void runtime_resume(struct device * dev)
|
|||||||
|
|
||||||
void dpm_runtime_resume(struct device * dev)
|
void dpm_runtime_resume(struct device * dev)
|
||||||
{
|
{
|
||||||
down(&dpm_sem);
|
mutex_lock(&dpm_mtx);
|
||||||
runtime_resume(dev);
|
runtime_resume(dev);
|
||||||
up(&dpm_sem);
|
mutex_unlock(&dpm_mtx);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dpm_runtime_resume);
|
EXPORT_SYMBOL(dpm_runtime_resume);
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ int dpm_runtime_suspend(struct device * dev, pm_message_t state)
|
|||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
down(&dpm_sem);
|
mutex_lock(&dpm_mtx);
|
||||||
if (dev->power.power_state.event == state.event)
|
if (dev->power.power_state.event == state.event)
|
||||||
goto Done;
|
goto Done;
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ int dpm_runtime_suspend(struct device * dev, pm_message_t state)
|
|||||||
if (!(error = suspend_device(dev, state)))
|
if (!(error = suspend_device(dev, state)))
|
||||||
dev->power.power_state = state;
|
dev->power.power_state = state;
|
||||||
Done:
|
Done:
|
||||||
up(&dpm_sem);
|
mutex_unlock(&dpm_mtx);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dpm_runtime_suspend);
|
EXPORT_SYMBOL(dpm_runtime_suspend);
|
||||||
@@ -78,8 +78,8 @@ EXPORT_SYMBOL(dpm_runtime_suspend);
|
|||||||
*/
|
*/
|
||||||
void dpm_set_power_state(struct device * dev, pm_message_t state)
|
void dpm_set_power_state(struct device * dev, pm_message_t state)
|
||||||
{
|
{
|
||||||
down(&dpm_sem);
|
mutex_lock(&dpm_mtx);
|
||||||
dev->power.power_state = state;
|
dev->power.power_state = state;
|
||||||
up(&dpm_sem);
|
mutex_unlock(&dpm_mtx);
|
||||||
}
|
}
|
||||||
#endif /* 0 */
|
#endif /* 0 */
|
||||||
|
|||||||
@@ -40,6 +40,14 @@ static inline char *suspend_verb(u32 event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
suspend_device_dbg(struct device *dev, pm_message_t state, char *info)
|
||||||
|
{
|
||||||
|
dev_dbg(dev, "%s%s%s\n", info, suspend_verb(state.event),
|
||||||
|
((state.event == PM_EVENT_SUSPEND) && device_may_wakeup(dev)) ?
|
||||||
|
", may wakeup" : "");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* suspend_device - Save state of one device.
|
* suspend_device - Save state of one device.
|
||||||
* @dev: Device.
|
* @dev: Device.
|
||||||
@@ -55,49 +63,21 @@ int suspend_device(struct device * dev, pm_message_t state)
|
|||||||
dev_dbg(dev, "PM: suspend %d-->%d\n",
|
dev_dbg(dev, "PM: suspend %d-->%d\n",
|
||||||
dev->power.power_state.event, state.event);
|
dev->power.power_state.event, state.event);
|
||||||
}
|
}
|
||||||
if (dev->power.pm_parent
|
|
||||||
&& dev->power.pm_parent->power.power_state.event) {
|
|
||||||
dev_err(dev,
|
|
||||||
"PM: suspend %d->%d, parent %s already %d\n",
|
|
||||||
dev->power.power_state.event, state.event,
|
|
||||||
dev->power.pm_parent->bus_id,
|
|
||||||
dev->power.pm_parent->power.power_state.event);
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->power.prev_state = dev->power.power_state;
|
if (dev->class && dev->class->suspend) {
|
||||||
|
suspend_device_dbg(dev, state, "class ");
|
||||||
if (dev->class && dev->class->suspend && !dev->power.power_state.event) {
|
|
||||||
dev_dbg(dev, "class %s%s\n",
|
|
||||||
suspend_verb(state.event),
|
|
||||||
((state.event == PM_EVENT_SUSPEND)
|
|
||||||
&& device_may_wakeup(dev))
|
|
||||||
? ", may wakeup"
|
|
||||||
: ""
|
|
||||||
);
|
|
||||||
error = dev->class->suspend(dev, state);
|
error = dev->class->suspend(dev, state);
|
||||||
suspend_report_result(dev->class->suspend, error);
|
suspend_report_result(dev->class->suspend, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!error && dev->type && dev->type->suspend && !dev->power.power_state.event) {
|
if (!error && dev->type && dev->type->suspend) {
|
||||||
dev_dbg(dev, "%s%s\n",
|
suspend_device_dbg(dev, state, "type ");
|
||||||
suspend_verb(state.event),
|
|
||||||
((state.event == PM_EVENT_SUSPEND)
|
|
||||||
&& device_may_wakeup(dev))
|
|
||||||
? ", may wakeup"
|
|
||||||
: ""
|
|
||||||
);
|
|
||||||
error = dev->type->suspend(dev, state);
|
error = dev->type->suspend(dev, state);
|
||||||
suspend_report_result(dev->type->suspend, error);
|
suspend_report_result(dev->type->suspend, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
|
if (!error && dev->bus && dev->bus->suspend) {
|
||||||
dev_dbg(dev, "%s%s\n",
|
suspend_device_dbg(dev, state, "");
|
||||||
suspend_verb(state.event),
|
|
||||||
((state.event == PM_EVENT_SUSPEND)
|
|
||||||
&& device_may_wakeup(dev))
|
|
||||||
? ", may wakeup"
|
|
||||||
: ""
|
|
||||||
);
|
|
||||||
error = dev->bus->suspend(dev, state);
|
error = dev->bus->suspend(dev, state);
|
||||||
suspend_report_result(dev->bus->suspend, error);
|
suspend_report_result(dev->bus->suspend, error);
|
||||||
}
|
}
|
||||||
@@ -108,21 +88,15 @@ int suspend_device(struct device * dev, pm_message_t state)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* This is called with interrupts off, only a single CPU
|
* This is called with interrupts off, only a single CPU
|
||||||
* running. We can't do down() on a semaphore (and we don't
|
* running. We can't acquire a mutex or semaphore (and we don't
|
||||||
* need the protection)
|
* need the protection)
|
||||||
*/
|
*/
|
||||||
static int suspend_device_late(struct device *dev, pm_message_t state)
|
static int suspend_device_late(struct device *dev, pm_message_t state)
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
if (dev->bus && dev->bus->suspend_late && !dev->power.power_state.event) {
|
if (dev->bus && dev->bus->suspend_late) {
|
||||||
dev_dbg(dev, "LATE %s%s\n",
|
suspend_device_dbg(dev, state, "LATE ");
|
||||||
suspend_verb(state.event),
|
|
||||||
((state.event == PM_EVENT_SUSPEND)
|
|
||||||
&& device_may_wakeup(dev))
|
|
||||||
? ", may wakeup"
|
|
||||||
: ""
|
|
||||||
);
|
|
||||||
error = dev->bus->suspend_late(dev, state);
|
error = dev->bus->suspend_late(dev, state);
|
||||||
suspend_report_result(dev->bus->suspend_late, error);
|
suspend_report_result(dev->bus->suspend_late, error);
|
||||||
}
|
}
|
||||||
@@ -153,18 +127,18 @@ int device_suspend(pm_message_t state)
|
|||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
might_sleep();
|
might_sleep();
|
||||||
down(&dpm_sem);
|
mutex_lock(&dpm_mtx);
|
||||||
down(&dpm_list_sem);
|
mutex_lock(&dpm_list_mtx);
|
||||||
while (!list_empty(&dpm_active) && error == 0) {
|
while (!list_empty(&dpm_active) && error == 0) {
|
||||||
struct list_head * entry = dpm_active.prev;
|
struct list_head * entry = dpm_active.prev;
|
||||||
struct device * dev = to_device(entry);
|
struct device * dev = to_device(entry);
|
||||||
|
|
||||||
get_device(dev);
|
get_device(dev);
|
||||||
up(&dpm_list_sem);
|
mutex_unlock(&dpm_list_mtx);
|
||||||
|
|
||||||
error = suspend_device(dev, state);
|
error = suspend_device(dev, state);
|
||||||
|
|
||||||
down(&dpm_list_sem);
|
mutex_lock(&dpm_list_mtx);
|
||||||
|
|
||||||
/* Check if the device got removed */
|
/* Check if the device got removed */
|
||||||
if (!list_empty(&dev->power.entry)) {
|
if (!list_empty(&dev->power.entry)) {
|
||||||
@@ -179,11 +153,11 @@ int device_suspend(pm_message_t state)
|
|||||||
error == -EAGAIN ? " (please convert to suspend_late)" : "");
|
error == -EAGAIN ? " (please convert to suspend_late)" : "");
|
||||||
put_device(dev);
|
put_device(dev);
|
||||||
}
|
}
|
||||||
up(&dpm_list_sem);
|
mutex_unlock(&dpm_list_mtx);
|
||||||
if (error)
|
if (error)
|
||||||
dpm_resume();
|
dpm_resume();
|
||||||
|
|
||||||
up(&dpm_sem);
|
mutex_unlock(&dpm_mtx);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user